From e8d1a728d184ef37b0d315abaf10e861fda46890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Tue, 16 Mar 2021 12:21:16 +0100 Subject: [PATCH] application: add JTAG scanner (only ID code for now) --- application.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/application.c b/application.c index 1342602..6b6b761 100644 --- a/application.c +++ b/application.c @@ -301,6 +301,138 @@ static void command_swd_scan(void* argument) printf("\n%u SWD interface(s) found\n", found); } +#define JTAG_SPEED 50 /**< time in us between clock edges (i.e. setting the clock speed) */ +static int8_t jtag_tms_ch = -1; /**< channel used for JTAG TCK output (-1 = not configured) */ +static int8_t jtag_tck_ch = -1; /**< channel used for JTAG TMS output (-1 = not configured) */ +static int8_t jtag_tdi_ch = -1; /**< channel used for JTAG TMS output (-1 = not configured) */ +static uint32_t jtag_tdo[CHANNEL_NUMBERS]; /**< possible TDO pin data bits, for each channel (1 for TCK/TMS/TDO pins) */ + +/** send data to JTAG pins + * @param[in] tms TMS bits to send (LSb first) + * @param[in] tdi TDI bits to send (LSb first) + * @param[in] nb number of bits to send + * @note TDO data will be stored in jtag_tdo (only between channel start and stop, and TDI/TMS/TCK set to 1) + */ +static void jtag_transaction(uint32_t tms, uint32_t tdi, uint8_t nb) +{ + if (jtag_tck_ch < 0 || jtag_tck_ch >= CHANNEL_NUMBERS || jtag_tms_ch < 0 || jtag_tms_ch >= CHANNEL_NUMBERS) { // ensure at least TCK and TMS are configured + return; + } + + // reset TDO values + for (uint8_t tdo = 0; tdo < LENGTH(jtag_tdo); tdo++) { + jtag_tdo[tdo] = UINT32_MAX; + } + + gpio_set(channel_ports[jtag_tck_ch], channel_pins[jtag_tck_ch]); // ensure we start with TCK high + for (uint8_t bit = 0; bit < nb && bit < 32; bit++) { // go through all bits + sleep_us(JTAG_SPEED); // wait for clock falling edge + // output change is on TCK falling edge + if (tms & 0x1) { + gpio_set(channel_ports[jtag_tms_ch], channel_pins[jtag_tms_ch]); // set TMS high + } else { + gpio_clear(channel_ports[jtag_tms_ch], channel_pins[jtag_tms_ch]); // set TMS low + } + tms >>= 1; // go to next bit + if (jtag_tdi_ch >= channel_start && jtag_tdi_ch < channel_stop) { // TDI is configured + if (tdi & 0x1) { + gpio_set(channel_ports[jtag_tdi_ch], channel_pins[jtag_tdi_ch]); // set TDI high + } else { + gpio_clear(channel_ports[jtag_tdi_ch], channel_pins[jtag_tdi_ch]); // set TDI low + } + } + tdi >>= 1; // go to next bit + gpio_clear(channel_ports[jtag_tck_ch], channel_pins[jtag_tck_ch]); // clock falling edge + sleep_us(JTAG_SPEED); // wait for clock rising edge + gpio_set(channel_ports[jtag_tck_ch], channel_pins[jtag_tck_ch]); // clock rising edge + for (uint8_t tdo = channel_start; tdo < channel_stop && tdo < CHANNEL_NUMBERS; tdo++) { // read TDO + if (tdo == jtag_tms_ch || tdo == jtag_tck_ch || tdo == jtag_tdi_ch) { // channel is already used + continue; // ignore output signal + } else if (0 == gpio_get(channel_ports[tdo], channel_pins[tdo])) { // signal is low + jtag_tdo[tdo] &= ~(1U << bit); // clear bit + } + } + } +} + +static void command_jtag_scan(void* argument) +{ + (void)argument; // we won't use the argument + + float* voltages = measure_voltages(); // measure voltages + if (voltages[1] < 0.5) { // check target voltage connection + puts("connect target voltage to test channel type\n"); + return; + } + + mux_select(-1); // disable multiplexer + gpio_set(GPIO_PORT(SIGNAL_PU_PIN), GPIO_PIN(SIGNAL_PU_PIN)); // ensure we are not pulling up the signal + gpio_set(GPIO_PORT(SIGNAL_PD_PIN), GPIO_PIN(SIGNAL_PD_PIN)); // ensure we are not pulling down the signal + gpio_clear(GPIO_PORT(TARGET_EN), GPIO_PIN(TARGET_EN)); // power level shifter + sleep_us(100); // wait a tiny bit for the pull-up to be active + + printf("searching JTAG on channels CH%02u-CH%02u\n", channel_start, channel_stop); + + printf("searching for TDO using IDCODE scan on TCK/TMS (%u combinations): ", (channel_stop - channel_start + 1) * (channel_stop - channel_start)); + //uint32_t tdo[CHANNEL_NUMBERS]; // the output bit stream for all channels + bool idcode[CHANNEL_NUMBERS]; // when an IDCODE has been found + //uint8_t found = 0; // number of JTAG ports found + jtag_tdi_ch = -1; // we don't use TDI for now + for (uint8_t tck = channel_start; tck <= channel_stop; tck++) { // use channel as TCK output + for (uint8_t tms = channel_start; tms <= channel_stop; tms++) { // use channel as TMS output + if (tck == tms) { // don't use the same channel for TCK and TMS + continue; + } + gpio_set(channel_ports[tck], channel_pins[tck]); // clock is idle high + gpio_mode_setup(channel_ports[tck], GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, channel_pins[tck]); // set channel for TCK as output + jtag_tck_ch = tck; // remember which channel we use for TCK for the transaction + gpio_set(channel_ports[tms], channel_pins[tms]); // start high (to go to reset state) + gpio_mode_setup(channel_ports[tms], GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, channel_pins[tms]); // set channel for TMS back to input + jtag_tms_ch = tms; // remember which channel we use for TMS for the transaction + // all other channel should already be inputs + jtag_transaction(0x3f | (0 << 6) | (1 << 7) | (0 << 8) | (0 << 9), 0, 6 + 1 + 1 + 1 + 1); // go back to JTAG TEST-LOGIC_RESET (5 bits should be enough to go from any state to RESET, but we a one just to be sure) -> RUN-TEST/IDLE -> SELECT-DR-SCAN -> CAPTURE-DR -> SHIFT-DR states + // initialize array + for (uint8_t i = 0; i < LENGTH(idcode); i++) { + idcode[i] = true; + } + bool idcode_scan = true; // if we need to check for an IDCODE + bool idcode_found = false; // if we found an IDCODE (in this combination) + uint8_t idcode_max = 20; // maximum number of chained IDCODEs + while (idcode_scan && idcode_max) { + idcode_scan = false; // stop scanning (unless we find an IDCODE) + jtag_transaction(0, 0, 32); // read 32-bit IDCODE + for (uint8_t tdo = channel_start; tdo < channel_stop; tdo++) { + if (tdo == tms || tdo == tck) { // channel already used for other signal + continue; + } else if (!idcode[tdo]) { // IDCODE already exhausted + continue; // any other data on the line should be noise + } else if (0 == jtag_tdo[tdo] || UINT32_MAX == jtag_tdo[tdo]) { // no IDCODE received (line constant low or pulled up + idcode[tdo] = false; + } else if (0 == (jtag_tdo[tdo] & 0x1)) { // RAO bit is wrong + continue; + } else { // IDCODE received + printf("\npossible IDCODE found: TCK=CH%02u TMS=CH%02u TDI=CH%02u IDCODE=%+08x (", tck, tms, tdo, jtag_tdo[tdo]); // show finding + print_idcode(jtag_tdo[tdo]); + puts(")"); + idcode_found = true; // remember we found an IDCODE + idcode_scan = true; // continue scanning for the next code + } + } + idcode_max--; // to not be stuck in a loop + } + if (idcode_found) { + putc('\n'); // continue dot pattern on new line + } + gpio_mode_setup(channel_ports[tck], GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel_pins[tck]); // set channel for TCK back to input + jtag_tck_ch = -1; // clear channel configuration + gpio_mode_setup(channel_ports[tms], GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel_pins[tms]); // set channel for TMS back to input + jtag_tms_ch = -1; // clear channel configuration + putc('.'); // one combination completed + } + } + putc('\n'); // all combinations completed +} + static void command_voltages(void* argument) { (void)argument; // we won't use the argument @@ -689,6 +821,14 @@ static const struct menu_command_t menu_commands[] = { .argument_description = NULL, .command_handler = &command_swd_scan, }, + { + .shortcut = 'j', + .name = "jtag", + .command_description = "scan for JTAG interfaces", + .argument = MENU_ARGUMENT_NONE, + .argument_description = NULL, + .command_handler = &command_jtag_scan, + }, { .shortcut = 'v', .name = "voltage",