Browse Source

start of ISP mode (mode2), MehFET SBW impl

isp-mehfet
sys64738 2 months ago
parent
commit
e92afbcc06
  1. 5
      CMakeLists.txt
  2. 2
      bsp/rp2040/bsp-info.h
  3. 11
      bsp/rp2040/m_default/dap_jtag.c
  4. 11
      bsp/rp2040/m_default/dap_swd.c
  5. 20
      bsp/rp2040/m_default/pinout.h
  6. 34
      bsp/rp2040/m_isp/bsp-feature.h
  7. 174
      bsp/rp2040/m_isp/mehfet_hw.c
  8. 83
      bsp/rp2040/m_isp/pinout.h
  9. 206
      bsp/rp2040/m_isp/sbw.pio
  10. 290
      bsp/rp2040/m_isp/sbw_hw.c
  11. 38
      bsp/rp2040/m_isp/sbw_hw.h
  12. 1
      host/dpctl/commands.py
  13. 1
      src/.gitignore
  14. 11
      src/m_default/_default.c
  15. 2
      src/m_default/cdc.h
  16. 2
      src/m_default/cdc_serprog.c
  17. 1
      src/m_default/vnd_i2ctinyusb.c
  18. 332
      src/m_isp/_isp.c
  19. 424
      src/m_isp/mehfet.c
  20. 95
      src/m_isp/mehfet.h
  21. 1
      src/m_sump/cdc_sump.c
  22. 10
      src/main.c
  23. 4
      src/modeset.c
  24. 2
      src/util.h
  25. 3
      src/vnd_cfg.c

5
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})

2
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

11
bsp/rp2040/m_default/dap_jtag.c

@ -4,6 +4,8 @@
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include "util.h"
#include "DAP_config.h"
#include <DAP.h>
@ -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;

11
bsp/rp2040/m_default/dap_swd.c

@ -8,6 +8,8 @@
#include "DAP_config.h"
#include <DAP.h>
#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);

20
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

34
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

174
bsp/rp2040/m_isp/mehfet_hw.c

@ -0,0 +1,174 @@
// vim: set et:
#include <hardware/timer.h>
#include <pico/time.h>
#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);
}

83
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=<led_pin> cmake"
#elif PICO_DEFAULT_LED_PIN == -1
#error "PICO_DEFAULT_LED_PIN is defined as -1, run PICOPROBE_LED=<led_pin> 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

206
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;
}*/
%}

290
bsp/rp2040/m_isp/sbw_hw.c

@ -0,0 +1,290 @@
// vim: set et:
#include <stdio.h>
#include <hardware/clocks.h>
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/pio_instructions.h>
#include <hardware/timer.h>
#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
}

38
bsp/rp2040/m_isp/sbw_hw.h

@ -0,0 +1,38 @@
#ifndef BSP_RP2040_SBW_HW_H
#define BSP_RP2040_SBW_HW_H
#include <stdint.h>
#include <stdbool.h>
/*#include <hardware/pio.h>*/
/*#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

1
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"]
}

1
src/.gitignore

@ -0,0 +1 @@
_*_test/

11
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

2
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

2
src/m_default/cdc_serprog.c

@ -2,6 +2,7 @@
#include <stdio.h>
#include "tusb_config.h"
#include <tusb.h>
#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)) {

1
src/m_default/vnd_i2ctinyusb.c

@ -11,6 +11,7 @@
#include <hardware/i2c.h>
#include "tusb_config.h"
#include <tusb.h>
#include <device/usbd_pvt.h>

332
src/m_isp/_isp.c

@ -0,0 +1,332 @@
// vim: set et:
/* UGLY HACK */
#define BSP_FEATURE_M_DEFAULT_H_
#include <tusb.h>
#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

424
src/m_isp/mehfet.c

@ -0,0 +1,424 @@
// vim: set et:
#include <string.h>
#include <tusb.h>
#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)