|
|
@ -73,6 +73,7 @@
|
|
|
|
#include <jtag/swd.h>
|
|
|
|
#include <jtag/swd.h>
|
|
|
|
#include <transport/transport.h>
|
|
|
|
#include <transport/transport.h>
|
|
|
|
#include <helper/time_support.h>
|
|
|
|
#include <helper/time_support.h>
|
|
|
|
|
|
|
|
#include <helper/log.h>
|
|
|
|
|
|
|
|
|
|
|
|
#if IS_CYGWIN == 1
|
|
|
|
#if IS_CYGWIN == 1
|
|
|
|
#include <windows.h>
|
|
|
|
#include <windows.h>
|
|
|
@ -83,6 +84,18 @@
|
|
|
|
/* FTDI access library includes */
|
|
|
|
/* FTDI access library includes */
|
|
|
|
#include "mpsse.h"
|
|
|
|
#include "mpsse.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DEBUG_IO(expr...) DEBUG_JTAG_IO(expr)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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 (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
|
|
|
|
#define JTAG_MODE_ALT (LSB_FIRST | NEG_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)
|
|
|
|
#define SWD_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
|
|
|
@ -95,6 +108,31 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE;
|
|
|
|
|
|
|
|
|
|
|
|
static bool swd_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
|
|
|
|
#define MAX_USB_IDS 8
|
|
|
|
/* vid = pid = 0 marks the end of the list */
|
|
|
|
/* vid = pid = 0 marks the end of the list */
|
|
|
|
static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 };
|
|
|
|
static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 };
|
|
|
@ -240,6 +278,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t * value_out)
|
|
|
|
return ERROR_OK;
|
|
|
|
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
|
|
|
|
* Function move_to_state
|
|
|
|
* moves the TAP controller from the current state to a
|
|
|
|
* moves the TAP controller from the current state to a
|
|
|
@ -268,7 +335,7 @@ static void move_to_state(tap_state_t goal_state)
|
|
|
|
for (int i = 0; i < tms_count; i++)
|
|
|
|
for (int i = 0; i < tms_count; i++)
|
|
|
|
tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1));
|
|
|
|
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,
|
|
|
|
&tms_bits,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
tms_count,
|
|
|
|
tms_count,
|
|
|
@ -322,7 +389,7 @@ static void ftdi_end_state(tap_state_t state)
|
|
|
|
static void ftdi_execute_runtest(struct jtag_command *cmd)
|
|
|
|
static void ftdi_execute_runtest(struct jtag_command *cmd)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int i;
|
|
|
|
uint8_t zero = 0;
|
|
|
|
static const uint8_t zero;
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_JTAG_IO("runtest %i cycles, end in %s",
|
|
|
|
DEBUG_JTAG_IO("runtest %i cycles, end in %s",
|
|
|
|
cmd->cmd.runtest->num_cycles,
|
|
|
|
cmd->cmd.runtest->num_cycles,
|
|
|
@ -336,7 +403,7 @@ static void ftdi_execute_runtest(struct jtag_command *cmd)
|
|
|
|
while (i > 0) {
|
|
|
|
while (i > 0) {
|
|
|
|
/* there are no state transitions in this code, so omit state tracking */
|
|
|
|
/* there are no state transitions in this code, so omit state tracking */
|
|
|
|
unsigned this_len = i > 7 ? 7 : i;
|
|
|
|
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;
|
|
|
|
i -= this_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -371,7 +438,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd)
|
|
|
|
DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
|
|
|
|
DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: Missing tap state tracking, also missing from ft2232.c! */
|
|
|
|
/* 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,
|
|
|
|
cmd->cmd.tms->bits,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
cmd->cmd.tms->num_bits,
|
|
|
|
cmd->cmd.tms->num_bits,
|
|
|
@ -418,7 +485,7 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd)
|
|
|
|
state_count++;
|
|
|
|
state_count++;
|
|
|
|
|
|
|
|
|
|
|
|
if (bit_count == 7 || num_states == 0) {
|
|
|
|
if (bit_count == 7 || num_states == 0) {
|
|
|
|
mpsse_clock_tms_cs_out(mpsse_ctx,
|
|
|
|
DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
|
|
|
|
&tms_byte,
|
|
|
|
&tms_byte,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
bit_count,
|
|
|
|
bit_count,
|
|
|
@ -472,7 +539,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()) {
|
|
|
|
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
|
|
|
|
/* 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. */
|
|
|
|
* 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,
|
|
|
|
field->out_value,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
field->in_value,
|
|
|
|
field->in_value,
|
|
|
@ -483,7 +550,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
|
|
|
|
if (field->out_value)
|
|
|
|
if (field->out_value)
|
|
|
|
bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1);
|
|
|
|
bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1);
|
|
|
|
uint8_t tms_bits = 0x01;
|
|
|
|
uint8_t tms_bits = 0x01;
|
|
|
|
mpsse_clock_tms_cs(mpsse_ctx,
|
|
|
|
DO_CLOCK_TMS_CS(mpsse_ctx,
|
|
|
|
&tms_bits,
|
|
|
|
&tms_bits,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
field->in_value,
|
|
|
|
field->in_value,
|
|
|
@ -492,7 +559,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
|
|
|
|
last_bit,
|
|
|
|
last_bit,
|
|
|
|
ftdi_jtag_mode);
|
|
|
|
ftdi_jtag_mode);
|
|
|
|
tap_set_state(tap_state_transition(tap_get_state(), 1));
|
|
|
|
tap_set_state(tap_state_transition(tap_get_state(), 1));
|
|
|
|
mpsse_clock_tms_cs_out(mpsse_ctx,
|
|
|
|
DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
|
|
|
|
&tms_bits,
|
|
|
|
&tms_bits,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
1,
|
|
|
@ -500,7 +567,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
|
|
|
|
ftdi_jtag_mode);
|
|
|
|
ftdi_jtag_mode);
|
|
|
|
tap_set_state(tap_state_transition(tap_get_state(), 0));
|
|
|
|
tap_set_state(tap_state_transition(tap_get_state(), 0));
|
|
|
|
} else
|
|
|
|
} else
|
|
|
|
mpsse_clock_data(mpsse_ctx,
|
|
|
|
DO_CLOCK_DATA(mpsse_ctx,
|
|
|
|
field->out_value,
|
|
|
|
field->out_value,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
field->in_value,
|
|
|
|
field->in_value,
|
|
|
@ -585,7 +652,7 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
|
|
|
|
while (num_cycles > 0) {
|
|
|
|
while (num_cycles > 0) {
|
|
|
|
/* there are no state transitions in this code, so omit state tracking */
|
|
|
|
/* there are no state transitions in this code, so omit state tracking */
|
|
|
|
unsigned this_len = num_cycles > 7 ? 7 : num_cycles;
|
|
|
|
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;
|
|
|
|
num_cycles -= this_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -599,12 +666,18 @@ static void ftdi_execute_command(struct jtag_command *cmd)
|
|
|
|
switch (cmd->type) {
|
|
|
|
switch (cmd->type) {
|
|
|
|
case JTAG_RESET:
|
|
|
|
case JTAG_RESET:
|
|
|
|
ftdi_execute_reset(cmd);
|
|
|
|
ftdi_execute_reset(cmd);
|
|
|
|
|
|
|
|
#if BUILD_FTDI_OSCAN1 == 1
|
|
|
|
|
|
|
|
oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */
|
|
|
|
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case JTAG_RUNTEST:
|
|
|
|
case JTAG_RUNTEST:
|
|
|
|
ftdi_execute_runtest(cmd);
|
|
|
|
ftdi_execute_runtest(cmd);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case JTAG_TLR_RESET:
|
|
|
|
case JTAG_TLR_RESET:
|
|
|
|
ftdi_execute_statemove(cmd);
|
|
|
|
ftdi_execute_statemove(cmd);
|
|
|
|
|
|
|
|
#if BUILD_FTDI_OSCAN1 == 1
|
|
|
|
|
|
|
|
oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */
|
|
|
|
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case JTAG_PATHMOVE:
|
|
|
|
case JTAG_PATHMOVE:
|
|
|
|
ftdi_execute_pathmove(cmd);
|
|
|
|
ftdi_execute_pathmove(cmd);
|
|
|
@ -678,6 +751,17 @@ static int ftdi_initialize(void)
|
|
|
|
/* A dummy SWD_EN would have zero mask */
|
|
|
|
/* A dummy SWD_EN would have zero mask */
|
|
|
|
if (sig->data_mask)
|
|
|
|
if (sig->data_mask)
|
|
|
|
ftdi_set_signal(sig, '1');
|
|
|
|
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);
|
|
|
|
mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff);
|
|
|
@ -711,6 +795,240 @@ static int ftdi_quit(void)
|
|
|
|
return ERROR_OK;
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
COMMAND_HANDLER(ftdi_handle_device_desc_command)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (CMD_ARGC == 1) {
|
|
|
|
if (CMD_ARGC == 1) {
|
|
|
@ -951,6 +1269,20 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command)
|
|
|
|
return ERROR_OK;
|
|
|
|
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_CTX, "oscan1 mode: %s.", oscan1_mode ? "on" : "off");
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static const struct command_registration ftdi_command_handlers[] = {
|
|
|
|
static const struct command_registration ftdi_command_handlers[] = {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
.name = "ftdi_device_desc",
|
|
|
|
.name = "ftdi_device_desc",
|
|
|
@ -1028,6 +1360,15 @@ static const struct command_registration ftdi_command_handlers[] = {
|
|
|
|
"allow signalling speed increase)",
|
|
|
|
"allow signalling speed increase)",
|
|
|
|
.usage = "(rising|falling)",
|
|
|
|
.usage = "(rising|falling)",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
#if BUILD_FTDI_OSCAN1 == 1
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
.name = "ftdi_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
|
|
|
|
COMMAND_REGISTRATION_DONE
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|