mdebug

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
MMakefile | 2++
Atools/dap-registers.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/jtag-dap.c | 497+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/jtag.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/jtag.h | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/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 +