|
From: openocd-gerrit <ope...@us...> - 2025-11-30 10:24:27
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Main OpenOCD repository".
The branch, master has been updated
via fc8f939d95e0b02e5d5dc70023a0ca48fde7aa5d (commit)
from 181547327f11ea56d24b1d15989df81355b2b7d0 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit fc8f939d95e0b02e5d5dc70023a0ca48fde7aa5d
Author: Antonio Borneo <bor...@gm...>
Date: Sat Jul 26 12:17:46 2025 +0200
Support two-wire cJTAG OSCAN1 and JScan3 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-collab/riscv-openocd/pull/320
- https://github.com/riscv-collab/riscv-openocd/pull/736
Signed-off-by: Greg Savin <gre...@si...>
Signed-off-by: mrv96 <mr...@us...>
Signed-off-by: Tim Newsome <ti...@si...>
Signed-off-by: Antonio Borneo <bor...@gm...>
Change-Id: Ia1daa2c01227c4b0005be947b2bb0de81a800874
Reviewed-on: https://review.openocd.org/c/openocd/+/6981
Tested-by: jenkins
diff --git a/configure.ac b/configure.ac
index 6efcf24f2..d563f2b66 100644
--- a/configure.ac
+++ b/configure.ac
@@ -130,6 +130,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
+ [[ftdi_cjtag], [cJTAG (OScan1, JScan3) tunneled thru MPSSE], [FTDI_CJTAG]],
[[ch347], [CH347 based devices], [CH347]],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 09bbd3dd9..82d2a9416 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -2781,6 +2781,35 @@ 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 cJTAG prococol is composed of two wires: TCKC (clock) and TMSC (data).
+TMSC is a bidirectional signal which is time-multiplexed alternating TDI, TMS and
+TDO. The multiplexing is achieved by a tri-state buffer which puts TMSC in Hi-Z
+when the device is supposed to take the control of the line (TDO phase).
+
+The ARM-JTAG-SWD adapter uses standard TRST and TMS signals to control TMSC
+direction. TRST is used by the adapter as selector for the multiplexers which set
+the JTAG probe in 2-wire mode. Whatever signal is used for this purpose, it must
+be defined with the name JTAG_SEL using @command{ftdi layout_signal}. JTAG_SEL is
+set to 0 during OScan1 initialization.
+
+Some JTAG probes like the Digilent JTAG-HS2, support cJTAG by using a
+separate pin to control when TMS is driven onto TMSC. You can use such
+probes by defining the signal TMSC_EN using
+@command{ftdi layout_signal TMSC_EN -data <mask>}.
+@end deffn
+
+@deffn {Command} {ftdi jscan3_mode} on|off
+Enable or disable JScan3 mode. This mode uses the classic 4-wire JTAG protocol
+in chips whose JTAG port is only compliant with the cJTAG standard (IEEE 1149.7).
+
+Since cJTAG needs a 2-wire escape sequence to select the operating mode,
+a cJTAG adapter like ARM-JTAG-SWD by Olimex is still required. This means
+that a cJTAG probe configuration script must be used too.
+@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 00b3d198b..c57972cce 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -75,6 +75,16 @@
/* FTDI access library includes */
#include "mpsse.h"
+#if BUILD_FTDI_CJTAG == 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)
@@ -85,6 +95,39 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE;
static bool swd_mode;
+#if BUILD_FTDI_CJTAG == 1
+#define ESCAPE_SEQ_OAC_BIT2 28
+
+static void cjtag_reset_online_activate(void);
+
+/*
+ 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_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int length, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int length, bool tdi, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset,
+ unsigned int length, bool tdi, uint8_t mode);
+
+static bool oscan1_mode;
+
+/*
+ The cJTAG 4-wire JScan3 allows to use standard JTAG protocol with cJTAG hardware
+*/
+static bool jscan3_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 };
@@ -230,6 +273,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t *value_out)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int 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 int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int 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 int out_offset,
+ unsigned int 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
@@ -258,7 +330,7 @@ static void move_to_state(enum tap_state 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,
@@ -325,7 +397,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 int 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;
}
@@ -360,7 +432,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd)
LOG_DEBUG_IO("TMS: %u 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,
@@ -407,7 +479,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,
@@ -461,7 +533,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,
@@ -476,7 +548,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
* 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,
@@ -486,7 +558,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,
@@ -495,7 +567,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,
@@ -504,7 +576,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,
@@ -585,7 +657,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 int 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;
}
@@ -597,10 +669,19 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
static void ftdi_execute_command(struct jtag_command *cmd)
{
switch (cmd->type) {
+#if BUILD_FTDI_CJTAG == 1
+ case JTAG_RESET:
+ if (cmd->cmd.reset->trst)
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+ break;
+#endif
case JTAG_RUNTEST:
ftdi_execute_runtest(cmd);
break;
case JTAG_TLR_RESET:
+#if BUILD_FTDI_CJTAG == 1
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+#endif
ftdi_execute_statemove(cmd);
break;
case JTAG_PATHMOVE:
@@ -675,6 +756,21 @@ 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_CJTAG == 1
+ } else if (oscan1_mode || jscan3_mode) {
+ struct signal *sig = find_signal_by_name("JTAG_SEL");
+ if (!sig) {
+ LOG_ERROR("A cJTAG 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');
+ } else if (jscan3_mode) {
+ LOG_ERROR("In JScan3 mode JTAG_SEL signal cannot be dummy, data mask needed");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+#endif
}
mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff);
@@ -706,6 +802,286 @@ static int ftdi_quit(void)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int length, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("%sout %d bits", in ? "in" : "", length);
+
+ for (unsigned int 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);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset + i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset, uint8_t *in,
+ unsigned int in_offset, unsigned int length, bool tdi, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("%sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
+
+ for (unsigned int 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);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset + i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned int out_offset,
+ unsigned int length, bool tdi, uint8_t mode)
+{
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
+}
+
+static void cjtag_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 cjtag_reset_online_activate(void)
+{
+ /* After TAP reset, the cJTAG-to-JTAG adapter is in offline and
+ non-activated state. Escape sequences are needed to bring the
+ TAP online and activated into the desired working 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");
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+ uint16_t tdovalue;
+
+ static 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 OScan1 activation -- OAC = 1100 -> 2 wires -- */
+ /* 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'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ };
+
+ if (!oscan1_mode && !jscan3_mode)
+ return; /* Nothing to do */
+
+ if (oscan1_mode && jscan3_mode) {
+ LOG_ERROR("Both oscan1_mode and jscan3_mode are \"on\". At most one of them can be enabled.");
+ 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;
+ }
+
+ if (jscan3_mode) {
+ /* Update the sequence above to enable JScan3 instead of OScan1 */
+ sequence[ESCAPE_SEQ_OAC_BIT2].tdi = '0';
+ sequence[ESCAPE_SEQ_OAC_BIT2 + 1].tdi = '0';
+ }
+
+ /* if defined TMSC_EN, replace tms with it */
+ if (tmsc_en)
+ tms = tmsc_en;
+
+ /* Send the sequence to the adapter */
+ for (size_t i = 0; i < ARRAY_SIZE(sequence); i++)
+ cjtag_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi);
+
+ /* If JScan3 mode, configure cJTAG adapter to 4-wire */
+ if (jscan3_mode)
+ ftdi_set_signal(find_signal_by_name("JTAG_SEL"), '1');
+
+ ftdi_get_signal(tdo, &tdovalue); /* Just to force a flush */
+}
+#endif /* #if BUILD_FTDI_CJTAG == 1 */
+
COMMAND_HANDLER(ftdi_handle_device_desc_command)
{
if (CMD_ARGC == 1) {
@@ -917,6 +1293,32 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 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;
+}
+
+COMMAND_HANDLER(ftdi_handle_jscan3_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], jscan3_mode);
+
+ command_print(CMD, "jscan3 mode: %s.", jscan3_mode ? "on" : "off");
+ return ERROR_OK;
+}
+#endif
+
static const struct command_registration ftdi_subcommand_handlers[] = {
{
.name = "device_desc",
@@ -978,6 +1380,22 @@ static const struct command_registration ftdi_subcommand_handlers[] = {
"allow signalling speed increase)",
.usage = "(rising|falling)",
},
+#if BUILD_FTDI_CJTAG == 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)",
+ },
+ {
+ .name = "jscan3_mode",
+ .handler = &ftdi_handle_jscan3_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use JScan3 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 000000000..25addb0ca
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# 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 000000000..85d66ef01
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# 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 000000000..ee09e81e2
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# 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
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 1 +
doc/openocd.texi | 29 ++
src/jtag/drivers/ftdi.c | 438 ++++++++++++++++++++-
tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg | 29 ++
tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg | 24 ++
tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg | 29 ++
6 files changed, 540 insertions(+), 10 deletions(-)
create mode 100644 tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
create mode 100644 tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
create mode 100644 tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
hooks/post-receive
--
Main OpenOCD repository
|