This defines another table of TMS paths, where many paths are shorter
than the current "short" paths. It's not enabled by default; the
still-undocumented "tms_sequence" path gets a new "minimal" option.
(Sometime after 0.3.x ships we might change that default...)
It also adds some new infrastructure:
- Initialize the table of TMS paths *symbolically* instead of
through cryptic bit-streams. Among other things, this gets
us documentation about what the paths actually are...
- Optional startup-time symbolic dump of the various path tables.
Again, this lets us see what the darn things really are.
# NYET ready to merge ... didn't even sanity test the new tables yet!
# Evidently some of the longer paths are there because JLink is more
# stable that way ... if so, update Jlink (e.g. for more TCK clocks
# on TLR). And it might be worth making two of the paths match SVF,
# so there are no exceptions: all PAUSE/SHIFT --> PAUSE/SHIFT paths
# go through UPDATE, *except* for xPAUSE back to xSHIFT (resuming a
# transfer).
---
src/jtag/interface.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++-
src/jtag/interface.h | 10 +
src/jtag/tcl.c | 22 ++-
3 files changed, 293 insertions(+), 15 deletions(-)
--- a/src/jtag/interface.c
+++ b/src/jtag/interface.c
@@ -192,6 +192,9 @@ static const struct tms_sequences short_
};
+/* Minimal table -- initialized later, from non-cryptic tables */
+static struct tms_sequences minimal_tms_seqs[6][6];
+
typedef const struct tms_sequences tms_table[6][6];
static tms_table *tms_seqs=&short_tms_seqs;
@@ -468,12 +471,277 @@ tap_state_t jtag_debug_state_machine(con
}
#endif // _DEBUG_JTAG_IO_
-void tap_use_new_tms_table(bool use_new)
+void tap_use_tms_table(enum tms_paths type)
{
- tms_seqs = use_new ? &short_tms_seqs : &old_tms_seqs;
+ switch (type) {
+ case TMS_NEW:
+ tms_seqs = &short_tms_seqs;
+ break;
+ case TMS_OLD:
+ tms_seqs = &old_tms_seqs;
+ break;
+ default:
+ tms_seqs = (tms_table *) &minimal_tms_seqs;
+ break;
+ }
}
-bool tap_uses_new_tms_table(void)
+
+enum tms_paths tap_tms_table_type(void)
{
- return tms_seqs == &short_tms_seqs;
+ if (tms_seqs == &short_tms_seqs)
+ return TMS_NEW;
+ if (tms_seqs == &old_tms_seqs)
+ return TMS_OLD;
+ return TMS_MINIMAL;
+}
+
+/*-----------------------------------------------------------------*/
+
+// #define DUMP_TAP_TABLES
+
+#ifdef DUMP_TAP_TABLES
+
+static void dump_tap_seq(enum tap_state cur, enum tap_state end)
+{
+ unsigned path = tap_get_tms_path(cur, end);
+ unsigned len = tap_get_tms_path_len(cur, end);
+
+ printf(" { %s", tap_state_name(cur));
+ while (len--) {
+ cur = tap_state_transition(cur, (path & 1) == 1);
+ path >>= 1;
+ printf(", %s", tap_state_name(cur));
+ }
+ printf(" }\n");
+}
+
+static void dump_tap_table(const char *label, tms_table *table)
+{
+ static const enum tap_state stable[6] = {
+ TAP_RESET, TAP_IDLE,
+ TAP_DRSHIFT, TAP_DRPAUSE,
+ TAP_IRSHIFT, TAP_IRPAUSE,
+ };
+
+ tms_seqs = table;
+ printf("TMS Table: %s\n", label);
+
+ for (int i = 0; i < 6; i++) {
+ printf(" /* from %s */\n", tap_state_name(stable[i]));
+ for (int j = 0; j < 6; j++)
+ dump_tap_seq(stable[i], stable[j]);
+ }
+ printf("\n");
+}
+
+#else /* !DUMP_TAP_TABLES */
+
+static void dump_tap_table(const char *label, tms_table *table)
+{
+ /* NOP */
+}
+
+#endif /* check DUMP_TAP_TABLES */
+
+/*-----------------------------------------------------------------*/
+
+/* Compile one path { start ... end } into a series of TMS values,
+ * and store the sequence and its length into the specified table.
+ */
+static int
+tap_seq_compile(const enum tap_state *path, struct tms_sequences table[6][6])
+{
+ unsigned i;
+ unsigned bitseq;
+ enum tap_state start, end;
+ enum tap_state cur, next;
+ struct tms_sequences *out;
+
+ start = end = *path;
+
+ /* convert path into a TMS sequence */
+ for (i = 0, bitseq = 0, cur = end;
+ (next = *++path) != TAP_INVALID && i < 8;
+ i++, cur = next, end = cur) {
+
+ /* TMS=0; or TMS=1; else fail */
+ if (tap_state_transition(cur, 0) == next)
+ continue;
+ else if (tap_state_transition(cur, 1) == next)
+ bitseq |= 1 << i;
+ else
+ return ERROR_FAIL;
+ }
+ if (next != TAP_INVALID)
+ return ERROR_FAIL;
+
+ /* update table appropriately */
+ out = &table[tap_move_ndx(start)][tap_move_ndx(end)];
+ out->bits = bitseq;
+ out->bit_count = i;
+
+ /* return how many tap_statet values we consumed */
+ return i;
+}
+
+/* The tap_paths data structures lets us specify TAP transitions using
+ * TAP names instead of requiring some binary encryption scheme.
+ *
+ * REVISIT: we can halve the minimal table size by using a flat array...
+ */
+typedef const enum tap_state tap_paths[36][9];
+
+/*
+ * Note that most transitions from PAUSE or SHIFT are required to use
+ * UPDATE ... and entry to RESET _always_ uses five TMS=1 cycles, just
+ * in case the "current state" is not what we think it is.
+ */
+static const tap_paths minimal_paths = {
+ /* from RESET */
+ { TAP_RESET, TAP_RESET, TAP_RESET, TAP_RESET, TAP_RESET, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_RESET, TAP_IDLE,
+ TAP_INVALID, },
+ { TAP_RESET, TAP_IDLE, TAP_DRSELECT, TAP_DRCAPTURE,
+ TAP_DRSHIFT,
+ TAP_INVALID, },
+ { TAP_RESET, TAP_IDLE, TAP_DRSELECT, TAP_DRCAPTURE,
+ TAP_DREXIT1, TAP_DRPAUSE,
+ TAP_INVALID, },
+ { TAP_RESET, TAP_IDLE, TAP_DRSELECT, TAP_IRSELECT, TAP_IRCAPTURE,
+ TAP_IRSHIFT,
+ TAP_INVALID, },
+ { TAP_RESET, TAP_IDLE, TAP_DRSELECT, TAP_IRSELECT, TAP_IRCAPTURE,
+ TAP_IREXIT1, TAP_IRPAUSE,
+ TAP_INVALID, },
+
+ /* from IDLE */
+ { TAP_IDLE, TAP_DRSELECT, TAP_IRSELECT, TAP_RESET, TAP_RESET, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_IDLE,
+ /* NOP ... use runtest() to force clocks */
+ TAP_INVALID, },
+ { TAP_IDLE, TAP_DRSELECT, TAP_DRCAPTURE,
+ TAP_DRSHIFT,
+ TAP_INVALID, },
+ { TAP_IDLE, TAP_DRSELECT, TAP_DRCAPTURE,
+ TAP_DREXIT1, TAP_DRPAUSE,
+ TAP_INVALID, },
+ { TAP_IDLE, TAP_DRSELECT, TAP_IRSELECT, TAP_IRCAPTURE,
+ TAP_IRSHIFT,
+ TAP_INVALID, },
+ { TAP_IDLE, TAP_DRSELECT, TAP_IRSELECT, TAP_IRCAPTURE,
+ TAP_IREXIT1, TAP_IRPAUSE,
+ TAP_INVALID, },
+
+ /* from DRSHIFT */
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRUPDATE,
+ TAP_DRSELECT, TAP_IRSELECT, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRUPDATE,
+ TAP_IDLE,
+ TAP_INVALID, },
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_DRCAPTURE, TAP_DRSHIFT,
+ TAP_INVALID, },
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRPAUSE,
+ /* no UPDATE */
+ TAP_INVALID, },
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_IRCAPTURE, TAP_IRSHIFT,
+ TAP_INVALID, },
+ { TAP_DRSHIFT, TAP_DREXIT1, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_IRCAPTURE, TAP_IREXIT1, TAP_IRPAUSE,
+ /* note: long! */
+ TAP_INVALID, },
+
+ /* from DRPAUSE */
+ { TAP_DRPAUSE, TAP_DREXIT2, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_DRPAUSE, TAP_DREXIT2, TAP_DRUPDATE,
+ TAP_IDLE,
+ TAP_INVALID, },
+ { TAP_DRPAUSE, TAP_DREXIT2, TAP_DRSHIFT,
+ /* no UPDATE */
+ TAP_INVALID, },
+ { TAP_DRPAUSE,
+ /* NOP ... note, SVF forces an UPDATE */
+ TAP_INVALID, },
+ { TAP_DRPAUSE, TAP_DREXIT2, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_IRCAPTURE, TAP_IRSHIFT,
+ TAP_INVALID, },
+ { TAP_DRPAUSE, TAP_DREXIT2, TAP_DRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_IRCAPTURE, TAP_IREXIT1, TAP_IRPAUSE,
+ /* note: long! */
+ TAP_INVALID, },
+
+ /* from IRSHIFT */
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRUPDATE, TAP_IDLE,
+ TAP_INVALID, },
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_DRCAPTURE, TAP_DRSHIFT,
+ TAP_INVALID, },
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_DRCAPTURE, TAP_DREXIT1, TAP_DRPAUSE,
+ TAP_INVALID, },
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_IRCAPTURE, TAP_IRSHIFT,
+ TAP_INVALID, },
+ { TAP_IRSHIFT, TAP_IREXIT1, TAP_IRPAUSE,
+ /* no UPDATE */
+ TAP_INVALID, },
+
+ /* from IRPAUSE */
+ { TAP_IRPAUSE, TAP_IREXIT2, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_IRSELECT, TAP_RESET,
+ TAP_INVALID, },
+ { TAP_IRPAUSE, TAP_IREXIT2, TAP_IRUPDATE,
+ TAP_IDLE,
+ TAP_INVALID, },
+ { TAP_IRPAUSE, TAP_IREXIT2, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_DRCAPTURE, TAP_DRSHIFT,
+ TAP_INVALID, },
+ { TAP_IRPAUSE, TAP_IREXIT2, TAP_IRUPDATE, TAP_DRSELECT,
+ TAP_DRCAPTURE, TAP_DREXIT1, TAP_DRPAUSE,
+ TAP_INVALID, },
+ { TAP_IRPAUSE, TAP_IREXIT2, TAP_IRSHIFT,
+ /* no UPDATE */
+ TAP_INVALID, },
+ { TAP_IRPAUSE,
+ /* NOP ... note, SVF forces an UPDATE */
+ TAP_INVALID, },
+};
+
+/* Compile a set of paths { start ... end } into a path table. */
+static int
+tap_table_compile(const tap_paths paths, struct tms_sequences table[6][6])
+{
+ int retval;
+ unsigned i;
+
+ memset(table, 0, sizeof(*table));
+ for (i = 0; i < 36; i++) {
+ retval = tap_seq_compile(paths[i], table);
+ if (retval < 0)
+ return retval;
+ }
+ return ERROR_OK;
+}
+
+static void tap_table_setup(void) __attribute__ ((constructor));
+
+static void tap_table_setup(void)
+{
+ tap_table_compile(minimal_paths, minimal_tms_seqs);
+
+ /* for debug */
+ dump_tap_table("minimal", (tms_table *)&minimal_tms_seqs);
+ dump_tap_table("old", &old_tms_seqs);
+ dump_tap_table("short(er)", &short_tms_seqs);
+ fflush(stdout);
}
--- a/src/jtag/interface.h
+++ b/src/jtag/interface.h
@@ -160,10 +160,12 @@ bool tap_is_state_stable(tap_state_t ast
*/
tap_state_t tap_state_transition(tap_state_t current_state, bool tms);
-/// Allow switching between old and new TMS tables. @see tap_get_tms_path
-void tap_use_new_tms_table(bool use_new);
-/// @returns True if new TMS table is active; false otherwise.
-bool tap_uses_new_tms_table(void);
+enum tms_paths { TMS_NEW = 0, TMS_OLD, TMS_MINIMAL };
+
+/// Allow switching between TMS tables. @see tap_get_tms_path
+void tap_use_tms_table(enum tms_paths type);
+/// @returns which TMS table is being used
+enum tms_paths tap_tms_table_type(void);
#ifdef _DEBUG_JTAG_IO_
/**
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -663,8 +663,9 @@ int jtag_register_commands(struct comman
COMMAND_ANY, "verify value captured during Capture-IR <enable |
disable>");
register_command(cmd_ctx, NULL, "verify_jtag",
handle_verify_jtag_command,
COMMAND_ANY, "verify value capture <enable | disable>");
- register_command(cmd_ctx, NULL, "tms_sequence",
handle_tms_sequence_command,
- COMMAND_ANY, "choose short(default) or long tms_sequence <short
| long>");
+ register_command(cmd_ctx, NULL, "tms_sequence",
+ handle_tms_sequence_command, COMMAND_ANY,
+ "choose 'short' (default), 'minimal' or 'long' JTAG paths");
return ERROR_OK;
}
@@ -1640,19 +1641,26 @@ static int handle_tms_sequence_command(s
if (argc == 1)
{
- bool use_new_table;
+ enum tms_paths type;
+
if (strcmp(args[0], "short") == 0)
- use_new_table = true;
+ type = TMS_NEW;
else if (strcmp(args[0], "long") == 0)
- use_new_table = false;
+ type = TMS_OLD;
+ else if (strcmp(args[0], "minimal") == 0)
+ type = TMS_MINIMAL;
else
return ERROR_COMMAND_SYNTAX_ERROR;
- tap_use_new_tms_table(use_new_table);
+ tap_use_tms_table(type);
}
command_print(cmd_ctx, "tms sequence is %s",
- tap_uses_new_tms_table() ? "short": "long");
+ ({ char *s; switch (tap_tms_table_type()) {
+ case TMS_NEW: s = "short"; break;
+ case TMS_OLD: s = "long"; break;
+ default: s = "minimal"; break;
+ }; s; }));
return ERROR_OK;
}
_______________________________________________
Openocd-development mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/openocd-development