application: add JTAG scanner (only ID code for now)
This commit is contained in:
parent
79a8357308
commit
e8d1a728d1
140
application.c
140
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",
|
||||
|
|
Loading…
Reference in New Issue