smi interface work in progress

bug_fixes_integration_tx
meexmachina 2021-07-18 15:42:03 +03:00
rodzic 8861797758
commit c7224f421a
4 zmienionych plików z 439 dodań i 4 usunięć

Wyświetl plik

@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.15)
project(cariboulite)
set(CMAKE_BUILD_TYPE Release)
#Bring the headers, such as Student.h into the project
@ -8,13 +9,13 @@ include_directories(${SUPER_DIR})
#However, the file(GLOB...) allows for wildcard additions:
set(SOURCES_LIB caribou_smi.c)
set(SOURCES ${SOURCES_LIB} test_caribou_smi.c)
set(EXTERN_LIBS ${SUPER_DIR}/io_utils/build/libio_utils.a)
#Generate the static library from the sources
add_library(caribou_smi STATIC ${SOURCES_LIB})
target_include_directories(caribou_smi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_executable(test_caribou_smi test_caribou_smi.c)
target_link_libraries(test_caribou_smi caribou_smi rt)
add_executable(test_caribou_smi ${SOURCES})
target_include_directories(test_caribou_smi rt pthread ${EXTERN_LIBS} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
#Set the location for library installation -- i.e., /usr/lib in this case
# not really necessary in this example. Use "sudo make install" to apply

Wyświetl plik

@ -1,9 +1,105 @@
#include <stdio.h>
#include "caribou_smi.h"
//=====================================================================
void caribou_smi_map_devices(caribou_smi_st* dev)
{
// map_periph(&dev->gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
map_periph(&dev->dma_regs, (void *)DMA_BASE, PAGE_SIZE);
map_periph(&dev->clk_regs, (void *)CLK_BASE, PAGE_SIZE);
map_periph(&dev->smi_regs, (void *)SMI_BASE, PAGE_SIZE);
}
//=====================================================================
// Free memory segments and exit
void caribou_smi_unmap_devices(caribou_smi_st* dev)
{
unmap_periph_mem(&dev->vc_mem);
unmap_periph_mem(&dev->smi_regs);
unmap_periph_mem(&dev->dma_regs);
//unmap_periph_mem(&dev->gpio_regs);
}
//=====================================================================
// Initialise SMI, given data width, time step, and setup/hold/strobe counts
// Step value is in nanoseconds: even numbers, 2 to 30
void init_smi(caribou_smi_st* dev,
int width,
int ns,
int setup,
int strobe,
int hold)
{
int divi = ns / 2;
dev->smi_cs = (SMI_CS_REG *) REG32(dev->smi_regs, SMI_CS);
dev->smi_l = (SMI_L_REG *) REG32(dev->smi_regs, SMI_L);
dev->smi_a = (SMI_A_REG *) REG32(dev->smi_regs, SMI_A);
dev->smi_d = (SMI_D_REG *) REG32(dev->smi_regs, SMI_D);
dev->smi_dmc = (SMI_DMC_REG *)REG32(dev->smi_regs, SMI_DMC);
dev->smi_dsr = (SMI_DSR_REG *)REG32(dev->smi_regs, SMI_DSR0);
dev->smi_dsw = (SMI_DSW_REG *)REG32(dev->smi_regs, SMI_DSW0);
dev->smi_dcs = (SMI_DCS_REG *)REG32(dev->smi_regs, SMI_DCS);
dev->smi_dca = (SMI_DCA_REG *)REG32(dev->smi_regs, SMI_DCA);
dev->smi_dcd = (SMI_DCD_REG *)REG32(dev->smi_regs, SMI_DCD);
dev->smi_cs->value = dev->smi_l->value = dev->smi_a->value = 0;
dev->smi_dsr->value = dev->smi_dsw->value = dev->smi_dcs->value = dev->smi_dca->value = 0;
if (*REG32(dev->clk_regs, CLK_SMI_DIV) != divi << 12)
{
*REG32(dev->clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
usleep(10);
while (*REG32(dev->clk_regs, CLK_SMI_CTL) & (1 << 7)) ;
usleep(10);
*REG32(dev->clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
usleep(10);
*REG32(dev->clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
usleep(10);
while ((*REG32(dev->clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0) ;
usleep(100);
}
if (dev->smi_cs->seterr)
{
dev->smi_cs->seterr = 1;
}
dev->smi_dsr->rsetup = dev->smi_dsw->wsetup = setup;
dev->smi_dsr->rstrobe = dev->smi_dsw->wstrobe = strobe;
dev->smi_dsr->rhold = dev->smi_dsw->whold = hold;
dev->smi_dmc->panicr = dev->smi_dmc->panicw = 8;
dev->smi_dmc->reqr = dev->smi_dmc->reqw = REQUEST_THRESH;
dev->smi_dsr->rwidth = dev->smi_dsw->wwidth = width;
}
//=====================================================================
int caribou_smi_init(caribou_smi_st* dev)
{
for (int i=0; i<ADC_NPINS; i++)
{
gpio_mode(ADC_D0_PIN+i, GPIO_IN);
}
gpio_mode(SMI_SOE_PIN, GPIO_ALT1);
init_smi(SMI_NUM_BITS, SMI_TIMING);
map_uncached_mem(&vc_mem, VC_MEM_SIZE(NSAMPLES+PRE_SAMP));
dev->smi_dmc->dmaen = 1;
dev->smi_cs->enable = 1;
dev->smi_cs->clear = 1;
rxbuff = adc_dma_start(&vc_mem, NSAMPLES);
smi_start(dev, NSAMPLES, 1);
while (dma_active(DMA_CHAN_A)) ;
adc_dma_end(dev, rxbuff, sample_data, NSAMPLES);
disp_reg_fields(smi_cs_regstrs, "CS", *REG32(dev->smi_regs, SMI_CS));
dev->smi_cs->enable = dev->smi_dcs->enable = 0;
for (int i=0; i<NSAMPLES; i++)
{
printf("%1.3f\n", val_volts(sample_data[i]));
}
dev->initialized = 1;
return 0;
}
@ -17,5 +113,149 @@ int caribou_smi_close(caribou_smi_st* dev)
}
dev->initialized = 0;
if (dev->gpio_regs.virt)
{
for (int i=0; i<ADC_NPINS; i++)
{
gpio_mode(ADC_D0_PIN+i, GPIO_IN);
}
}
if (dev->smi_regs.virt)
{
*REG32(dev->smi_regs, SMI_CS) = 0;
}
stop_dma(DMA_CHAN_A);
caribou_smi_unmap_devices(dev);
return 0;
}
//===========================================================================
// Start SMI, given number of samples, optionally pack bytes into words
void caribou_smi_start(caribou_smi_st* dev, int nsamples, int pre_samp, int packed)
{
dev->smi_l->len = nsamples + pre_samp;
dev->smi_cs->pxldat = (packed != 0);
dev->smi_cs->enable = 1;
dev->smi_cs->clear = 1;
dev->smi_cs->start = 1;
}
//===========================================================================
// Start DMA for SMI ADC, return Rx data buffer
uint32_t *adc_dma_start(caribou_smi_mem_map_st *mp, int nsamp)
{
DMA_CB *cbs=mp->virt;
uint32_t *data=(uint32_t *)(cbs+4), *pindata=data+8, *modes=data+0x10;
uint32_t *modep1=data+0x18, *modep2=modep1+1, *rxdata=data+0x20, i;
// Get current mode register values
for (i=0; i<3; i++)
modes[i] = modes[i+3] = *REG32(gpio_regs, GPIO_MODE0 + i*4);
// Get mode values with ADC pins set to SMI
for (i=ADC_D0_PIN; i<ADC_D0_PIN+ADC_NPINS; i++)
mode_word(&modes[i/10], i%10, GPIO_ALT1);
// Copy mode values into 32-bit words
*modep1 = modes[1];
*modep2 = modes[2];
*pindata = 1 << TEST_PIN;
enable_dma(DMA_CHAN_A);
// Control blocks 0 and 1: enable SMI I/P pins
cbs[0].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_WAIT_RESP;
#if USE_TEST_PIN
cbs[0].tfr_len = 4;
cbs[0].srce_ad = MEM_BUS_ADDR(mp, pindata);
cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_SET0);
cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
#else
cbs[0].tfr_len = 4;
cbs[0].srce_ad = MEM_BUS_ADDR(mp, modep1);
cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+4);
cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
#endif
cbs[1].tfr_len = 4;
cbs[1].srce_ad = MEM_BUS_ADDR(mp, modep2);
cbs[1].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+8);
cbs[1].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
// Control block 2: read data
cbs[2].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_DEST_INC;
cbs[2].tfr_len = (nsamp + PRE_SAMP) * SAMPLE_SIZE;
cbs[2].srce_ad = REG_BUS_ADDR(smi_regs, SMI_D);
cbs[2].dest_ad = MEM_BUS_ADDR(mp, rxdata);
cbs[2].next_cb = MEM_BUS_ADDR(mp, &cbs[3]);
// Control block 3: disable SMI I/P pins
cbs[3].ti = DMA_CB_SRCE_INC | DMA_CB_DEST_INC;
#if USE_TEST_PIN
cbs[3].tfr_len = 4;
cbs[3].srce_ad = MEM_BUS_ADDR(mp, pindata);
cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_CLR0);
#else
cbs[3].tfr_len = 3 * 4;
cbs[3].srce_ad = MEM_BUS_ADDR(mp, &modes[3]);
cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0);
#endif
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
return(rxdata);
}
//===========================================================================
// ADC DMA is complete, get data
int adc_dma_end(void *buff, uint16_t *data, int nsamp)
{
uint16_t *bp = (uint16_t *)buff;
int i;
for (i=0; i<nsamp+PRE_SAMP; i++)
{
if (i >= PRE_SAMP)
*data++ = bp[i] >> 4;
}
return(nsamp);
}
//===========================================================================
// Display bit values in register
void caribou_smi_disp_reg_fields(char *regstrs, char *name, uint32_t val)
{
char *p=regstrs, *q, *r=regstrs;
uint32_t nbits, v;
printf("%s %08X", name, val);
while ((q = strchr(p, ':')) != 0)
{
p = q + 1;
nbits = 0;
while (*p>='0' && *p<='9')
nbits = nbits * 10 + *p++ - '0';
v = val & ((1 << nbits) - 1);
val >>= nbits;
if (v && *r!='_')
printf(" %.*s=%X", q-r, r, v);
while (*p==',' || *p==' ')
p = r = p + 1;
}
printf("\n");
}
//===========================================================================
// Display SMI registers
// SMI register names for diagnostic print
static char *smi_regstrs[] = {
"CS","LEN","A","D","DSR0","DSW0","DSR1","DSW1",
"DSR2","DSW2","DSR3","DSW3","DMC","DCS","DCA","DCD",""
};
void caribou_smi_disp_smi(caribou_smi_st* dev)
{
volatile uint32_t *p=REG32(dev->smi_regs, SMI_CS);
int i=0;
while (smi_regstrs[i][0])
{
printf("%4s=%08X ", smi_regstrs[i++], *p++);
if (i%8==0 || smi_regstrs[i][0]==0)
printf("\n");
}
}

