DragonProbe/bsp/rp2040/m_isp/mehfet_hw.c

206 lines
6.5 KiB
C

// vim: set et:
#include <hardware/gpio.h>
#include <hardware/timer.h>
#include <pico/time.h>
#include "util.h"
#include "m_isp/pinout.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
gpio_set_function(PINOUT_SBW_TCK , GPIO_FUNC_NULL);
gpio_set_function(PINOUT_SBW_TDIO, GPIO_FUNC_NULL);
}
__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()) {
mehfet_hw_deinit();
return "SBW PIO init failed";
}
return NULL;
}
void mehfet_hw_disconnect(void) {
sbw_deinit();
}
void mehfet_hw_set_clkspeed(bool fast) {
if (fast) {
sbw_set_freq(true, 350e3);
} else {
sbw_set_freq(false, 50e3);
}
}
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);
}
enum mehfet_resettap_status mehfet_hw_reset_tap(enum mehfet_resettap_flags flags) {
enum mehfet_resettap_status rv = 0;
if (flags & mehfet_rsttap_do_reset) {
// 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);
}
if (flags & mehfet_rsttap_fuse_do) {
// TDI always 1
// TMS=01010110 // same sequence as above, but without TAP reset
const uint8_t tms_seq = 0x6a;
sbw_tms_sequence(8, true, &tms_seq);
}
if (flags & mehfet_rsttap_fuse_read) {
for (size_t i = 0; i < 3; ++i) {
mehfet_hw_shift_ir(0x14); // CNTRL_SIG_CAPTURE
uint16_t dr = mehfet_hw_shift_dr16(0xaaaa);
if (dr == 0x5555) {
rv |= mehfet_rsttap_fuse_blown;
break;
}
}
}
return rv;
}
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);
}