From 448f1896cf69a4732ec3ed028d0d3532642d4592 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Mon, 27 Sep 2021 16:10:02 +0200 Subject: [PATCH] 110% working SBW signals, finally --- src/main.c | 31 +++++++- src/pio_sbw.c | 208 ++++++++++++++++++++++++++++++++++---------------- src/pio_sbw.h | 13 +++- src/sbw.pio | 50 ++++++++---- 4 files changed, 215 insertions(+), 87 deletions(-) diff --git a/src/main.c b/src/main.c index e93aa3d..670254c 100644 --- a/src/main.c +++ b/src/main.c @@ -6,20 +6,47 @@ #include +#include #include #include #include "pio_sbw.h" void printbuf(const uint8_t* buf, size_t size) { - for (int i = 0; i < size; ++i) + for (size_t i = 0; i < size; ++i) printf("%02x%c", buf[i], i % 16 == 15 ? '\n' : ' '); } int main() { + gpio_init(PINOUT_SBW_TCK); + gpio_set_function(PINOUT_SBW_TCK, GPIO_FUNC_SIO); + gpio_set_dir(PINOUT_SBW_TCK, true); + gpio_put(PINOUT_SBW_TCK, true); + stdio_init_all(); bool s = sbw_init(); - printf("%s", s ? "inited" : "failure"); + printf("%s\n", s ? "inited" : "failure"); + + uint8_t tdi = 0x0f, tdo = 0; + sbw_sequence(8, true, &tdi, &tdo); + printf("seq done, tdo=%02x\n", tdo); + + uint8_t tms = 0xf0; + sbw_tms_sequence(8, true, &tms); + printf("tmsseq done\n"); + + printf("%s tclk:\n", "set"); + sbw_set_tclk(); + printf("%s tclk:\n", "clr"); + sbw_clr_tclk(); + printf("%s tclk:\n", "set"); + sbw_set_tclk(); + printf("%s tclk:\n", "clr"); + sbw_clr_tclk(); + + printf("doing a tclk burst now!\n"); + sbw_tclk_burst(16); + printf("done.\n"); return 0; } diff --git a/src/pio_sbw.c b/src/pio_sbw.c index 9ea27a6..b731b1e 100644 --- a/src/pio_sbw.c +++ b/src/pio_sbw.c @@ -16,6 +16,9 @@ int sbw_piosm = -1, sbw_offset = -1; +static uint last_tclk = 1; +static uint8_t last_tdi = 0xff, last_tms = 0xff; + bool sbw_init(void) { if (sbw_piosm >= 0 || sbw_offset >= 0) return false; @@ -32,6 +35,9 @@ bool sbw_init(void) { sbw_pio_init(PINOUT_SBW_PIO, sbw_piosm, sbw_offset, 200e3, PINOUT_SBW_TCK, PINOUT_SBW_TDIO); + last_tdi = last_tms = 0xff; + last_tclk = 1; + return true; } @@ -56,90 +62,160 @@ void sbw_set_freq(bool tclk, float freq) { } } -void sbw_sequence(uint32_t ncyc, uint32_t tms, const uint8_t* tdi, uint8_t* tdo) { - static uint64_t last_tdi = ~(uint64_t)0; - static uint64_t devnull = 0; +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; } + +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; - // fuck this im not doing this rn sorry - // TODO: finish this tomorrow + 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)) 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, uint32_t tdi, const uint8_t* tms) { +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)) tight_loop_contents(); + + last_tdi = tdi ? 0xff : 0; + last_tms = (tms[nbytes - 1] & (1 << (ncyc & 7))) ? 0xff : 0; } -static int last_tclk = 1; +void sbw_clrset_tclk(bool value) { + sbw_pio_loadbearing_set_setpins(PINOUT_SBW_PIO, value ? 1 : 0); // new value -void sbw_clr_tclk(void) { + // pre-TCLK value + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(last_tclk); -} -void sbw_set_tclk(void) { + // 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); + + // wait until done + while (!sbw_pio_is_idle(PINOUT_SBW_PIO, sbw_piosm)) tight_loop_contents(); + + last_tclk = value ? 1 : 0; } void sbw_tclk_burst(uint32_t ncyc) { + sbw_pio_loadbearing_set_outpins(PINOUT_SBW_PIO); -} + uint32_t txremain = ((ncyc + 7) >> 3) * 2; -// Just 8 bit functions provided here. The PIO program supports any frame size -// 1...32, but the software to do the necessary FIFO shuffling is left as an -// exercise for the reader :) -// -// Likewise we only provide MSB-first here. To do LSB-first, you need to -// - Do shifts when reading from the FIFO, for general case n != 8, 16, 32 -// - Do a narrow read at a one halfword or 3 byte offset for n == 16, 8 -// in order to get the read data correctly justified. + // MSB-first + uint8_t pattern = last_tclk ? 0x55 : 0xaa; -/*void __time_critical_func(pio_spi_write8_blocking)(const pio_spi_inst_t *spi, const uint8_t *src, size_t len) { - size_t tx_remain = len, rx_remain = len; - // Do 8 bit accesses on FIFO, so that write data is byte-replicated. This - // gets us the left-justification for free (for MSB-first shift-out) - io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; - io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; - while (tx_remain || rx_remain) { - if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { - *txfifo = *src++; - --tx_remain; - } - if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { - (void) *rxfifo; - --rx_remain; + // pre-TCLK value + piosm_txf_wait(); + piosm_txf(16) = sbw_pio_gen_sety(last_tclk); + + // 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)) tight_loop_contents(); + + // last_tclk doesn't change - always an even number of TCLK half-cycles } -void __time_critical_func(pio_spi_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *dst, size_t len) { - size_t tx_remain = len, rx_remain = len; - io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; - io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; - while (tx_remain || rx_remain) { - if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { - *txfifo = 0; - --tx_remain; - } - if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { - *dst++ = *rxfifo; - --rx_remain; - } - } -} - -void __time_critical_func(pio_spi_write8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, - size_t len) { - size_t tx_remain = len, rx_remain = len; - io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; - io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; - while (tx_remain || rx_remain) { - if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { - *txfifo = *src++; - --tx_remain; - } - if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { - *dst++ = *rxfifo; - --rx_remain; - } - } -}*/ - diff --git a/src/pio_sbw.h b/src/pio_sbw.h index 6a8eaef..5d2ac4c 100644 --- a/src/pio_sbw.h +++ b/src/pio_sbw.h @@ -21,11 +21,16 @@ void sbw_deinit(void); void sbw_set_freq(bool tclk, float freq); -void sbw_sequence(uint32_t ncyc, uint32_t tms, const uint8_t* tdi, uint8_t* tdo); -void sbw_tms_sequence(uint32_t ncyc, uint32_t tdi, const uint8_t* tms); +bool sbw_get_last_tms(void); +bool sbw_get_last_tdi(void); -void sbw_clr_tclk(void); -void sbw_set_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); diff --git a/src/sbw.pio b/src/sbw.pio index 0a4f5ec..8b98fd4 100644 --- a/src/sbw.pio +++ b/src/sbw.pio @@ -46,6 +46,9 @@ sbw_seq_iter: 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 @@ -94,7 +97,7 @@ PUBLIC sbw_tclk_burst: nop side 0 ; target reads TMS at falling edge mov pins, y side 0 ; during low phase, prepare TCLK ; tdi slot: - nop side 1 ; wait a bit + 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 @@ -122,28 +125,36 @@ static inline void sbw_pio_init(PIO pio, uint sm, uint prog_offs, 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 - // MOSI, SCK output are low, MISO is input - pio_sm_set_pins_with_mask(pio, sm, - (1u << pin_sbwclk), (1u << pin_sbwclk) | (1u << pin_sbwio)); pio_sm_set_pindirs_with_mask(pio, sm, - (1u << pin_sbwclk), (1u << pin_sbwclk) | (1u << pin_sbwio)); - pio_gpio_init(pio, pin_sbwclk); - pio_gpio_init(pio, pin_sbwio ); + (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); - pio_sm_init(pio, sm, prog_offs, &c); + 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 @@ -162,31 +173,40 @@ static inline void sbw_pio_set_tclkfreq(PIO pio, uint sm, float freq) { pio_sm_set_clkdiv(pio, sm, (float)clock_get_hz(clk_sys) / (24 * freq)); } -static inline uint16_t sbw_pio_gen_sety(uint y) { - return pio_encode_set(pio_y, y) | pio_encode_sideset(1, 1); +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_inx(uint bits) { - return pio_encode_in(pio_x, bits) | pio_encode_sideset(1, 1); +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); + 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); + 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); + 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) { + // FIXME: writes 32 bits!!! 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) { + // FIXME: writes 32 bits!!! 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 value) { + return pio_sm_get_pc(pio, value) < sbw_offset_sbw_seq; +} + %}