From e92afbcc0610982973ae2c9f4885d18c916debab Mon Sep 17 00:00:00 2001 From: sys64738 Date: Tue, 5 Oct 2021 16:47:32 +0200 Subject: [PATCH] start of ISP mode (mode2), MehFET SBW impl --- CMakeLists.txt | 5 + bsp/rp2040/bsp-info.h | 2 +- bsp/rp2040/m_default/dap_jtag.c | 11 +- bsp/rp2040/m_default/dap_swd.c | 11 +- bsp/rp2040/m_default/pinout.h | 20 +- bsp/rp2040/m_isp/bsp-feature.h | 34 +++ bsp/rp2040/m_isp/mehfet_hw.c | 174 +++++++++++++ bsp/rp2040/m_isp/pinout.h | 83 +++++++ bsp/rp2040/m_isp/sbw.pio | 206 ++++++++++++++++ bsp/rp2040/m_isp/sbw_hw.c | 290 ++++++++++++++++++++++ bsp/rp2040/m_isp/sbw_hw.h | 38 +++ host/dpctl/commands.py | 1 + src/.gitignore | 1 + src/m_default/_default.c | 11 +- src/m_default/cdc.h | 2 +- src/m_default/cdc_serprog.c | 2 + src/m_default/vnd_i2ctinyusb.c | 1 + src/m_isp/_isp.c | 332 +++++++++++++++++++++++++ src/m_isp/mehfet.c | 424 ++++++++++++++++++++++++++++++++ src/m_isp/mehfet.h | 95 +++++++ src/m_sump/cdc_sump.c | 1 + src/main.c | 10 + src/modeset.c | 4 +- src/util.h | 2 + src/vnd_cfg.c | 3 +- 25 files changed, 1728 insertions(+), 35 deletions(-) create mode 100644 bsp/rp2040/m_isp/bsp-feature.h create mode 100644 bsp/rp2040/m_isp/mehfet_hw.c create mode 100644 bsp/rp2040/m_isp/pinout.h create mode 100644 bsp/rp2040/m_isp/sbw.pio create mode 100644 bsp/rp2040/m_isp/sbw_hw.c create mode 100644 bsp/rp2040/m_isp/sbw_hw.h create mode 100644 src/.gitignore create mode 100644 src/m_isp/_isp.c create mode 100644 src/m_isp/mehfet.c create mode 100644 src/m_isp/mehfet.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aa80748..96f251a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,8 @@ 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_isp/_isp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/m_isp/mehfet.c ${CMAKE_CURRENT_SOURCE_DIR}/src/m_jscan/_jscan.c ${CMAKE_CURRENT_SOURCE_DIR}/src/m_jscan/jscan.c ${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/_sump.c @@ -89,6 +91,8 @@ target_sources(${PROJECT} PUBLIC ${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}/bsp/${FAMILY}/m_isp/mehfet_hw.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_isp/sbw_hw.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_jscan/jscan_hw.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_sump/sump_hw.c ) @@ -140,6 +144,7 @@ if(FAMILY STREQUAL "rp2040") pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_swd.pio) pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_uart_rx.pio) pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/swo_manchester_encoding.pio) + pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_isp/sbw.pio) pico_add_extra_outputs(${PROJECT}) diff --git a/bsp/rp2040/bsp-info.h b/bsp/rp2040/bsp-info.h index d2fd1a8..c54a56d 100644 --- a/bsp/rp2040/bsp-info.h +++ b/bsp/rp2040/bsp-info.h @@ -20,6 +20,6 @@ #else #define CFG_TUD_CDC 2 #endif -#define CFG_TUD_VENDOR 1 +#define CFG_TUD_VENDOR 2 #endif diff --git a/bsp/rp2040/m_default/dap_jtag.c b/bsp/rp2040/m_default/dap_jtag.c index c6d3f05..a20a7c4 100644 --- a/bsp/rp2040/m_default/dap_jtag.c +++ b/bsp/rp2040/m_default/dap_jtag.c @@ -4,6 +4,8 @@ #include #include +#include "util.h" + #include "DAP_config.h" #include @@ -172,15 +174,6 @@ void PORT_JTAG_SETUP(void) { 50*1000, PINOUT_JTAG_TCK, PINOUT_JTAG_TDI, PINOUT_JTAG_TDO); } -static uint8_t bitswap(uint8_t in) { - static const uint8_t lut[16] = { - 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, - 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf - }; - - return (lut[in&0xf] << 4) | lut[in>>4]; -} - void JTAG_Sequence(uint32_t info, const uint8_t* tdi, uint8_t* tdo) { float div = (float)clock_get_hz(clk_sys) / (4 * DAP_Data.clock_freq); if (div < 2) div = 2; diff --git a/bsp/rp2040/m_default/dap_swd.c b/bsp/rp2040/m_default/dap_swd.c index 3407927..f03584f 100644 --- a/bsp/rp2040/m_default/dap_swd.c +++ b/bsp/rp2040/m_default/dap_swd.c @@ -8,6 +8,8 @@ #include "DAP_config.h" #include +#include "util.h" + #include "dap_swd.pio.h" #define SWD_PIO @@ -135,15 +137,6 @@ inline static void PIN_SWDIO_SET_PIO(void) { (1u << PINOUT_SWDIO), (1u << PINOUT_SWDIO)); } -static uint8_t bitswap(uint8_t in) { - static const uint8_t lut[16] = { - 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, - 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf - }; - - return (lut[in&0xf] << 4) | lut[in>>4]; -} - void SWD_Sequence(uint32_t info, const uint8_t* swdo, uint8_t* swdi) { pio_sm_set_enabled(PINOUT_JTAG_PIO_DEV, swdsm, true); //busy_wait_us_32(0); diff --git a/bsp/rp2040/m_default/pinout.h b/bsp/rp2040/m_default/pinout.h index d7235a9..f672ee7 100644 --- a/bsp/rp2040/m_default/pinout.h +++ b/bsp/rp2040/m_default/pinout.h @@ -19,7 +19,7 @@ #define PINOUT_JTAG_nTRST 6 #define PINOUT_JTAG_nRESET 7 #define PINOUT_JTAG_PIO_DEV pio0 -#define PINOUT_JTAG_SWO_DEV pio1 +#define PINOUT_JTAG_SWO_DEV pio0 // SPI config #define PINOUT_SPI_DEV spi1 @@ -62,15 +62,17 @@ * SWO-MC 1 * * PIO: - * PIO0: (max. 4) - * JTAG 1 - * SWD 1 - * PIO1: (max. 4) - * SWO 2 + * PIO0: (max. 4 SM, max. 32 insn) + * JTAG 1 6 + * SWD 1 11 + * SWO 2 6 (manchester) + 9 (uart) * - * UART0: stdio + * PIO0 IS NOW FULL! + * PIO1: (max. 4 SM, max. 32 insn) * - * UART1: USB-CDC/DAP-UART + * UART: stdio + * 0: stdio + * 1: USB-CDC/DAP-UART * * SPI: * 1: SPI access @@ -79,7 +81,7 @@ * 0: I2C access * * ADC: - * + * temperature sensor */ #endif diff --git a/bsp/rp2040/m_isp/bsp-feature.h b/bsp/rp2040/m_isp/bsp-feature.h new file mode 100644 index 0000000..b9cfedb --- /dev/null +++ b/bsp/rp2040/m_isp/bsp-feature.h @@ -0,0 +1,34 @@ + +#ifndef BSP_FEATURE_M_ISP_H_ +#define BSP_FEATURE_M_ISP_H_ + +#define DBOARD_HAS_UART +#define DBOARD_HAS_CMSISDAP +#define DBOARD_HAS_MEHFET + +#include "bsp-info.h" + +enum { + HID_N_CMSISDAP = 0, + + HID_N__NITF +}; +enum { + CDC_N_UART = 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_MEHFET, + + VND_N__NITF +}; + +#endif + diff --git a/bsp/rp2040/m_isp/mehfet_hw.c b/bsp/rp2040/m_isp/mehfet_hw.c new file mode 100644 index 0000000..a089caa --- /dev/null +++ b/bsp/rp2040/m_isp/mehfet_hw.c @@ -0,0 +1,174 @@ +// vim: set et: + +#include +#include + +#include "util.h" +#include "m_isp/sbw_hw.h" + +#include "m_isp/mehfet.h" + +void mehfet_hw_delay_ms(uint32_t t) { busy_wait_ms (t); } +void mehfet_hw_delay_us(uint32_t t) { busy_wait_us_32(t); } + +static absolute_time_t target; +void mehfet_hw_timer_start(bool us, uint32_t to_reach) { + target = us ? make_timeout_time_us(to_reach) : make_timeout_time_ms(to_reach); +} +bool mehfet_hw_timer_reached(void) { return time_reached(target); } + + +void mehfet_hw_init(void) { + // don't init things just yet: PIO SM1 probably needs to be shared between + // multiple ISP/ICE/... protocols, so we only init stuff once actually starting + + // TODO: init pin gpio mux stuff (to GPIO and not PIO first!) + // ^: or not: this will probably keep the target into reset, maybe not + // desired... +} +void mehfet_hw_deinit(void) { + // shrug + sbw_deinit(); // can't hurt + // TODO: reset pin gpio mux stuff +} + +__attribute__((__const__)) +enum mehfet_caps mehfet_hw_get_caps(void) { + // only support SBW for now. we could add JTAG, but this will probably + // become messy very quickly, as it's behind CMSIS-DAP. implement later + // if someone needs it. + return mehfet_cap_sbw_entryseq | + mehfet_cap_has_reset_tap | mehfet_cap_has_irshift | mehfet_cap_has_drshift; +} + +const char* /*error string, NULL if no error*/ mehfet_hw_connect(enum mehfet_conn conn) { + sbw_preinit(conn & mehfet_conn_nrstmask); + + if (!sbw_init()) { + // TODO: release target + // TODO: reset pin gpio mux stuff + return "SBW PIO init failed"; + } + + return NULL; +} +void mehfet_hw_disconnect(void) { + sbw_deinit(); +} + +void mehfet_hw_reset_target(void) { + +} +uint8_t mehfet_hw_get_old_lines(void) { + return (sbw_get_last_tclk() ? 1 : 0) + | (sbw_get_last_tms () ? 2 : 0) + | (sbw_get_last_tdi () ? 4 : 0); +} + +void mehfet_hw_tdio_seq(uint32_t ncyc, bool tmslvl, const uint8_t* tdi, uint8_t* tdo) { + sbw_sequence(ncyc, tmslvl, tdi, tdo); +} +void mehfet_hw_tms_seq(uint32_t ncyc, bool tdilvl, const uint8_t* tms) { + sbw_tms_sequence(ncyc, tdilvl, tms); +} +void mehfet_hw_tclk_edge(bool newtclk) { + sbw_clrset_tclk(newtclk); +} +void mehfet_hw_tclk_burst(uint32_t ncyc) { + sbw_tclk_burst(ncyc); +} + +void mehfet_hw_reset_tap(void) { + // TDI always 1 + // TMS=1,1,1,1,1,1 -- reset TAP state to initial + // TMS=0 -- test-logic-reset to run-test/idle + // TMS=1,0,1,0,1 -- perform fuse check + // TMS=1,0 -- back to run-test/idle (needed for SBW only) + + //const uint16_t tms_seq = 0x1abf;//0x3f | (0<<6) | (0x15 << 7) | (0x1 << 12); + const uint8_t tms_seq[2] = {0xbf,0x1a}; + sbw_tms_sequence(14, true, tms_seq); +} + +uint8_t mehfet_hw_shift_ir(uint8_t newir) { + // 1100: run-test/idle -> select-dr-scan -> select-ir-scan -> capture-ir -> shift-ir + const uint8_t tms_seqa = 0x03; + const uint8_t tms_seqb = 0x03 >> 1; + sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seqa); + sbw_tms_sequence(3, true, &tms_seqb); + + // 7 data bits with TMS=0 + // 1 data bit with TMS=1 (to exit1-ir) + uint8_t res = 0, resb = 0, newir2 = newir >> 7; + sbw_sequence(7, false, &newir , &res ); + sbw_sequence(1, true , &newir2, &resb); + res |= resb << 7; + + // TMS=1 (to update-ir) + // TMS=0 (to run-test/idle) + const uint8_t tms_seq_2a = 0x01; + const uint8_t tms_seq_2b = 0x01 >> 1; + sbw_tms_sequence(1, true, &tms_seq_2a); + sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seq_2b); + + return bitswap(res); // fsr also needed here +} + +static void bitswap_n(uint32_t nbytes, uint8_t* data) { + for (uint32_t i = 0, j = nbytes - 1; i < nbytes; ++i, --j) { + if (i == j) data[i] = bitswap(data[i]); + else { + uint8_t tmp = bitswap(data[i]); + data[i] = bitswap(data[j]); + data[j] = tmp; + } + } +} + +void mehfet_hw_shift_dr(uint32_t nbits, uint8_t* drin, uint8_t* drout) { + // 100: run-test/idle -> select-dr-scan -> capture-dr -> shift-dr + const uint8_t tms_seqa = 0x01; + const uint8_t tms_seqb = 0x01 >> 1; + sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seqa); + sbw_tms_sequence(2, true, &tms_seqb); + + if (nbits == 16) { // fast path: DR is often 16 bits wide + // DR content is MSB-first instead of LSB-first (IR is the latter) + uint16_t newdr = bitswap(drin[1]) | ((uint16_t)bitswap(drin[0]) << 8); + + // 15 data bits with TMS=0 + // 1 data bit with TMS=1 (to exit1-dr) + uint16_t res = 0; + uint8_t newdr2 = newdr >> 15, resb = 0; + // this is little-endian-only, but that's fine on the rp2040 + sbw_sequence(15, false, (const uint8_t*)&newdr, (uint8_t*)&res); + sbw_sequence( 1, true , &newdr2, &resb); + res |= (uint16_t)resb << 15; + + drout[0] = bitswap(res >> 8); + drout[1] = bitswap(res & 0xff); + } else { + uint32_t nbytes = (nbits + 7) >> 3; + + // DR content is MSB-first instead of LSB-first (IR is the latter) + bitswap_n(nbytes, drin); + + // n-1 data bits with TMS=0 + // 1 data bit with TMS=1 (to exit1-dr) + uint8_t newdr2, resb = 0; + newdr2 = drin[nbytes - 1] >> ((nbits - 1) & 7); + sbw_sequence(nbits - 1, false, drin, drout); + sbw_sequence(1, true , &newdr2, &resb); + drout[nbytes - 1] |= resb << ((nbits - 1) & 7); + + bitswap_n(nbytes, drout); + } + + // TMS=1 (to update-dr) + // TMS=0 (to run-test/idle) + const uint8_t tms_seq_2a = 0x01; + const uint8_t tms_seq_2b = 0x01 >> 1; + sbw_tms_sequence(1, true, &tms_seq_2a); + sbw_tms_sequence(1, sbw_get_last_tclk(), &tms_seq_2b); +} + diff --git a/bsp/rp2040/m_isp/pinout.h b/bsp/rp2040/m_isp/pinout.h new file mode 100644 index 0000000..c1ac176 --- /dev/null +++ b/bsp/rp2040/m_isp/pinout.h @@ -0,0 +1,83 @@ +// vim: set et ts=8: + +#ifndef PINOUT_H_ +#define PINOUT_H_ + +// NOTE NOTE NOTE: as mode2 shares some stuff with mode1 (CMSIS-DAP, UART), +// make sure that pinouts are compatible etc. + +// UART config +#define PINOUT_UART_TX 8 +#define PINOUT_UART_RX 9 +/*#define PINOUT_UART_CTS 10 +#define PINOUT_UART_RTS 11*/ +#define PINOUT_UART_INTERFACE uart1 +#define PINOUT_UART_BAUDRATE 115200 + +// JTAG config +#define PINOUT_JTAG_TCK 2 // == SWCLK +#define PINOUT_JTAG_TMS 3 // == SWDIO +#define PINOUT_JTAG_TDI 4 +#define PINOUT_JTAG_TDO 5 +#define PINOUT_JTAG_nTRST 6 +#define PINOUT_JTAG_nRESET 7 +#define PINOUT_JTAG_PIO_DEV pio0 +#define PINOUT_JTAG_SWO_DEV pio0 + +// SBW config +#define PINOUT_SBW_PIO pio1 +#define PINOUT_SBW_TCK 14 +#define PINOUT_SBW_TDIO 15 + +// LED config + +// you can change these two as you like +#define PINOUT_LED_CONNECTED 1 +#define PINOUT_LED_RUNNING 0 + +#ifndef PINOUT_LED +#ifndef PICO_DEFAULT_LED_PIN +#error "PICO_DEFAULT_LED_PIN is not defined, run PICOPROBE_LED= cmake" +#elif PICO_DEFAULT_LED_PIN == -1 +#error "PICO_DEFAULT_LED_PIN is defined as -1, run PICOPROBE_LED= cmake" +#else +#define PINOUT_LED PICO_DEFAULT_LED_PIN +#endif +#endif /* PICOPROBE_LED */ + +/* + * HARDWARE RESOURCE USAGE: + * + * IRQ: + * DMA0 DAP-UART + * UART1 DAP-UART + * + * DMA: (max. 12) + * DAP-UART 2 + * SWO-UART 1 + * SWO-MC 1 + * + * PIO: + * PIO0: (max. 4 SM, max. 32 insn) + * JTAG 1 6 + * SWD 1 11 + * SWO 2 6 (manchester) + 9 (uart) + * + * PIO0 IS NOW FULL! + * PIO1: (max. 4 SM, max. 32 insn) + * SBW 1 32 + * + * UART: stdio + * 0: stdio + * 1: USB-CDC/DAP-UART + * + * SPI: + * + * I2C: + * + * ADC: + * + */ + +#endif + diff --git a/bsp/rp2040/m_isp/sbw.pio b/bsp/rp2040/m_isp/sbw.pio new file mode 100644 index 0000000..1bafa53 --- /dev/null +++ b/bsp/rp2040/m_isp/sbw.pio @@ -0,0 +1,206 @@ + +.program sbw +.side_set 1 + +; Pin assignments: +; - SBWTCK is side-set pin 0 +; - SBWTDIO is OUT/IN pin 0 +; +; Autopush and autopull must be enabled, set to 8 +; SBWTDIO input should not be guarded with sync flipflops, as TDO xfers are synchronous + +PUBLIC start: + ; SBWTCK hi to not lose/reset debug mode + pull side 1 ; clear leftover OSR bits, pull in new data +startloop: + out exec, 16 side 1 ; use for set y, 0/1 ; in x, num ; jmp addr + jmp startloop side 1 + +; "subroutine" "calling convention" +; * set y, 0/1 : initial TMS (sbw_seq) / TDI (sbw_tms_seq) / TCLK (sbw_tclk_burst) value +; * in x, num : number of JTAG cycles (sbw_seq/sbw_tms_seq) / TCLK half-cycles (sbw_tclk_burst) +; * jmp subroutine +; * ^ all 'side 1' + +; y: static TMS value to use +; x: number of JTAG clock cycles minus one +; TDI output gets sourced bit by bit from the TX FIFO +; TDO input gets sent bit by bit to the RX FIFO +PUBLIC sbw_seq: +sbw_seq_iter: + ; tms slot: + set pindirs, 1 side 1 ; SBWTDIO is now output + mov pins, y side 1 ; output static TMS value + nop side 0 [1] ; target reads TMS at falling edge + ; tdi slot: + out pins, 1 side 1 [1] ; output TDI from FIFO + nop side 0 [1] ; target reads TDI at falling edge + ; tdo slot: + set pindirs, 0 side 1 [1] ; we need some clock pulse + nop side 0 ; give target some time to drive IO + in pins, 1 side 0 ; input TDO + + jmp x--, sbw_seq_iter side 1 ; also gives target some time to stop driving IO + ; NOTE: there's a glitch: if TMS=TDO!=TDI and !=TDI_next, + ; then there's a short glitch (wrong voltage level) in the + ; TDO->TMS high clock phase. it's benign. + + push side 1 ; flush ISR + jmp start side 1 + + +; y: static TDI value to use +; x: number of JTAG clock cycles minus one +; TMS output gets sourced bit by bit from the TX FIFO +PUBLIC sbw_tms_seq: + ; tms slot + set pindirs, 1 side 1 ; SBWTDIO is now output + out pins, 1 side 1 ; output TMS from FIFO + nop side 0 [1] ; target reads TMS at falling edge + ; tdi slot + mov pins, y side 1 [1] ; output static TDI value + nop side 0 [1] ; target reads TDI at falling edge + ; tdo slot + set pindirs, 0 side 1 [1] + jmp x--, sbw_tms_seq side 0 [1] ; ignore returned TDO, go back + + jmp start side 1 + + +; stationary SBWTCK values are: +; jmp +; TMS TDI TDO |TMS TDI TDO +; | | | || | | +; 1100110011001110011001100 ... +; +; a full cycle takes 13 cycles, a single slot 4 +; the SM divider should be sysclk/(4*baudrate) +; baudrate shouldn't exceed 20 MHz (typical value is 18 MHz?) +; SBWTCK LOW phases shouldn't exceed 7us, so the baudrate should be at least 40 kHz + +; y: initial/previous TCLK value +; x: number of TCLK *half*-cycles minus one! +; TCLK values get sourced from the TX FIFO. best is to use a DMA with fixed +; source address, with value 0x55/0xaa (depending on y) for strobes, or +; 0x00/0xff (and x=0) for a single set/clear. +; alternatively, one could set the "load-bearing instruction" to a +; "set pins, 0/1 side 1 [12]" for a fixed value useful for single sets/clears +PUBLIC sbw_tclk_burst: + set pindirs, 1 side 1 ; SBWTDIO is now output + ; tms slot: + set pins, 0 side 1 ; stay in run-test/idle TAP state + nop side 0 ; target reads TMS at falling edge + mov pins, y side 0 ; during low phase, prepare TCLK + ; tdi slot: + nop side 1 [12] ; wait a bit + burst_loop: +;PUBLIC sbw_tclk_burst_loadbearing_insn: + out pins, 1 side 1 [12] ; in the middle of TDI hiphase: do TCLK + jmp x--, burst_loop side 1 [12] + nop side 0 [1] ; need a low clock edge for TDO + ; tdo slot: + set pindirs, 0 side 1 [1] + nop side 0 [1] + + ;jmp start side 1 ; not needed because of wrapping +; 32 insns -- filling one entire PIO instruction memory + +; TODO: update this paragraph +; a full TCLK cycle in this burst mode takes 24 PIOSM cycles. at a "standard" +; baudrate of 18 MHz, this ends up being 375 kHz, which is in the required +; range when doing eg. flash programming. at the max baudrate (20 MHz), the +; TCLK speed is 417 kHz, which is still ok. max TCLK would be, if PIOSM freq is +; 125 MHz, 2.6 MHz, which is good enough imo +; TODO: determine minimum baudrate for stable TCLK (12 MHz?) + + +% c-sdk { +static inline void sbw_pio_init(PIO pio, uint sm, uint prog_offs, + float freq, uint pin_sbwclk, uint pin_sbwio) { + if (freq < 72e3) freq = 72e3; + if (freq > 20e6) freq = 20e6; + + pio_sm_set_enabled(pio, sm, false); + + pio_sm_config c = sbw_program_get_default_config(prog_offs); + sm_config_set_out_pins(&c, pin_sbwio, 1); + sm_config_set_set_pins(&c, pin_sbwio, 1); + sm_config_set_in_pins(&c, pin_sbwio); + sm_config_set_sideset_pins(&c, pin_sbwclk); + sm_config_set_out_shift(&c, false, true, 8); + sm_config_set_in_shift(&c, false, true, 8); + sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (4 * freq)); + pio_sm_init(pio, sm, prog_offs, &c); + + // SBWTDIO is low, SBWTCK is high, SBWTDIO is input by default + pio_sm_set_pindirs_with_mask(pio, sm, + (1u << pin_sbwclk) | (1u << pin_sbwio), (1u << pin_sbwclk) | (1u << pin_sbwio)); + + // need to do an extra set with sideset to make sure clk stays high and + // there's no glitch + pio_sm_exec(pio, sm, pio_encode_set(pio_pins, 1) | pio_encode_sideset(1, 1)); + + // SBW is mostly synchronous, bypass input synchroniser to reduce delay + hw_set_bits(&pio->input_sync_bypass, 1u << pin_sbwio); + + gpio_set_pulls(pin_sbwio, false, true); // SBWTDIO is pulldn + + pio_sm_set_enabled(pio, sm, true); + + // set padsbank func to PIO *after* initing PIO, otherwise a glitch occurs + pio_gpio_init(pio, pin_sbwclk); + pio_gpio_init(pio, pin_sbwio ); +} + +// meant for sbw_seq/sbw_tms_seq +static inline void sbw_pio_set_baudrate(PIO pio, uint sm, float freq) { + if (freq < 72e3) freq = 72e3; + if (freq > 20e6) freq = 20e6; + + pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / (4 * freq)); +} + +// meant for sbw_tclk_burst +static inline void sbw_pio_set_tclkfreq(PIO pio, uint sm, float freq) { + if (freq < 250e3) freq = 250e3; + if (freq > 450e3) freq = 450e3; + + pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / (24 * freq)); +} + +static inline uint16_t sbw_pio_gen_setx(uint x) { + return pio_encode_set(pio_x, x) | pio_encode_sideset(1, 1) | (1<<12); +} +static inline uint16_t sbw_pio_gen_sety(uint y) { + return pio_encode_set(pio_y, y) | pio_encode_sideset(1, 1) | (1<<12); +} +static inline uint16_t sbw_pio_gen_outx(uint bits) { + return pio_encode_out(pio_x, bits) | pio_encode_sideset(1, 1) | (1<<12); +} +// subroutine is one of "sbw_offset_sbw_seq", "sbw_offset_sbw_tms_seq", "sbw_offset_sbw_tclk_burst" +static inline uint16_t sbw_pio_gen_jmp(uint subroutine) { + return pio_encode_jmp(subroutine) | pio_encode_sideset(1, 1) | (1<<12); +} + +/*static inline uint16_t sbw_pio_loadbearing_gen_outpins(void) { + return pio_encode_out(pio_pins, 1) | pio_encode_sideset(1, 1) | pio_encode_delay(12) | (1<<12); +} +static inline uint16_t sbw_pio_loadbearing_gen_setpins(uint value) { + return pio_encode_set(pio_pins, value) | pio_encode_sideset(1, 1) | pio_encode_delay(12) | (1<<12); +} + +static inline void sbw_pio_loadbearing_set_outpins(PIO pio) { + pio->instr_mem[sbw_offset_sbw_tclk_burst_loadbearing_insn] = + sbw_pio_loadbearing_gen_outpins(); +} +static inline void sbw_pio_loadbearing_set_setpins(PIO pio, uint value) { + pio->instr_mem[sbw_offset_sbw_tclk_burst_loadbearing_insn] = + sbw_pio_loadbearing_gen_setpins(value); +}*/ + +/*static inline bool sbw_pio_is_idle(PIO pio, uint sm, uint offset) { + return pio_sm_get_pc(pio, sm) < sbw_offset_sbw_seq + offset; +}*/ + +%} + diff --git a/bsp/rp2040/m_isp/sbw_hw.c b/bsp/rp2040/m_isp/sbw_hw.c new file mode 100644 index 0000000..73563de --- /dev/null +++ b/bsp/rp2040/m_isp/sbw_hw.c @@ -0,0 +1,290 @@ +// vim: set et: + +#include + +#include +#include +#include +#include +#include +#include + +#include "m_isp/pinout.h" +#include "m_isp/sbw_hw.h" + +#include "sbw.pio.h" + +void sbw_preinit(bool nrst) { + // TODO: nrst + + //SLAU320AJ 2.3.1.1 ; SLAS722G p36: + // TEST/SBWTCK low for >100 us: reset debug state + // set nRST/NMI/SBWTDIO low: avoid sending an NMI when the debugger detaches + // TEST/SBWTCK high for >1 us : signal that we want a debugger + // nRST/NMI/SBWTDIO high: we want SBW + // TEST low for >0.025us <7us: latch on "we want SBW" signal + // TEST high again for >1 us: ready to SBW + + //gpio_put(PINOUT_SBW_TCK , false); + gpio_put(PINOUT_SBW_TDIO, true ); + gpio_put(PINOUT_SBW_TDIO, false); + gpio_set_dir(PINOUT_SBW_TCK , true); + gpio_set_dir(PINOUT_SBW_TDIO, true); + gpio_set_function(PINOUT_SBW_TCK , GPIO_FUNC_SIO); + gpio_set_function(PINOUT_SBW_TDIO, GPIO_FUNC_SIO); + + /*// TCK, TDIO now low + busy_wait_ms(4);//busy_wait_us_32(150); // reset debug state while keeping CPU in reset + + gpio_put(PINOUT_SBW_TCK , true ); // we want a debugger + busy_wait_us_32(1); + gpio_put(PINOUT_SBW_TDIO, true ); // we want SBW + busy_wait_ms(20);//busy_wait_us_32(1); + gpio_put(PINOUT_SBW_TCK , false); // latch "we want SBW" + busy_wait_us_32(3); + gpio_put(PINOUT_SBW_TCK , true ); // start SBW stuff + busy_wait_ms(5);//busy_wait_us_32(100); // wait a bit more*/ + + gpio_put(PINOUT_SBW_TCK , false); + gpio_put(PINOUT_SBW_TDIO, true); + busy_wait_ms(4); // reset TEST logic + gpio_put(PINOUT_SBW_TDIO, true); + busy_wait_us_32(1); + gpio_put(PINOUT_SBW_TCK , true); + busy_wait_ms(20); // activate TEST logic + + // "phase 1" + gpio_put(PINOUT_SBW_TDIO, true); + busy_wait_us_32(60); + + // "phase 2" + gpio_put(PINOUT_SBW_TCK , false); + + // "phase 3" + busy_wait_us_32(1); + + // "phase 4" + gpio_put(PINOUT_SBW_TCK , true); + busy_wait_us_32(60); + + // "phase 5" + busy_wait_ms(5); +} + +static int sbw_piosm = -1, sbw_offset = -1; + +static bool last_tclk = true; +static uint8_t last_tdi = 0xff, last_tms = 0xff; + +bool sbw_init(void) { + if (sbw_piosm >= 0 || sbw_offset >= 0) return false; + + if (!pio_can_add_program(PINOUT_SBW_PIO, &sbw_program)) return false; + sbw_offset = pio_add_program(PINOUT_SBW_PIO, &sbw_program); + + sbw_piosm = pio_claim_unused_sm(PINOUT_SBW_PIO, false); + if (sbw_piosm < 0) { + pio_remove_program(PINOUT_SBW_PIO, &sbw_program, sbw_offset); + sbw_offset = -1; + return false; + } + + // need to start at 50 kHz: fuse check needs TMS cycles with a low phase + // of at least 5us. 50 kHz is below the required time (the actual maximum + // frequency would be around 80 kHz), but the exact frequency doesn't + // matter much as we'll switch to a higher one once the check has been + // completed + sbw_pio_init(PINOUT_SBW_PIO, sbw_piosm, sbw_offset, 50e3, + PINOUT_SBW_TCK, PINOUT_SBW_TDIO); + + last_tdi = last_tms = 0xff; + last_tclk = true; + + return true; +} + +void sbw_deinit(void) { + if (sbw_piosm >= 0) { + pio_sm_set_enabled(PINOUT_SBW_PIO, sbw_piosm, false); + pio_sm_unclaim(PINOUT_SBW_PIO, sbw_piosm); + sbw_piosm = -1; + } + + if (sbw_offset >= 0) { + pio_remove_program(PINOUT_SBW_PIO, &sbw_program, sbw_offset); + sbw_offset = -1; + } +} + +void sbw_set_freq(bool tclk, float freq) { + if (tclk) { + sbw_pio_set_tclkfreq(PINOUT_SBW_PIO, sbw_piosm, freq); + } else { + sbw_pio_set_baudrate(PINOUT_SBW_PIO, sbw_piosm, freq); + } +} + +static uint8_t bitswap(uint8_t in) { + static const uint8_t lut[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + + return (lut[in&0xf] << 4) | lut[in>>4]; +} + +#define piosm_txf(width) (*(io_wo_##width *)&PINOUT_SBW_PIO->txf[sbw_piosm]) +#define piosm_rxf(width) (*(io_ro_##width *)&PINOUT_SBW_PIO->rxf[sbw_piosm]) +#define piosm_txf_wait() while (pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) tight_loop_contents() + +bool sbw_get_last_tms(void) { return last_tms; } +bool sbw_get_last_tdi(void) { return last_tdi; } +bool sbw_get_last_tclk(void) { return last_tclk; } + +void sbw_sequence(uint32_t ncyc, bool tms, const uint8_t* tdi, uint8_t* tdo) { + if (ncyc == 0) return; + + uint32_t nbytes = (ncyc + 7) >> 3; + uint32_t last_shift = (8 - ncyc) & 7; + + uint32_t txremain = nbytes, + rxremain = last_shift ? nbytes : (nbytes + 1); + + // initial TMS value in y + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(tms ? 1 : 0); + + // number of cycles in x + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_outx(32); + piosm_txf_wait(); + piosm_txf(32) = ncyc - 1; + + // jmp to correct subroutine + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_seq + sbw_offset); + + for (size_t oi = 0, ii = 0; txremain || rxremain; tight_loop_contents()) { + if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) { + piosm_txf(8) = bitswap(tdi ? tdi[ii] : last_tdi); + --txremain; + ++ii; + } + + if (rxremain && !pio_sm_is_rx_fifo_empty(PINOUT_SBW_PIO, sbw_piosm)) { + uint8_t ov = piosm_rxf(8); + --rxremain; + + if (tdo && oi < nbytes) { + if (last_shift && oi == nbytes - 1) { + tdo[oi] = bitswap(ov) >> last_shift; + } else { + tdo[oi] = bitswap(ov); + } + ++oi; + } + } + } + + //while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents(); + + if (tdi) last_tdi = (tdi[nbytes - 1] & (1 << (ncyc & 7))) ? 0xff : 0; + last_tms = tms ? 0xff : 0; +} + +void sbw_tms_sequence(uint32_t ncyc, bool tdi, const uint8_t* tms) { + if (ncyc == 0 || !tms) return; + + uint32_t nbytes = (ncyc + 7) >> 3; + uint32_t txremain = nbytes; + + // initial TDI value in y + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(tdi ? 1 : 0); + + // number of cycles in x + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_outx(32); + piosm_txf_wait(); + piosm_txf(32) = ncyc - 1; + + // jmp to correct subroutine + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tms_seq + sbw_offset); + + for (size_t ii = 0; txremain; tight_loop_contents()) { + if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) { + piosm_txf(8) = bitswap(tms[ii]); + --txremain; + ++ii; + } + } + + //while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents(); + + last_tdi = tdi ? 0xff : 0; + last_tms = (tms[nbytes - 1] & (1 << (ncyc & 7))) ? 0xff : 0; +} + +void sbw_clrset_tclk(bool value) { + //sbw_pio_loadbearing_set_setpins(PINOUT_SBW_PIO, value ? 1 : 0); // new value + + // pre-TCLK value + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(last_tclk ? 1 : 0); + + // only one TCLK + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_setx(0); + + // jmp to subroutine + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tclk_burst + sbw_offset); + + // always use out pins, 1 + piosm_txf_wait(); + piosm_txf(8) = value ? 0xff : 0; + + // wait until done + /*while ( sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents(); + while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();*/ + + last_tclk = value; +} + +void sbw_tclk_burst(uint32_t ncyc) { + //sbw_pio_loadbearing_set_outpins(PINOUT_SBW_PIO); + + uint32_t txremain = ((ncyc + 7) >> 3) * 2; + + // MSB-first + uint8_t pattern = last_tclk ? 0x55 : 0xaa; + + // pre-TCLK value + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(last_tclk ? 1 : 0); + + // number of TCLK half-cycles in x + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_outx(32); + piosm_txf_wait(); + piosm_txf(32) = ncyc*2 - 1; + + // jmp to subroutine + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_jmp(sbw_offset_sbw_tclk_burst + sbw_offset); + + for (; txremain; tight_loop_contents()) { + if (txremain && !pio_sm_is_tx_fifo_full(PINOUT_SBW_PIO, sbw_piosm)) { + piosm_txf(8) = pattern; + --txremain; + } + } + + // wait until done + /*while ( sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents(); + while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm, sbw_offset)) tight_loop_contents();*/ + + // last_tclk doesn't change - always an even number of TCLK half-cycles +} + diff --git a/bsp/rp2040/m_isp/sbw_hw.h b/bsp/rp2040/m_isp/sbw_hw.h new file mode 100644 index 0000000..228e373 --- /dev/null +++ b/bsp/rp2040/m_isp/sbw_hw.h @@ -0,0 +1,38 @@ + +#ifndef BSP_RP2040_SBW_HW_H +#define BSP_RP2040_SBW_HW_H + +#include +#include +/*#include */ + +/*#define PINOUT_SBW_PIO pio0 +#define PINOUT_SBW_TCK 8 +#define PINOUT_SBW_TDIO 9*/ + +//extern int sbw_piosm, sbw_offset; + +// does the debug handshake/SBW setup thingy, call before sbw_init() +void sbw_preinit(bool nrst); + +bool sbw_init(void); +void sbw_deinit(void); + +void sbw_set_freq(bool tclk, float freq); + +bool sbw_get_last_tms(void); +bool sbw_get_last_tdi(void); +bool sbw_get_last_tclk(void); + +void sbw_sequence(uint32_t ncyc, bool tms, const uint8_t* tdi, uint8_t* tdo); +void sbw_tms_sequence(uint32_t ncyc, bool tdi, const uint8_t* tms); + +void sbw_clrset_tclk(bool tclk); + +static inline void sbw_clr_tclk(void) { sbw_clrset_tclk(false); } +static inline void sbw_set_tclk(void) { sbw_clrset_tclk(true ); } + +void sbw_tclk_burst(uint32_t ncyc); + +#endif + diff --git a/host/dpctl/commands.py b/host/dpctl/commands.py index 580c8d3..e0d8b98 100644 --- a/host/dpctl/commands.py +++ b/host/dpctl/commands.py @@ -10,6 +10,7 @@ from .protocol import * FEATURES_OF_MODE = { 1: ["UART", "CMSIS-DAP", "SPI", "I2C", "temperature sensor", "1-wire"], + 2: ["UART", "CMSIS-DAP", "MehFET"], 3: ["JTAG", "SWD"], 4: ["SUMP"] } diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..8a73f18 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +_*_test/ diff --git a/src/m_default/_default.c b/src/m_default/_default.c index 591171e..9882d05 100644 --- a/src/m_default/_default.c +++ b/src/m_default/_default.c @@ -36,10 +36,12 @@ enum m_default_feature { }; #ifdef DBOARD_HAS_UART -static cothread_t uartthread; -static uint8_t uartstack[THREAD_STACK_SIZE]; +extern cothread_t m_def_uartthread; +extern uint8_t m_def_uartstack[THREAD_STACK_SIZE]; +__attribute__((__weak__)) cothread_t m_def_uartthread; +__attribute__((__weak__)) uint8_t m_def_uartstack[THREAD_STACK_SIZE]; -static void uart_thread_fn(void) { +__attribute__((__weak__)) void m_def_uart_thread_fn(void) { cdc_uart_init(); thread_yield(); while (1) { @@ -47,6 +49,9 @@ static void uart_thread_fn(void) { thread_yield(); } } +#define uartthread m_def_uartthread +#define uartstack m_def_uartstack +#define uart_thread_fn m_def_uart_thread_fn #endif #ifdef DBOARD_HAS_SPI diff --git a/src/m_default/cdc.h b/src/m_default/cdc.h index cf0cd96..9cb5ec7 100644 --- a/src/m_default/cdc.h +++ b/src/m_default/cdc.h @@ -2,7 +2,7 @@ #ifndef CDC_H_ #define CDC_H_ -#include "m_default/bsp-feature.h" +/*#include "m_default/bsp-feature.h"*/ /* BSP function prototypes for various USB-CDC interfaces */ #ifdef DBOARD_HAS_UART diff --git a/src/m_default/cdc_serprog.c b/src/m_default/cdc_serprog.c index af6070c..705016a 100644 --- a/src/m_default/cdc_serprog.c +++ b/src/m_default/cdc_serprog.c @@ -2,6 +2,7 @@ #include +#include "tusb_config.h" #include #include "m_default/bsp-feature.h" @@ -57,6 +58,7 @@ uint32_t sp_spi_get_buf_limit(void) { return sizeof(rx_buf) - 1; } +// TODO: this is duplicated several times over the codebase, maybe reduce this static uint8_t read_byte_cdc(void) { while (rxavail <= 0) { if (!tud_cdc_n_connected(CDC_N_SERPROG) || !tud_cdc_n_available(CDC_N_SERPROG)) { diff --git a/src/m_default/vnd_i2ctinyusb.c b/src/m_default/vnd_i2ctinyusb.c index 354cd6e..bcf9c41 100644 --- a/src/m_default/vnd_i2ctinyusb.c +++ b/src/m_default/vnd_i2ctinyusb.c @@ -11,6 +11,7 @@ #include +#include "tusb_config.h" #include #include diff --git a/src/m_isp/_isp.c b/src/m_isp/_isp.c new file mode 100644 index 0000000..212562d --- /dev/null +++ b/src/m_isp/_isp.c @@ -0,0 +1,332 @@ +// vim: set et: + +/* UGLY HACK */ +#define BSP_FEATURE_M_DEFAULT_H_ + +#include + +#include "mode.h" +#include "thread.h" +#include "usbstdio.h" +#include "vnd_cfg.h" + +#include "m_isp/bsp-feature.h" + +/* CMSIS-DAP */ +#include "DAP_config.h" /* ARM code *assumes* this is included prior to DAP.h */ +#include "DAP.h" +/* CDC UART */ +#include "m_default/cdc.h" /* yeah just reuse this one */ +/* MehFET */ +#include "m_isp/mehfet.h" + +enum m_isp_cmds { + misp_cmd_idk = mode_cmd__specific, +}; +enum m_isp_feature { + misp_feat_uart = 1<<0, + misp_feat_cmsisdap = 1<<1, + misp_feat_mehfet = 1<<2, +}; + +#ifdef DBOARD_HAS_UART +extern cothread_t m_def_uartthread; +extern uint8_t m_def_uartstack[THREAD_STACK_SIZE]; +__attribute__((__weak__)) cothread_t m_def_uartthread; +__attribute__((__weak__)) uint8_t m_def_uartstack[THREAD_STACK_SIZE]; + +__attribute__((__weak__)) void m_def_uart_thread_fn(void) { + cdc_uart_init(); + thread_yield(); + while (1) { + cdc_uart_task(); + thread_yield(); + } +} +#define uartthread m_def_uartthread +#define uartstack m_def_uartstack +#define uart_thread_fn m_def_uart_thread_fn +#endif + +#ifdef DBOARD_HAS_MEHFET +static cothread_t mehfetthread; +static uint8_t mehfetstack[THREAD_STACK_SIZE]; + +static void mehfet_thread_fn(void) { + mehfet_init(); + thread_yield(); + while (1) { + mehfet_task(); + thread_yield(); + } +} +#endif + +static void enter_cb(void) { +#ifdef USE_USBCDC_FOR_STDIO + stdio_usb_set_itf_num(CDC_N_STDIO); +#endif + vnd_cfg_set_itf_num(VND_N_CFG); + + // TODO: CMSISDAP? + + // HACK: we init UART stuff first: UART inits gpio 10,11 pinmux fn to UART + // flow control signals, which is ok for mode1, but conflicts with + // stuff in mode2 +#ifdef DBOARD_HAS_UART + uartthread = co_derive(uartstack, sizeof uartstack, uart_thread_fn); + thread_enter(uartthread); // will call cdc_uart_init() on correct thread +#endif +#ifdef DBOARD_HAS_MEHFET + mehfetthread = co_derive(mehfetstack, sizeof mehfetstack, mehfet_thread_fn); + thread_enter(mehfetthread); +#endif +} +static void leave_cb(void) { + // TODO: CMSISDAP? + +#ifdef DBOARD_HAS_MEHFET + mehfet_deinit(); +#endif +#ifdef DBOARD_HAS_UART + cdc_uart_deinit(); +#endif +} + +static void task_cb(void) { +#ifdef DBOARD_HAS_UART + tud_task(); + thread_enter(uartthread); +#endif +#ifdef DBOARD_HAS_MEHFET + tud_task(); + thread_enter(mehfetthread); +#endif +} + +static void handle_cmd_cb(uint8_t cmd) { + uint8_t resp = 0; + + switch (cmd) { + case mode_cmd_get_features: +#ifdef DBOARD_HAS_UART + resp |= misp_feat_uart; +#endif +#ifdef DBOARD_HAS_CMSISDAP + resp |= misp_feat_cmsisdap; +#endif +#ifdef DBOARD_HAS_MEHFET + resp |= misp_feat_mehfet; +#endif + vnd_cfg_write_resp(cfg_resp_ok, 1, &resp); + break; + default: + vnd_cfg_write_strf(cfg_resp_illcmd, "unknown mode1 command %02x", cmd); + break; + } +} + +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + + STRID_CONFIG, + + STRID_IF_VND_CFG, + STRID_IF_HID_CMSISDAP, + STRID_IF_CDC_UART, + STRID_IF_VND_MEHFET, + STRID_IF_CDC_STDIO, +}; +enum { +#if CFG_TUD_VENDOR > 0 + ITF_NUM_VND_CFG, +#endif +#ifdef DBOARD_HAS_CMSISDAP + ITF_NUM_HID_CMSISDAP, +#endif +#ifdef DBOARD_HAS_UART + ITF_NUM_CDC_UART_COM, + ITF_NUM_CDC_UART_DATA, +#endif +#ifdef DBOARD_HAS_MEHFET + ITF_NUM_VND_MEHFET, +#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_CMSISDAP + + TUD_HID_INOUT_DESC_LEN +#endif +#ifdef DBOARD_HAS_UART + + TUD_CDC_DESC_LEN +#endif +#ifdef DBOARD_HAS_MEHFET + + TUD_VENDOR_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_HID_CMSISDAP 0x02 +#define EPNUM_CDC_UART_OUT 0x03 +#define EPNUM_CDC_UART_IN 0x83 +#define EPNUM_CDC_UART_NOTIF 0x84 +#define EPNUM_VND_MEHFET_OUT 0x05 +#define EPNUM_VND_MEHFET_IN 0x85 +#define EPNUM_CDC_STDIO_OUT 0x06 +#define EPNUM_CDC_STDIO_IN 0x86 +#define EPNUM_CDC_STDIO_NOTIF 0x87 + +// clang-format off +#if CFG_TUD_HID > 0 +static const uint8_t desc_hid_report[] = { // ugh + TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE) +}; +#endif +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_CMSISDAP + TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_CMSISDAP, STRID_IF_HID_CMSISDAP, + 0 /*HID_PROTOCOL_NONE*/, sizeof(desc_hid_report), EPNUM_HID_CMSISDAP, + 0x80 | (EPNUM_HID_CMSISDAP + 0), CFG_TUD_HID_EP_BUFSIZE, 1), +#endif + +#ifdef DBOARD_HAS_UART + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_UART_COM, STRID_IF_CDC_UART, EPNUM_CDC_UART_NOTIF, + CFG_TUD_CDC_RX_BUFSIZE, EPNUM_CDC_UART_OUT, EPNUM_CDC_UART_IN, CFG_TUD_CDC_RX_BUFSIZE), +#endif + +#ifdef DBOARD_HAS_MEHFET + TUD_VENDOR_DESCRIPTOR_EX(ITF_NUM_VND_MEHFET, STRID_IF_VND_MEHFET, EPNUM_VND_MEHFET_OUT, + EPNUM_VND_MEHFET_IN, CFG_TUD_VENDOR_RX_BUFSIZE, '4', '3'), +#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, + + // no hw info here, or the name will be too long >__> + // CMSIS-DAP spec: + // "The Product String must contain 'CMSIS-DAP' somewhere in the string. + // This is used by the debuggers to identify a CMSIS-DAP compliant Debug + // Unit that is connected to a host computer." + [STRID_PRODUCT] = INFO_PRODUCT_BARE " CMSIS-DAP", + [STRID_CONFIG] = "Configuration descriptor", + // max string length check: ||||||||||||||||||||||||||||||| + [STRID_IF_VND_CFG ] = "Device cfg/ctl interface", + [STRID_IF_HID_CMSISDAP] = "CMSIS-DAP HID interface", + [STRID_IF_CDC_UART] = "UART CDC interface", + [STRID_IF_VND_MEHFET] = "MehFET MSP430 debug interface", +#ifdef USE_USBCDC_FOR_STDIO + [STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)", +#endif +}; +// clang-format on + +#ifdef DBOARD_HAS_CMSISDAP +static const uint8_t* my_hid_descriptor_report_cb(uint8_t instance) { + (void)instance; + + return desc_hid_report; +} +/*static uint16_t my_hid_get_report_cb(uint8_t instance, uint8_t report_id, + hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + // TODO not implemented + (void)instance; + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + + return 0; +}*/ + +static void my_hid_set_report_cb(uint8_t instance, uint8_t report_id, + hid_report_type_t report_type, uint8_t const* rx_buffer, uint16_t bufsize) { + static uint8_t tx_buffer[CFG_TUD_HID_EP_BUFSIZE]; + uint32_t response_size = TU_MIN(CFG_TUD_HID_EP_BUFSIZE, bufsize); + + (void)instance; + (void)report_id; + (void)report_type; + + DAP_ProcessCommand(rx_buffer, tx_buffer); + + tud_hid_report(0, tx_buffer, response_size); +} +#endif + +#if CFG_TUD_CDC > 0 +static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) { + switch (itf) { +/*#ifdef DBOARD_HAS_UART + case CDC_N_UART: + cdc_uart_set_coding(line_coding->bit_rate, line_coding->stop_bits, + line_coding->parity, line_coding->data_bits); + break; +#endif*/ +#ifdef USE_USBCDC_FOR_STDIO + case CDC_N_STDIO: + stdio_usb_line_coding_cb(line_coding); + break; +#endif + } +} +#endif + +extern struct mode m_02_isp; +// clang-format off +struct mode m_02_isp = { + .name = "In-system-programming/debugging 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, + +#if defined(DBOARD_HAS_CMSISDAP) && CFG_TUD_HID > 0 +#if 0 + .tud_hid_get_report_cb = my_hid_get_report_cb, +#endif + .tud_hid_set_report_cb = my_hid_set_report_cb, + .tud_hid_descriptor_report_cb = my_hid_descriptor_report_cb, +#endif + +#if CFG_TUD_CDC > 0 + .tud_cdc_line_coding_cb = my_cdc_line_coding_cb, +#endif +}; +// clang-format on + diff --git a/src/m_isp/mehfet.c b/src/m_isp/mehfet.c new file mode 100644 index 0000000..891242e --- /dev/null +++ b/src/m_isp/mehfet.c @@ -0,0 +1,424 @@ +// vim: set et: + +#include + +#include + +#include "info.h" +#include "thread.h" + +#include "m_isp/bsp-feature.h" +#include "m_isp/mehfet.h" + +struct cmdlen { + uint32_t len; + uint8_t cmd; +}; + +#define BUFSIZE 256 + +static uint8_t rx_buf[BUFSIZE]; +static uint8_t tx_buf[BUFSIZE]; + +static uint32_t rxavail, rxpos, txpos; + +// TODO: this is duplicated several times over the codebase, maybe reduce this +static uint8_t read_byte(void) { + while (rxavail <= 0) { + if (!tud_vendor_n_mounted(VND_N_MEHFET) || !tud_vendor_n_available(VND_N_MEHFET)) { + thread_yield(); + continue; + } + + rxpos = 0; + rxavail = tud_vendor_n_read(VND_N_MEHFET, rx_buf, sizeof rx_buf); + + if (rxavail == 0) thread_yield(); + } + + uint8_t rv = rx_buf[rxpos]; + ++rxpos; + --rxavail; + + return rv; +} +/*static void drop_incoming(void) { + rxavail = 0; + rxpos = 0; + + // empty tinyusb internal buffer + if (tud_vendor_n_mounted(VND_N_CFG)) { + while (tud_vendor_n_available(VND_N_CFG)) { + tud_vendor_n_read(VND_N_CFG, rx_buf, sizeof rx_buf); + } + } +}*/ +static void write_flush(void) { + // TODO: is this needed? + while (tud_vendor_n_write_available(VND_N_CFG) < txpos) { + thread_yield(); + } + + tud_vendor_n_write(VND_N_CFG, tx_buf, txpos); + txpos = 0; +} +static void write_byte(uint8_t v) { + if (txpos == sizeof tx_buf) { + write_flush(); + } + + tx_buf[txpos] = v; + ++txpos; +} + +//////////////// + +static uint32_t plpos; +static struct cmdlen read_cmd_len(void) { + uint8_t cmd = read_byte(), + lastbyte = cmd; + uint32_t l = 0; + + for (size_t i = 0; (i < 4) && (lastbyte & 0x80); ++i) { + lastbyte = read_byte(); + + uint8_t mask = (i == 3) ? 0xff : 0x7f; + l |= (lastbyte & mask) << (i * 8); + } + + plpos = 0; + return (struct cmdlen){ .len = l, .cmd = cmd }; +} + +static inline uint8_t read_pl(void) { + ++plpos; + return read_byte(); +} +static void flush_pl(uint32_t len) { + while (plpos < len) read_byte(); +} + +static void write_resp(enum mehfet_status stat, size_t resplen, const uint8_t* resp) { + //if (stat != mehfet_ok) drop_incoming(); + + write_byte((stat & 0x7f) | (resplen ? 0x80 : 0)); + + for (size_t i = 0, len2 = resplen; (i < 4) && len2; ++i) { + uint8_t nextv; + if (i == 3) { + nextv = (uint8_t)len2; + } else { + nextv = len2 & 0x7f; + if ((len2 >> 7) != 0) nextv |= 0x80; + } + len2 >>= 7; + + write_byte(nextv); + } + + for (size_t i = 0; i < resplen; ++i) + write_byte(resp[i]); + + write_flush(); +} +static void write_resp_str(enum mehfet_status stat, const char* str) { + write_resp(stat, strlen(str)+1 /* include null terminator */, (const uint8_t*)str); +} + +/////////////// + +static uint8_t connstat; + +void mehfet_init(void) { + rxavail = 0; + rxpos = 0; + txpos = 0; + plpos = 0; + + connstat = mehfet_conn_none; + + mehfet_hw_init(); +} +void mehfet_deinit(void) { + if (connstat != mehfet_conn_none) { + mehfet_hw_disconnect(); + connstat = mehfet_conn_none; + } + + mehfet_hw_deinit(); +} + +void mehfet_task(void) { + struct cmdlen cmdhdr = read_cmd_len(); + + switch (cmdhdr.cmd) { + case mehfet_info: + if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Info takes no parameters"); + else { + // TODO: add flag once Loop has been implemented + uint32_t caps = mehfet_hw_get_caps() /*| mehfet_cap_has_loop*/; + uint16_t ver = MEHFET_PROTO_VER; + uint8_t pktbuf_l2 = __builtin_ctz(sizeof tx_buf); + const char* name = INFO_PRODUCT(INFO_BOARDNAME); + + size_t bufsize = strlen(name) + 1 + 8; + if (bufsize > 256) // uuuuuh, (stack size is 512b) + write_resp_str(mehfet_error, "not enough space for info buffer"); + + uint8_t buf[bufsize]; + buf[0] = (caps >> 0) & 0xff; + buf[1] = (caps >> 8) & 0xff; + buf[2] = (caps >> 16) & 0xff; + buf[3] = (caps >> 24) & 0xff; + + buf[4] = (ver >> 0) & 0xff; + buf[5] = (ver >> 8) & 0xff; + + buf[6] = pktbuf_l2; + buf[7] = 0; // reserved + + memcpy(&buf[8], name, bufsize - 8); + + write_resp(mehfet_ok, bufsize, buf); + } + break; + case mehfet_status: + if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Status takes no parameters"); + else write_resp(mehfet_ok, 1, &connstat); + break; + case mehfet_connect: + if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "Connect takes one parameter byte"); + else if (connstat != mehfet_conn_none) { + write_resp_str(mehfet_badstate, "Already connected"); + } else { + enum mehfet_conn conn = read_pl(); + + // check if we can actually do this + enum mehfet_conn conntyp = conn & mehfet_conn_typemask; + switch (conntyp) { + case mehfet_conn_jtag_noentry: + if (!(mehfet_hw_get_caps() & mehfet_cap_jtag_noentry)) { + write_resp(mehfet_nocaps, 0, NULL); + goto EXIT; + } + break; + case mehfet_conn_jtag_entryseq: + if (!(mehfet_hw_get_caps() & mehfet_cap_jtag_entryseq)) { + write_resp(mehfet_nocaps, 0, NULL); + goto EXIT; + } + break; + case mehfet_conn_sbw_entryseq: + if (!(mehfet_hw_get_caps() & mehfet_cap_sbw_entryseq)) { + write_resp(mehfet_nocaps, 0, NULL); + goto EXIT; + } + break; + case mehfet_conn_auto: + if (mehfet_hw_get_caps() & mehfet_cap_sbw_entryseq) + conntyp = mehfet_conn_sbw_entryseq; + else if (mehfet_hw_get_caps() & mehfet_cap_jtag_entryseq) + conntyp = mehfet_conn_jtag_entryseq; + else if (mehfet_hw_get_caps() & mehfet_cap_jtag_noentry) + conntyp = mehfet_conn_jtag_noentry; + else { + write_resp_str(mehfet_nocaps, "Connect: no mode implemented in hardware..."); + goto EXIT; + } + break; + default: + write_resp_str(mehfet_badargs, "Connect: invalid connection mode"); + goto EXIT; + } + + const char* resp = mehfet_hw_connect(conntyp | (conn & mehfet_conn_nrstmask)); + if (!resp) { + connstat = conntyp; + write_resp(mehfet_ok, 0, NULL); + } else + write_resp_str(mehfet_error, resp); + } + break; + case mehfet_disconnect: + if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "Disconnect takes no parameters"); + else { + if (connstat != mehfet_conn_none) mehfet_hw_disconnect(); + + write_resp(mehfet_ok, 0, NULL); + } + break; + case mehfet_delay: + if (cmdhdr.len != 4) write_resp_str(mehfet_badargs, "Delay takes 4 parameter bytes"); + else { + uint32_t v = 0; + v |= (uint32_t)read_pl() << 0 ; + v |= (uint32_t)read_pl() << 8 ; + v |= (uint32_t)read_pl() << 16; + v |= (uint32_t)read_pl() << 24; + + bool exact = v & (1u<<31), us = v & (1u<<30); + v &= (1u << 30) - 1; + + if (exact) { + if (us) mehfet_hw_delay_us(v); + else mehfet_hw_delay_ms(v); + } else { + mehfet_hw_timer_start(us, v); + while (!mehfet_hw_timer_reached()) thread_yield(); + } + + write_resp(mehfet_ok, 0, NULL); + } + break; + case mehfet_reset_target: + if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "ResetTarget takes no parameters"); + else { + mehfet_hw_reset_target(); + write_resp(mehfet_ok, 0, NULL); + } + break; + case mehfet_get_old_lines: + if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "GetOldLines takes no parameters"); + else { + uint8_t v = mehfet_hw_get_old_lines(); + write_resp(mehfet_ok, 1, &v); + } + break; + case mehfet_tdio_seq: + if (cmdhdr.len < 6) write_resp_str(mehfet_badargs, + "TdioSequence: need at least a TMS level, number of cycles and some TDI data (at least 6 bytes)"); + // stack size is 512b + else if (cmdhdr.len > 128 + 5) write_resp_str(mehfet_badargs, + "TdioSequence: too much data to process, can do max. 1024 bits (128B) at once"); + else { + uint32_t ncyc = 0; + ncyc |= (uint32_t)read_pl() << 0 ; + ncyc |= (uint32_t)read_pl() << 8 ; + ncyc |= (uint32_t)read_pl() << 16; + ncyc |= (uint32_t)read_pl() << 24; + + bool tmslvl = read_pl() != 0; + + size_t nbytes = (ncyc + 7) >> 3; + if (nbytes != cmdhdr.len - 5) { + write_resp_str(mehfet_badargs, "TdioSequence: bad ncyc<->payload length"); + } else { + uint8_t tdi_stuff[nbytes], tdo_stuff[nbytes]; + + for (size_t i = 0; i < nbytes; ++i) tdi_stuff[i] = read_pl(); + + mehfet_hw_tdio_seq(ncyc, tmslvl, tdi_stuff, tdo_stuff); + + write_resp(mehfet_ok, nbytes, tdo_stuff); + } + } + break; + case mehfet_tms_seq: + if (cmdhdr.len < 6) write_resp_str(mehfet_badargs, + "TmsSequence: need a TDI level, the number of cycles and some TMS data (at least 6 bytes)"); + // stack size is 512b + else if (cmdhdr.len > 256 + 5) write_resp_str(mehfet_badargs, + "TmsSequence: too much data to process, can do max. 2048 bits (256B) at once"); + else { + uint32_t ncyc = 0; + ncyc |= (uint32_t)read_pl() << 0 ; + ncyc |= (uint32_t)read_pl() << 8 ; + ncyc |= (uint32_t)read_pl() << 16; + ncyc |= (uint32_t)read_pl() << 24; + + bool tdilvl = read_pl() != 0; + + size_t nbytes = (ncyc + 7) >> 3; + if (nbytes != cmdhdr.len - 5) { + write_resp_str(mehfet_badargs, "TmsSequence: bad ncyc<->payload length"); + } else { + uint8_t tms_stuff[nbytes]; + + for (size_t i = 0; i < nbytes; ++i) tms_stuff[i] = read_pl(); + + mehfet_hw_tms_seq(ncyc, tdilvl, tms_stuff); + + write_resp(mehfet_ok, 0, NULL); + } + } + break; + case mehfet_tclk_edge: + if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "TclkEdge takes one parameter byte"); + else { + uint8_t newtclk = read_pl(); + mehfet_hw_tclk_edge(newtclk); + write_resp(mehfet_ok, 0, NULL); + } + break; + case mehfet_tclk_burst: + if (cmdhdr.len != 4) write_resp_str(mehfet_badargs, "TclkBurst takes 4 parameter bytes"); + else { + uint32_t ncyc = 0; + ncyc |= (uint32_t)read_pl() << 0 ; + ncyc |= (uint32_t)read_pl() << 8 ; + ncyc |= (uint32_t)read_pl() << 16; + ncyc |= (uint32_t)read_pl() << 24; + + mehfet_hw_tclk_burst(ncyc); + write_resp(mehfet_ok, 0, NULL); + } + break; + + case mehfet_reset_tap: + if (!(mehfet_hw_get_caps() & mehfet_cap_has_reset_tap)) write_resp(mehfet_nocaps, 0, NULL); + else if (cmdhdr.len != 0) write_resp_str(mehfet_badargs, "ResetTAP takes no parameters"); + else { + mehfet_hw_reset_tap(); + write_resp(mehfet_ok, 0, NULL); + } + break; + case mehfet_irshift: + if (!(mehfet_hw_get_caps() & mehfet_cap_has_irshift)) write_resp(mehfet_nocaps, 0, NULL); + else if (cmdhdr.len != 1) write_resp_str(mehfet_badargs, "IRshift takes one parameter byte"); + else { + uint8_t newir = read_pl(); + uint8_t oldir = mehfet_hw_shift_ir(newir); + write_resp(mehfet_ok, 1, &oldir); + } + break; + case mehfet_drshift: + if (!(mehfet_hw_get_caps() & mehfet_cap_has_drshift)) write_resp(mehfet_nocaps, 0, NULL); + else if (cmdhdr.len < 5) write_resp_str(mehfet_badargs, + "DRshift takes at least a bit count and some data (at least 5 bytes)"); + else if (cmdhdr.len > 128 + 4) write_resp_str(mehfet_badargs, + "DRshift: too much data to process, can do max. 1024 bits (128B)"); + else { + uint32_t nbits = 0; + nbits |= (uint32_t)read_pl() << 0 ; + nbits |= (uint32_t)read_pl() << 8 ; + nbits |= (uint32_t)read_pl() << 16; + nbits |= (uint32_t)read_pl() << 24; + + size_t nbytes = (nbits + 7) >> 3; + if (nbytes != cmdhdr.len - 4) { + write_resp_str(mehfet_badargs, "DRshift: bad nbits<->payload length"); + } else { + uint8_t newdr[nbytes], olddr[nbytes]; + + for (size_t i = 0; i < nbytes; ++i) newdr[i] = read_pl(); + + mehfet_hw_shift_dr(nbits, newdr, olddr); + + write_resp(mehfet_ok, nbytes, olddr); + } + } + break; + + case mehfet_loop: + // TODO + write_resp_str(mehfet_nocaps, "not implemented yet, sorry"); + break; + + default: + write_resp(mehfet_invalidcmd, 0, NULL); + } + +EXIT: + flush_pl(cmdhdr.len); +} + diff --git a/src/m_isp/mehfet.h b/src/m_isp/mehfet.h new file mode 100644 index 0000000..6fbed4c --- /dev/null +++ b/src/m_isp/mehfet.h @@ -0,0 +1,95 @@ + +#ifndef M_ISP_MEHFET +#define M_ISP_MEHFET + +#include +#include + +#define MEHFET_PROTO_VER 0x0001 + +void mehfet_init(void); +void mehfet_deinit(void); +void mehfet_task(void); + +enum mehfet_cmd { + mehfet_info = 0x01, + mehfet_status = 0x02, + mehfet_connect = 0x03, + mehfet_disconnect = 0x04, + mehfet_delay = 0x05, + mehfet_reset_target = 0x06, + mehfet_get_old_lines = 0x07, + mehfet_tdio_seq = 0x08, + mehfet_tms_seq = 0x09, + mehfet_tclk_edge = 0x0a, + mehfet_tclk_burst = 0x0b, + mehfet_reset_tap = 0x0c, + mehfet_irshift = 0x0d, + mehfet_drshift = 0x0e, + mehfet_loop = 0x0f, +}; + +enum mehfet_status { + mehfet_ok = 0x00, + + mehfet_badargs = 0x7b, + mehfet_nocaps = 0x7c, + mehfet_badstate = 0x7d, + mehfet_invalidcmd = 0x7e, + mehfet_error = 0x7f +}; + +enum mehfet_caps { + mehfet_cap_jtag_noentry = 1<<0, + mehfet_cap_jtag_entryseq = 1<<1, + mehfet_cap_sbw_entryseq = 1<<2, + + mehfet_cap_has_reset_tap = 1<< 8, + mehfet_cap_has_irshift = 1<< 9, + mehfet_cap_has_drshift = 1<<10, + mehfet_cap_has_loop = 1<<11, +}; + +enum mehfet_conn { + mehfet_conn_none = 0, + mehfet_conn_auto = 0, + + mehfet_conn_jtag_noentry = 1, + mehfet_conn_jtag_entryseq = 2, + mehfet_conn_sbw_entryseq = 3, + + mehfet_conn_typemask = 0x7f, + mehfet_conn_nrstmask = 0x80 +}; + +// hw routines + +void mehfet_hw_init(void); +void mehfet_hw_deinit(void); + +__attribute__((__const__)) +enum mehfet_caps mehfet_hw_get_caps(void); + +const char* /*error string, NULL if no error*/ mehfet_hw_connect(enum mehfet_conn conn); +void mehfet_hw_disconnect(void); + +void mehfet_hw_delay_ms(uint32_t t); +void mehfet_hw_delay_us(uint32_t t); +void mehfet_hw_timer_start(bool us, uint32_t to_reach); +bool mehfet_hw_timer_reached(void); + +void mehfet_hw_reset_target(void); +uint8_t mehfet_hw_get_old_lines(void); + +void mehfet_hw_tdio_seq(uint32_t ncyc, bool tmslvl, const uint8_t* tdi, uint8_t* tdo); +void mehfet_hw_tms_seq(uint32_t ncyc, bool tdilvl, const uint8_t* tms); +void mehfet_hw_tclk_edge(bool newtclk); +void mehfet_hw_tclk_burst(uint32_t ncyc); + +void mehfet_hw_reset_tap(void); +uint8_t mehfet_hw_shift_ir(uint8_t newir); +// drin is not const here, as the implementation may want to shuffle stuff around +void mehfet_hw_shift_dr(uint32_t nbits, uint8_t* drin, uint8_t* drout); + +#endif + diff --git a/src/m_sump/cdc_sump.c b/src/m_sump/cdc_sump.c index 334a2b4..9335402 100644 --- a/src/m_sump/cdc_sump.c +++ b/src/m_sump/cdc_sump.c @@ -27,6 +27,7 @@ */ #include + #include #include "alloc.h" diff --git a/src/main.c b/src/main.c index 54902ad..df8a725 100644 --- a/src/main.c +++ b/src/main.c @@ -14,6 +14,16 @@ static cothread_t vndcfg_thread; static uint8_t vndcfg_stack[THREAD_STACK_SIZE]; +// shrug, somewhere +uint8_t bitswap(uint8_t in) { + static const uint8_t lut[16] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + + return (lut[in&0xf] << 4) | lut[in>>4]; +} + static void vndcfg_thread_fn(void) { vnd_cfg_init(); thread_yield(); diff --git a/src/modeset.c b/src/modeset.c index 53ed69c..f781bd0 100644 --- a/src/modeset.c +++ b/src/modeset.c @@ -6,13 +6,13 @@ #include "board.h" /* bsp_reset_bootloader() */ #include "mode.h" -extern struct mode m_01_default, m_03_jscan, m_04_sump; +extern struct mode m_01_default, m_02_isp, m_03_jscan, 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! - NULL, // mode 2 (hw chip programming stuff) not implemented yet + &m_02_isp, //NULL, // mode 2 (hw chip programming stuff) not implemented yet &m_03_jscan, &m_04_sump, NULL, // terminating entry diff --git a/src/util.h b/src/util.h index ae47e2d..d4b6d33 100644 --- a/src/util.h +++ b/src/util.h @@ -15,5 +15,7 @@ uint8_t get_unique_id_u8 (uint8_t * desc_str); uint8_t get_unique_id_u16(uint16_t* desc_str); // clang-format on +uint8_t bitswap(uint8_t in); + #endif diff --git a/src/vnd_cfg.c b/src/vnd_cfg.c index a6cd4b6..2526750 100644 --- a/src/vnd_cfg.c +++ b/src/vnd_cfg.c @@ -12,7 +12,7 @@ #if CFG_TUD_VENDOR > 0 static uint8_t rx_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; -static uint8_t tx_buf[CFG_TUD_VENDOR_TX_BUFSIZE]; +static uint8_t tx_buf[CFG_TUD_VENDOR_RX_BUFSIZE]; static uint32_t rxavail, rxpos, txpos; @@ -30,6 +30,7 @@ void vnd_cfg_set_itf_num(int itf) { VND_N_CFG = itf; } +// TODO: this is duplicated several times over the codebase, maybe reduce this uint8_t vnd_cfg_read_byte(void) { while (rxavail <= 0) { if (!tud_vendor_n_mounted(VND_N_CFG) || !tud_vendor_n_available(VND_N_CFG)) {