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 <greg.savin@sifive.com> Signed-off-by: mrv96 <mrv96@users.noreply.github.com> Signed-off-by: Tim Newsome <tim@sifive.com> Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com> Change-Id: Ia1daa2c01227c4b0005be947b2bb0de81a800874 Reviewed-on: https://review.openocd.org/c/openocd/+/6981 Tested-by: jenkins
This commit is contained in:
@@ -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]],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
29
tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
Normal file
29
tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
Normal file
@@ -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
|
||||
24
tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
Normal file
24
tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
Normal file
@@ -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
|
||||
29
tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
Normal file
29
tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user