110% working SBW signals, finally
This commit is contained in:
parent
5dfd44123f
commit
448f1896cf
31
src/main.c
31
src/main.c
|
@ -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;
|
||||
}
|
||||
|
|
216
src/pio_sbw.c
216
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
50
src/sbw.pio
50
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;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
|
Loading…
Reference in New Issue