Wyświetl plik

@ -1,10 +1,40 @@
#ifndef __CARIBOU_SMI_H__
#define __CARIBOU_SMI_H__
#include "caribou_smi_defs.h"
typedef struct
{
int fd; // File descriptor
int h; // Memory handle
int size; // Memory size
void *bus; // Bus address
void *virt; // Virtual address
void *phys; // Physical address
} caribou_smi_mem_map_st;
#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt)+(uint32_t)(x)))
typedef struct
{
int initialized;
extern caribou_smi_mem_map_st gpio_regs, dma_regs;
caribou_smi_mem_map_st vc_mem, clk_regs, smi_regs;
volatile SMI_CS_REG *smi_cs;
volatile SMI_L_REG *smi_l;
volatile SMI_A_REG *smi_a;
volatile SMI_D_REG *smi_d;
volatile SMI_DMC_REG *smi_dmc;
volatile SMI_DSR_REG *smi_dsr;
volatile SMI_DSW_REG *smi_dsw;
volatile SMI_DCS_REG *smi_dcs;
volatile SMI_DCA_REG *smi_dca;
volatile SMI_DCD_REG *smi_dcd;
} caribou_smi_st;
int caribou_smi_init(caribou_smi_st* dev);

Wyświetl plik

@ -0,0 +1,164 @@
#ifndef __CARIBOU_SMI_DEFS_H__
#define __CARIBOU_SMI_DEFS_H__
#include <stdint.h>
// Location of peripheral registers in physical memory
//#define PHYS_REG_BASE 0x20000000 // Pi Zero or 1
//#define PHYS_REG_BASE 0x3F000000 // Pi 2 or 3
#define PHYS_REG_BASE 0xFE000000 // Pi 4
// Location of peripheral registers in bus memory
#define BUS_REG_BASE 0x7E000000
//==========================================================
// SMI
//==========================================================
// Register definitions
#define SMI_BASE (PHYS_REG_BASE + 0x600000)
#define SMI_CS 0x00 // Control & status
#define SMI_L 0x04 // Transfer length
#define SMI_A 0x08 // Address
#define SMI_D 0x0c // Data
#define SMI_DSR0 0x10 // Read settings device 0
#define SMI_DSW0 0x14 // Write settings device 0
#define SMI_DSR1 0x18 // Read settings device 1
#define SMI_DSW1 0x1c // Write settings device 1
#define SMI_DSR2 0x20 // Read settings device 2
#define SMI_DSW2 0x24 // Write settings device 2
#define SMI_DSR3 0x28 // Read settings device 3
#define SMI_DSW3 0x2c // Write settings device 3
#define SMI_DMC 0x30 // DMA control
#define SMI_DCS 0x34 // Direct control/status
#define SMI_DCA 0x38 // Direct address
#define SMI_DCD 0x3c // Direct data
#define SMI_FD 0x40 // FIFO debug
#define SMI_REGLEN (SMI_FD * 4)
// Data widths
#define SMI_8_BITS 0
#define SMI_16_BITS 1
#define SMI_18_BITS 2
#define SMI_9_BITS 3
// DMA request
#define DMA_SMI_DREQ 4
// Union of 32-bit value with register bitfields
#define REG_DEF(name, fields) typedef union {struct {volatile uint32_t fields;}; volatile uint32_t value;} name
// Control and status register
#define SMI_CS_FIELDS \
enable:1, done:1, active:1, start:1, clear:1, write:1, _x1:2,\
teen:1, intd:1, intt:1, intr:1, pvmode:1, seterr:1, pxldat:1, edreq:1,\
_x2:8, _x3:1, aferr:1, txw:1, rxr:1, txd:1, rxd:1, txe:1, rxf:1
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
// Data length register
#define SMI_L_FIELDS \
len:32
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
// Address & device number
#define SMI_A_FIELDS \
addr:6, _x1:2, dev:2
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
// Data FIFO
#define SMI_D_FIELDS \
data:32
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
// DMA control register
#define SMI_DMC_FIELDS \
reqw:6, reqr:6, panicw:6, panicr:6, dmap:1, _x1:3, dmaen:1
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
// Device settings: read (1 of 4)
#define SMI_DSR_FIELDS \
rstrobe:7, rdreq:1, rpace:7, rpaceall:1, \
rhold:6, fsetup:1, mode68:1, rsetup:6, rwidth:2
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
// Device settings: write (1 of 4)
#define SMI_DSW_FIELDS \
wstrobe:7, wdreq:1, wpace:7, wpaceall:1, \
whold:6, wswap:1, wformat:1, wsetup:6, wwidth:2
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
// Direct control register
#define SMI_DCS_FIELDS \
enable:1, start:1, done:1, write:1
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
// Direct control address & device number
#define SMI_DCA_FIELDS \
addr:6, _x1:2, dev:2
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
// Direct control data
#define SMI_DCD_FIELDS \
data:32
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
// Debug register
#define SMI_FLVL_FIELDS \
fcnt:6, _x1:2, flvl:6
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
#define CLK_SMI_CTL 0xb0
#define CLK_SMI_DIV 0xb4
//==========================================================
// DMA
//==========================================================
// DMA channels and data requests
#define DMA_CHAN_A 10
#define DMA_CHAN_B 11
#define DMA_PWM_DREQ 5
#define DMA_SPI_TX_DREQ 6
#define DMA_SPI_RX_DREQ 7
#define DMA_BASE (PHYS_REG_BASE + 0x007000)
// DMA register addresses offset by 0x100 * chan_num
#define DMA_CS 0x00
#define DMA_CONBLK_AD 0x04
#define DMA_TI 0x08
#define DMA_SRCE_AD 0x0c
#define DMA_DEST_AD 0x10
#define DMA_TXFR_LEN 0x14
#define DMA_STRIDE 0x18
#define DMA_NEXTCONBK 0x1c
#define DMA_DEBUG 0x20
#define DMA_REG(ch, r) ((r)==DMA_ENABLE ? DMA_ENABLE : (ch)*0x100+(r))
#define DMA_ENABLE 0xff0
// DMA register values
#define DMA_WAIT_RESP (1 << 3)
#define DMA_CB_DEST_INC (1 << 4)
#define DMA_DEST_DREQ (1 << 6)
#define DMA_CB_SRCE_INC (1 << 8)
#define DMA_SRCE_DREQ (1 << 10)
#define DMA_PRIORITY(n) ((n) << 16)
// Size of memory page
#define PAGE_SIZE 0x1000
// Round up to nearest page
#define PAGE_ROUNDUP(n) ((n)%PAGE_SIZE==0 ? (n) : ((n)+PAGE_SIZE)&~(PAGE_SIZE-1))
//==========================================================
// CLOCK
//==========================================================
// Clock registers and values
#define CLK_BASE (PHYS_REG_BASE + 0x101000)
#define CLK_PWM_CTL 0xa0
#define CLK_PWM_DIV 0xa4
#define CLK_PASSWD 0x5a000000
#define PWM_CLOCK_ID 0xa
#endif // __CARIBOU_SMI_DEFS_H__