110% working SBW signals, finally

This commit is contained in:
Triss 2021-09-27 16:10:02 +02:00
parent 5dfd44123f
commit 448f1896cf
4 changed files with 215 additions and 87 deletions

View File

@ -6,20 +6,47 @@
#include <stdio.h>
#include <hardware/gpio.h>
#include <pico/stdlib.h>
#include <pico/binary_info.h>
#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;
}

View File

@ -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;
// MSB-first
uint8_t pattern = last_tclk ? 0x55 : 0xaa;
// 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
}
// 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.
/*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;
}
}
}
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;
}
}
}*/

View File

@ -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);

View File

@ -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;
}
%}