// vim: set et: #include #include #include #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); }