This is an automated email from Gerrit. "Tim Newsome <t...@sifive.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6981
-- gerrit commit 808d8d56139e343537d64771c1d7fe175018420f Author: Tim Newsome <t...@sifive.com> Date: Mon May 16 14:02:30 2022 -0700 Support two-wire cJTAG OSCAN1 using FTDI adapters cJTAG OSCAN1, in lieu of 4-wire JTAG, is starting to be a configuration option for some SiFive hardware. An FTDI-based adapter that can be configured to drive the bidirectional pin TMSC is assumed for this topology. Specifically, the Olimex ARM-USB-TINY-H with the ARM-JTAG-SWD adapter, connected to a SiFive cJTAG-enabled target board is the only known concrete topology, currently. But in theory, other FTDI based devices that can drive a two-wire bidirectional signaling pattern could be made to work in this scheme in the future. These code changes are offered as a way to drive that topology. It's translating IR/DR and JTAG traversal commands to the two-wire clocking and signaling. See https://github.com/riscv/riscv-openocd/pull/320 Signed-off-by: Tim Newsome <t...@sifive.com> Change-Id: Ia1daa2c01227c4b0005be947b2bb0de81a800874 diff --git a/configure.ac b/configure.ac index 95b2d50ae1..60d1ed674d 100644 --- a/configure.ac +++ b/configure.ac @@ -110,6 +110,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])]) m4_define([USB1_ADAPTERS], [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]], + [[ftdi_oscan1], [cJTAG OSCAN1 tunneled thru MPSSE], [FTDI_OSCAN1]], [[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]], [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]], [[ulink], [Keil ULINK JTAG Programmer], [ULINK]], diff --git a/doc/openocd.texi b/doc/openocd.texi index be9e5ac085..c1839c6da6 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -2594,6 +2594,13 @@ minimal impact on the target system. Avoid floating inputs, conflicting outputs and initially asserted reset signals. @end deffn +@deffn {Command} {ftdi oscan1_mode} on|off +Enable or disable OSCAN1 mode. This mode is intended for use with an adapter, +such as the ARM-JTAG-SWD by Olimex, that sits in between the FTDI chip and the +target. The adapter uses the normal JTAG signals to control TCKC and TMSC +(bidirectional) signals used in 2-wire cJTAG. +@end deffn + @deffn {Command} {ftdi layout_signal} name [@option{-data}|@option{-ndata} data_mask] [@option{-input}|@option{-ninput} input_mask] [@option{-oe}|@option{-noe} oe_mask] [@option{-alias}|@option{-nalias} name] Creates a signal with the specified @var{name}, controlled by one or more FTDI GPIO pins via a range of possible buffer connections. The masks are FTDI GPIO diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 7671cee5d0..2ecff7f862 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -85,6 +85,16 @@ /* FTDI access library includes */ #include "mpsse.h" +#if BUILD_FTDI_OSCAN1 == 1 +#define DO_CLOCK_DATA clock_data +#define DO_CLOCK_TMS_CS clock_tms_cs +#define DO_CLOCK_TMS_CS_OUT clock_tms_cs_out +#else +#define DO_CLOCK_DATA mpsse_clock_data +#define DO_CLOCK_TMS_CS mpsse_clock_tms_cs +#define DO_CLOCK_TMS_CS_OUT mpsse_clock_tms_cs_out +#endif + #define JTAG_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT) #define JTAG_MODE_ALT (LSB_FIRST | NEG_EDGE_IN | NEG_EDGE_OUT) #define SWD_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT) @@ -95,6 +105,31 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE; static bool swd_mode; +#if BUILD_FTDI_OSCAN1 == 1 +/* + The cJTAG 2-wire OSCAN1 protocol, in lieu of 4-wire JTAG, is a configuration option + for some SoCs. An FTDI-based adapter that can be configured to appropriately drive + the bidirectional pin TMSC is able to drive OSCAN1 protocol. For example, an Olimex + ARM-USB-TINY-H with the ARM-JTAG-SWD adapter, connected to a cJTAG-enabled + target board is such a topology. A TCK cycle with TMS=1/TDI=N translates to a TMSC + output of N, and a TCK cycle with TMS=0 translates to a TMSC input from the target back + to the adapter/probe. The OSCAN1 protocol uses 3 TCK cycles to generate the data flow + that is equivalent to that of a single TCK cycle in 4-wire JTAG. The OSCAN1-related + code in this module translates IR/DR scan commanads and JTAG state traversal commands + to the two-wire clocking and signaling of OSCAN1 protocol, if placed into oscan1 mode + during initialization. +*/ +static void oscan1_reset_online_activate(void); +static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, uint8_t mode); +static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, bool tdi, uint8_t mode); +static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, + unsigned length, bool tdi, uint8_t mode); + +static bool oscan1_mode; +#endif + #define MAX_USB_IDS 8 /* vid = pid = 0 marks the end of the list */ static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 }; @@ -240,6 +275,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t *value_out) return ERROR_OK; } +#if BUILD_FTDI_OSCAN1 == 1 +static void clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, uint8_t mode) +{ + if (oscan1_mode) + oscan1_mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode); + else + mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode); +} + +static void clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, bool tdi, uint8_t mode) +{ + if (oscan1_mode) + oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode); + else + mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode); +} + +static void clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, + unsigned length, bool tdi, uint8_t mode) +{ + if (oscan1_mode) + oscan1_mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode); + else + mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode); +} +#endif + /** * Function move_to_state * moves the TAP controller from the current state to a @@ -268,7 +332,7 @@ static void move_to_state(tap_state_t goal_state) for (int i = 0; i < tms_count; i++) tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1)); - mpsse_clock_tms_cs_out(mpsse_ctx, + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms_bits, 0, tms_count, @@ -322,7 +386,7 @@ static void ftdi_end_state(tap_state_t state) static void ftdi_execute_runtest(struct jtag_command *cmd) { int i; - uint8_t zero = 0; + static const uint8_t zero; LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles, @@ -336,7 +400,7 @@ static void ftdi_execute_runtest(struct jtag_command *cmd) while (i > 0) { /* there are no state transitions in this code, so omit state tracking */ unsigned this_len = i > 7 ? 7 : i; - mpsse_clock_tms_cs_out(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode); + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode); i -= this_len; } @@ -371,7 +435,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd) LOG_DEBUG_IO("TMS: %d bits", cmd->cmd.tms->num_bits); /* TODO: Missing tap state tracking, also missing from ft2232.c! */ - mpsse_clock_tms_cs_out(mpsse_ctx, + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, cmd->cmd.tms->bits, 0, cmd->cmd.tms->num_bits, @@ -418,7 +482,7 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd) state_count++; if (bit_count == 7 || num_states == 0) { - mpsse_clock_tms_cs_out(mpsse_ctx, + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms_byte, 0, bit_count, @@ -472,7 +536,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd) if (i == cmd->cmd.scan->num_fields - 1 && tap_get_state() != tap_get_end_state()) { /* Last field, and we're leaving IRSHIFT/DRSHIFT. Clock last bit during tap * movement. This last field can't have length zero, it was checked above. */ - mpsse_clock_data(mpsse_ctx, + DO_CLOCK_DATA(mpsse_ctx, field->out_value, 0, field->in_value, @@ -482,12 +546,8 @@ static void ftdi_execute_scan(struct jtag_command *cmd) uint8_t last_bit = 0; if (field->out_value) bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1); - - /* If endstate is TAP_IDLE, clock out 1-1-0 (->EXIT1 ->UPDATE ->IDLE) - * Otherwise, clock out 1-0 (->EXIT1 ->PAUSE) - */ uint8_t tms_bits = 0x03; - mpsse_clock_tms_cs(mpsse_ctx, + DO_CLOCK_TMS_CS(mpsse_ctx, &tms_bits, 0, field->in_value, @@ -497,7 +557,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd) ftdi_jtag_mode); tap_set_state(tap_state_transition(tap_get_state(), 1)); if (tap_get_end_state() == TAP_IDLE) { - mpsse_clock_tms_cs_out(mpsse_ctx, + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms_bits, 1, 2, @@ -506,7 +566,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd) tap_set_state(tap_state_transition(tap_get_state(), 1)); tap_set_state(tap_state_transition(tap_get_state(), 0)); } else { - mpsse_clock_tms_cs_out(mpsse_ctx, + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms_bits, 2, 1, @@ -515,7 +575,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd) tap_set_state(tap_state_transition(tap_get_state(), 0)); } } else - mpsse_clock_data(mpsse_ctx, + DO_CLOCK_DATA(mpsse_ctx, field->out_value, 0, field->in_value, @@ -596,7 +656,7 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd) while (num_cycles > 0) { /* there are no state transitions in this code, so omit state tracking */ unsigned this_len = num_cycles > 7 ? 7 : num_cycles; - mpsse_clock_tms_cs_out(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode); + DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode); num_cycles -= this_len; } @@ -608,11 +668,19 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd) static void ftdi_execute_command(struct jtag_command *cmd) { switch (cmd->type) { + case JTAG_RESET: +#if BUILD_FTDI_OSCAN1 == 1 + oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */ +#endif + break; case JTAG_RUNTEST: ftdi_execute_runtest(cmd); break; case JTAG_TLR_RESET: ftdi_execute_statemove(cmd); +#if BUILD_FTDI_OSCAN1 == 1 + oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */ +#endif break; case JTAG_PATHMOVE: ftdi_execute_pathmove(cmd); @@ -691,6 +759,17 @@ static int ftdi_initialize(void) /* A dummy SWD_EN would have zero mask */ if (sig->data_mask) ftdi_set_signal(sig, '1'); +#if BUILD_FTDI_OSCAN1 == 1 + } else if (oscan1_mode) { + struct signal *sig = find_signal_by_name("JTAG_SEL"); + if (!sig) { + LOG_ERROR("OSCAN1 mode is active but JTAG_SEL signal is not defined"); + return ERROR_JTAG_INIT_FAILED; + } + /* A dummy JTAG_SEL would have zero mask */ + if (sig->data_mask) + ftdi_set_signal(sig, '0'); +#endif } mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff); @@ -722,6 +801,254 @@ static int ftdi_quit(void) return ERROR_OK; } +#if BUILD_FTDI_OSCAN1 == 1 +static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, uint8_t mode) +{ + static const uint8_t zero; + static const uint8_t one = 1; + + LOG_DEBUG_IO("oscan1_mpsse_clock_data: %sout %d bits", in ? "in" : "", length); + + for (unsigned i = 0; i < length; i++) { + int bitnum; + uint8_t bit; + + /* OSCAN1 uses 3 separate clocks */ + + /* drive TMSC to the *negation* of the desired TDI value */ + bitnum = out_offset + i; + bit = out ? ((out[bitnum/8] >> (bitnum%8)) & 0x1) : 0; + + /* Try optimized case first: if desired TDI bit is 1, then we + can fuse what would otherwise be the first two MPSSE commands */ + if (bit) { + const uint8_t tmsbits = 0x3; /* 1, 1 */ + mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, false, mode); + } else { + /* Can't fuse because TDI varies; less efficient */ + mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, bit ? 0 : 1, mode); + + /* drive TMSC to desired TMS value (always zero in this context) */ + mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, false, mode); + } + + /* drive another TCK without driving TMSC (TDO cycle) */ + mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode); + } +} + + +static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in, + unsigned in_offset, unsigned length, bool tdi, uint8_t mode) +{ + static const uint8_t zero; + static const uint8_t one = 1; + + LOG_DEBUG_IO("oscan1_mpsse_clock_tms_cs: %sout %d bits, tdi=%d", in ? "in" : "", length, tdi); + + for (unsigned i = 0; i < length; i++) { + int bitnum; + uint8_t tmsbit; + uint8_t tdibit; + + /* OSCAN1 uses 3 separate clocks */ + + /* drive TMSC to the *negation* of the desired TDI value */ + tdibit = tdi ? 0 : 1; + + /* drive TMSC to desired TMS value */ + bitnum = out_offset + i; + tmsbit = ((out[bitnum/8] >> (bitnum%8)) & 0x1); + + if (tdibit == tmsbit) { + /* Can squash into a single MPSSE command */ + const uint8_t tmsbits = 0x3; + mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, tdibit, mode); + } else { + /* Unoptimized case, can't formulate with a single command */ + mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, tdibit, mode); + mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, (tmsbit != 0), mode); + } + + /* drive another TCK without driving TMSC (TDO cycle) */ + mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode); + } +} + + +static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, + unsigned length, bool tdi, uint8_t mode) +{ + oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode); +} + + +static void oscan1_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct signal *tms, + char tmsvalue, struct signal *tdi, char tdivalue) +{ + ftdi_set_signal(tms, tmsvalue); + ftdi_set_signal(tdi, tdivalue); + ftdi_set_signal(tck, tckvalue); +} + +static void oscan1_reset_online_activate(void) +{ + /* After TAP reset, the OSCAN1-to-JTAG adapter is in offline and + non-activated state. Escape sequences are needed to bring + the TAP online and activated into OSCAN1 mode. */ + + struct signal *tck = find_signal_by_name("TCK"); + struct signal *tdi = find_signal_by_name("TDI"); + struct signal *tms = find_signal_by_name("TMS"); + struct signal *tdo = find_signal_by_name("TDO"); + uint16_t tdovalue; + + static const struct { + int8_t tck; + int8_t tms; + int8_t tdi; + } sequence[] = { + /* TCK=0, TMS=1, TDI=0 (drive TMSC to 0 baseline) */ + {'0', '1', '0'}, + + /* Drive cJTAG escape sequence for TAP reset - 8 TMSC edges */ + /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */ + {'0', '1', '0'}, + + /* 3 TCK pulses for padding */ + /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */ + {'0', '1', '0'}, + + /* Drive cJTAG escape sequence for SELECT */ + /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0, TAP reset that was just setup occurs here too) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */ + {'1', '1', '1'}, + /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */ + {'0', '1', '0'}, + + /* Drive cJTAG escape sequence for activation */ + /* TCK=1, TMS=1, TDI=0 (rising edge TCK with TMSC still 0... online mode activated... also OAC bit0==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... OAC bit1==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */ + {'0', '1', '1'}, + /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit2==1) */ + {'1', '1', '1'}, + /* TCK=0, TMS=1, TDI=1 (falling edge TCK, TMSC stays high) */ + {'0', '1', '1'}, + /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit3==1) */ + {'1', '1', '1'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit0==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit1==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit2==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */ + {'0', '1', '1'}, + /* TCK=1, TMS=1, TDI=1 (rising edge TCK... EC bit3==1) */ + {'1', '1', '1'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit0==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit1==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit2==0) */ + {'1', '1', '0'}, + /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */ + {'0', '1', '0'}, + /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit3==0) */ + {'1', '1', '0'}, + }; + + + if (!oscan1_mode) + return; + + + if (!tck) { + LOG_ERROR("Can't run cJTAG online/activate escape sequences: TCK signal is not defined"); + return; + } + + if (!tdi) { + LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDI signal is not defined"); + return; + } + + if (!tms) { + LOG_ERROR("Can't run cJTAG online/activate escape sequences: TMS signal is not defined"); + return; + } + + if (!tdo) { + LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDO signal is not defined"); + return; + } + + /* Send the sequence to the adapter */ + for (size_t i = 0; i < sizeof(sequence)/sizeof(sequence[0]); i++) + oscan1_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi); + + ftdi_get_signal(tdo, &tdovalue); /* Just to force a flush */ +} + +#endif /* #if BUILD_FTDI_OSCAN1 == 1 */ + COMMAND_HANDLER(ftdi_handle_device_desc_command) { if (CMD_ARGC == 1) { @@ -933,6 +1260,20 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command) return ERROR_OK; } +#if BUILD_FTDI_OSCAN1 == 1 +COMMAND_HANDLER(ftdi_handle_oscan1_mode_command) +{ + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (CMD_ARGC == 1) + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], oscan1_mode); + + command_print(CMD, "oscan1 mode: %s.", oscan1_mode ? "on" : "off"); + return ERROR_OK; +} +#endif + static const struct command_registration ftdi_subcommand_handlers[] = { { .name = "device_desc", @@ -994,6 +1335,15 @@ static const struct command_registration ftdi_subcommand_handlers[] = { "allow signalling speed increase)", .usage = "(rising|falling)", }, +#if BUILD_FTDI_OSCAN1 == 1 + { + .name = "oscan1_mode", + .handler = &ftdi_handle_oscan1_mode_command, + .mode = COMMAND_ANY, + .help = "set to 'on' to use OSCAN1 mode for signaling, otherwise 'off' (default is 'off')", + .usage = "(on|off)", + }, +#endif COMMAND_REGISTRATION_DONE }; diff --git a/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg new file mode 100644 index 0000000000..6939d00267 --- /dev/null +++ b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg @@ -0,0 +1,27 @@ +# +# Olimex ARM JTAG SWD adapter +# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/ +# + +# +# Olimex ARM-USB-TINY-H +# +# http://www.olimex.com/dev/arm-usb-tiny-h.html +# + +interface ftdi +ftdi oscan1_mode on +ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H" +ftdi vid_pid 0x15ba 0x002a + +ftdi layout_init 0x0808 0x0a1b +ftdi layout_signal nSRST -oe 0x0200 +# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100 +ftdi layout_signal LED -data 0x0800 + +# These signals are used for cJTAG escape sequence on initialization only +ftdi layout_signal TCK -data 0x0001 +ftdi layout_signal TDI -data 0x0002 +ftdi layout_signal TDO -input 0x0004 +ftdi layout_signal TMS -data 0x0008 +ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100 diff --git a/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg new file mode 100644 index 0000000000..36ac587620 --- /dev/null +++ b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg @@ -0,0 +1,22 @@ +# +# Olimex ARM-USB-OCD-H (using cJTAG) +# +# http://www.olimex.com/dev/arm-usb-ocd-h.html +# + +interface ftdi +ftdi oscan1_mode on +ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-OCD-H" +ftdi vid_pid 0x15ba 0x002b + +ftdi layout_init 0x0808 0x0a1b +ftdi layout_signal nSRST -oe 0x0200 +# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100 +ftdi layout_signal LED -data 0x0800 + +# These signals are used for cJTAG escape sequence on initialization only +ftdi layout_signal TCK -data 0x0001 +ftdi layout_signal TDI -data 0x0002 +ftdi layout_signal TDO -input 0x0004 +ftdi layout_signal TMS -data 0x0008 +ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100 diff --git a/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg new file mode 100644 index 0000000000..13378b394a --- /dev/null +++ b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg @@ -0,0 +1,27 @@ +# +# Olimex ARM JTAG SWD adapter +# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/ +# + +# +# Olimex ARM-USB-TINY-H (using cJTAG) +# +# http://www.olimex.com/dev/arm-usb-tiny-h.html +# + +interface ftdi +ftdi oscan1_mode on +ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H" +ftdi vid_pid 0x15ba 0x002a + +ftdi layout_init 0x0808 0x0a1b +ftdi layout_signal nSRST -oe 0x0200 +# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100 +ftdi layout_signal LED -data 0x0800 + +# These signals are used for cJTAG escape sequence on initialization only +ftdi layout_signal TCK -data 0x0001 +ftdi layout_signal TDI -data 0x0002 +ftdi layout_signal TDO -input 0x0004 +ftdi layout_signal TMS -data 0x0008 +ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100 --