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);
|
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)
|
static void command_voltages(void* argument)
|
||||||
{
|
{
|
||||||
(void)argument; // we won't use the argument
|
(void)argument; // we won't use the argument
|
||||||
|
@ -689,6 +821,14 @@ static const struct menu_command_t menu_commands[] = {
|
||||||
.argument_description = NULL,
|
.argument_description = NULL,
|
||||||
.command_handler = &command_swd_scan,
|
.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',
|
.shortcut = 'v',
|
||||||
.name = "voltage",
|
.name = "voltage",
|
||||||
|
|
Loading…
Reference in New Issue