commit 420887d81db7234e51430cc02ae01488214dde7e
parent f1c36c3c126fe91c67dceb47dcfccf464ddbd095
Author: Brian Swetland <swetland@frotz.net>
Date: Sun, 10 Jan 2016 19:20:44 -0800
debugger: initial JTAG transport work
- uses new JTAG opcodes for swdp firmware
- targets TI CC1310 and CC2650 right now (assumes ICEPICK in front, CJTAG, etc)
- todo: target definition abstraction, more flexible target init
Diffstat:
M | Makefile | | | 2 | ++ |
A | tools/dap-registers.h | | | 75 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tools/jtag-dap.c | | | 497 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tools/jtag.c | | | 286 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tools/jtag.h | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tools/ti-icepick.h | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 1002 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -15,6 +15,8 @@ SRCS := tools/debugger.c \
tools/debugger-core.c \
tools/debugger-commands.c \
tools/gdb-bridge.c \
+ tools/jtag.c \
+ tools/jtag-dap.c \
tools/arm-m-debug.c \
tools/linenoise.c \
tools/lkdebug.c \
diff --git a/tools/dap-registers.h b/tools/dap-registers.h
@@ -0,0 +1,75 @@
+#ifndef _DAP_REGISTERS_H_
+#define _DAP_REGISTERS_H_
+
+/* ARM DAP Controller (4bit IR) */
+#define DAP_IR_SIZE 4
+
+#define DAP_IR_ABORT 0x08
+#define DAP_IR_DPACC 0x0A
+#define DAP_IR_APACC 0x0B
+#define DAP_IR_IDCODE 0x0E
+#define DAP_IR_BYPASS 0x0F
+
+/* DPACC/APACC DR bits */
+#define XPACC_STATUS(n) ((n) & 0x3)
+#define XPACC_WAIT 0x1
+#define XPACC_OK 0x2
+#define XPACC_RD(a) (0x1 | (((a) >> 1) & 6))
+#define XPACC_WR(a,v) ((((u64)(v)) << 3) | (0x0 | (((a) >> 1) & 6)))
+
+/* DP addresses */
+#define DPACC_RESERVED 0x0
+#define DPACC_CSW 0x4
+#define DPACC_SELECT 0x8
+#define DPACC_RDBUFF 0xC
+
+#define DPCSW_CSYSPWRUPACK (1 << 31)
+#define DPCSW_CSYSPWRUPREQ (1 << 30)
+#define DPCSW_CDBGPWRUPACK (1 << 29)
+#define DPCSW_CDBGPWRUPREQ (1 << 28)
+#define DPCSW_CDBGRSTACK (1 << 27)
+#define DPCSW_CDBGRSTREQ (1 << 26)
+#define DPCSW_TRNCNT(n) (((n) & 0x3FF) << 12)
+#define DPCSW_MASKLANE(n) (((n) & 0xF) << 8) // pushed verify or compare
+#define DPCSW_WDATAERR (1 << 7) // reserved on jtag
+#define DPCSW_READOK (1 << 6) // reserved on jtag
+#define DPCSW_STICKYERR (1 << 5)
+#define DPCSW_STICKYCMP (1 << 4)
+#define DPCSW_TRNMODE_NORMAL (0 << 2)
+#define DPCSW_TRNMODE_PUSH_VRFY (1 << 2)
+#define DPCSW_TRNMODE_PUSH_CMP (2 << 2)
+#define DPCSW_STICKYORUN (1 << 1)
+#define DPCSW_ORUNDETECT (1 << 0)
+
+#define DPSEL_APSEL(n) (((n) & 0xFF) << 24)
+#define DPSEL_APBANKSEL(a) ((a) & 0xF0)
+#define DPSEL_CTRLSEL (1 << 0) // reserved on jtag
+
+/* Reading RDBUFF returns 0, has no side effects */
+/* Can be used to obtain final read result and ack values at end of seq */
+
+/* AP addresses */
+#define APACC_CSW 0x00
+#define APACC_TAR 0x04
+#define APACC_DRW 0x0C
+#define APACC_BD0 0x10
+#define APACC_BD1 0x14
+#define APACC_BD2 0x18
+#define APACC_BD3 0x1C
+#define APACC_CFG 0xF4
+#define APACC_BASE 0xF8
+#define APACC_IDR 0xFC
+
+#define APCSW_DBGSWEN (1 << 31)
+#define APCSW_PROT_MASK 0x7F000000
+#define APCSW_SPIDEN (1 << 23) // ro
+#define APCSW_TRBUSY (1 << 7) // ro
+#define APCSW_DEVICEEN (1 << 6) // ro
+#define APCSW_INCR_NONE (0 << 4)
+#define APCSW_INCR_SINGLE (1 << 4)
+#define APCSW_INCR_PACKED (2 << 4) // may not be supported
+#define APCSW_SIZE8 (0 << 0) // may not be supported
+#define APCSW_SIZE16 (1 << 0) // may not be supported
+#define APCSW_SIZE32 (2 << 0)
+
+#endif
diff --git a/tools/jtag-dap.c b/tools/jtag-dap.c
@@ -0,0 +1,497 @@
+/* jtag-dap.c
+ *
+ * Copyright 2015 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fw/types.h>
+#include "debugger.h"
+#include "jtag.h"
+#include "dap-registers.h"
+
+#define CSW_ERRORS (DPCSW_STICKYERR | DPCSW_STICKYCMP | DPCSW_STICKYORUN)
+#define CSW_ENABLES (DPCSW_CSYSPWRUPREQ | DPCSW_CDBGPWRUPREQ | DPCSW_ORUNDETECT)
+
+typedef struct DAP {
+ jtag_txn *jtag;
+ u32 cached_ir;
+ u32 ir_pre, ir_post;
+ u32 dr_pre, dr_post;
+} DAP;
+
+void dap_init_jtag(DAP *dap) {
+ jtag_txn_init(dap->jtag);
+ dap->cached_ir = 0xFFFFFFFF;
+ dap->jtag->ir_pre = dap->ir_pre;
+ dap->jtag->ir_post = dap->ir_post;
+ dap->jtag->dr_pre = dap->dr_pre;
+ dap->jtag->dr_post = dap->dr_post;
+ dap->jtag->state = JTAG_IDLE;
+}
+
+void dap_init(DAP *dap, jtag_txn *t, u32 ir_pre, u32 ir_post, u32 dr_pre, u32 dr_post) {
+ dap->jtag = t;
+ dap->ir_pre = ir_pre;
+ dap->ir_post = ir_post;
+ dap->dr_pre = dr_pre;
+ dap->dr_post = dr_post;
+ dap_init_jtag(dap);
+}
+
+static void q_dap_ir_wr(DAP *dap, u32 ir) {
+ if (dap->cached_ir != ir) {
+ dap->cached_ir = ir;
+ jtag_ir(dap->jtag, 4, ir);
+ }
+}
+
+// force ir write even if redundant
+// used for a timing hack
+static void _q_dap_ir_wr(DAP *dap, u32 ir) {
+ dap->cached_ir = ir;
+ jtag_ir(dap->jtag, 4, ir);
+}
+
+static void q_dap_dr_io(DAP *dap, u32 bitcount, u64 wdata, u64 *rdata) {
+ if (rdata) {
+ *rdata = 0;
+ }
+ jtag_dr(dap->jtag, bitcount, wdata, rdata);
+}
+
+static void q_dap_abort(DAP *dap) {
+ jtag_ir(dap->jtag, 4, DAP_IR_ABORT);
+ jtag_dr(dap->jtag, 35, 8, NULL);
+ dap->cached_ir = 0xFFFFFFFF;
+}
+
+static int dap_commit_jtag(DAP *dap) {
+ int r = jtag_txn_exec(dap->jtag);
+ dap_init_jtag(dap);
+ return r;
+}
+
+// queue a DPCSW status query, commit jtag txn
+static int dap_commit(DAP *dap) {
+ u64 a, b;
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dr_io(dap, 35, XPACC_RD(DPACC_CSW), &a);
+ q_dap_dr_io(dap, 35, XPACC_RD(DPACC_RDBUFF), &b);
+ dap->cached_ir = 0xFFFFFFFF;
+ if (dap_commit_jtag(dap)) {
+ return -1;
+ }
+#if 0
+ xprintf(XCORE, "DP_CSW %08x(%x) RDBUF %08x(%x)\n",
+ ((u32) (a >> 3)), ((u32) (a & 7)),
+ ((u32) (b >> 3)), ((u32) (b & 7)));
+#endif
+ if (XPACC_STATUS(a) != XPACC_OK) {
+ xprintf(XCORE, "dap: invalid txn status\n");
+ return -1;
+ }
+ if (XPACC_STATUS(b) != XPACC_OK) {
+ xprintf(XCORE, "dap: cannot read status\n");
+ return -1;
+ }
+ b >>= 3;
+ if (b & DPCSW_STICKYORUN) {
+ xprintf(XCORE, "dap: overrun\n");
+ return -1;
+ }
+ if (b & DPCSW_STICKYERR) {
+ xprintf(XCORE, "dap: error\n");
+ return -1;
+ }
+ return 0;
+}
+
+int dap_dp_rd(DAP *dap, u32 addr, u32 *val) {
+ u64 u;
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dr_io(dap, 35, XPACC_RD(addr), NULL);
+ q_dap_dr_io(dap, 35, XPACC_RD(DPACC_RDBUFF), &u);
+ if (dap_commit_jtag(dap)) {
+ return -1;
+ }
+ if (XPACC_STATUS(u) != XPACC_OK) {
+ return -1;
+ }
+ *val = u >> 3;
+ return 0;
+}
+
+static void q_dap_dp_wr(DAP *dap, u32 addr, u32 val) {
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dr_io(dap, 35, XPACC_WR(addr, val), NULL);
+ //q_dap_dr_io(dap, 35, XPACC_RD(DPACC_RDBUFF), &s->u);
+}
+
+int dap_dp_wr(DAP *dap, u32 addr, u32 val) {
+ q_dap_dp_wr(dap, addr, val);
+ return dap_commit_jtag(dap);
+}
+
+int dap_ap_rd(DAP *dap, u32 apnum, u32 addr, u32 *val) {
+ u64 u;
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dp_wr(dap, DPACC_SELECT, DPSEL_APSEL(apnum) | DPSEL_APBANKSEL(addr));
+ q_dap_ir_wr(dap, DAP_IR_APACC);
+ q_dap_dr_io(dap, 35, XPACC_RD(addr), NULL);
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dr_io(dap, 35, XPACC_RD(DPACC_RDBUFF), &u);
+ // TODO: redundant ir wr
+ if (dap_commit(dap)) {
+ return -1;
+ }
+ *val = (u >> 3);
+ return 0;
+}
+
+void q_dap_ap_wr(DAP *dap, u32 apnum, u32 addr, u32 val) {
+ q_dap_ir_wr(dap, DAP_IR_DPACC);
+ q_dap_dp_wr(dap, DPACC_SELECT, DPSEL_APSEL(apnum) | DPSEL_APBANKSEL(addr));
+ q_dap_ir_wr(dap, DAP_IR_APACC);
+ q_dap_dr_io(dap, 35, XPACC_WR(addr, val), NULL);
+}
+
+int dap_ap_wr(DAP *dap, u32 apnum, u32 addr, u32 val) {
+ q_dap_ap_wr(dap, apnum, addr, val);
+ return dap_commit(dap);
+}
+
+int dap_mem_wr32(DAP *dap, u32 n, u32 addr, u32 val) {
+ if (addr & 3)
+ return -1;
+ q_dap_ap_wr(dap, n, APACC_CSW,
+ 0x23000000 | APCSW_DBGSWEN | APCSW_INCR_NONE | APCSW_SIZE32); //XXX
+ q_dap_ap_wr(dap, n, APACC_TAR, addr);
+ q_dap_ap_wr(dap, n, APACC_DRW, val);
+ return dap_commit(dap);
+}
+
+int dap_mem_rd32(DAP *dap, u32 n, u32 addr, u32 *val) {
+ if (addr & 3)
+ return -1;
+ q_dap_ap_wr(dap, n, APACC_CSW,
+ 0x23000000 | APCSW_DBGSWEN | APCSW_INCR_NONE | APCSW_SIZE32); //XXX
+ q_dap_ap_wr(dap, n, APACC_TAR, addr);
+ if (dap_ap_rd(dap, n, APACC_DRW, val))
+ return -1;
+ return 0;
+}
+
+
+int dap_attach(DAP *dap) {
+ unsigned n;
+ u32 x;
+
+ // make sure we abort any ongoing transactions first
+ q_dap_abort(dap);
+
+ // attempt to power up and clear errors
+ for (n = 0; n < 10; n++) {
+ if (dap_dp_wr(dap, DPACC_CSW, CSW_ERRORS | CSW_ENABLES))
+ continue;
+ if (dap_dp_rd(dap, DPACC_CSW, &x))
+ continue;
+ if (x & CSW_ERRORS)
+ continue;
+ if (!(x & DPCSW_CSYSPWRUPACK))
+ continue;
+ if (!(x & DPCSW_CDBGPWRUPACK))
+ continue;
+ return 0;
+ }
+ xprintf(XCORE, "dap: attach failed\n");
+ return -1;
+}
+
+void dap_clear_errors(DAP *dap) {
+ q_dap_dp_wr(dap, DPACC_CSW, CSW_ERRORS | CSW_ENABLES);
+}
+
+static int read4xid(DAP *dap, u32 n, u32 addr, u32 *val) {
+ u32 a,b,c,d;
+ if (dap_mem_rd32(dap, n, addr + 0x00, &a)) return -1;
+ if (dap_mem_rd32(dap, n, addr + 0x04, &b)) return -1;
+ if (dap_mem_rd32(dap, n, addr + 0x08, &c)) return -1;
+ if (dap_mem_rd32(dap, n, addr + 0x0C, &d)) return -1;
+ *val = (a & 0xFF) | ((b & 0xFF) << 8) | ((c & 0xFF) << 16) | ((d & 0xFF) << 24);
+ return 0;
+}
+
+static int readinfo(DAP *dap, u32 n, u32 base, u32 *cid, u32 *pid0, u32 *pid1) {
+ if (read4xid(dap, n, base + 0xFF0, cid)) return -1;
+ if (read4xid(dap, n, base + 0xFE0, pid0)) return -1;
+ if (read4xid(dap, n, base + 0xFD0, pid1)) return -1;
+ return 0;
+}
+
+static void dumptable(DAP *dap, u32 n, u32 base) {
+ u32 cid, pid0, pid1, memtype;
+ u32 x, addr;
+ int i;
+
+ xprintf(XCORE, "TABLE @%08x ", base);
+ if (readinfo(dap, n, base, &cid, &pid0, &pid1)) {
+ xprintf(XCORE, "<error reading cid & pid>\n");
+ return;
+ }
+ if (dap_mem_rd32(dap, n, base + 0xFCC, &memtype)) {
+ xprintf(XCORE, "<error reading memtype>\n");
+ return;
+ }
+ xprintf(XCORE, "CID %08x PID %08x %08x %dKB%s\n", cid, pid1, pid0,
+ 4 * (1 + ((pid1 & 0xF0) >> 4)),
+ (memtype & 1) ? " SYSMEM": "");
+ for (i = 0; i < 128; i++) {
+ if (dap_mem_rd32(dap, n, base + i * 4, &x)) break;
+ if (x == 0) break;
+ if ((x & 3) != 3) continue;
+ addr = base + (x & 0xFFFFF000);
+ if (readinfo(dap, n, addr, &cid, &pid0, &pid1)) {
+ xprintf(XCORE, " <error reading cid & pid>\n");
+ continue;
+ }
+ xprintf(XCORE, " %02d: @%08x CID %08x PID %08x %08x %dKB\n",
+ i, addr, cid, pid1, pid0,
+ 4 * (1 + ((pid1 & 0xF0) >> 4)));
+ if (((cid >> 12) & 0xF) == 1) {
+ dumptable(dap, n, addr);
+ }
+ }
+}
+
+int dap_probe(DAP *dap) {
+ unsigned n;
+ u32 x, y;
+
+ for (n = 0; n < 256; n++) {
+ if (dap_ap_rd(dap, n, APACC_IDR, &x))
+ break;
+ if (x == 0)
+ break;
+ y = 0;
+ dap_ap_rd(dap, n, APACC_BASE, &y);
+ xprintf(XCORE, "AP%d ID=%08x BASE=%08x\n", n, x, y);
+ if (y && (y != 0xFFFFFFFF)) {
+ dumptable(dap, n, y & 0xFFFFF000);
+ }
+ if (dap_ap_rd(dap, n, APACC_CSW, &x) == 0)
+ xprintf(XCORE, "AP%d CSW=%08x\n", n, x);
+ }
+ return 0;
+}
+
+static int jtag_error = 0;
+
+#include "ti-icepick.h"
+
+#define IDCODE_ARM_M3 0x4ba00477
+#define IPCODE_TI_ICEPICK 0x41611cc0
+#define IDCODE_CC1310 0x2b9be02f
+#define IDCODE_CC2650 0x8b99a02f
+
+int connect_ti(void) {
+ u64 x0, x1;
+ int retry = 5;
+ jtag_txn t;
+
+ while (retry > 0) {
+ jtag_txn_init(&t);
+ jtag_any_to_rti(&t);
+
+ // Enable 4-wire JTAG
+ jtag_ir(&t, 6, 0x3F);
+ jtag_cjtag_open(&t);
+ jtag_cjtag_cmd(&t, 2, 9);
+ jtag_ir(&t, 6, 0x3F);
+
+ // sit in IDLE for a bit (not sure if useful)
+ jtag_txn_append(&t, 64, 0, 0, NULL);
+
+ // Read IDCODE and IPCODE
+ jtag_ir(&t, 6, IP_IR_IDCODE);
+ jtag_dr(&t, 32, 0x00000000, &x0);
+ jtag_ir(&t, 6, IP_IR_IPCODE);
+ jtag_dr(&t, 32, 0x00000000, &x1);
+ jtag_txn_exec(&t);
+
+ if (x1 == IPCODE_TI_ICEPICK) {
+ if (x0 == IDCODE_CC1310) {
+ break;
+ }
+ if (x0 == IDCODE_CC2650) {
+ break;
+ }
+ xprintf(XCORE, "wat!\n");
+ }
+ retry--;
+ }
+ if (retry == 0) {
+ //xprintf(XCORE, "cannot connect (%08x %08x)\n", (u32)x0, (u32)x1);
+ return -1;
+ }
+ xprintf(XSWD, "attach: IDCODE %08x %08x\n", (u32)x0, (u32)x1);
+
+ jtag_txn_init(&t);
+ t.state = JTAG_IDLE;
+
+ // enable router access
+ jtag_ir(&t, 6, IP_IR_CONNECT);
+ jtag_dr(&t, 8, IP_CONNECT_WR_KEY, NULL);
+
+ // add ARM DAP to the scan chain
+ jtag_ir(&t, 6, IP_IR_ROUTER);
+ jtag_dr_p(&t, 32, IP_RTR_WR | IP_RTR_BLK(IP_BLK_DEBUG_TLCB) |
+ IP_RTR_REG(IP_DBG_TAP0) |
+ IP_RTR_VAL(IP_DBG_SELECT_TAP | IP_DBG_INHIBIT_SLEEP | IP_DBG_FORCE_ACTIVE), NULL);
+
+ // commit router changes by going to IDLE state
+ jtag_txn_move(&t, JTAG_IDLE);
+
+ // idle for a few clocks to let the new TAP path settle
+ jtag_txn_append(&t, 8, 0, 0, NULL);
+ jtag_txn_exec(&t);
+
+#if 0
+ jtag_txn_init(&t);
+ t.state = JTAG_IDLE;
+ t.ir_pre = 4;
+ t.dr_pre = 1;
+ jtag_ir(&t, 6, IP_IR_ROUTER);
+ jtag_dr(&t, 32, IP_RTR_WR | IP_RTR_BLK(IP_BLK_CONTROL) | IP_RTR_REG(IP_CTL_CONTROL) |
+ IP_RTR_VAL(IP_CTL_SYSTEM_RESET), NULL);
+ jtag_txn_exec(&t);
+#endif
+
+ // we should now be stable, try to read DAP IDCODE
+ jtag_txn_init(&t);
+ t.state = JTAG_IDLE;
+ t.ir_post = 6;
+ t.dr_post = 1;
+ jtag_ir(&t, 4, 0xE); // IDCODE
+ jtag_dr(&t, 32, 0, &x0);
+ jtag_txn_exec(&t);
+
+ if (x0 != IDCODE_ARM_M3) {
+ xprintf(XDATA, "cannot find DAP (%08x)\n", (u32)x0);
+ return -1;
+ }
+
+ return 0;
+}
+
+int _attach(void) {
+ jtag_txn t;
+ DAP dap;
+ jtag_error = -1;
+ if (connect_ti()) {
+ return -1;
+ }
+ dap_init(&dap, &t, 0, 6, 0, 1);
+ if (dap_attach(&dap)) {
+ return -1;
+ }
+ xprintf(XSWD, "attach: JTAG DAP ok\n");
+ jtag_error = 0;
+ return 0;
+}
+
+static int _mem_rd_32(u32 addr, u32 *val) {
+ jtag_txn t;
+ DAP dap;
+ if ((addr & 3) || jtag_error) {
+ return -1;
+ }
+ dap_init(&dap, &t, 0, 6, 0, 1);
+ jtag_error = dap_mem_rd32(&dap, 0, addr, val);
+ return jtag_error;
+}
+
+static int _mem_wr_32(u32 addr, u32 val) {
+ jtag_txn t;
+ DAP dap;
+ if ((addr & 3) || jtag_error) {
+ return -1;
+ }
+ dap_init(&dap, &t, 0, 6, 0, 1);
+ jtag_error = dap_mem_wr32(&dap, 0, addr, val);
+ return jtag_error;
+}
+
+static int _mem_rd_32_c(u32 addr, u32 *data, int count) {
+ jtag_txn t;
+ DAP dap;
+ if ((addr & 3) || jtag_error) {
+ return -1;
+ }
+ dap_init(&dap, &t, 0, 6, 0, 1);
+ while (count > 0) {
+ if (dap_mem_rd32(&dap, 0, addr, data)) {
+ jtag_error = -1;
+ return -1;
+ }
+ addr += 4;
+ data++;
+ count--;
+ }
+ return 0;
+}
+
+static int _mem_wr_32_c(u32 addr, u32 *data, int count) {
+ jtag_txn t;
+ DAP dap;
+ if ((addr & 3) || jtag_error) {
+ return -1;
+ }
+ dap_init(&dap, &t, 0, 6, 0, 1);
+ while (count > 0) {
+ if (dap_mem_wr32(&dap, 0, addr, *data)) {
+ jtag_error = -1;
+ return -1;
+ }
+ addr += 4;
+ data++;
+ count--;
+ }
+ return 0;
+}
+
+static int _clear_error(void) {
+ if (jtag_error) {
+ //XXX this is a lighter weight operation in SWDP
+ _attach();
+ }
+ return jtag_error;
+}
+
+static int _error(void) {
+ return jtag_error;
+}
+
+debug_transport JTAG_TRANSPORT = {
+ .attach = _attach,
+ .error = _error,
+ .clear_error = _clear_error,
+ .mem_rd_32 = _mem_rd_32,
+ .mem_wr_32 = _mem_wr_32,
+ .mem_rd_32_c = _mem_rd_32_c,
+ .mem_wr_32_c = _mem_wr_32_c,
+};
diff --git a/tools/jtag.c b/tools/jtag.c
@@ -0,0 +1,286 @@
+/* jtag transport
+ *
+ * Copyright 2015 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <fw/types.h>
+
+#include "debugger.h"
+#include "rswdp.h"
+#include "jtag.h"
+
+static const char *JSTATE[17] = {
+ "RESET", "IDLE", "DRSELECT", "DRCAPTURE",
+ "DRSHIFT", "DREXIT1", "DRPAUSE", "DREXIT2",
+ "DRUPDATE", "IRSELECT", "IRCAPTURE", "IRSHIFT",
+ "IREXIT1", "IRPAUSE", "IREXIT1", "IRUPDATE",
+ "UNKNOWN"
+};
+
+static u64 ONES = 0xFFFFFFFFFFFFFFFFUL;
+
+void jtag_txn_init(jtag_txn *tx) {
+ memset(tx, 0, sizeof(jtag_txn));
+ tx->state = JTAG_UNKNOWN;
+}
+
+int jtag_txn_exec(jtag_txn *t) {
+ unsigned n, off = 0;
+ int r;
+ if (t->status) {
+ return t->status;
+ }
+ //xprintf(XCORE, "jtag exec %d bits\n", t->txc);
+ r = jtag_io(t->txc, t->tms, t->tdi, t->tdo);
+ for (n = 0; n < t->rxc; n++) {
+ unsigned count = t->bits[n];
+ if (t->ptr[n]) {
+ unsigned bit = 0;
+ u64 x = 0;
+ while (count > 0) {
+ x |= ((u64) ((t->tdo[off >> 5] >> (off & 31)) & 1)) << bit;
+ off++;
+ bit++;
+ count--;
+ }
+ *t->ptr[n] = x;
+ } else {
+ off += count;
+ }
+ }
+ return r;
+}
+
+void jtag_txn_append(jtag_txn *t, unsigned count, u64 tms, u64 tdi, u64 *tdo) {
+ unsigned txc = t->txc;
+
+ if (txc == JTAG_MAX_RESULTS) {
+ xprintf(XCORE, "jtag append txn overflow\n");
+ t->status = -1;
+ return;
+ }
+ if ((count > 64) || ((JTAG_MAX_BITS - t->bitcount) < count)) {
+ xprintf(XCORE, "jtag append bits overflow\n");
+ t->status = -1;
+ return;
+ }
+ t->bitcount += count;
+
+// xprintf(XCORE, "jtag append %2d bits %016lx %016lx\n", count, tms, tdi);
+
+ t->ptr[t->rxc] = tdo;
+ t->bits[t->rxc] = count;
+ t->rxc++;
+
+ while (count > 0) {
+ t->tms[txc >> 5] |= (tms & 1) << (txc & 31);
+ t->tdi[txc >> 5] |= (tdi & 1) << (txc & 31);
+ tms >>= 1;
+ tdi >>= 1;
+ count--;
+ txc++;
+ }
+ t->txc = txc;
+}
+
+void jtag_txn_move(jtag_txn *t, unsigned dst) {
+ if (t->state == dst) {
+ // nothing to do
+ return;
+ }
+ switch (t->state) {
+ case JTAG_IDLE:
+ if (dst == JTAG_IRSHIFT) {
+ // Idle -> SelDR -> SelIR -> CapIR -> ShiftIR
+ // 1 1 0 0
+ jtag_txn_append(t, 4, 0b0011, 0b1111, NULL);
+ } else if (dst == JTAG_DRSHIFT) {
+ // Idle -> SelDR -> CapDR -> ShiftDR
+ // 1 0 0
+ jtag_txn_append(t, 3, 0b001, 0b111, NULL);
+ } else {
+ goto oops;
+ }
+ break;
+ case JTAG_DRPAUSE:
+ case JTAG_IRPAUSE:
+ if (dst == JTAG_IRSHIFT) {
+ // PauseDR -> Exit2DR -> UpDR -> SelDR -> SelIR -> CapIR -> ShiftIR
+ // PauseIR -> Exit2IR -> UpIR -> SelDR -> SelIR -> CapIR -> ShiftIR
+ // 1 1 1 1 0 0
+ jtag_txn_append(t, 6, 0b001111, 0b111111, NULL);
+ } else if (dst == JTAG_DRSHIFT) {
+ // PauseDR -> Exit2DR -> UpDR -> SelDR -> CapDR -> ShiftDR
+ // PauseIR -> Exit2IR -> UpIR -> SelDR -> CapDR -> ShiftDR
+ // 1 1 1 0 0
+ jtag_txn_append(t, 5, 0b00111, 0b11111, NULL);
+ } else if (dst == JTAG_IDLE) {
+ // PauseDR -> Exit2DR -> UpDR -> Idle
+ // PauseIR -> Exit2IR -> UpIR -> Idle
+ // 1 1 0
+ jtag_txn_append(t, 3, 0b011, 0b111, NULL);
+ } else {
+ goto oops;
+ }
+ break;
+ case JTAG_DREXIT1:
+ case JTAG_IREXIT1:
+ if (dst == JTAG_IRSHIFT) {
+ // Exit1DR -> UpDR -> SelDR -> SelIR -> CapIR -> ShiftIR
+ // Exit1IR -> UpIR -> SelDR -> SelIR -> CapIR -> ShiftIR
+ // 1 1 1 0 0
+ jtag_txn_append(t, 5, 0b00111, 0b11111, NULL);
+ } else if (dst == JTAG_DRSHIFT) {
+ // Exit1DR -> UpDR -> SelDR -> CapDR -> ShiftDR
+ // Exit1IR -> UpIR -> SelDR -> CapDR -> ShiftDR
+ // 1 1 0 0
+ jtag_txn_append(t, 4, 0b0011, 0b1111, NULL);
+ } else if (dst == JTAG_IDLE) {
+ // Exit1DR -> UpDR -> Idle
+ // Exit1IR -> UpIR -> Idle
+ // 1 0
+ jtag_txn_append(t, 2, 0b01, 0b11, NULL);
+ } else if (dst == JTAG_DRPAUSE) {
+ // Exit1DR -> PauseDR
+ // Exit1IR -> PauseIR
+ // 0
+ jtag_txn_append(t, 1, 0b0, 0b1, NULL);
+ } else {
+ goto oops;
+ }
+ break;
+ default:
+oops:
+ xprintf(XCORE, "jtag move from %s to %s unsupported\n",
+ JSTATE[t->state], JSTATE[dst]);
+ t->status = -1;
+ t->state = JTAG_UNKNOWN;
+ return;
+ }
+ t->state = dst;
+}
+
+void jtag_cjtag_open(jtag_txn *t) {
+ jtag_txn_move(t, JTAG_IDLE);
+ // Idle -> SelDR -> CapDR -> Exit1DR -> UpDR ZBS#1
+ // 1 0 1 1
+ // UpDR -> SelDR -> CapDR -> Exit1DR -> UpDR ZBS#2
+ // 1 0 1 1
+ // UpDR -> SelDR -> CapDR -> ShiftDR -> Exit1DR -> UpDR -> Idle One Bit Scan
+ // 1 0 0 1 1 0
+ jtag_txn_append(t, 14, 0b01100111011101, 0, NULL);
+}
+
+void jtag_cjtag_cmd(jtag_txn *t, unsigned cp0, unsigned cp1) {
+ jtag_txn_move(t, JTAG_IDLE);
+ if ((cp0 > 15) || (cp1 > 15)) {
+ xprintf(XCORE, "jtag invalid cjtag parameters %d %d\n", cp0, cp1);
+ t->status = -1;
+ return;
+ }
+ if (cp0 == 0) {
+ // Idle -> SelDR -> CapDR -> Exit1DR -> UpDR -> Idle
+ // 1 0 1 1 0
+ jtag_txn_append(t, 5, 0b01101, 0, NULL);
+ } else {
+ // Idle -> SelDR -> CapDR -> ShiftDR (xN) -> Exit1DR -> UpDR -> Idle
+ // 1 0 0 1 1 0
+ jtag_txn_append(t, 5 + cp0, ((0b011 << cp0) << 2) | 0b01, 0, NULL);
+ }
+ if (cp1 == 0) {
+ // Idle -> SelDR -> CapDR -> Exit1DR -> UpDR -> Idle
+ // 1 0 1 1 0
+ jtag_txn_append(t, 5, 0b01101, 0, NULL);
+ } else {
+ // Idle -> SelDR -> CapDR -> ShiftDR (xN) -> Exit1DR -> UpDR -> Idle
+ // 1 0 0 1 1 0
+ jtag_txn_append(t, 5 + cp1, ((0b011 << cp1) << 2) | 0b01, 0, NULL);
+ }
+}
+
+void jtag_any_to_rti(jtag_txn *t) {
+ // 5x TMS=1 is sufficient, but throw a couple more in for luck
+ jtag_txn_append(t, 9, 0xFF, 0x1FF, NULL);
+ t->state = JTAG_IDLE;
+}
+
+static void _jtag_shiftir(jtag_txn *t, unsigned count, u64 tx, u64 *rx) {
+ // all bits but last, tms=0 (stay in ShiftIR), last tms=1 (goto Exit1IR)
+ if (t->state != JTAG_IRSHIFT) {
+ xprintf(XCORE, "jtag invalid state (%s) in shiftir\n", JSTATE[t->state]);
+ t->status = -1;
+ }
+ if (t->ir_pre) {
+ jtag_txn_append(t, t->ir_pre, 0, ONES, NULL);
+ }
+ if (t->ir_post) {
+ u64 tms = 1UL << (t->ir_post - 1);
+ jtag_txn_append(t, count, 0, tx, rx);
+ jtag_txn_append(t, t->ir_post, tms, ONES, NULL);
+ } else {
+ u64 tms = 1UL << (count - 1);
+ jtag_txn_append(t, count, tms, tx, rx);
+ }
+ t->state = JTAG_IREXIT1;
+}
+
+static void _jtag_shiftdr(jtag_txn *t, unsigned count, u64 tx, u64 *rx) {
+ if (t->state != JTAG_DRSHIFT) {
+ xprintf(XCORE, "jtag invalid state (%s) in shiftir\n", JSTATE[t->state]);
+ t->status = -1;
+ }
+ // all bits but last, tms=0 (stay in ShiftDR), last tms=1 (goto Exit1DR)
+ if (t->dr_pre) {
+ jtag_txn_append(t, t->dr_pre, 0, ONES, NULL);
+ }
+ if (t->dr_post) {
+ u64 tms = 1UL << (t->dr_post - 1);
+ jtag_txn_append(t, count, 0, tx, rx);
+ jtag_txn_append(t, t->dr_post, tms, ONES, NULL);
+ } else {
+ u64 tms = 1UL << (count - 1);
+ jtag_txn_append(t, count, tms, tx, rx);
+ }
+ t->state = JTAG_DREXIT1;
+}
+
+void jtag_ir(jtag_txn *t, unsigned count, u64 ir) {
+ jtag_txn_move(t, JTAG_IRSHIFT);
+ _jtag_shiftir(t, count, ir, NULL);
+ jtag_txn_move(t, JTAG_IDLE);
+}
+
+void jtag_dr(jtag_txn *t, unsigned count, u64 dr, u64 *out) {
+ jtag_txn_move(t, JTAG_DRSHIFT);
+ _jtag_shiftdr(t, count, dr, out);
+ jtag_txn_move(t, JTAG_IDLE);
+}
+
+void jtag_ir_p(jtag_txn *t, unsigned count, u64 ir) {
+ jtag_txn_move(t, JTAG_IRSHIFT);
+ _jtag_shiftir(t, count, ir, NULL);
+ jtag_txn_move(t, JTAG_IRPAUSE);
+}
+
+void jtag_dr_p(jtag_txn *t, unsigned count, u64 dr, u64 *out) {
+ jtag_txn_move(t, JTAG_DRSHIFT);
+ _jtag_shiftdr(t, count, dr, out);
+ jtag_txn_move(t, JTAG_DRPAUSE);
+}
+
+
diff --git a/tools/jtag.h b/tools/jtag.h
@@ -0,0 +1,86 @@
+/* jtag transport
+ *
+ * Copyright 2015 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _JTAG_H_
+#define _JTAG_H_
+
+#define JTAG_RESET 0
+#define JTAG_IDLE 1
+#define JTAG_DRSELECT 2
+#define JTAG_DRCAPTURE 3
+#define JTAG_DRSHIFT 4
+#define JTAG_DREXIT1 5
+#define JTAG_DRPAUSE 6
+#define JTAG_DREXIT2 7
+#define JTAG_DRUPDATE 8
+#define JTAG_IRSELECT 9
+#define JTAG_IRCAPTURE 10
+#define JTAG_IRSHIFT 11
+#define JTAG_IREXIT1 12
+#define JTAG_IRPAUSE 13
+#define JTAG_IREXIT2 14
+#define JTAG_IRUPDATE 15
+#define JTAG_UNKNOWN 16
+
+
+#define JTAG_MAX_WORDS 256
+#define JTAG_MAX_BITS ((JTAG_MAX_WORDS) * 4)
+#define JTAG_MAX_RESULTS 256
+
+typedef struct jtag_txn {
+ u32 tms[JTAG_MAX_WORDS];
+ u32 tdi[JTAG_MAX_WORDS];
+ u32 tdo[JTAG_MAX_WORDS];
+ u8 bits[JTAG_MAX_RESULTS];
+ u64 *ptr[JTAG_MAX_RESULTS];
+ unsigned txc;
+ unsigned rxc;
+ unsigned bitcount;
+ int status;
+ u32 ir_pre;
+ u32 ir_post;
+ u32 dr_pre;
+ u32 dr_post;
+ unsigned state;
+} jtag_txn;
+
+
+void jtag_txn_init(jtag_txn *tx);
+int jtag_txn_exec(jtag_txn *t);
+
+void jtag_txn_move(jtag_txn *t, unsigned dst);
+
+// clock out 5+ TMS 1s and get to RESET, then IDLE from anywhere
+void jtag_any_to_rti(jtag_txn *t);
+
+// scan into IR or DR and end in IDLE state
+void jtag_ir(jtag_txn *t, unsigned count, u64 ir);
+void jtag_dr(jtag_txn *t, unsigned count, u64 dr, u64 *out);
+
+// scan into IR or DR but end in PauseIR or PauseDR state, not IDLE
+void jtag_ir_p(jtag_txn *t, unsigned count, u64 ir);
+void jtag_dr_p(jtag_txn *t, unsigned count, u64 dr, u64 *out);
+
+void jtag_cjtag_open(jtag_txn *t);
+void jtag_cjtag_cmd(jtag_txn *t, unsigned cp0, unsigned cp1);
+
+// clock raw TMS/TDI bit streams, does not check or update t->state
+void jtag_txn_append(jtag_txn *t, unsigned count, u64 tms, u64 tdi, u64 *tdo);
+
+
+#endif
+
diff --git a/tools/ti-icepick.h b/tools/ti-icepick.h
@@ -0,0 +1,56 @@
+#ifndef _TI_ICEPICK_H_
+#define _TI_ICEPICK_H_
+
+#define IP_IR_WIDTH 6
+
+#define IP_IR_BYPASS 0x3F // W = 1
+#define IP_IR_ROUTER 0x02 // W = 32, only when CONNECTED
+#define IP_IR_IDCODE 0x04 // W = 32
+#define IP_IR_IPCODE 0x05 // W = 32
+#define IP_IR_CONNECT 0x07 // W = 8
+#define IP_IR_USERCODE 0x08 // W = 32
+
+#define IP_CONNECT_RD 0x00
+#define IP_CONNECT_WR_KEY 0x89
+
+// scanout returns results from previous scan
+// loading IR between scans may result in incorrect readback
+#define IP_RTR_WR (1 << 31)
+#define IP_RTR_BLK(n) (((n) & 7) << 28)
+#define IP_RTR_REG(n) (((n) & 15) << 24)
+#define IP_RTR_VAL(n) ((n) & 0x00FFFFFF)
+
+#define IP_RTR_OK (1 << 31) // last write succeeded
+
+#define IP_BLK_CONTROL 0
+#define IP_BLK_TEST_TLCB 1 // TLCB = TAP Linking Control Block
+#define IP_BLK_DEBUG_TLCB 2
+
+// CONTROL block registers
+#define IP_CTL_ZEROS 0
+#define IP_CTL_CONTROL 1
+#define IP_CTL_BLOCK_SYS_RESET (1 << 6)
+#define IP_CTL_SYSTEM_RESET (1 << 0)
+
+#define IP_CTL_LINKING_MODE 2
+#define IP_CTL_ALWAYS_FIRST (0 << 1) // TAP always closest to TDI
+#define IP_CTL_DISAPPEAR_FOREVER (3 << 1) // TAP vanishes until power-on-reset
+#define IP_CTL_ACTIVATE_MODE (1 << 0) // Commit on next RunTestIdle
+
+// DEBUG TLCB registers
+#define IP_DBG_TAP0 0
+#define IP_DBG_INHIBIT_SLEEP (1 << 20)
+#define IP_DBG_IN_RESET (1 << 17) // RD
+#define IP_DBG_RELEASE_FROM_WIR (1 << 17) // WR release from wait-in-reset
+#define IP_DBG_RESET_NORMAL (0 << 14) // reset normally
+#define IP_DBG_RESET_WAIT (1 << 14) // wait after reset
+#define IP_DBG_RESET_CANCEL (4 << 14) // cancel command lockout
+#define IP_DBG_VISIBLE_TAP (1 << 9) // RD
+#define IP_DBG_SELECT_TAP (1 << 8) // RW 1 = Make Visible on RTI
+#define IP_DBG_FORCE_ACTIVE (1 << 3) // RW 1 = override app power/clock
+#define IP_DBG_TAP_ACCESSIBLE (1 << 1) // RO 0 = no TAP (security)
+#define IP_DBG_TAP_PRESENT (1 << 0) // RO 0 = no TAP (hw)
+
+// ICE Melter wakes up JTAG after 8 TCKs -- must wait 200uS
+#endif
+