application: add JTAG scanner (only ID code for now)

This commit is contained in:
King Kévin 2021-03-16 12:21:16 +01:00
parent 79a8357308
commit e8d1a728d1
1 changed files with 140 additions and 0 deletions

View File

@ -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",