WIP refactoring
This commit is contained in:
parent
7056224c4f
commit
4eea88b4f1
|
@ -13,6 +13,7 @@ add_executable(picoprobe
|
|||
src/probe.c
|
||||
src/cdc_uart.c
|
||||
src/cdc_sump.c
|
||||
src/cdc_sump_pico.c
|
||||
src/get_serial.c
|
||||
)
|
||||
|
||||
|
|
681
src/cdc_sump.c
681
src/cdc_sump.c
|
@ -40,113 +40,13 @@
|
|||
#include "picoprobe_config.h"
|
||||
#include "cdc_sump.h"
|
||||
|
||||
#if false
|
||||
#define sump_irq_debug(format,args...) picoprobe_debug(format, ## args)
|
||||
#else
|
||||
#define sump_irq_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define CDC_INTF 1
|
||||
|
||||
#define SAMPLING_DIVIDER 4 // minimal sysclk sampling divider
|
||||
|
||||
#define SAMPLING_GPIO_FIRST 6
|
||||
#define SAMPLING_GPIO_LAST 21
|
||||
#define SAMPLING_BITS (SAMPLING_GPIO_LAST-SAMPLING_GPIO_FIRST+1)
|
||||
#define SAMPLING_BYTES ((SAMPLING_BITS+7)/8)
|
||||
#define SAMPLING_GPIO_MASK (((1 << SAMPLING_BITS) - 1) << SAMPLING_GPIO_FIRST)
|
||||
|
||||
#define SAMPLING_GPIO_TEST 22
|
||||
|
||||
#if SAMPLING_BITS != 8 && SAMPLING_BITS != 16
|
||||
#error "Correct sampling width (8 or 16 bits)"
|
||||
#endif
|
||||
|
||||
#define SAMPLING_PIO pio1
|
||||
#define SAMPLING_PIO_SM 0u
|
||||
|
||||
#define SAMPLING_DMA_IRQ DMA_IRQ_1
|
||||
#define sump_dma_set_irq_channel_mask_enabled dma_set_irq1_channel_mask_enabled
|
||||
#define sump_dma_ints (dma_hw->ints1)
|
||||
|
||||
#define SUMP_SAMPLE_MASK ((1<<SAMPLING_BITS)-1)
|
||||
#define SUMP_BYTE0_OR ((~SUMP_SAMPLE_MASK) & 0xff)
|
||||
#define SUMP_BYTE1_OR ((~SUMP_SAMPLE_MASK >> 8) & 0xff)
|
||||
|
||||
#define SUMP_DMA_CH_FIRST 0
|
||||
#define SUMP_DMA_CH_LAST 7
|
||||
#define SUMP_DMA_CHANNELS (SUMP_DMA_CH_LAST-SUMP_DMA_CH_FIRST+1)
|
||||
#define SUMP_DMA_MASK (((1<<SUMP_DMA_CHANNELS)-1) << SUMP_DMA_CH_FIRST)
|
||||
|
||||
#if PICO_NO_FLASH
|
||||
#define SUMP_MEMORY_SIZE 102400 // 100kB
|
||||
#else
|
||||
#define SUMP_MEMORY_SIZE 204800 // 200kB
|
||||
#endif
|
||||
#define SUMP_MAX_CHUNK_SIZE 4096
|
||||
|
||||
#if (SUMP_MEMORY_SIZE % SUMP_MAX_CHUNK_SIZE) != 0
|
||||
#error "Invalid maximal chunk size!"
|
||||
#endif
|
||||
|
||||
#if (SUMP_MEMORY_SIZE / SUMP_MAX_CHUNK_SIZE) < SUMP_DMA_CHANNELS
|
||||
#error "DMA buffer and DMA channels out of sync!"
|
||||
#endif
|
||||
|
||||
#define SUMP_STATE_CONFIG 0
|
||||
#define SUMP_STATE_INIT 1
|
||||
#define SUMP_STATE_TRIGGER 2
|
||||
#define SUMP_STATE_SAMPLING 3
|
||||
#define SUMP_STATE_DUMP 4
|
||||
#define SUMP_STATE_ERROR 5
|
||||
|
||||
#define ONE_MHZ 1000000u
|
||||
|
||||
struct _trigger {
|
||||
uint32_t mask;
|
||||
uint32_t value;
|
||||
uint16_t delay;
|
||||
uint8_t channel;
|
||||
uint8_t level;
|
||||
bool serial;
|
||||
bool start;
|
||||
};
|
||||
|
||||
static struct _sump {
|
||||
|
||||
/* internal states */
|
||||
bool cdc_connected;
|
||||
uint8_t cmd[5]; // command
|
||||
uint8_t cmd_pos; // command buffer position
|
||||
uint8_t state; // SUMP_STATE_*
|
||||
uint8_t width; // in bytes, 1 = 8 bits, 2 = 16 bits
|
||||
uint8_t trigger_index;
|
||||
uint32_t pio_prog_offset;
|
||||
uint32_t read_start;
|
||||
uint64_t timestamp_start;
|
||||
|
||||
/* protocol config */
|
||||
uint32_t divider; // clock divider
|
||||
uint32_t read_count;
|
||||
uint32_t delay_count;
|
||||
uint32_t flags;
|
||||
struct _trigger trigger[4];
|
||||
|
||||
/* DMA buffer */
|
||||
uint32_t chunk_size; // in bytes
|
||||
uint32_t dma_start;
|
||||
uint32_t dma_count;
|
||||
uint32_t dma_curr_idx; // current DMA channel (index)
|
||||
uint32_t dma_pos;
|
||||
uint32_t next_count;
|
||||
uint8_t buffer[SUMP_MEMORY_SIZE];
|
||||
|
||||
} sump;
|
||||
|
||||
#define AS_16P(a) (*(uint16_t *)(a))
|
||||
|
||||
static void
|
||||
picoprobe_debug_hexa(uint8_t *buf, uint32_t len)
|
||||
struct _sump sump;
|
||||
|
||||
/* utility functions ======================================================= */
|
||||
|
||||
static void picoprobe_debug_hexa(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
uint32_t l;
|
||||
for (l = 0; len > 0; len--, l++) {
|
||||
|
@ -156,8 +56,7 @@ picoprobe_debug_hexa(uint8_t *buf, uint32_t len)
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
sump_add_metas(uint8_t * buf, uint8_t tag, const char *str)
|
||||
static uint8_t *sump_add_metas(uint8_t * buf, uint8_t tag, const char *str)
|
||||
{
|
||||
*buf++ = tag;
|
||||
while (*str)
|
||||
|
@ -166,21 +65,19 @@ sump_add_metas(uint8_t * buf, uint8_t tag, const char *str)
|
|||
return buf;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
sump_add_meta1(uint8_t * buf, uint8_t tag, uint8_t val)
|
||||
static uint8_t *sump_add_meta1(uint8_t * buf, uint8_t tag, uint8_t val)
|
||||
{
|
||||
buf[0] = tag;
|
||||
buf[1] = val;
|
||||
return buf + 2;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
sump_add_meta4(uint8_t * buf, uint8_t tag, uint32_t val)
|
||||
static uint8_t *sump_add_meta4(uint8_t * buf, uint8_t tag, uint32_t val)
|
||||
{
|
||||
buf[0] = tag;
|
||||
// this is a bit weird, but libsigrok decodes Big-Endian words here
|
||||
// the commands use Little-Endian
|
||||
#if false
|
||||
#if 0
|
||||
buf[1] = val;
|
||||
buf[2] = val >> 8;
|
||||
buf[3] = val >> 16;
|
||||
|
@ -194,297 +91,7 @@ sump_add_meta4(uint8_t * buf, uint8_t tag, uint32_t val)
|
|||
return buf + 5;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_meta(void)
|
||||
{
|
||||
char cpu[32];
|
||||
uint8_t buf[128], *ptr = buf, *wptr = buf;
|
||||
uint32_t sysclk;
|
||||
|
||||
sysclk = clock_get_hz(clk_sys) / SAMPLING_DIVIDER;
|
||||
sprintf(cpu, "RP2040 %uMhz", sysclk / ONE_MHZ);
|
||||
ptr = sump_add_metas(ptr, SUMP_META_NAME, "Picoprobe Logic Analyzer v1");
|
||||
ptr = sump_add_metas(ptr, SUMP_META_FPGA_VERSION, "No FPGA :-( PIO+DMA!");
|
||||
ptr = sump_add_metas(ptr, SUMP_META_CPU_VERSION, cpu);
|
||||
ptr = sump_add_meta4(ptr, SUMP_META_SAMPLE_RATE, sysclk);
|
||||
ptr = sump_add_meta4(ptr, SUMP_META_SAMPLE_RAM, SUMP_MEMORY_SIZE);
|
||||
ptr = sump_add_meta1(ptr, SUMP_META_PROBES_B, SAMPLING_BITS);
|
||||
ptr = sump_add_meta1(ptr, SUMP_META_PROTOCOL_B, 2);
|
||||
*ptr++ = SUMP_META_END;
|
||||
while (wptr != ptr)
|
||||
wptr += tud_cdc_n_write(CDC_INTF, wptr, ptr - wptr);
|
||||
tud_cdc_n_write_flush(CDC_INTF);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_id(void)
|
||||
{
|
||||
tud_cdc_n_write_str(CDC_INTF, "1ALS");
|
||||
tud_cdc_n_write_flush(CDC_INTF);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
sump_calc_sysclk_divider()
|
||||
{
|
||||
uint32_t divider = sump.divider, v;
|
||||
const uint32_t common_divisor = 4;
|
||||
|
||||
if (divider > 65535)
|
||||
divider = 65535;
|
||||
// return the fractional part in lowest byte (8 bits)
|
||||
if (sump.flags & SUMP_FLAG1_DDR) {
|
||||
// 125Mhz support
|
||||
divider *= 128 / common_divisor;
|
||||
} else {
|
||||
divider *= 256 / common_divisor;
|
||||
}
|
||||
v = clock_get_hz(clk_sys);
|
||||
assert((v % ONE_MHZ) == 0);
|
||||
// conversion from 100Mhz to sysclk
|
||||
v = ((v / ONE_MHZ) * divider) / ((100 / common_divisor) * SAMPLING_DIVIDER);
|
||||
v *= sump.width;
|
||||
if (v > 65535 * 256)
|
||||
v = 65535 * 256;
|
||||
else if (v <= 255)
|
||||
v = 256;
|
||||
picoprobe_debug("%s(): %u %u -> %u (%.4f)\n", __func__,
|
||||
clock_get_hz(clk_sys), sump.divider, v, (float)v / 256.0);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_pio_program(void)
|
||||
{
|
||||
uint16_t prog[] = {
|
||||
pio_encode_in(pio_pins, 8),
|
||||
pio_encode_in(pio_pins, 16)
|
||||
};
|
||||
struct pio_program program = {
|
||||
.instructions = prog,
|
||||
.length = count_of(prog),
|
||||
.origin = -1
|
||||
};
|
||||
picoprobe_debug("%s(): 0x%04x 0x%04x len=%u\n", __func__, prog[0], prog[1], program.length);
|
||||
sump.pio_prog_offset = pio_add_program(SAMPLING_PIO, &program);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_pio_init(void)
|
||||
{
|
||||
pio_sm_config c;
|
||||
uint off, gpio = SAMPLING_GPIO_FIRST, divider;
|
||||
|
||||
#if SAMPLING_BITS > 8
|
||||
if (sump.width == 1 && (sump.flags & SUMP_FLAG1_GR0_DISABLE) != 0)
|
||||
gpio += 8;
|
||||
#endif
|
||||
// loop the IN instruction forewer (8-bit and 16-bit version)
|
||||
c = pio_get_default_sm_config();
|
||||
sm_config_set_in_pins(&c, gpio);
|
||||
off = sump.pio_prog_offset + (sump.width - 1);
|
||||
sm_config_set_wrap(&c, off, off);
|
||||
divider = sump_calc_sysclk_divider();
|
||||
sm_config_set_clkdiv_int_frac(&c, divider >> 8, divider & 0xff);
|
||||
sm_config_set_in_shift(&c, true, true, 32);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
pio_sm_init(SAMPLING_PIO, SAMPLING_PIO_SM, off, &c);
|
||||
picoprobe_debug("%s(): pc=0x%02x [0x%02x], gpio=%u\n", __func__,
|
||||
off, sump.pio_prog_offset, gpio);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
sump_pwm_slice_init(uint gpio, uint clock, bool swap_levels)
|
||||
{
|
||||
uint32_t clksys = clock_get_hz(clk_sys), clkdiv, slice, tmp;
|
||||
uint16_t top = 5, level_a = 1, level_b = 4;
|
||||
|
||||
// correction for low speed PWM
|
||||
while ((clksys / clock / top) & ~0xff) {
|
||||
top *= 1000;
|
||||
level_a *= 1000;
|
||||
level_b *= 1000;
|
||||
}
|
||||
clkdiv = clksys / clock / top;
|
||||
// pwm setup
|
||||
slice = pwm_gpio_to_slice_num(gpio);
|
||||
gpio_set_function(gpio, GPIO_FUNC_PWM);
|
||||
gpio_set_function(gpio + 1, GPIO_FUNC_PWM);
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_wrap(&c, top - 1);
|
||||
pwm_config_set_clkdiv_int(&c, clkdiv);
|
||||
pwm_init(slice, &c, false);
|
||||
if (swap_levels) {
|
||||
uint16_t tmp = level_a;
|
||||
level_a = level_b;
|
||||
level_b = tmp;
|
||||
}
|
||||
pwm_set_both_levels(slice, level_a, level_b);
|
||||
picoprobe_debug("%s(): gpio=%u clkdiv=%u top=%u level=%u/%u freq=%.4fMhz (req %.4fMhz)\n",
|
||||
__func__, gpio, clkdiv, top, level_a, level_b,
|
||||
(float)clksys / (float)clkdiv / (float)top / 1000000.0,
|
||||
(float)clock / 1000000.0);
|
||||
return 1u << slice;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
sump_calib_init(void)
|
||||
{
|
||||
uint32_t clksys = clock_get_hz(clk_sys), clkdiv, slice;
|
||||
const uint32_t clock = 5 * ONE_MHZ;
|
||||
const uint16_t top = 10, level_a = 5;
|
||||
|
||||
// set 5Mhz PWM on test pin
|
||||
|
||||
// should not go beyond 255!
|
||||
clkdiv = clksys / clock / top;
|
||||
|
||||
// pwm setup
|
||||
slice = pwm_gpio_to_slice_num(SAMPLING_GPIO_TEST);
|
||||
gpio_set_function(SAMPLING_GPIO_TEST, GPIO_FUNC_PWM);
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_wrap(&c, top - 1);
|
||||
pwm_config_set_clkdiv_int(&c, clkdiv);
|
||||
pwm_init(slice, &c, false);
|
||||
pwm_set_both_levels(slice, level_a, level_a);
|
||||
picoprobe_debug("%s(): gpio=%u clkdiv=%u top=%u level=%u/%u freq=%.4fMhz (req %.4fMhz)\n",
|
||||
__func__, SAMPLING_GPIO_TEST, clkdiv, top, level_a, level_a,
|
||||
(float)clksys / (float)clkdiv / (float)top / 1000000.0,
|
||||
(float)clock / 1000000.0);
|
||||
return 1u << slice;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
sump_test_init(void)
|
||||
{
|
||||
// Initialize test PWMs
|
||||
const uint32_t gpio = SAMPLING_GPIO_FIRST;
|
||||
uint32_t mask;
|
||||
// 10Mhz PWM
|
||||
mask = sump_pwm_slice_init(gpio, 10000000, false);
|
||||
// 1Mhz PWM
|
||||
mask |= sump_pwm_slice_init(gpio + 2, 1000000, false);
|
||||
// 1kHz PWM
|
||||
mask |= sump_pwm_slice_init(gpio + 4, 1000, false);
|
||||
#if SAMPLING_BITS > 8
|
||||
// 1kHz PWM (second byte)
|
||||
mask |= sump_pwm_slice_init(gpio + 8, 1000, true);
|
||||
#endif
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_test_done(void)
|
||||
{
|
||||
const uint32_t gpio = SAMPLING_GPIO_FIRST;
|
||||
uint32_t i;
|
||||
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio), false);
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 2), false);
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 4), false);
|
||||
#if SAMPLING_BITS > 8
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 8), false);
|
||||
#endif
|
||||
for (i = SAMPLING_GPIO_FIRST; i <= SAMPLING_GPIO_LAST; i++)
|
||||
gpio_set_function(i, GPIO_FUNC_NULL);
|
||||
// test pin
|
||||
pwm_set_enabled(SAMPLING_GPIO_TEST, false);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_set_chunk_size(void)
|
||||
{
|
||||
uint32_t clk_hz;
|
||||
|
||||
clk_hz = clock_get_hz(clk_sys) / (sump_calc_sysclk_divider() / 256);
|
||||
// the goal is to transfer around 125 DMA chunks per second
|
||||
// for slow sampling rates
|
||||
sump.chunk_size = 1;
|
||||
while (clk_hz > 125 && sump.chunk_size < SUMP_MAX_CHUNK_SIZE) {
|
||||
sump.chunk_size *= 2;
|
||||
clk_hz /= 2;
|
||||
}
|
||||
picoprobe_debug("%s(): 0x%04x\n", __func__, sump.chunk_size);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_dma_program(uint ch, uint32_t pos)
|
||||
{
|
||||
dma_channel_config cfg = dma_channel_get_default_config(SUMP_DMA_CH_FIRST + ch);
|
||||
channel_config_set_read_increment(&cfg, false);
|
||||
channel_config_set_write_increment(&cfg, true);
|
||||
channel_config_set_dreq(&cfg, pio_get_dreq(SAMPLING_PIO, SAMPLING_PIO_SM, false));
|
||||
channel_config_set_chain_to(&cfg, SUMP_DMA_CH_FIRST + ((ch + 1) % SUMP_DMA_CHANNELS));
|
||||
channel_config_set_transfer_data_size(&cfg, sump.width == 1 ? DMA_SIZE_8 : DMA_SIZE_16);
|
||||
dma_channel_configure(SUMP_DMA_CH_FIRST + ch, &cfg,
|
||||
sump.buffer + pos,
|
||||
&SAMPLING_PIO->rxf[SAMPLING_PIO_SM],
|
||||
sump.chunk_size / sump.width,
|
||||
false);
|
||||
picoprobe_debug("%s() %u: w=0x%08x r=0x%08x t=0x%08x -> %u\n", __func__,
|
||||
SUMP_DMA_CH_FIRST + ch,
|
||||
sump.buffer + pos,
|
||||
&SAMPLING_PIO->rxf[SAMPLING_PIO_SM],
|
||||
sump.chunk_size / sump.width,
|
||||
SUMP_DMA_CH_FIRST + ((ch + 1) % SUMP_DMA_CHANNELS));
|
||||
}
|
||||
|
||||
static void
|
||||
sump_dma_init(uint8_t state)
|
||||
{
|
||||
uint32_t i, count, dma_transfer_size, pwm_mask = 0, irq_state;
|
||||
uint8_t *dma_start;
|
||||
|
||||
sump.dma_start = 0;
|
||||
sump.dma_pos = 0;
|
||||
sump.dma_curr_idx = 0;
|
||||
|
||||
picoprobe_debug("%s(): read=0x%08x delay=0x%08x divider=%u\n", __func__,
|
||||
sump.read_count, sump.delay_count, sump.divider);
|
||||
|
||||
count = sump.read_count;
|
||||
if (count > SUMP_MEMORY_SIZE)
|
||||
count = SUMP_MEMORY_SIZE;
|
||||
sump.dma_count = count;
|
||||
if (sump.read_count <= sump.delay_count)
|
||||
sump.next_count = sump.read_count;
|
||||
else
|
||||
sump.next_count = sump.read_count - sump.delay_count;
|
||||
sump.next_count *= sump.width;
|
||||
sump.read_start = 0;
|
||||
|
||||
picoprobe_debug("%s(): buffer = 0x%08x, dma_count=0x%08x next_count=0x%08x\n", __func__,
|
||||
sump.buffer, sump.dma_count, sump.next_count);
|
||||
|
||||
sump_pio_init();
|
||||
|
||||
pwm_mask = sump_calib_init();
|
||||
if (sump.flags & SUMP_FLAG1_EXT_TEST) {
|
||||
pwm_mask |= sump_test_init();
|
||||
} else {
|
||||
sump_test_done();
|
||||
}
|
||||
|
||||
// limit chunk size for slow sampling
|
||||
sump_set_chunk_size();
|
||||
|
||||
for (i = 0; i < SUMP_DMA_CHANNELS; i++)
|
||||
sump_dma_program(i, i * sump.chunk_size);
|
||||
|
||||
// let's go
|
||||
irq_state = save_and_disable_interrupts();
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, true);
|
||||
if (pwm_mask)
|
||||
pwm_set_mask_enabled(pwm_mask);
|
||||
dma_channel_start(SUMP_DMA_CH_FIRST);
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, true);
|
||||
sump.timestamp_start = time_us_64();
|
||||
restore_interrupts(irq_state);
|
||||
|
||||
sump.state = state;
|
||||
}
|
||||
|
||||
static void *
|
||||
sump_analyze_trigger8(void *ptr, uint32_t size)
|
||||
static void *sump_analyze_trigger8(void *ptr, uint32_t size)
|
||||
{
|
||||
uint8_t *src = ptr;
|
||||
uint8_t v;
|
||||
|
@ -508,8 +115,7 @@ __next:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
sump_analyze_trigger16(void *ptr, uint32_t size)
|
||||
static void *sump_analyze_trigger16(void *ptr, uint32_t size)
|
||||
{
|
||||
uint16_t *src = ptr;
|
||||
uint16_t v;
|
||||
|
@ -533,8 +139,7 @@ __next:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
sump_analyze_trigger(void *ptr, uint32_t size)
|
||||
static void *sump_analyze_trigger(void *ptr, uint32_t size)
|
||||
{
|
||||
if (sump.width == 1)
|
||||
return sump_analyze_trigger8(ptr, size);
|
||||
|
@ -542,20 +147,83 @@ sump_analyze_trigger(void *ptr, uint32_t size)
|
|||
return sump_analyze_trigger16(ptr, size);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_dma_done(void)
|
||||
uint32_t sump_calc_sysclk_divider()
|
||||
{
|
||||
uint32_t divider = sump.divider, v;
|
||||
const uint32_t common_divisor = 4;
|
||||
|
||||
if (divider > 65535)
|
||||
divider = 65535;
|
||||
// return the fractional part in lowest byte (8 bits)
|
||||
if (sump.flags & SUMP_FLAG1_DDR) {
|
||||
// 125Mhz support
|
||||
divider *= 128 / common_divisor;
|
||||
} else {
|
||||
divider *= 256 / common_divisor;
|
||||
}
|
||||
v = sump_hw_get_sysclk();
|
||||
assert((v % ONE_MHZ) == 0);
|
||||
// conversion from 100Mhz to sysclk
|
||||
v = ((v / ONE_MHZ) * divider) / ((100 / common_divisor) * SAMPLING_DIVIDER);
|
||||
v *= sump.width;
|
||||
if (v > 65535 * 256)
|
||||
v = 65535 * 256;
|
||||
else if (v <= 255)
|
||||
v = 256;
|
||||
picoprobe_debug("%s(): %u %u -> %u (%.4f)\n", __func__,
|
||||
sump_hw_get_sysclk(), sump.divider, v, (float)v / 256.0);
|
||||
return v;
|
||||
}
|
||||
|
||||
void sump_set_chunk_size(void)
|
||||
{
|
||||
uint32_t clk_hz;
|
||||
|
||||
clk_hz = sump_hw_get_sysclk() / (sump_calc_sysclk_divider() / 256);
|
||||
// the goal is to transfer around 125 DMA chunks per second
|
||||
// for slow sampling rates
|
||||
sump.chunk_size = 1;
|
||||
while (clk_hz > 125 && sump.chunk_size < SUMP_MAX_CHUNK_SIZE) {
|
||||
sump.chunk_size *= 2;
|
||||
clk_hz /= 2;
|
||||
}
|
||||
picoprobe_debug("%s(): 0x%04x\n", __func__, sump.chunk_size);
|
||||
}
|
||||
|
||||
/* hardware handling ======================================================= */
|
||||
|
||||
void sump_dma_program(uint ch, uint32_t pos) // TODO
|
||||
{
|
||||
dma_channel_config cfg = dma_channel_get_default_config(SUMP_DMA_CH_FIRST + ch);
|
||||
channel_config_set_read_increment(&cfg, false);
|
||||
channel_config_set_write_increment(&cfg, true);
|
||||
channel_config_set_dreq(&cfg, pio_get_dreq(SAMPLING_PIO, SAMPLING_PIO_SM, false));
|
||||
channel_config_set_chain_to(&cfg, SUMP_DMA_CH_FIRST + ((ch + 1) % SUMP_DMA_CHANNELS));
|
||||
channel_config_set_transfer_data_size(&cfg, sump.width == 1 ? DMA_SIZE_8 : DMA_SIZE_16);
|
||||
dma_channel_configure(SUMP_DMA_CH_FIRST + ch, &cfg,
|
||||
sump.buffer + pos,
|
||||
&SAMPLING_PIO->rxf[SAMPLING_PIO_SM],
|
||||
sump.chunk_size / sump.width,
|
||||
false);
|
||||
picoprobe_debug("%s() %u: w=0x%08x r=0x%08x t=0x%08x -> %u\n", __func__,
|
||||
SUMP_DMA_CH_FIRST + ch,
|
||||
sump.buffer + pos,
|
||||
&SAMPLING_PIO->rxf[SAMPLING_PIO_SM],
|
||||
sump.chunk_size / sump.width,
|
||||
SUMP_DMA_CH_FIRST + ((ch + 1) % SUMP_DMA_CHANNELS));
|
||||
}
|
||||
|
||||
static void sump_dma_done(void) // TODO
|
||||
{
|
||||
uint64_t us;
|
||||
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, false);
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
sump_hw_xfer_stop();
|
||||
us = time_us_64() - sump.timestamp_start;
|
||||
picoprobe_debug("%s(): sampling time = %llu.%llu\n", __func__, us / 1000000ull, us % 1000000ull);
|
||||
sump.state = SUMP_STATE_DUMP;
|
||||
}
|
||||
|
||||
static int
|
||||
sump_dma_next(uint32_t pos)
|
||||
static int sump_dma_next(uint32_t pos) // TODO: lots!
|
||||
{
|
||||
uint32_t tmp, delay_bytes;
|
||||
uint8_t *ptr;
|
||||
|
@ -589,8 +257,7 @@ sump_dma_next(uint32_t pos)
|
|||
return delay_bytes - tmp;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_dma_chain_to_self(uint ch)
|
||||
static void sump_dma_chain_to_self(uint ch)
|
||||
{
|
||||
dma_channel_config cfg;
|
||||
|
||||
|
@ -600,8 +267,7 @@ sump_dma_chain_to_self(uint ch)
|
|||
dma_channel_set_config(ch, &cfg, false);
|
||||
}
|
||||
|
||||
void __isr
|
||||
sump_dma_irq_handler(void)
|
||||
void __isr sump_dma_irq_handler(void) // TODO: lots!
|
||||
{
|
||||
uint32_t ch, mask, loop = 0;
|
||||
|
||||
|
@ -666,8 +332,69 @@ __retry:
|
|||
goto __retry;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_run(void)
|
||||
/* --- */
|
||||
|
||||
static void sump_xfer_start(uint8_t state)
|
||||
{
|
||||
uint32_t count, dma_transfer_size, pwm_mask = 0, irq_state;
|
||||
uint8_t *dma_start;
|
||||
|
||||
sump.dma_start = 0;
|
||||
sump.dma_pos = 0;
|
||||
sump.dma_curr_idx = 0;
|
||||
|
||||
picoprobe_debug("%s(): read=0x%08x delay=0x%08x divider=%u\n", __func__,
|
||||
sump.read_count, sump.delay_count, sump.divider);
|
||||
|
||||
count = sump.read_count;
|
||||
if (count > SUMP_MEMORY_SIZE)
|
||||
count = SUMP_MEMORY_SIZE;
|
||||
sump.dma_count = count;
|
||||
if (sump.read_count <= sump.delay_count)
|
||||
sump.next_count = sump.read_count;
|
||||
else
|
||||
sump.next_count = sump.read_count - sump.delay_count;
|
||||
sump.next_count *= sump.width;
|
||||
sump.read_start = 0;
|
||||
|
||||
picoprobe_debug("%s(): buffer = 0x%08x, dma_count=0x%08x next_count=0x%08x\n", __func__,
|
||||
sump.buffer, sump.dma_count, sump.next_count);
|
||||
|
||||
sump.timestamp_start = sump_hw_xfer_start(sump.width, sump.flags);
|
||||
|
||||
sump.state = state;
|
||||
}
|
||||
|
||||
/* SUMP proto command handling ============================================= */
|
||||
|
||||
static void sump_do_meta(void)
|
||||
{
|
||||
char cpu[32];
|
||||
uint8_t buf[128], *ptr = buf, *wptr = buf;
|
||||
uint32_t sysclk;
|
||||
|
||||
sysclk = sump_hw_get_sysclk() / SAMPLING_DIVIDER;
|
||||
sprintf(cpu, "RP2040 %uMhz", sysclk / ONE_MHZ);
|
||||
ptr = sump_add_metas(ptr, SUMP_META_NAME, "Picoprobe Logic Analyzer v1");
|
||||
ptr = sump_add_metas(ptr, SUMP_META_FPGA_VERSION, "No FPGA :-( PIO+DMA!");
|
||||
ptr = sump_add_metas(ptr, SUMP_META_CPU_VERSION, cpu);
|
||||
ptr = sump_add_meta4(ptr, SUMP_META_SAMPLE_RATE, sysclk);
|
||||
ptr = sump_add_meta4(ptr, SUMP_META_SAMPLE_RAM, SUMP_MEMORY_SIZE);
|
||||
ptr = sump_add_meta1(ptr, SUMP_META_PROBES_B, SAMPLING_BITS);
|
||||
ptr = sump_add_meta1(ptr, SUMP_META_PROTOCOL_B, 2);
|
||||
*ptr++ = SUMP_META_END;
|
||||
while (wptr != ptr)
|
||||
wptr += tud_cdc_n_write(CDC_INTF, wptr, ptr - wptr);
|
||||
tud_cdc_n_write_flush(CDC_INTF);
|
||||
}
|
||||
|
||||
static void sump_do_id(void)
|
||||
{
|
||||
tud_cdc_n_write_str(CDC_INTF, "1ALS");
|
||||
tud_cdc_n_write_flush(CDC_INTF);
|
||||
}
|
||||
|
||||
static void sump_do_run(void)
|
||||
{
|
||||
uint8_t state;
|
||||
uint32_t i, tmask = 0;
|
||||
|
@ -690,54 +417,36 @@ sump_do_run(void)
|
|||
state = SUMP_STATE_SAMPLING;
|
||||
}
|
||||
|
||||
sump_dma_init(state);
|
||||
sump_xfer_start(state);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_finish(void)
|
||||
static void sump_do_finish(void)
|
||||
{
|
||||
if (sump.state == SUMP_STATE_TRIGGER || sump.state == SUMP_STATE_SAMPLING) {
|
||||
sump.state = SUMP_STATE_DUMP;
|
||||
sump_dma_done();
|
||||
sump_dma_done(); // TODO
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_stop(void)
|
||||
static void sump_do_stop(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (sump.state == SUMP_STATE_INIT)
|
||||
return;
|
||||
// IRQ and PIO fast stop
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, false);
|
||||
// DMA abort
|
||||
for (i = SUMP_DMA_CH_FIRST; i <= SUMP_DMA_CH_LAST; i++)
|
||||
dma_channel_abort(i);
|
||||
// IRQ status cleanup
|
||||
sump_dma_ints = SUMP_DMA_MASK;
|
||||
// PIO cleanup
|
||||
pio_sm_clear_fifos(SAMPLING_PIO, SAMPLING_PIO_SM);
|
||||
pio_sm_restart(SAMPLING_PIO, SAMPLING_PIO_SM);
|
||||
// test
|
||||
sump_test_done();
|
||||
|
||||
sump_hw_stop();
|
||||
|
||||
// protocol state
|
||||
sump.state = SUMP_STATE_INIT;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_do_reset(void)
|
||||
static void sump_do_reset(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
sump_do_stop();
|
||||
memset(&sump.trigger, 0, sizeof(sump.trigger));
|
||||
}
|
||||
|
||||
static void
|
||||
sump_set_flags(uint32_t flags)
|
||||
static void sump_set_flags(uint32_t flags)
|
||||
{
|
||||
uint8_t width;
|
||||
|
||||
|
@ -756,8 +465,7 @@ sump_set_flags(uint32_t flags)
|
|||
sump.width = width;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_update_counts(uint32_t val)
|
||||
static void sump_update_counts(uint32_t val)
|
||||
{
|
||||
/*
|
||||
* This just sets up how many samples there should be before
|
||||
|
@ -780,24 +488,21 @@ sump_update_counts(uint32_t val)
|
|||
sump.delay_count = delay_count;
|
||||
}
|
||||
|
||||
static void
|
||||
sump_set_trigger_mask(uint trig, uint32_t val)
|
||||
static void sump_set_trigger_mask(uint trig, uint32_t val)
|
||||
{
|
||||
struct _trigger *t = &sump.trigger[trig];
|
||||
t->mask = val;
|
||||
picoprobe_debug("%s(): idx=%u val=0x%08x\n", __func__, trig, val);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_set_trigger_value(uint trig, uint32_t val)
|
||||
static void sump_set_trigger_value(uint trig, uint32_t val)
|
||||
{
|
||||
struct _trigger *t = &sump.trigger[trig];
|
||||
t->value = val;
|
||||
picoprobe_debug("%s(): idx=%u val=0x%08x\n", __func__, trig, val);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_set_trigger_config(uint trig, uint32_t val)
|
||||
static void sump_set_trigger_config(uint trig, uint32_t val)
|
||||
{
|
||||
struct _trigger *t = &sump.trigger[trig];
|
||||
t->start = (val & 0x08000000) != 0;
|
||||
|
@ -809,8 +514,9 @@ sump_set_trigger_config(uint trig, uint32_t val)
|
|||
__func__, trig, val, t->start, t->serial, t->channel, t->level, t->delay);
|
||||
}
|
||||
|
||||
static void
|
||||
sump_rx_short(uint8_t cmd)
|
||||
/* UART protocol handling ================================================== */
|
||||
|
||||
static void sump_rx_short(uint8_t cmd)
|
||||
{
|
||||
picoprobe_debug("%s(): 0x%02x\n", __func__, cmd);
|
||||
switch (cmd) {
|
||||
|
@ -839,8 +545,7 @@ sump_rx_short(uint8_t cmd)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sump_rx_long(uint8_t * cmd)
|
||||
static void sump_rx_long(uint8_t * cmd)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
|
@ -888,12 +593,11 @@ sump_rx_long(uint8_t * cmd)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
sump_rx(uint8_t *buf, uint count)
|
||||
void sump_rx(uint8_t *buf, uint count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
#if false
|
||||
#if 0
|
||||
picoprobe_debug("%s(): ", __func__);
|
||||
picoprobe_debug_hexa(buf, count);
|
||||
picoprobe_debug("\n");
|
||||
|
@ -910,8 +614,7 @@ sump_rx(uint8_t *buf, uint count)
|
|||
}
|
||||
}
|
||||
|
||||
static uint
|
||||
sump_tx_empty(uint8_t *buf, uint len)
|
||||
static uint sump_tx_empty(uint8_t *buf, uint len)
|
||||
{
|
||||
uint32_t i, count;
|
||||
uint8_t a, b;
|
||||
|
@ -968,8 +671,7 @@ sump_tx_empty(uint8_t *buf, uint len)
|
|||
return i;
|
||||
}
|
||||
|
||||
static uint
|
||||
sump_tx8(uint8_t *buf, uint len)
|
||||
static uint sump_tx8(uint8_t *buf, uint len)
|
||||
{
|
||||
uint32_t i, count;
|
||||
uint8_t *ptr;
|
||||
|
@ -1014,8 +716,7 @@ sump_tx8(uint8_t *buf, uint len)
|
|||
return i;
|
||||
}
|
||||
|
||||
static uint
|
||||
sump_tx16(uint8_t *buf, uint len)
|
||||
static uint sump_tx16(uint8_t *buf, uint len)
|
||||
{
|
||||
uint32_t i, count;
|
||||
volatile uint8_t *ptr;
|
||||
|
@ -1063,8 +764,7 @@ sump_tx16(uint8_t *buf, uint len)
|
|||
return i;
|
||||
}
|
||||
|
||||
static uint
|
||||
sump_fill_tx(uint8_t *buf, uint len)
|
||||
static uint sump_fill_tx(uint8_t *buf, uint len)
|
||||
{
|
||||
uint ret;
|
||||
|
||||
|
@ -1091,8 +791,7 @@ sump_fill_tx(uint8_t *buf, uint len)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
cdc_sump_init_connect(void)
|
||||
static void cdc_sump_init_connect(void)
|
||||
{
|
||||
uint32_t pio_off;
|
||||
|
||||
|
@ -1107,38 +806,9 @@ cdc_sump_init_connect(void)
|
|||
picoprobe_debug("%s(): memory buffer %u bytes\n", __func__, SUMP_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
cdc_sump_init(void)
|
||||
void cdc_sump_init(void)
|
||||
{
|
||||
uint i;
|
||||
|
||||
// claim DMA channels
|
||||
dma_claim_mask(SUMP_DMA_MASK);
|
||||
|
||||
// claim PIO state machine and add program
|
||||
pio_claim_sm_mask(SAMPLING_PIO, 1u << SAMPLING_PIO_SM);
|
||||
sump_pio_program();
|
||||
|
||||
// high bus priority to the DMA
|
||||
bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;
|
||||
|
||||
// GPIO init
|
||||
gpio_set_dir_in_masked(SAMPLING_GPIO_MASK);
|
||||
gpio_put_masked(SAMPLING_GPIO_MASK, 0);
|
||||
for (i = SAMPLING_GPIO_FIRST; i <= SAMPLING_GPIO_LAST; i++) {
|
||||
gpio_set_function(i, GPIO_FUNC_NULL);
|
||||
gpio_set_pulls(i, false, false);
|
||||
}
|
||||
|
||||
// test GPIO pin
|
||||
gpio_set_dir(SAMPLING_GPIO_TEST, true);
|
||||
gpio_put(SAMPLING_GPIO_TEST, true);
|
||||
gpio_set_function(SAMPLING_GPIO_TEST, GPIO_FUNC_PWM);
|
||||
|
||||
// set exclusive interrupt handler
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
irq_set_exclusive_handler(SAMPLING_DMA_IRQ, sump_dma_irq_handler);
|
||||
sump_dma_set_irq_channel_mask_enabled(SUMP_DMA_MASK, true);
|
||||
sump_hw_init();
|
||||
|
||||
cdc_sump_init_connect();
|
||||
|
||||
|
@ -1146,8 +816,7 @@ cdc_sump_init(void)
|
|||
}
|
||||
|
||||
#define MAX_UART_PKT 64
|
||||
void
|
||||
cdc_sump_task(void)
|
||||
void cdc_sump_task(void)
|
||||
{
|
||||
uint8_t buf[MAX_UART_PKT];
|
||||
|
||||
|
@ -1175,8 +844,8 @@ cdc_sump_task(void)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
cdc_sump_line_coding(cdc_line_coding_t const *line_coding)
|
||||
void cdc_sump_line_coding(cdc_line_coding_t const *line_coding);
|
||||
void cdc_sump_line_coding(cdc_line_coding_t const *line_coding)
|
||||
{
|
||||
picoprobe_info("Sump new baud rate %d\n", line_coding->bit_rate);
|
||||
}
|
||||
|
|
118
src/cdc_sump.h
118
src/cdc_sump.h
|
@ -53,10 +53,124 @@ inline static int SUMP_CMD_IS_SHORT(int cmd) {
|
|||
return !(cmd & 0x80); // crude but works
|
||||
}
|
||||
|
||||
void __isr sump_dma_irq_handler(void);
|
||||
/* **** */
|
||||
|
||||
#if 0
|
||||
#define sump_irq_debug(format,args...) picoprobe_debug(format, ## args)
|
||||
#else
|
||||
#define sump_irq_debug(format,...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define CDC_INTF 1
|
||||
|
||||
#define SAMPLING_DIVIDER 4 // minimal sysclk sampling divider
|
||||
|
||||
#define SAMPLING_GPIO_FIRST 6
|
||||
#define SAMPLING_GPIO_LAST 21
|
||||
#define SAMPLING_BITS (SAMPLING_GPIO_LAST-SAMPLING_GPIO_FIRST+1)
|
||||
#define SAMPLING_BYTES ((SAMPLING_BITS+7)/8)
|
||||
#define SAMPLING_GPIO_MASK (((1 << SAMPLING_BITS) - 1) << SAMPLING_GPIO_FIRST)
|
||||
|
||||
#define SAMPLING_GPIO_TEST 22
|
||||
|
||||
#if SAMPLING_BITS != 8 && SAMPLING_BITS != 16
|
||||
#error "Correct sampling width (8 or 16 bits)"
|
||||
#endif
|
||||
|
||||
#define SAMPLING_PIO pio1
|
||||
#define SAMPLING_PIO_SM 0u
|
||||
|
||||
#define SAMPLING_DMA_IRQ DMA_IRQ_1
|
||||
#define sump_dma_set_irq_channel_mask_enabled dma_set_irq1_channel_mask_enabled
|
||||
#define sump_dma_ints (dma_hw->ints1)
|
||||
|
||||
#define SUMP_SAMPLE_MASK ((1<<SAMPLING_BITS)-1)
|
||||
#define SUMP_BYTE0_OR ((~SUMP_SAMPLE_MASK) & 0xff)
|
||||
#define SUMP_BYTE1_OR ((~SUMP_SAMPLE_MASK >> 8) & 0xff)
|
||||
|
||||
#define SUMP_DMA_CH_FIRST 0
|
||||
#define SUMP_DMA_CH_LAST 7
|
||||
#define SUMP_DMA_CHANNELS (SUMP_DMA_CH_LAST-SUMP_DMA_CH_FIRST+1)
|
||||
#define SUMP_DMA_MASK (((1<<SUMP_DMA_CHANNELS)-1) << SUMP_DMA_CH_FIRST)
|
||||
|
||||
#if PICO_NO_FLASH
|
||||
#define SUMP_MEMORY_SIZE 102400 // 100kB
|
||||
#else
|
||||
#define SUMP_MEMORY_SIZE 204800 // 200kB
|
||||
#endif
|
||||
#define SUMP_MAX_CHUNK_SIZE 4096
|
||||
|
||||
#if (SUMP_MEMORY_SIZE % SUMP_MAX_CHUNK_SIZE) != 0
|
||||
#error "Invalid maximal chunk size!"
|
||||
#endif
|
||||
|
||||
#if (SUMP_MEMORY_SIZE / SUMP_MAX_CHUNK_SIZE) < SUMP_DMA_CHANNELS
|
||||
#error "DMA buffer and DMA channels out of sync!"
|
||||
#endif
|
||||
|
||||
#define SUMP_STATE_CONFIG 0
|
||||
#define SUMP_STATE_INIT 1
|
||||
#define SUMP_STATE_TRIGGER 2
|
||||
#define SUMP_STATE_SAMPLING 3
|
||||
#define SUMP_STATE_DUMP 4
|
||||
#define SUMP_STATE_ERROR 5
|
||||
|
||||
#define ONE_MHZ 1000000u
|
||||
|
||||
/* **** */
|
||||
|
||||
void sump_set_chunk_size(void);
|
||||
uint32_t sump_calc_sysclk_divider();
|
||||
void sump_rx(uint8_t *buf, uint count);
|
||||
void cdc_sump_init(void);
|
||||
void cdc_sump_task(void);
|
||||
void cdc_sump_line_coding(cdc_line_coding_t const *line_coding);
|
||||
|
||||
void sump_hw_init(void);
|
||||
void sump_hw_stop(void);
|
||||
uint32_t sump_hw_get_sysclk(void);
|
||||
uint64_t sump_hw_xfer_start(uint8_t width, int flags);
|
||||
void sump_hw_xfer_stop(void);
|
||||
void __isr sump_dma_irq_handler(void);
|
||||
|
||||
struct _trigger {
|
||||
uint32_t mask;
|
||||
uint32_t value;
|
||||
uint16_t delay;
|
||||
uint8_t channel;
|
||||
uint8_t level;
|
||||
bool serial;
|
||||
bool start;
|
||||
};
|
||||
|
||||
struct _sump {
|
||||
/* internal states */
|
||||
bool cdc_connected;
|
||||
uint8_t cmd[5]; // command
|
||||
uint8_t cmd_pos; // command buffer position
|
||||
uint8_t state; // SUMP_STATE_*
|
||||
uint8_t width; // in bytes, 1 = 8 bits, 2 = 16 bits
|
||||
uint8_t trigger_index;
|
||||
uint32_t pio_prog_offset;
|
||||
uint32_t read_start;
|
||||
uint64_t timestamp_start;
|
||||
|
||||
/* protocol config */
|
||||
uint32_t divider; // clock divider
|
||||
uint32_t read_count;
|
||||
uint32_t delay_count;
|
||||
uint32_t flags;
|
||||
struct _trigger trigger[4];
|
||||
|
||||
/* DMA buffer */
|
||||
uint32_t chunk_size; // in bytes
|
||||
uint32_t dma_start;
|
||||
uint32_t dma_count;
|
||||
uint32_t dma_curr_idx; // current DMA channel (index)
|
||||
uint32_t dma_pos;
|
||||
uint32_t next_count;
|
||||
uint8_t buffer[SUMP_MEMORY_SIZE];
|
||||
};
|
||||
|
||||
extern struct _sump sump;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
|
||||
#include <pico/stdlib.h>
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/pwm.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <hardware/structs/bus_ctrl.h>
|
||||
|
||||
#include "picoprobe_config.h"
|
||||
#include "cdc_sump.h"
|
||||
|
||||
uint32_t pio_prog_offset;
|
||||
|
||||
uint32_t sump_hw_get_sysclk(void)
|
||||
{
|
||||
return clock_get_hz(clk_sys);
|
||||
}
|
||||
|
||||
static void sump_pio_init(uint8_t width, bool nogr0)
|
||||
{
|
||||
pio_sm_config c;
|
||||
uint off, gpio = SAMPLING_GPIO_FIRST, divider;
|
||||
|
||||
#if SAMPLING_BITS > 8
|
||||
if (width == 1 && nogr0)
|
||||
gpio += 8;
|
||||
#endif
|
||||
// loop the IN instruction forewer (8-bit and 16-bit version)
|
||||
c = pio_get_default_sm_config();
|
||||
sm_config_set_in_pins(&c, gpio);
|
||||
off = pio_prog_offset + (width - 1);
|
||||
sm_config_set_wrap(&c, off, off);
|
||||
divider = sump_calc_sysclk_divider();
|
||||
sm_config_set_clkdiv_int_frac(&c, divider >> 8, divider & 0xff);
|
||||
sm_config_set_in_shift(&c, true, true, 32);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
pio_sm_init(SAMPLING_PIO, SAMPLING_PIO_SM, off, &c);
|
||||
picoprobe_debug("%s(): pc=0x%02x [0x%02x], gpio=%u\n", __func__,
|
||||
off, pio_prog_offset, gpio);
|
||||
}
|
||||
|
||||
static void sump_pio_program(void)
|
||||
{
|
||||
uint16_t prog[] = {
|
||||
pio_encode_in(pio_pins, 8),
|
||||
pio_encode_in(pio_pins, 16)
|
||||
};
|
||||
struct pio_program program = {
|
||||
.instructions = prog,
|
||||
.length = count_of(prog),
|
||||
.origin = -1
|
||||
};
|
||||
picoprobe_debug("%s(): 0x%04x 0x%04x len=%u\n", __func__, prog[0], prog[1], program.length);
|
||||
pio_prog_offset = pio_add_program(SAMPLING_PIO, &program); // TODO
|
||||
}
|
||||
|
||||
static uint32_t sump_pwm_slice_init(uint gpio, uint clock, bool swap_levels)
|
||||
{
|
||||
uint32_t clksys = sump_hw_get_sysclk(), clkdiv, slice, tmp;
|
||||
uint16_t top = 5, level_a = 1, level_b = 4;
|
||||
|
||||
// correction for low speed PWM
|
||||
while ((clksys / clock / top) & ~0xff) {
|
||||
top *= 1000;
|
||||
level_a *= 1000;
|
||||
level_b *= 1000;
|
||||
}
|
||||
clkdiv = clksys / clock / top;
|
||||
// pwm setup
|
||||
slice = pwm_gpio_to_slice_num(gpio);
|
||||
gpio_set_function(gpio, GPIO_FUNC_PWM);
|
||||
gpio_set_function(gpio + 1, GPIO_FUNC_PWM);
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_wrap(&c, top - 1);
|
||||
pwm_config_set_clkdiv_int(&c, clkdiv);
|
||||
pwm_init(slice, &c, false);
|
||||
if (swap_levels) {
|
||||
uint16_t tmp = level_a;
|
||||
level_a = level_b;
|
||||
level_b = tmp;
|
||||
}
|
||||
pwm_set_both_levels(slice, level_a, level_b);
|
||||
picoprobe_debug("%s(): gpio=%u clkdiv=%u top=%u level=%u/%u freq=%.4fMhz (req %.4fMhz)\n",
|
||||
__func__, gpio, clkdiv, top, level_a, level_b,
|
||||
(float)clksys / (float)clkdiv / (float)top / 1000000.0,
|
||||
(float)clock / 1000000.0);
|
||||
return 1u << slice;
|
||||
}
|
||||
|
||||
static uint32_t sump_calib_init(void)
|
||||
{
|
||||
uint32_t clksys = sump_hw_get_sysclk(), clkdiv, slice;
|
||||
const uint32_t clock = 5 * ONE_MHZ;
|
||||
const uint16_t top = 10, level_a = 5;
|
||||
|
||||
// set 5Mhz PWM on test pin
|
||||
|
||||
// should not go beyond 255!
|
||||
clkdiv = clksys / clock / top;
|
||||
|
||||
// pwm setup
|
||||
slice = pwm_gpio_to_slice_num(SAMPLING_GPIO_TEST);
|
||||
gpio_set_function(SAMPLING_GPIO_TEST, GPIO_FUNC_PWM);
|
||||
pwm_config c = pwm_get_default_config();
|
||||
pwm_config_set_wrap(&c, top - 1);
|
||||
pwm_config_set_clkdiv_int(&c, clkdiv);
|
||||
pwm_init(slice, &c, false);
|
||||
pwm_set_both_levels(slice, level_a, level_a);
|
||||
picoprobe_debug("%s(): gpio=%u clkdiv=%u top=%u level=%u/%u freq=%.4fMhz (req %.4fMhz)\n",
|
||||
__func__, SAMPLING_GPIO_TEST, clkdiv, top, level_a, level_a,
|
||||
(float)clksys / (float)clkdiv / (float)top / 1000000.0,
|
||||
(float)clock / 1000000.0);
|
||||
return 1u << slice;
|
||||
}
|
||||
|
||||
static uint32_t sump_test_init(void)
|
||||
{
|
||||
// Initialize test PWMs
|
||||
const uint32_t gpio = SAMPLING_GPIO_FIRST;
|
||||
uint32_t mask;
|
||||
// 10Mhz PWM
|
||||
mask = sump_pwm_slice_init(gpio, 10000000, false);
|
||||
// 1Mhz PWM
|
||||
mask |= sump_pwm_slice_init(gpio + 2, 1000000, false);
|
||||
// 1kHz PWM
|
||||
mask |= sump_pwm_slice_init(gpio + 4, 1000, false);
|
||||
#if SAMPLING_BITS > 8
|
||||
// 1kHz PWM (second byte)
|
||||
mask |= sump_pwm_slice_init(gpio + 8, 1000, true);
|
||||
#endif
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void sump_test_done(void)
|
||||
{
|
||||
const uint32_t gpio = SAMPLING_GPIO_FIRST;
|
||||
uint32_t i;
|
||||
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio), false);
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 2), false);
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 4), false);
|
||||
#if SAMPLING_BITS > 8
|
||||
pwm_set_enabled(pwm_gpio_to_slice_num(gpio + 8), false);
|
||||
#endif
|
||||
for (i = SAMPLING_GPIO_FIRST; i <= SAMPLING_GPIO_LAST; i++)
|
||||
gpio_set_function(i, GPIO_FUNC_NULL);
|
||||
// test pin
|
||||
pwm_set_enabled(SAMPLING_GPIO_TEST, false);
|
||||
}
|
||||
|
||||
uint64_t sump_hw_xfer_start(uint8_t width, int flags)
|
||||
{
|
||||
uint32_t pwm_mask = 0, irq_state, i;
|
||||
sump_pio_init(width, flags & SUMP_FLAG1_GR0_DISABLE);
|
||||
|
||||
pwm_mask = sump_calib_init();
|
||||
if (flags & SUMP_FLAG1_EXT_TEST) {
|
||||
pwm_mask |= sump_test_init();
|
||||
} else {
|
||||
sump_test_done();
|
||||
}
|
||||
|
||||
// limit chunk size for slow sampling
|
||||
sump_set_chunk_size();
|
||||
|
||||
for (i = 0; i < SUMP_DMA_CHANNELS; i++)
|
||||
sump_dma_program(i, i * sump.chunk_size);
|
||||
|
||||
// let's go
|
||||
irq_state = save_and_disable_interrupts();
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, true);
|
||||
if (pwm_mask)
|
||||
pwm_set_mask_enabled(pwm_mask);
|
||||
dma_channel_start(SUMP_DMA_CH_FIRST);
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, true);
|
||||
restore_interrupts(irq_state);
|
||||
|
||||
return time_us_64();
|
||||
}
|
||||
void sump_hw_xfer_stop(void)
|
||||
{
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, false);
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
}
|
||||
|
||||
void sump_hw_init(void)
|
||||
{
|
||||
uint i;
|
||||
|
||||
// claim DMA channels
|
||||
dma_claim_mask(SUMP_DMA_MASK);
|
||||
|
||||
// claim PIO state machine and add program
|
||||
pio_claim_sm_mask(SAMPLING_PIO, 1u << SAMPLING_PIO_SM);
|
||||
sump_pio_program();
|
||||
|
||||
// high bus priority to the DMA
|
||||
bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;
|
||||
|
||||
// GPIO init
|
||||
gpio_set_dir_in_masked(SAMPLING_GPIO_MASK);
|
||||
gpio_put_masked(SAMPLING_GPIO_MASK, 0);
|
||||
for (i = SAMPLING_GPIO_FIRST; i <= SAMPLING_GPIO_LAST; i++) {
|
||||
gpio_set_function(i, GPIO_FUNC_NULL);
|
||||
gpio_set_pulls(i, false, false);
|
||||
}
|
||||
|
||||
// test GPIO pin
|
||||
gpio_set_dir(SAMPLING_GPIO_TEST, true);
|
||||
gpio_put(SAMPLING_GPIO_TEST, true);
|
||||
gpio_set_function(SAMPLING_GPIO_TEST, GPIO_FUNC_PWM);
|
||||
|
||||
// set exclusive interrupt handler
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
irq_set_exclusive_handler(SAMPLING_DMA_IRQ, sump_dma_irq_handler);
|
||||
sump_dma_set_irq_channel_mask_enabled(SUMP_DMA_MASK, true);
|
||||
}
|
||||
void sump_hw_stop(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// IRQ and PIO fast stop
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, false);
|
||||
// DMA abort
|
||||
for (i = SUMP_DMA_CH_FIRST; i <= SUMP_DMA_CH_LAST; i++)
|
||||
dma_channel_abort(i);
|
||||
// IRQ status cleanup
|
||||
sump_dma_ints = SUMP_DMA_MASK;
|
||||
// PIO cleanup
|
||||
pio_sm_clear_fifos(SAMPLING_PIO, SAMPLING_PIO_SM);
|
||||
pio_sm_restart(SAMPLING_PIO, SAMPLING_PIO_SM);
|
||||
// test
|
||||
sump_test_done();
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
// UART0 for Picoprobe debug
|
||||
// UART1 for picoprobe to target device
|
||||
|
||||
void cdc_sump_line_coding(cdc_line_coding_t const *line_coding);
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
|
||||
if (itf == 0)
|
||||
cdc_uart_line_coding(line_coding);
|
||||
|
|
Loading…
Reference in New Issue