SUMP logic analyzer mode, stolen from piocprobe-sump (mode 4)
This commit is contained in:
parent
8f1f4b1ad0
commit
82ff925001
|
@ -72,11 +72,13 @@ target_sources(${PROJECT} PUBLIC
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/cdc_serprog.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/tempsensor.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/m_default/vnd_i2ctinyusb.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/_sump.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/cdc_sump.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/cdc_uart.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/i2c_tinyusb.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/spi_serprog.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/src/m_default2/0def.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_sump/sump_hw.c
|
||||
)
|
||||
if(USE_USBCDC_FOR_STDIO)
|
||||
target_sources(${PROJECT} PUBLIC
|
||||
|
@ -100,7 +102,8 @@ add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR}
|
|||
|
||||
if(FAMILY STREQUAL "rp2040")
|
||||
target_link_libraries(${PROJECT} pico_stdlib pico_unique_id hardware_spi
|
||||
pico_fix_rp2040_usb_device_enumeration hardware_i2c hardware_adc
|
||||
hardware_i2c hardware_adc hardware_pio hardware_dma hardware_pwm
|
||||
pico_fix_rp2040_usb_device_enumeration
|
||||
tinyusb_device tinyusb_board tinyusb_additions)
|
||||
|
||||
if(USE_USBCDC_FOR_STDIO)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
The below copyright and permission notice applies to portions of the following
|
||||
files, which have been modified from their original versions in
|
||||
<https://github.com/perexg/picoprobe-sump> (the "picoprobe-sump repository")
|
||||
|
||||
- TODO
|
||||
|
||||
The below notice does not apply to any modifications made to the above files
|
||||
since the versions present in the picoprobe-usb repository, nor to any files
|
||||
not present in the picoprobe-usb repository.
|
||||
|
||||
|
||||
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Jaroslav Kysela <perex@perex.cz>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -231,6 +231,7 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC)
|
|||
|
||||
- [ ] A name
|
||||
- [ ] A (VID and) PID, and maybe better subclass & protocol IDs for the vnd cfg itf
|
||||
- [ ] More Pico SDK meta/buildinfo
|
||||
- [x] CMSIS-DAP JTAG implementation
|
||||
- [x] Flashrom/SPI support using Serprog
|
||||
- Parallel ROM flashing support, too, by having the device switch into a
|
||||
|
@ -258,6 +259,7 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC)
|
|||
- [x] A proper interface for sending commands etc. instead of shoehorning it
|
||||
into Serprog.
|
||||
- Can probably be included in the "Better USB interface stuff".
|
||||
- [ ] make modes persistent?
|
||||
- [ ] JTAG pinout detector
|
||||
- https://github.com/cyphunk/JTAGenum
|
||||
- https://github.com/travisgoodspeed/goodfet/blob/master/firmware/apps/jscan/jscan.c
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#ifndef BSP_FEATURE_M_SUMP_H_
|
||||
#define BSP_FEATURE_M_SUMP_H_
|
||||
|
||||
#define DBOARD_HAS_SUMP
|
||||
|
||||
#include "bsp-info.h"
|
||||
|
||||
enum {
|
||||
HID_N__NITF = 0
|
||||
};
|
||||
enum {
|
||||
CDC_N_SUMP = 0,
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
CDC_N_STDIO,
|
||||
#endif
|
||||
|
||||
CDC_N__NITF
|
||||
};
|
||||
enum {
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
VND_N_CFG = 0,
|
||||
#endif
|
||||
|
||||
VND_N__NITF
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,367 @@
|
|||
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/irq.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/pwm.h>
|
||||
#include <hardware/structs/bus_ctrl.h>
|
||||
#include <hardware/sync.h>
|
||||
#include <pico/platform.h>
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bsp-info.h"
|
||||
#include "m_sump/sump.h"
|
||||
|
||||
|
||||
#include "m_sump/sump_hw.h"
|
||||
|
||||
#define SAMPLING_GPIO_MASK (((1 << SAMPLING_BITS) - 1) << SAMPLING_GPIO_FIRST)
|
||||
|
||||
#define SAMPLING_GPIO_TEST 22
|
||||
|
||||
#define SAMPLING_PIO pio1
|
||||
#define SAMPLING_PIO_SM 0u
|
||||
|
||||
#define SAMPLING_DMA_IRQ DMA_IRQ_1
|
||||
#define sump_dma_ints (dma_hw->ints1)
|
||||
|
||||
#define sump_dma_set_irq_channel_mask_enabled dma_set_irq1_channel_mask_enabled
|
||||
|
||||
#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)
|
||||
|
||||
#define sump_irq_debug(format, ...) ((void)0)
|
||||
#define picoprobe_info(format, ...) ((void)0)
|
||||
#define picoprobe_debug(format, ...) ((void)0)
|
||||
#define picoprobe_dump(format, ...) ((void)0)
|
||||
|
||||
static uint16_t prog[2];
|
||||
// clang-format off
|
||||
static const struct pio_program program = {
|
||||
.instructions = prog,
|
||||
.length = count_of(prog),
|
||||
.origin = -1
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static uint32_t pio_prog_offset;
|
||||
static uint32_t dma_curr_idx = 0;
|
||||
static uint32_t oldprio;
|
||||
|
||||
uint32_t sump_hw_get_sysclk(void) { return clock_get_hz(clk_sys); }
|
||||
|
||||
void sump_hw_get_cpu_name(char cpu[32]) {
|
||||
snprintf(cpu, 32, INFO_BOARDNAME " @ %lu MHz",
|
||||
sump_hw_get_sysclk() / (ONE_MHZ * SAMPLING_DIVIDER));
|
||||
}
|
||||
void sump_hw_get_hw_name(char hw[32]) {
|
||||
snprintf(hw, 32, INFO_BOARDNAME " rev%hhu, ROM v%hhu", rp2040_chip_version(),
|
||||
rp2040_rom_version());
|
||||
}
|
||||
|
||||
static void sump_pio_init(uint8_t width, bool nogr0) {
|
||||
uint32_t gpio = SAMPLING_GPIO_FIRST;
|
||||
|
||||
#if SAMPLING_BITS > 8
|
||||
if (width == 1 && nogr0) gpio += 8;
|
||||
#endif
|
||||
// loop the IN instruction forewer (8-bit and 16-bit version)
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_in_pins(&c, gpio);
|
||||
uint32_t off = pio_prog_offset + (width - 1);
|
||||
sm_config_set_wrap(&c, off, off);
|
||||
|
||||
uint32_t 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) {
|
||||
prog[0] = pio_encode_in(pio_pins, 8);
|
||||
prog[1] = pio_encode_in(pio_pins, 16);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static uint32_t sump_pwm_slice_init(uint32_t gpio, uint32_t clock, bool swap_levels) {
|
||||
uint32_t clksys = sump_hw_get_sysclk();
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t clkdiv = clksys / clock / top;
|
||||
|
||||
// pwm setup
|
||||
uint32_t 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;
|
||||
|
||||
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 (uint32_t 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_dma_chain_to_self(uint32_t ch) {
|
||||
dma_channel_config cfg;
|
||||
|
||||
ch += SUMP_DMA_CH_FIRST;
|
||||
cfg = dma_get_channel_config(ch);
|
||||
channel_config_set_chain_to(&cfg, ch);
|
||||
dma_channel_set_config(ch, &cfg, false);
|
||||
}
|
||||
|
||||
void sump_hw_capture_setup_next(
|
||||
uint32_t ch, uint32_t mask, uint32_t chunk_size, uint32_t next_count, uint8_t width) {
|
||||
if ((next_count % chunk_size) == 0) {
|
||||
ch = (mask + dma_curr_idx - 1) % SUMP_DMA_CHANNELS;
|
||||
sump_dma_chain_to_self(ch);
|
||||
ch = (ch + 1) % SUMP_DMA_CHANNELS;
|
||||
} else {
|
||||
ch = (mask + dma_curr_idx) % SUMP_DMA_CHANNELS;
|
||||
dma_channel_set_trans_count(
|
||||
ch + SUMP_DMA_CH_FIRST, (next_count % chunk_size) / width, false);
|
||||
}
|
||||
sump_irq_debug("%s(): %u: t=0x%08x\n", __func__, ch + SUMP_DMA_CH_FIRST,
|
||||
(next_count % chunk_size) / width);
|
||||
|
||||
// break chain, reset unused DMA chunks
|
||||
// clear all chains for high-speed DMAs
|
||||
mask = SUMP_DMA_CHANNELS - ((next_count + chunk_size - 1) / chunk_size);
|
||||
while (mask > 0) {
|
||||
sump_dma_chain_to_self(ch);
|
||||
sump_irq_debug(
|
||||
"%s(): %u -> %u\n", __func__, ch + SUMP_DMA_CH_FIRST, ch + SUMP_DMA_CH_FIRST);
|
||||
ch = (ch + 1) % SUMP_DMA_CHANNELS;
|
||||
mask--;
|
||||
}
|
||||
}
|
||||
static void __isr sump_hw_dma_irq_handler(void) {
|
||||
uint32_t loop = 0;
|
||||
|
||||
while (1) {
|
||||
uint32_t ch = SUMP_DMA_CH_FIRST + dma_curr_idx;
|
||||
uint32_t mask = 1u << ch;
|
||||
|
||||
if ((sump_dma_ints & mask) == 0) break;
|
||||
|
||||
// acknowledge interrupt
|
||||
sump_dma_ints = mask;
|
||||
|
||||
dma_curr_idx = (dma_curr_idx + 1) % SUMP_DMA_CHANNELS;
|
||||
|
||||
dma_channel_set_write_addr(ch, sump_capture_get_next_dest(SUMP_DMA_CHANNELS), false);
|
||||
// sump_irq_debug("%s(): %u: w=0x%08x, state=%u\n", __func__, ch, sump_dma_get_next_dest(),
|
||||
// sump.state);
|
||||
|
||||
sump_capture_callback(ch, SUMP_DMA_CHANNELS);
|
||||
|
||||
// are we slow?
|
||||
if (++loop == SUMP_DMA_CHANNELS) {
|
||||
sump_capture_callback_cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sump_dma_program(
|
||||
uint32_t ch, uint32_t pos, uint8_t width, uint32_t chunk_size, uint8_t* destbuf) {
|
||||
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, width == 1 ? DMA_SIZE_8 : DMA_SIZE_16);
|
||||
|
||||
dma_channel_configure(SUMP_DMA_CH_FIRST + ch, &cfg, destbuf + pos,
|
||||
&SAMPLING_PIO->rxf[SAMPLING_PIO_SM], chunk_size / width, false);
|
||||
|
||||
picoprobe_debug("%s() %u: w=0x%08x r=0x%08x t=0x%08x -> %u\n", __func__, SUMP_DMA_CH_FIRST + ch,
|
||||
destbuf + pos, &SAMPLING_PIO->rxf[SAMPLING_PIO_SM], chunk_size / width,
|
||||
SUMP_DMA_CH_FIRST + ((ch + 1) % SUMP_DMA_CHANNELS));
|
||||
}
|
||||
|
||||
/*uint64_t*/ void sump_hw_capture_start(
|
||||
uint8_t width, int flags, uint32_t chunk_size, uint8_t* destbuf) {
|
||||
sump_pio_init(width, flags & SUMP_FLAG1_GR0_DISABLE);
|
||||
|
||||
dma_curr_idx = 0;
|
||||
|
||||
uint32_t pwm_mask = sump_calib_init();
|
||||
if (flags & SUMP_FLAG1_EXT_TEST) {
|
||||
pwm_mask |= sump_test_init();
|
||||
} else {
|
||||
sump_test_done();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < SUMP_DMA_CHANNELS; i++)
|
||||
sump_dma_program(i, i * chunk_size, width, chunk_size, destbuf);
|
||||
|
||||
// let's go
|
||||
uint32_t 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_capture_stop(void) {
|
||||
pio_sm_set_enabled(SAMPLING_PIO, SAMPLING_PIO_SM, false);
|
||||
irq_set_enabled(SAMPLING_DMA_IRQ, false);
|
||||
}
|
||||
|
||||
void sump_hw_init(void) {
|
||||
// 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
|
||||
oldprio = bus_ctrl_hw->priority;
|
||||
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 (uint32_t 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_hw_dma_irq_handler);
|
||||
sump_dma_set_irq_channel_mask_enabled(SUMP_DMA_MASK, true);
|
||||
}
|
||||
|
||||
void sump_hw_stop(void) {
|
||||
// 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 (uint32_t 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();
|
||||
}
|
||||
|
||||
void sump_hw_deinit(void) {
|
||||
sump_hw_stop();
|
||||
|
||||
sump_dma_set_irq_channel_mask_enabled(SUMP_DMA_MASK, false);
|
||||
|
||||
gpio_set_dir(SAMPLING_GPIO_TEST, false);
|
||||
gpio_set_function(SAMPLING_GPIO_TEST, GPIO_FUNC_NULL);
|
||||
|
||||
bus_ctrl_hw->priority = oldprio;
|
||||
|
||||
pio_remove_program(SAMPLING_PIO, &program, pio_prog_offset);
|
||||
pio_sm_unclaim(SAMPLING_PIO, SAMPLING_PIO_SM);
|
||||
|
||||
for (uint32_t i = SUMP_DMA_CH_FIRST; i <= SUMP_DMA_CH_LAST; ++i) dma_channel_unclaim(i);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef BSP_SUMP_HW_PICO_H
|
||||
#define BSP_SUMP_HW_PICO_H
|
||||
|
||||
#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)
|
||||
|
||||
#if PICO_NO_FLASH
|
||||
#define SUMP_MEMORY_SIZE 102400 // 100kB
|
||||
#else
|
||||
#define SUMP_MEMORY_SIZE 204800 // 200kB
|
||||
#endif
|
||||
#define SUMP_MAX_CHUNK_SIZE 4096
|
||||
|
||||
#endif
|
11
dmctl2.py
11
dmctl2.py
|
@ -87,25 +87,24 @@ epout.write(b'\x12') # get mode1 features
|
|||
print("stat=%d"%stat)
|
||||
print(res)
|
||||
|
||||
print("echo time!")
|
||||
epout.write(b'\x14\x00\x42') # I2C echo!
|
||||
epout.write(b'\x40') # get mode4 name
|
||||
(stat, res) = rdresp(epin)
|
||||
print("stat=%d"%stat)
|
||||
print(res)
|
||||
|
||||
epout.write(b'\x1f\x14\x00\x43')
|
||||
epout.write(b'\x41') # get mode4 version
|
||||
(stat, res) = rdresp(epin)
|
||||
print("stat=%d"%stat)
|
||||
print(res)
|
||||
|
||||
epout.write(b'\x14\x00\x44') # I2C echo!
|
||||
epout.write(b'\x42') # get mode4 features
|
||||
(stat, res) = rdresp(epin)
|
||||
print("stat=%d"%stat)
|
||||
print(res)
|
||||
|
||||
### ATTEMPT A MODESET ###
|
||||
|
||||
#epout.write(b'\x03\x02') # set cur mode
|
||||
#print('[%s]'%(', '.join(hex(x) for x in epin.read(3)))) # result: status, payload len, mode
|
||||
epout.write(b'\x03\x04') # set cur mode
|
||||
print('[%s]'%(', '.join(hex(x) for x in epin.read(3)))) # result: status, payload len, mode
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
// vim: set et:
|
||||
|
||||
#include <tusb.h>
|
||||
|
||||
#include "mode.h"
|
||||
#include "thread.h"
|
||||
#include "vnd_cfg.h"
|
||||
|
||||
#include "m_sump/bsp-feature.h"
|
||||
|
||||
/* CDC SUMP */
|
||||
#include "m_sump/sump.h"
|
||||
|
||||
enum m_default_feature {
|
||||
msump_feat_sump = 1<<0,
|
||||
};
|
||||
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
static cothread_t sumpthread;
|
||||
static uint8_t sumpstack[THREAD_STACK_SIZE];
|
||||
|
||||
static void sump_thread_fn(void) {
|
||||
cdc_sump_init();
|
||||
thread_yield();
|
||||
while (1) {
|
||||
cdc_sump_task();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void stdio_usb_set_itf_num(int itf); // TODO: move to a header!
|
||||
|
||||
static void enter_cb(void) {
|
||||
stdio_usb_set_itf_num(CDC_N_STDIO);
|
||||
vnd_cfg_set_itf_num(VND_N_CFG);
|
||||
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
sumpthread = co_derive(sumpstack, sizeof sumpstack, sump_thread_fn);
|
||||
thread_enter(sumpthread);
|
||||
#endif
|
||||
}
|
||||
static void leave_cb(void) {
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
cdc_sump_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void task_cb(void) {
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
tud_task();
|
||||
thread_enter(sumpthread);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void handle_cmd_cb(uint8_t cmd) {
|
||||
uint8_t resp = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case mode_cmd_get_features:
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
resp |= msump_feat_sump;
|
||||
#endif
|
||||
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
|
||||
break;
|
||||
default:
|
||||
vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode4 command %02x", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
|
||||
STRID_CONFIG,
|
||||
|
||||
STRID_IF_VND_CFG,
|
||||
STRID_IF_CDC_SUMP,
|
||||
STRID_IF_CDC_STDIO,
|
||||
};
|
||||
enum {
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
ITF_NUM_VND_CFG,
|
||||
#endif
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
ITF_NUM_CDC_SUMP_COM,
|
||||
ITF_NUM_CDC_SUMP_DATA,
|
||||
#endif
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
ITF_NUM_CDC_STDIO_COM,
|
||||
ITF_NUM_CDC_STDIO_DATA,
|
||||
#endif
|
||||
|
||||
ITF_NUM__TOTAL
|
||||
};
|
||||
enum {
|
||||
CONFIG_TOTAL_LEN
|
||||
= TUD_CONFIG_DESC_LEN
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
+ TUD_VENDOR_DESC_LEN
|
||||
#endif
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
+ TUD_CDC_DESC_LEN
|
||||
#endif
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
+ TUD_CDC_DESC_LEN
|
||||
#endif
|
||||
};
|
||||
|
||||
#define EPNUM_VND_CFG_OUT 0x01
|
||||
#define EPNUM_VND_CFG_IN 0x81
|
||||
#define EPNUM_CDC_SUMP_OUT 0x02
|
||||
#define EPNUM_CDC_SUMP_IN 0x82
|
||||
#define EPNUM_CDC_SUMP_NOTIF 0x83
|
||||
#define EPNUM_CDC_STDIO_OUT 0x04
|
||||
#define EPNUM_CDC_STDIO_IN 0x84
|
||||
#define EPNUM_CDC_STDIO_NOTIF 0x85
|
||||
|
||||
// clang-format off
|
||||
// TODO: replace magic 64s by actual buffer size macros
|
||||
static const uint8_t desc_configuration[] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM__TOTAL, STRID_CONFIG, CONFIG_TOTAL_LEN,
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
#if CFG_TUD_VENDOR > 0
|
||||
TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_CFG, STRID_IF_VND_CFG, EPNUM_VND_CFG_OUT,
|
||||
EPNUM_VND_CFG_IN, CFG_TUD_VENDOR_RX_BUFSIZE, VND_CFG_SUBCLASS, VND_CFG_PROTOCOL),
|
||||
#endif
|
||||
|
||||
#ifdef DBOARD_HAS_SUMP
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_SUMP_COM, STRID_IF_CDC_SUMP, EPNUM_CDC_SUMP_NOTIF,
|
||||
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_SUMP_OUT, EPNUM_CDC_SUMP_IN, CFG_TUD_CDC_RX_BUFSIZE),
|
||||
#endif
|
||||
|
||||
#ifdef USE_USBCDC_FOR_STDIO
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_STDIO_COM, STRID_IF_CDC_STDIO, EPNUM_CDC_STDIO_NOTIF,
|
||||
CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_STDIO_OUT, EPNUM_CDC_STDIO_IN, CFG_TUD_CDC_RX_BUFSIZE),
|
||||
#endif
|
||||
};
|
||||
static const char* string_desc_arr[] = {
|
||||
NULL,
|
||||
|
||||
[STRID_CONFIG] = "Configuration descriptor",
|
||||
// max string length check: |||||||||||||||||||||||||||||||
|
||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",
|
||||
[STRID_IF_CDC_SUMP ] = "SUMP LA CDC interface",
|
||||
[STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)",
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
extern struct mode m_04_sump;
|
||||
// clang-format off
|
||||
struct mode m_04_sump = {
|
||||
.name = "SUMP logic analyzer mode",
|
||||
.version = 0x0010,
|
||||
.n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
|
||||
|
||||
.usb_desc = desc_configuration,
|
||||
.string_desc = string_desc_arr,
|
||||
|
||||
.enter = enter_cb,
|
||||
.leave = leave_cb,
|
||||
.task = task_cb,
|
||||
.handle_cmd = handle_cmd_cb,
|
||||
};
|
||||
// clang-format on
|
||||
|
|
@ -0,0 +1,817 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*
|
||||
* Protocol link: https://www.sump.org/projects/analyzer/protocol
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <tusb.h>
|
||||
|
||||
#include "info.h"
|
||||
#include "m_sump/bsp-feature.h"
|
||||
#include "m_sump/sump.h"
|
||||
#include "m_sump/sump_hw.h"
|
||||
|
||||
#define picoprobe_debug(format, ...) ((void)0)
|
||||
#define picoprobe_info(format, ...) ((void)0)
|
||||
|
||||
#define CDC_INTF CDC_N_SUMP
|
||||
|
||||
#if SAMPLING_BITS != 8 && SAMPLING_BITS != 16
|
||||
#error "Correct sampling width (8 or 16 bits)"
|
||||
#endif
|
||||
|
||||
#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 AS_16P(a) (*(uint16_t*)(a))
|
||||
|
||||
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;
|
||||
|
||||
/* utility functions ======================================================= */
|
||||
|
||||
/*static void picoprobe_debug_hexa(uint8_t *buf, uint32_t len) {
|
||||
uint32_t l;
|
||||
for (l = 0; len > 0; len--, l++) {
|
||||
if (l != 0)
|
||||
putchar(':');
|
||||
printf("%02x", *buf++);
|
||||
}
|
||||
}*/
|
||||
|
||||
static uint8_t* sump_add_metas(uint8_t* buf, uint8_t tag, const char* str) {
|
||||
*buf++ = tag;
|
||||
while (*str) *buf++ = (uint8_t)(*str++);
|
||||
*buf++ = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
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) {
|
||||
buf[0] = tag;
|
||||
// this is a bit weird, but libsigrok decodes Big-Endian words here
|
||||
// the commands use Little-Endian
|
||||
#if 0
|
||||
buf[1] = val;
|
||||
buf[2] = val >> 8;
|
||||
buf[3] = val >> 16;
|
||||
buf[4] = val >> 24;
|
||||
#else
|
||||
buf[1] = val >> 24;
|
||||
buf[2] = val >> 16;
|
||||
buf[3] = val >> 8;
|
||||
buf[4] = val;
|
||||
#endif
|
||||
return buf + 5;
|
||||
}
|
||||
|
||||
static void* sump_analyze_trigger8(void* ptr, uint32_t size) {
|
||||
(void)size;
|
||||
|
||||
uint8_t* src = ptr;
|
||||
uint8_t tmask = sump.trigger[sump.trigger_index].mask;
|
||||
uint8_t tvalue = sump.trigger[sump.trigger_index].value;
|
||||
uint32_t count = sump.chunk_size;
|
||||
|
||||
for (count = sump.chunk_size; count > 0; count--) {
|
||||
uint32_t v = *src++;
|
||||
if ((v & tmask) != tvalue) continue;
|
||||
|
||||
while (1) {
|
||||
if (sump.trigger[sump.trigger_index].start) return src;
|
||||
|
||||
sump.trigger_index++;
|
||||
tmask = sump.trigger[sump.trigger_index].mask;
|
||||
tvalue = sump.trigger[sump.trigger_index].value;
|
||||
|
||||
if (tmask != 0 || tvalue != 0) break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* sump_analyze_trigger16(void* ptr, uint32_t size) {
|
||||
(void)size;
|
||||
|
||||
uint16_t* src = ptr;
|
||||
uint16_t tmask = sump.trigger[0].mask;
|
||||
uint16_t tvalue = sump.trigger[0].value;
|
||||
uint32_t count = sump.chunk_size;
|
||||
|
||||
for (count = sump.chunk_size / 2; count > 0; count--) {
|
||||
uint32_t v = *src++;
|
||||
if ((v & tmask) != tvalue) continue;
|
||||
|
||||
while (1) {
|
||||
if (sump.trigger[sump.trigger_index].start) return src;
|
||||
|
||||
sump.trigger_index++;
|
||||
tmask = sump.trigger[sump.trigger_index].mask;
|
||||
tvalue = sump.trigger[sump.trigger_index].value;
|
||||
|
||||
if (tmask != 0 || tvalue != 0) break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* sump_analyze_trigger(void* ptr, uint32_t size) {
|
||||
if (sump.width == 1)
|
||||
return sump_analyze_trigger8(ptr, size);
|
||||
else
|
||||
return sump_analyze_trigger16(ptr, size);
|
||||
}
|
||||
|
||||
uint32_t sump_calc_sysclk_divider() {
|
||||
const uint32_t common_divisor = 4;
|
||||
uint32_t divider = sump.divider;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t 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;
|
||||
}
|
||||
|
||||
static void sump_set_chunk_size(void) {
|
||||
uint32_t 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);
|
||||
}
|
||||
|
||||
/* data capture ============================================================ */
|
||||
|
||||
static void sump_capture_done(void) {
|
||||
sump_hw_capture_stop();
|
||||
/*uint64_t 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_capture_next(uint32_t pos) {
|
||||
if (sump.state != SUMP_STATE_TRIGGER) {
|
||||
sump_capture_done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// waiting for the trigger samples
|
||||
uint8_t* ptr = sump_analyze_trigger(sump.buffer + pos, sump.chunk_size);
|
||||
if (ptr == NULL) {
|
||||
// call this routine again right after next chunk
|
||||
return sump.chunk_size;
|
||||
}
|
||||
|
||||
sump.state = SUMP_STATE_SAMPLING;
|
||||
|
||||
// calculate read start
|
||||
uint32_t tmp = (sump.read_count - sump.delay_count) * sump.width;
|
||||
pos = ptr - sump.buffer;
|
||||
sump.read_start = (pos - tmp) % SUMP_MEMORY_SIZE;
|
||||
|
||||
// calculate the samples after trigger
|
||||
uint32_t delay_bytes = sump.delay_count * sump.width;
|
||||
tmp = sump.chunk_size - (pos % sump.chunk_size);
|
||||
if (tmp >= delay_bytes) {
|
||||
sump_capture_done();
|
||||
return 0;
|
||||
}
|
||||
return delay_bytes - tmp;
|
||||
}
|
||||
|
||||
uint8_t* sump_capture_get_next_dest(uint32_t numch) {
|
||||
return sump.buffer + (sump.dma_pos + numch * sump.chunk_size) % SUMP_MEMORY_SIZE;
|
||||
}
|
||||
|
||||
void sump_capture_callback_cancel(void) {
|
||||
sump_capture_done();
|
||||
sump.state = SUMP_STATE_ERROR;
|
||||
}
|
||||
|
||||
void sump_capture_callback(uint32_t ch, uint32_t numch) {
|
||||
// reprogram the current DMA channel to the tail
|
||||
if (sump.next_count <= sump.chunk_size) {
|
||||
sump.next_count = sump_capture_next(sump.dma_pos);
|
||||
if (sump.state == SUMP_STATE_DUMP) return;
|
||||
} else {
|
||||
sump.next_count -= sump.chunk_size;
|
||||
}
|
||||
// sump_irq_debug("%s(): next=0x%x\n", __func__, sump.next_count);
|
||||
|
||||
sump.dma_pos += sump.chunk_size;
|
||||
sump.dma_pos %= SUMP_MEMORY_SIZE;
|
||||
|
||||
if (sump.state == SUMP_STATE_SAMPLING && sump.next_count >= sump.chunk_size &&
|
||||
sump.next_count < numch * sump.chunk_size) {
|
||||
// set the last DMA segment to correct size to avoid overwrites
|
||||
uint32_t mask = sump.next_count / sump.chunk_size;
|
||||
|
||||
sump_hw_capture_setup_next(ch, mask, sump.chunk_size, sump.next_count, sump.width);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
||||
static void sump_xfer_start(uint8_t state) {
|
||||
sump.dma_start = 0;
|
||||
sump.dma_pos = 0;
|
||||
|
||||
picoprobe_debug("%s(): read=0x%08x delay=0x%08x divider=%u\n", __func__, sump.read_count,
|
||||
sump.delay_count, sump.divider);
|
||||
|
||||
uint32_t 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);
|
||||
|
||||
// limit chunk size for slow sampling
|
||||
sump_set_chunk_size();
|
||||
|
||||
/*sump.timestamp_start =*/sump_hw_capture_start(
|
||||
sump.width, sump.flags, sump.chunk_size, sump.buffer);
|
||||
|
||||
sump.state = state;
|
||||
}
|
||||
|
||||
/* SUMP proto command handling ============================================= */
|
||||
|
||||
static void sump_do_meta(void) {
|
||||
char cpu[32];
|
||||
uint8_t buf[128], *ptr = buf, *wptr = buf;
|
||||
|
||||
ptr = sump_add_metas(ptr, SUMP_META_NAME, INFO_PRODUCT_BARE " Logic Analyzer v1");
|
||||
sump_hw_get_hw_name(cpu);
|
||||
ptr = sump_add_metas(ptr, SUMP_META_FPGA_VERSION, cpu);
|
||||
sump_hw_get_cpu_name(cpu);
|
||||
ptr = sump_add_metas(ptr, SUMP_META_CPU_VERSION, cpu);
|
||||
ptr = sump_add_meta4(ptr, SUMP_META_SAMPLE_RATE, sump_hw_get_sysclk() / SAMPLING_DIVIDER);
|
||||
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;
|
||||
|
||||
assert(ptr < &buf[128] && "Stack overflow! aaaa!");
|
||||
|
||||
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 tmask = 0;
|
||||
bool tstart = false;
|
||||
|
||||
if (sump.width == 0) {
|
||||
// invalid config, dump something nice
|
||||
sump.state = SUMP_STATE_DUMP;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count_of(sump.trigger); i++) {
|
||||
tstart |= sump.trigger[i].start;
|
||||
tmask |= sump.trigger[i].mask;
|
||||
}
|
||||
|
||||
if (tstart && tmask) {
|
||||
state = SUMP_STATE_TRIGGER;
|
||||
sump.trigger_index = 0;
|
||||
} else {
|
||||
state = SUMP_STATE_SAMPLING;
|
||||
}
|
||||
|
||||
sump_xfer_start(state);
|
||||
}
|
||||
|
||||
static void sump_do_finish(void) {
|
||||
if (sump.state == SUMP_STATE_TRIGGER || sump.state == SUMP_STATE_SAMPLING) {
|
||||
sump.state = SUMP_STATE_DUMP;
|
||||
sump_capture_done();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void sump_do_stop(void) {
|
||||
if (sump.state == SUMP_STATE_INIT) return;
|
||||
|
||||
sump_hw_stop();
|
||||
|
||||
// protocol state
|
||||
sump.state = SUMP_STATE_INIT;
|
||||
}
|
||||
|
||||
static void sump_do_reset(void) {
|
||||
sump_do_stop();
|
||||
memset(&sump.trigger, 0, sizeof(sump.trigger));
|
||||
}
|
||||
|
||||
static void sump_set_flags(uint32_t flags) {
|
||||
uint32_t width = 2;
|
||||
sump.flags = flags;
|
||||
|
||||
if (flags & SUMP_FLAG1_GR0_DISABLE) width--;
|
||||
if (flags & SUMP_FLAG1_GR1_DISABLE) width--;
|
||||
// we don't support 24-bit or 32-bit capture - sorry
|
||||
if ((flags & SUMP_FLAG1_GR2_DISABLE) == 0) width = 0;
|
||||
if ((flags & SUMP_FLAG1_GR3_DISABLE) == 0) width = 0;
|
||||
|
||||
picoprobe_debug("%s(): sample %u bytes\n", __func__, width);
|
||||
|
||||
sump.width = width;
|
||||
}
|
||||
|
||||
static void sump_update_counts(uint32_t val) {
|
||||
/*
|
||||
* This just sets up how many samples there should be before
|
||||
* and after the trigger fires. The read_count is total samples
|
||||
* to return and delay_count number of samples after
|
||||
* the trigger.
|
||||
*
|
||||
* This sets the buffer splits like 0/100, 25/75, 50/50
|
||||
* for example if read_count == delay_count then we should
|
||||
* return all samples starting from the trigger point.
|
||||
* If delay_count < read_count we return
|
||||
* (read_count - delay_count) of samples from before
|
||||
* the trigger fired.
|
||||
*/
|
||||
|
||||
uint32_t read_count = ((val & 0xffff) + 1) * 4;
|
||||
uint32_t delay_count = ((val >> 16) + 1) * 4;
|
||||
|
||||
if (delay_count > read_count) read_count = delay_count;
|
||||
|
||||
sump.read_count = read_count;
|
||||
sump.delay_count = delay_count;
|
||||
}
|
||||
|
||||
static void sump_set_trigger_mask(uint32_t 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(uint32_t 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(uint32_t trig, uint32_t val) {
|
||||
struct _trigger* t = &sump.trigger[trig];
|
||||
t->start = (val & 0x08000000) != 0;
|
||||
t->serial = (val & 0x02000000) != 0;
|
||||
t->channel = ((val >> 20) & 0x0f) | ((val >> (24 - 4)) & 0x10);
|
||||
t->level = (val >> 16) & 3;
|
||||
t->delay = val & 0xffff;
|
||||
|
||||
picoprobe_debug("%s(): idx=%u val=0x%08x (start=%u serial=%u channel=%u level=%u delay=%u)\n",
|
||||
__func__, trig, val, t->start, t->serial, t->channel, t->level, t->delay);
|
||||
}
|
||||
|
||||
/* UART protocol handling ================================================== */
|
||||
|
||||
static void sump_rx_short(uint8_t cmd) {
|
||||
picoprobe_debug("%s(): 0x%02x\n", __func__, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SUMP_CMD_RESET: sump_do_reset(); break;
|
||||
case SUMP_CMD_ARM: sump_do_run(); break;
|
||||
case SUMP_CMD_ID: sump_do_id(); break;
|
||||
case SUMP_CMD_META: sump_do_meta(); break;
|
||||
case SUMP_CMD_FINISH: sump_do_finish(); break;
|
||||
case SUMP_CMD_QUERY_INPUT: break;
|
||||
case SUMP_CMD_ADVANCED_ARM: sump_do_run(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sump_rx_long(uint8_t* cmd) {
|
||||
uint32_t val = cmd[1] | (cmd[2] << 8) | (cmd[3] << 16) | (cmd[4] << 24);
|
||||
|
||||
picoprobe_debug("%s(): [0x%02x] 0x%08x\n", __func__, cmd[0], val);
|
||||
|
||||
switch (cmd[0]) {
|
||||
case SUMP_CMD_SET_SAMPLE_RATE:
|
||||
sump_do_stop();
|
||||
sump.divider = val + 1;
|
||||
break;
|
||||
case SUMP_CMD_SET_COUNTS:
|
||||
sump_do_stop();
|
||||
sump_update_counts(val);
|
||||
break;
|
||||
case SUMP_CMD_SET_FLAGS:
|
||||
sump_do_stop();
|
||||
sump_set_flags(val);
|
||||
break;
|
||||
case SUMP_CMD_SET_ADV_TRG_SELECT:
|
||||
case SUMP_CMD_SET_ADV_TRG_DATA: break; /* not implemented */
|
||||
|
||||
case SUMP_CMD_SET_BTRG0_MASK:
|
||||
case SUMP_CMD_SET_BTRG1_MASK:
|
||||
case SUMP_CMD_SET_BTRG2_MASK:
|
||||
case SUMP_CMD_SET_BTRG3_MASK:
|
||||
sump_set_trigger_mask((cmd[0] - SUMP_CMD_SET_BTRG0_MASK) / 3, val);
|
||||
break;
|
||||
|
||||
case SUMP_CMD_SET_BTRG0_VALUE:
|
||||
case SUMP_CMD_SET_BTRG1_VALUE:
|
||||
case SUMP_CMD_SET_BTRG2_VALUE:
|
||||
case SUMP_CMD_SET_BTRG3_VALUE:
|
||||
sump_set_trigger_value((cmd[0] - SUMP_CMD_SET_BTRG0_VALUE) / 3, val);
|
||||
break;
|
||||
|
||||
case SUMP_CMD_SET_BTRG0_CONFIG:
|
||||
case SUMP_CMD_SET_BTRG1_CONFIG:
|
||||
case SUMP_CMD_SET_BTRG2_CONFIG:
|
||||
case SUMP_CMD_SET_BTRG3_CONFIG:
|
||||
sump_set_trigger_config((cmd[0] - SUMP_CMD_SET_BTRG0_CONFIG) / 3, val);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
static void sump_rx(uint8_t* buf, uint32_t count) {
|
||||
if (count == 0) return;
|
||||
#if 0
|
||||
picoprobe_debug("%s(): ", __func__);
|
||||
picoprobe_debug_hexa(buf, count);
|
||||
picoprobe_debug("\n");
|
||||
#endif
|
||||
|
||||
while (count-- > 0) {
|
||||
sump.cmd[sump.cmd_pos++] = *buf++;
|
||||
|
||||
if (SUMP_CMD_IS_SHORT(sump.cmd[0])) {
|
||||
sump_rx_short(sump.cmd[0]);
|
||||
sump.cmd_pos = 0;
|
||||
} else if (sump.cmd_pos >= 5) {
|
||||
sump_rx_long(sump.cmd);
|
||||
sump.cmd_pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t sump_tx_empty(uint8_t* buf, uint32_t len) {
|
||||
uint32_t i;
|
||||
|
||||
uint32_t count = sump.read_count;
|
||||
// picoprobe_debug("%s: count=%u\n", __func__, count);
|
||||
uint8_t a = 0x55;
|
||||
|
||||
if (sump.flags & SUMP_FLAG1_ENABLE_RLE) {
|
||||
count += count & 1; // align up
|
||||
if (sump.width == 1) {
|
||||
for (i = 0; i < len && count > 0; count -= 2, i += 2) {
|
||||
*buf++ = 0x81; // RLE mark + two samples
|
||||
*buf++ = a;
|
||||
a ^= 0xff;
|
||||
}
|
||||
if (i > sump.read_count)
|
||||
sump.read_count = 0;
|
||||
else
|
||||
sump.read_count -= i;
|
||||
} else if (sump.width == 2) {
|
||||
for (i = 0; i < len && count > 0; count -= 2, i += 4) {
|
||||
*buf++ = 0x01; // two samples
|
||||
*buf++ = 0x80; // RLE mark + two samples
|
||||
*buf++ = a;
|
||||
*buf++ = a;
|
||||
a ^= 0xff;
|
||||
}
|
||||
|
||||
if (i / 2 > sump.read_count)
|
||||
sump.read_count = 0;
|
||||
else
|
||||
sump.read_count -= i / 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (sump.width == 1) {
|
||||
for (i = 0; i < len && count > 0; count--, i++) {
|
||||
*buf++ = a;
|
||||
a ^= 0xff;
|
||||
}
|
||||
|
||||
sump.read_count -= i;
|
||||
} else if (sump.width == 2) {
|
||||
for (i = 0; i < len && count > 0; count--, i += 2) {
|
||||
*buf++ = a;
|
||||
*buf++ = a;
|
||||
a ^= 0xff;
|
||||
}
|
||||
|
||||
sump.read_count -= i / 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// picoprobe_debug("%s: ret=%u\n", __func__, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint32_t sump_tx8(uint8_t* buf, uint32_t len) {
|
||||
uint32_t i;
|
||||
uint32_t count = sump.read_count;
|
||||
// picoprobe_debug("%s: count=%u, start=%u\n", __func__, count);
|
||||
uint8_t* ptr = sump.buffer + (sump.read_start + count) % SUMP_MEMORY_SIZE;
|
||||
|
||||
if (sump.flags & SUMP_FLAG1_ENABLE_RLE) {
|
||||
uint8_t b, rle_last = 0x80, rle_count = 0;
|
||||
|
||||
for (i = 0; i + 1 < len && count > 0; count--) {
|
||||
if (ptr == sump.buffer) ptr = sump.buffer + SUMP_MEMORY_SIZE;
|
||||
|
||||
b = *(--ptr) & 0x7f;
|
||||
|
||||
if (b != rle_last) {
|
||||
if (rle_count > 0) {
|
||||
*((uint16_t*)buf) = (rle_count - 1) | 0x80 | ((uint16_t)rle_last << 8);
|
||||
buf += 2;
|
||||
i += 2;
|
||||
sump.read_count -= rle_count;
|
||||
}
|
||||
|
||||
rle_last = b;
|
||||
rle_count = 1;
|
||||
continue;
|
||||
}
|
||||
if (++rle_count == 0x80) {
|
||||
*((uint16_t*)buf) = (rle_count - 1) | 0x80 | ((uint16_t)rle_last << 8);
|
||||
buf += 2;
|
||||
i += 2;
|
||||
sump.read_count -= rle_count;
|
||||
rle_count = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len && count > 0; i++, count--) {
|
||||
if (ptr == sump.buffer) ptr = sump.buffer + SUMP_MEMORY_SIZE;
|
||||
|
||||
*buf++ = *(--ptr);
|
||||
}
|
||||
|
||||
sump.read_count -= i;
|
||||
}
|
||||
|
||||
// picoprobe_debug("%s: ret=%u\n", __func__, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint32_t sump_tx16(uint8_t* buf, uint32_t len) {
|
||||
uint32_t i;
|
||||
uint32_t count = sump.read_count;
|
||||
// picoprobe_debug("%s: count=%u, start=%u\n", __func__, count, sump.read_count);
|
||||
volatile uint8_t* ptr = sump.buffer + (sump.read_start + count * 2) % SUMP_MEMORY_SIZE;
|
||||
|
||||
if (sump.flags & SUMP_FLAG1_ENABLE_RLE) {
|
||||
uint16_t rle_last = 0x8000, rle_count = 0;
|
||||
|
||||
for (i = 0; i + 3 < len && count > 0; count--) {
|
||||
if (ptr == sump.buffer) ptr = sump.buffer + SUMP_MEMORY_SIZE;
|
||||
|
||||
ptr -= 2;
|
||||
|
||||
uint32_t b = *((uint16_t*)ptr) & 0x7fff;
|
||||
|
||||
if (b != rle_last) {
|
||||
if (rle_count > 0) {
|
||||
*((uint32_t*)buf) = (rle_count - 1) | 0x8000 | ((uint32_t)rle_last << 16);
|
||||
buf += 4;
|
||||
i += 4;
|
||||
sump.read_count -= rle_count;
|
||||
}
|
||||
|
||||
rle_last = b;
|
||||
rle_count = 1;
|
||||
continue;
|
||||
}
|
||||
if (++rle_count == 0x8000) {
|
||||
*((uint32_t*)buf) = (rle_count - 1) | 0x8000 | ((uint32_t)rle_last << 16);
|
||||
buf += 4;
|
||||
i += 4;
|
||||
sump.read_count -= rle_count;
|
||||
rle_count = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i + 1 < len && count > 0; i += 2, count--) {
|
||||
if (ptr == sump.buffer) ptr = sump.buffer + SUMP_MEMORY_SIZE;
|
||||
|
||||
ptr -= 2;
|
||||
*((uint16_t*)buf) = *((uint16_t*)ptr);
|
||||
buf += 2;
|
||||
}
|
||||
|
||||
sump.read_count -= i / 2;
|
||||
}
|
||||
|
||||
// picoprobe_debug("%s: ret=%u\n", __func__, i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint32_t sump_fill_tx(uint8_t* buf, uint32_t len) {
|
||||
uint32_t ret;
|
||||
|
||||
assert((len & 3) == 0);
|
||||
if (sump.read_count == 0) {
|
||||
sump.state = SUMP_STATE_CONFIG;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sump.state == SUMP_STATE_DUMP) {
|
||||
if (sump.width == 1) {
|
||||
ret = sump_tx8(buf, len);
|
||||
} else if (sump.width == 2) {
|
||||
ret = sump_tx16(buf, len);
|
||||
} else {
|
||||
// invalid
|
||||
ret = sump_tx_empty(buf, len);
|
||||
}
|
||||
} else {
|
||||
// invalid or error
|
||||
ret = sump_tx_empty(buf, len);
|
||||
}
|
||||
|
||||
if (ret == 0) sump.state = SUMP_STATE_CONFIG;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sump_init_connect(void) {
|
||||
memset(&sump, 0, sizeof(sump));
|
||||
sump.width = 1;
|
||||
sump.divider = 1000; // a safe value
|
||||
sump.read_count = 256;
|
||||
sump.delay_count = 256;
|
||||
}
|
||||
|
||||
void cdc_sump_init(void) {
|
||||
sump_hw_init();
|
||||
|
||||
sump_init_connect();
|
||||
|
||||
picoprobe_debug("%s(): memory buffer %u bytes\n", __func__, SUMP_MEMORY_SIZE);
|
||||
}
|
||||
void cdc_sump_deinit(void) {
|
||||
sump_hw_deinit();
|
||||
|
||||
memset(&sump, 0, sizeof(sump));
|
||||
}
|
||||
|
||||
#define MAX_UART_PKT 64
|
||||
void cdc_sump_task(void) {
|
||||
uint8_t buf[MAX_UART_PKT];
|
||||
|
||||
if (tud_cdc_n_connected(CDC_INTF)) {
|
||||
if (!sump.cdc_connected) {
|
||||
sump_init_connect();
|
||||
sump.cdc_connected = true;
|
||||
}
|
||||
|
||||
if (sump.state == SUMP_STATE_DUMP || sump.state == SUMP_STATE_ERROR) {
|
||||
if (tud_cdc_n_write_available(CDC_INTF) >= sizeof(buf)) {
|
||||
uint32_t tx_len = sump_fill_tx(buf, sizeof(buf));
|
||||
tud_cdc_n_write(CDC_INTF, buf, tx_len);
|
||||
tud_cdc_n_write_flush(CDC_INTF);
|
||||
}
|
||||
}
|
||||
if (tud_cdc_n_available(CDC_INTF)) {
|
||||
uint32_t cmd_len = tud_cdc_n_read(CDC_INTF, buf, sizeof(buf));
|
||||
sump_rx(buf, cmd_len);
|
||||
}
|
||||
/*if (sump.state == SUMP_STATE_TRIGGER || sump.state == SUMP_STATE_SAMPLING)
|
||||
led_signal_activity(1);*/
|
||||
} else if (!sump.cdc_connected) {
|
||||
sump.cdc_connected = false;
|
||||
sump_do_reset();
|
||||
}
|
||||
}
|
||||
|
||||
// we could hook the callback, but it's not really used, so, eh.
|
||||
/*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);
|
||||
}*/
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
#ifndef SUMP_H
|
||||
#define SUMP_H
|
||||
|
||||
#define SUMP_META_END 0
|
||||
#define SUMP_META_NAME 1
|
||||
#define SUMP_META_FPGA_VERSION 2
|
||||
#define SUMP_META_CPU_VERSION 3
|
||||
#define SUMP_META_SAMPLE_RAM 0x21
|
||||
#define SUMP_META_SAMPLE_RATE 0x23
|
||||
#define SUMP_META_PROBES_B 0x40
|
||||
#define SUMP_META_PROTOCOL_B 0x41
|
||||
|
||||
#define SUMP_FLAG1_DDR (1<<0) /* "demux", apparently */
|
||||
#define SUMP_FLAG1_GR0_DISABLE (1<<2)
|
||||
#define SUMP_FLAG1_GR1_DISABLE (1<<3)
|
||||
#define SUMP_FLAG1_GR2_DISABLE (1<<4)
|
||||
#define SUMP_FLAG1_GR3_DISABLE (1<<5)
|
||||
#define SUMP_FLAG1_ENABLE_RLE (1<<8)
|
||||
#define SUMP_FLAG1_EXT_TEST (1<<10)
|
||||
|
||||
#define SUMP_CMD_RESET 0
|
||||
#define SUMP_CMD_ARM 1
|
||||
#define SUMP_CMD_ID 2
|
||||
#define SUMP_CMD_META 4
|
||||
#define SUMP_CMD_FINISH 5
|
||||
|
||||
/* demon core extensions */
|
||||
#define SUMP_CMD_QUERY_INPUT 6
|
||||
#define SUMP_CMD_ADVANCED_ARM 0xF
|
||||
|
||||
#define SUMP_CMD_SET_SAMPLE_RATE 0x80
|
||||
#define SUMP_CMD_SET_COUNTS 0x81
|
||||
#define SUMP_CMD_SET_FLAGS 0x82
|
||||
|
||||
/* demon core extensiosns */
|
||||
#define SUMP_CMD_SET_ADV_TRG_SELECT 0x9E
|
||||
#define SUMP_CMD_SET_ADV_TRG_DATA 0x9F
|
||||
|
||||
#define SUMP_CMD_SET_BTRG0_MASK 0xC0
|
||||
#define SUMP_CMD_SET_BTRG1_MASK 0xC4
|
||||
#define SUMP_CMD_SET_BTRG2_MASK 0xC8
|
||||
#define SUMP_CMD_SET_BTRG3_MASK 0xCC
|
||||
#define SUMP_CMD_SET_BTRG0_VALUE 0xC1
|
||||
#define SUMP_CMD_SET_BTRG1_VALUE 0xC5
|
||||
#define SUMP_CMD_SET_BTRG2_VALUE 0xC9
|
||||
#define SUMP_CMD_SET_BTRG3_VALUE 0xCD
|
||||
#define SUMP_CMD_SET_BTRG0_CONFIG 0xC2
|
||||
#define SUMP_CMD_SET_BTRG1_CONFIG 0xC6
|
||||
#define SUMP_CMD_SET_BTRG2_CONFIG 0xCA
|
||||
#define SUMP_CMD_SET_BTRG3_CONFIG 0xCE
|
||||
|
||||
inline static int SUMP_CMD_IS_SHORT(int cmd) {
|
||||
return !(cmd & 0x80); // crude but works
|
||||
}
|
||||
|
||||
/* **** */
|
||||
|
||||
#define ONE_MHZ 1000000u
|
||||
|
||||
/* **** */
|
||||
|
||||
uint32_t sump_calc_sysclk_divider();
|
||||
|
||||
uint8_t *sump_capture_get_next_dest(uint32_t numch);
|
||||
void sump_capture_callback_cancel(void);
|
||||
void sump_capture_callback(uint32_t ch, uint32_t numch);
|
||||
|
||||
void cdc_sump_init(void);
|
||||
void cdc_sump_deinit(void);
|
||||
void cdc_sump_task(void);
|
||||
|
||||
/* --- */
|
||||
|
||||
void sump_hw_get_cpu_name(char cpu[32]);
|
||||
void sump_hw_get_hw_name(char hw[32]);
|
||||
|
||||
uint32_t sump_hw_get_sysclk(void);
|
||||
|
||||
void sump_hw_init(void);
|
||||
void sump_hw_deinit(void);
|
||||
|
||||
void sump_hw_capture_setup_next(uint32_t ch, uint32_t mask, uint32_t chunk_size, uint32_t next_count, uint8_t width);
|
||||
void sump_hw_capture_start(uint8_t width, int flags, uint32_t chunk_size, uint8_t *destbuf);
|
||||
void sump_hw_capture_stop(void);
|
||||
void sump_hw_stop(void);
|
||||
|
||||
#endif
|
|
@ -4,13 +4,15 @@
|
|||
|
||||
#include "mode.h"
|
||||
|
||||
extern struct mode m_01_default/*, m_02_default2*/;
|
||||
extern struct mode m_01_default, m_04_sump;
|
||||
|
||||
// clang-format off
|
||||
const struct mode* const mode_list[16] = {
|
||||
NULL, // dummy 0 entry
|
||||
&m_01_default, // entry 1 CANNOT be NULL!
|
||||
//&m_02_default2,
|
||||
NULL, // mode 2 (jtag scan) not implemented yet
|
||||
NULL, // mode 3 (hw chip programming stuff) not implemented yet
|
||||
&m_04_sump,
|
||||
NULL, // terminating entry
|
||||
};
|
||||
// clang-format on
|
||||
|
|
Loading…
Reference in New Issue