xdebug

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

commit 5b0bd390bb3272cd709b5f8c3d6ef6738d1bc47d
parent 2e9320d1df9bb1947311cd9aad517719fd724431
Author: Brian Swetland <swetland@frotz.net>
Date:   Fri,  3 Mar 2023 04:09:40 -0800

transport: improve attach, rig up memory access via MAP

Diffstat:
MMakefile | 5++---
Msrc/arm-debug.h | 2++
Asrc/transport-arm-debug.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/transport-dap.c | 564+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/transport-private.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/transport.c | 582------------------------------------------------------------------------------
Msrc/transport.h | 45++++++++++++++++++++++++++++-----------------
Msrc/xdebug.c | 30+++++++++++++-----------------
8 files changed, 766 insertions(+), 619 deletions(-)

diff --git a/Makefile b/Makefile @@ -3,7 +3,7 @@ all: out/xdebug CFLAGS := -Wall -g -O1 -SRCS := src/xdebug.c src/transport.c src/usb.c +SRCS := src/xdebug.c src/transport-arm-debug.c src/transport-dap.c src/usb.c LIBS := -lusb-1.0 OBJS := $(addprefix out/,$(patsubst %.c,%.o,$(filter %.c,$(SRCS)))) @@ -12,7 +12,6 @@ out/xdebug: $(OBJS) @mkdir -p $(dir $@) gcc -o $@ -Wall -g -O1 $(OBJS) $(LIBS) - $(OBJS): out/%.o: %.c $(XDEPS) @mkdir -p $(dir $@) gcc $(CFLAGS) -c $< -MD -MP -MT $@ -MF $(@:%o=%d) -o $@ @@ -20,5 +19,5 @@ $(OBJS): out/%.o: %.c $(XDEPS) -include $(OBJS:%o=%d) clean: - rm -f out/ + rm -rf out/ diff --git a/src/arm-debug.h b/src/arm-debug.h @@ -103,3 +103,5 @@ #define MAP_CSW_SEC_DBG_EN 0x00800000U // Secure Debug Enable #define MAP_CSW_PROT_MASK 0x7F000000U #define MAP_CSW_DBG_SW_EN 0x80000000U // Debug SW Access Enable + +#define MAP_CSW_KEEP 0xFF00FF00U // preserve mode/type/prot fields diff --git a/src/transport-arm-debug.c b/src/transport-arm-debug.c @@ -0,0 +1,79 @@ +// Copyright 2023, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include "transport.h" +#include "transport-private.h" + +#include "arm-debug.h" + +static void dc_q_map_csw_wr(DC* dc, uint32_t val) { + if (val != dc->map_csw_cache) { + dc->map_csw_cache = val; + dc_q_ap_wr(dc, MAP_CSW, val | dc->map_csw_keep); + } +} + +static void dc_q_map_tar_wr(DC* dc, uint32_t val) { + if (val != dc->map_tar_cache) { + dc->map_tar_cache = val; + dc_q_ap_wr(dc, MAP_TAR, val); + } +} + + +void dc_q_mem_rd32(DC* dc, uint32_t addr, uint32_t* val) { + if (addr & 3) { + dc->qerror = DC_ERR_BAD_PARAMS; + } else { + dc_q_map_csw_wr(dc, MAP_CSW_SZ_32 | MAP_CSW_INC_OFF | MAP_CSW_DEVICE_EN); + dc_q_map_tar_wr(dc, addr); + dc_q_ap_rd(dc, MAP_DRW, val); + } +} + +void dc_q_mem_wr32(DC* dc, uint32_t addr, uint32_t val) { + if (addr & 3) { + dc->qerror = DC_ERR_BAD_PARAMS; + } else { + dc_q_map_csw_wr(dc, MAP_CSW_SZ_32 | MAP_CSW_INC_OFF | MAP_CSW_DEVICE_EN); + dc_q_map_tar_wr(dc, addr); + dc_q_ap_wr(dc, MAP_DRW, val); + } +} + +int dc_mem_rd32(DC* dc, uint32_t addr, uint32_t* val) { + dc_q_init(dc); + dc_q_mem_rd32(dc, addr, val); + return dc_q_exec(dc); +} + +int dc_mem_wr32(DC* dc, uint32_t addr, uint32_t val) { + dc_q_init(dc); + dc_q_mem_wr32(dc, addr, val); + return dc_q_exec(dc); +} + + + +int dc_mem_rd_words(dctx_t* dc, uint32_t addr, uint32_t num, uint32_t* ptr) { + while (num > 0) { + dc_q_mem_rd32(dc, addr, ptr); + num--; + addr += 4; + ptr++; + } + return dc_q_exec(dc); +} + +int dc_mem_wr_words(dctx_t* dc, uint32_t addr, uint32_t num, const uint32_t* ptr) { + while (num > 0) { + dc_q_mem_wr32(dc, addr, *ptr); + num--; + addr += 4; + ptr++; + } + return dc_q_exec(dc); +} + + + diff --git a/src/transport-dap.c b/src/transport-dap.c @@ -0,0 +1,564 @@ +// Copyright 2023, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "usb.h" +#include "arm-debug.h" +#include "cmsis-dap-protocol.h" +#include "transport.h" +#include "transport-private.h" + +int dap_get_info(DC* dc, unsigned di, void *out, unsigned minlen, unsigned maxlen) { + uint8_t buf[256 + 2]; + buf[0] = DAP_Info; + buf[1] = di; + if (usb_write(dc->usb, buf, 2) != 2) { + return DC_ERR_IO; + } + int sz = usb_read(dc->usb, buf, 256 + 2); + if ((sz < 2) || (buf[0] != DAP_Info)) { + return DC_ERR_PROTOCOL; + } + if ((buf[1] < minlen) || (buf[1] > maxlen)) { + return DC_ERR_PROTOCOL; + } + memcpy(out, buf + 2, buf[1]); + return buf[1]; +} + +int dap_cmd(DC* dc, const void* tx, unsigned txlen, void* rx, unsigned rxlen) { + uint8_t cmd = ((const uint8_t*) tx)[0]; + dump("TX>", tx, txlen); + if (usb_write(dc->usb, tx, txlen) != txlen) { + ERROR("dap_cmd(0x%02x): usb write error\n", cmd); + return DC_ERR_IO; + } + int sz = usb_read(dc->usb, rx, rxlen); + if (sz < 1) { + ERROR("dap_cmd(0x%02x): usb read error\n", cmd); + return DC_ERR_IO; + } + dump("RX>", rx, rxlen); + if (((uint8_t*) rx)[0] != cmd) { + ERROR("dap_cmd(0x%02x): unsupported (0x%02x)\n", + cmd, ((uint8_t*) rx)[0]); + return DC_ERR_UNSUPPORTED; + } + return sz; +} + +int dap_cmd_std(DC* dc, const char* name, uint8_t* io, + unsigned txlen, unsigned rxlen) { + int r = dap_cmd(dc, io, txlen, io, rxlen); + if (r < 0) { + return r; + } + if (io[1] != 0) { + ERROR("%s status 0x%02x\n", name, io[1]); + return DC_ERR_REMOTE; + } + return 0; +} + +static int dap_connect(DC* dc) { + uint8_t io[2] = { DAP_Connect, PORT_SWD }; + return dap_cmd_std(dc, "dap_connect()", io, 2, 2); +} + +static int dap_swd_configure(DC* dc, unsigned cfg) { + uint8_t io[2] = { DAP_SWD_Configure, cfg }; + return dap_cmd_std(dc, "dap_swd_configure()", io, 2, 2); +} + +int dc_set_clock(DC* dc, uint32_t hz) { + uint8_t io[5] = { DAP_SWJ_Clock, + hz, hz >> 8, hz >> 16, hz >> 24 }; + return dap_cmd_std(dc, "dap_swj_clock()", io, 5, 2); +} + +static int dap_xfer_config(DC* dc, unsigned idle, unsigned wait, unsigned match) { + // clamp to allowed max values + if (idle > 255) idle = 255; + if (wait > 65535) wait = 65535; + if (match > 65535) match = 65535; + + // do nothing if unchanged from last set values + if ((dc->cfg_idle == idle) && + (dc->cfg_wait == wait) && + (dc->cfg_match == match)) { + return 0; + } + + // cache new values + dc->cfg_idle = idle; + dc->cfg_wait = wait; + dc->cfg_match = match; + + // inform the probe + uint8_t io[6] = { DAP_TransferConfigure, idle, wait, wait >> 8, match, match >> 8}; + return dap_cmd_std(dc, "dap_transfer_configure()", io, 6, 2); +} + +static void dc_q_clear(DC* dc) { + dc->txnext = dc->txbuf + 3; + dc->rxnext = dc->rxptr; + dc->txavail = dc->max_packet_size - 3; + dc->rxavail = dc->max_packet_size - 3; + dc->qerror = 0; + dc->txbuf[0] = DAP_Transfer; + dc->txbuf[1] = 0; // Index 0 for SWD + dc->txbuf[2] = 0; // Count 0 initially + + // TODO: less conservative mode: don't always invalidate + dc->dp_select_cache = INVALID; + dc->cfg_mask = INVALID; + dc->map_csw_cache = INVALID; + dc->map_tar_cache = INVALID; +} + +void dc_q_init(DC* dc) { + // TODO: handle error cleanup, re-attach, etc + dc_q_clear(dc); +} + +// unpack the status bits into a useful status code +static int dc_decode_status(unsigned n) { + unsigned ack = n & RSP_ACK_MASK; + if (n & RSP_ProtocolError) { + ERROR("DAP SWD Parity Error\n"); + return DC_ERR_SWD_PARITY; + } + switch (ack) { + case RSP_ACK_OK: + break; + case RSP_ACK_WAIT: + ERROR("DAP SWD WAIT (Timeout)\n"); + return DC_ERR_TIMEOUT; + case RSP_ACK_FAULT: + ERROR("DAP SWD FAULT\n"); + return DC_ERR_SWD_FAULT; + case RSP_ACK_MASK: // all bits set + ERROR("DAP SWD SILENT\n"); + return DC_ERR_SWD_SILENT; + default: + ERROR("DAP SWD BOGUS\n"); + return DC_ERR_SWD_BOGUS; + } + if (n & RSP_ValueMismatch) { + ERROR("DAP Value Mismatch\n"); + return DC_ERR_MATCH; + } + return DC_OK; +} + +int dc_q_exec(DC* dc) { + // if we're already in error, don't generate more usb traffic + if (dc->qerror) { + int r = dc->qerror; + dc_q_clear(dc); + return r; + } + // if we have no work to do, succeed + if (dc->txbuf[2] == 0) { + return 0; + } + int sz = dc->txnext - dc->txbuf; + dump("TX>", dc->txbuf, sz); + if (usb_write(dc->usb, dc->txbuf, sz) != sz) { + ERROR("dc_q_exec() usb write error\n"); + return DC_ERR_IO; + } + sz = 3 + (dc->rxnext - dc->rxptr) * 4; + uint8_t rxbuf[1024]; + memset(rxbuf, 0xEE, 1024); // DEBUG + int n = usb_read(dc->usb, rxbuf, sz); + if (n < 0) { + ERROR("dc_q_exec() usb read error\n"); + return DC_ERR_IO; + } + dump("RX>", rxbuf, sz); + if ((n < 3) || (rxbuf[0] != DAP_Transfer)) { + ERROR("dc_q_exec() bad response\n"); + return DC_ERR_PROTOCOL; + } + int r = dc_decode_status(rxbuf[2]); + if (r == DC_OK) { + // how many response words available? + n = (n - 3) / 4; + uint8_t* rxptr = rxbuf + 3; + for (unsigned i = 0; i < n; i++) { + memcpy(dc->rxptr[i], rxptr, 4); + rxptr += 4; + } + } + + dc_q_clear(dc); + return r; +} + +// internal use only -- queue raw dp reads and writes +// these do not check req for correctness +static void dc_q_raw_rd(DC* dc, unsigned req, uint32_t* val) { + if ((dc->txavail < 1) || (dc->rxavail < 4)) { + // exec q to make space for more work, + // but if there's an error, latch it + if ((dc->qerror = dc_q_exec(dc)) != DC_OK) { + return; + } + } + dc->txnext[0] = req; + dc->rxnext[0] = val; + dc->txnext += 1; + dc->rxnext += 1; + dc->txbuf[2] += 1; + dc->txavail -= 1; + dc->rxavail -= 4; +} + +static void dc_q_raw_wr(DC* dc, unsigned req, uint32_t val) { + if (dc->txavail < 5) { + // exec q to make space for more work, + // but if there's an error, latch it + if ((dc->qerror = dc_q_exec(dc)) != DC_OK) { + return; + } + } + dc->txnext[0] = req; + memcpy(dc->txnext + 1, &val, 4); + dc->txnext += 5; + dc->txavail -= 5; + dc->txbuf[2] += 1; +} + +// adjust DP.SELECT for desired DP access, if necessary +void dc_q_dp_sel(DC* dc, uint32_t dpaddr) { + // DP address is BANK:4 REG:4 + if (dpaddr & 0xFFFFFF03U) { + ERROR("invalid DP addr 0x%08x\n", dpaddr); + dc->qerror = DC_ERR_FAILED; + return; + } + // only register 4 cares about the value of DP.SELECT.DPBANK + // so do nothing unless we're setting a register 4 variant + if ((dpaddr & 0xF) != 0x4) { + return; + } + uint32_t mask = DP_SELECT_DPBANK(0xFU); + uint32_t addr = DP_SELECT_DPBANK((dpaddr >> 4)); + uint32_t select = (dc->dp_select & mask) | addr; + if (select != dc->dp_select_cache) { + dc->dp_select_cache = select; + dc_q_raw_wr(dc, XFER_DP | XFER_WR | DP_SELECT, select); + } +} + +// adjust DP.SELECT for desired AP access, if necessary +void dc_q_ap_sel(DC* dc, uint32_t apaddr) { + // AP address is AP:8 BANK:4 REG:4 + if (apaddr & 0xFFFF0003U) { + ERROR("invalid DP addr 0x%08x\n", apaddr); + dc->qerror = DC_ERR_FAILED; + return; + } + // we always return DPBANK to 0 when adjusting AP & APBANK + // since it preceeds an AP write which will need DPBANK at 0 + uint32_t select = + DP_SELECT_AP((apaddr & 0xFF00U) << 16) | + DP_SELECT_APBANK(apaddr >> 4); + if (select != dc->dp_select_cache) { + dc->dp_select_cache = select; + dc_q_raw_wr(dc, XFER_DP | XFER_WR | DP_SELECT, select); + } +} + +// DP and AP reads and writes +// DP.SELECT will be adjusted as necessary to ensure proper addressing +void dc_q_dp_rd(DC* dc, unsigned dpaddr, uint32_t* val) { + if (dc->qerror) return; + dc_q_dp_sel(dc, dpaddr); + dc_q_raw_rd(dc, XFER_DP | XFER_RD | (dpaddr & 0x0C), val); +} + +void dc_q_dp_wr(DC* dc, unsigned dpaddr, uint32_t val) { + if (dc->qerror) return; + dc_q_dp_sel(dc, dpaddr); + dc_q_raw_wr(dc, XFER_DP | XFER_WR | (dpaddr & 0x0C), val); +} + +void dc_q_ap_rd(DC* dc, unsigned apaddr, uint32_t* val) { + if (dc->qerror) return; + dc_q_ap_sel(dc, apaddr); + dc_q_raw_rd(dc, XFER_AP | XFER_RD | (apaddr & 0x0C), val); +} + +void dc_q_ap_wr(DC* dc, unsigned apaddr, uint32_t val) { + if (dc->qerror) return; + dc_q_ap_sel(dc, apaddr); + dc_q_raw_wr(dc, XFER_AP | XFER_WR | (apaddr & 0x0C), val); +} + +void dc_q_set_mask(DC* dc, uint32_t mask) { + if (dc->qerror) return; + if (dc->cfg_mask == mask) return; + dc->cfg_mask = mask; + dc_q_raw_wr(dc, XFER_WR | XFER_MatchMask, mask); +} + +void dc_set_match_retry(dctx_t* dc, unsigned num) { + if (dc->qerror) return; + dap_xfer_config(dc, dc->cfg_idle, dc->cfg_wait, num); +} + +void dc_q_ap_match(DC* dc, unsigned apaddr, uint32_t val) { + if (dc->qerror) return; + dc_q_ap_sel(dc, apaddr); + dc_q_raw_wr(dc, XFER_AP | XFER_RD | XFER_ValueMatch | (apaddr & 0x0C), val); +} + +void dc_q_dp_match(DC* dc, unsigned apaddr, uint32_t val) { + if (dc->qerror) return; + dc_q_ap_sel(dc, apaddr); + dc_q_raw_wr(dc, XFER_DP | XFER_RD | XFER_ValueMatch | (apaddr & 0x0C), val); +} + +// convenience wrappers for single reads and writes +int dc_dp_rd(DC* dc, unsigned dpaddr, uint32_t* val) { + dc_q_init(dc); + dc_q_dp_rd(dc, dpaddr, val); + return dc_q_exec(dc); +} +int dc_dp_wr(DC* dc, unsigned dpaddr, uint32_t val) { + dc_q_init(dc); + dc_q_dp_wr(dc, dpaddr, val); + return dc_q_exec(dc); +} +int dc_ap_rd(DC* dc, unsigned apaddr, uint32_t* val) { + dc_q_init(dc); + dc_q_ap_rd(dc, apaddr, val); + return dc_q_exec(dc); +} +int dc_ap_wr(DC* dc, unsigned apaddr, uint32_t val) { + dc_q_init(dc); + dc_q_ap_wr(dc, apaddr, val); + return dc_q_exec(dc); +} + +// SWD Attach Sequence: +// 1. Send >50 1s and then the JTAG to SWD escape code +// (in case this is a JTAG-SWD DAP in JTAG mode) +// 2. Send >8 1s and then the Selection Alert Sequence +// and then the SWD Activation Code +// (in case this is a SWD v2 DAP in Dormant State) +// 3. Send >50 1s and then 4 0s -- the Line Reset Sequence +// 4. If multidrop, issue a write to DP.TARGETSEL, but +// ignore the ACK response +// 5. Issue a read from DP.IDR + +static uint8_t attach_cmd[54] = { + DAP_SWD_Sequence, 5, + + // [--- 64 1s ----------------------------------] + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + // [JTAG2SWD] [- 16 1s ] [--------------------- + 0x00, 0x9E, 0xE7, 0xFF, 0xFF, 0x92, 0xF3, 0x09, 0x62, + // ----- Selection Alert Sequence --------------- + 0x00, 0x95, 0x2D, 0x85, 0x86, 0xE9, 0xAF, 0xDD, 0xE3, + // ---------------------] [Act Code] [--------- + 0x00, 0xA2, 0x0E, 0xBC, 0x19, 0xA0, 0xF1, 0xFF, 0xFF, + // ----- Line Reset Sequence -------] + 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + + // WR DP TARGETSEL + 0x08, 0x99, + // 5 bits idle + 0x85, + // WR VALUE:32, PARTY:1, ZEROs:7 + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int _dc_attach(DC* dc, unsigned flags, uint32_t tgt, uint32_t* idcode) { + uint8_t rsp[3]; + + if (flags & DC_MULTIDROP) { + // Copy and patch the attach sequence to include + // the DP.TARGETSEL write and insert the target + // id and parity + uint8_t cmd[54]; + memcpy(cmd, attach_cmd, 54); + cmd[1] = 8; + memcpy(cmd + 49, &tgt, sizeof(tgt)); + cmd[53] = __builtin_parity(tgt); + dap_cmd(dc, cmd, 54, rsp, 3); + } else { + // use the common part of the attach sequence, as-is + dap_cmd(dc, attach_cmd, 45, rsp, 2); + } + + // Issue a bare DP.IDR read, as required after a line reset + // or line reset + target select + dc_q_init(dc); + dc_q_raw_rd(dc, XFER_DP | XFER_RD | XFER_00, idcode); + int r = dc_q_exec(dc); + return r; +} + +int dc_attach(DC* dc, unsigned flags, unsigned tgt, uint32_t* idcode) { + uint32_t n; + + _dc_attach(dc, 0, 0, &n); + printf("IDCODE %08x\n", n); + + // If this is a RP2040, we need to connect in multidrop + // mode before doing anything else. + if ((n == 0x0bc12477) && (tgt == 0)) { + dc_dp_rd(dc, DP_TARGETID, &n); + if (n == 0x01002927) { // RP2040 + _dc_attach(dc, DC_MULTIDROP, 0x01002927, &n); + } + } + + dc_dp_rd(dc, DP_CS, &n); + printf("CTRL/STAT %08x\n", n); + + // clear all sticky errors + dc_dp_wr(dc, DP_ABORT, DP_ABORT_ALLCLR); + + // power up and wait for ack + dc_q_init(dc); + dc_q_set_mask(dc, DP_CS_CDBGPWRUPACK | DP_CS_CSYSPWRUPACK); + dc_q_dp_wr(dc, DP_CS, DP_CS_CDBGPWRUPREQ | DP_CS_CSYSPWRUPREQ); + dc_q_dp_match(dc, DP_CS, DP_CS_CDBGPWRUPACK | DP_CS_CSYSPWRUPACK); + dc_q_dp_rd(dc, DP_CS, &n); + dc_q_ap_rd(dc, MAP_CSW, &dc->map_csw_keep); + dc_q_exec(dc); + printf("CTRL/STAT %08x\n", n); + printf("MAP.CSW %08x\n", dc->map_csw_keep); + + dc->map_csw_keep &= MAP_CSW_KEEP; + + return 0; +} + + +static usb_handle* usb_connect(void) { + usb_handle *usb; + + usb = usb_open(0x1fc9, 0x0143, 0); + if (usb == 0) { + usb = usb_open(0x2e8a, 0x000c, 42); + if (usb == 0) { + ERROR("cannot find device\n"); + return NULL; + } + } + return usb; +} + +// setup a newly connected DAP device +static int dap_configure(DC* dc) { + uint8_t buf[256 + 2]; + uint32_t n32; + uint16_t n16; + uint8_t n8; + + // invalidate cached state + dc->cfg_idle = INVALID; + dc->cfg_wait = INVALID; + dc->cfg_match = INVALID; + dc->cfg_mask = INVALID; + + dc->map_csw_keep = 0; + dc->map_csw_cache = INVALID; + dc->map_tar_cache = INVALID; + + // setup default packet limits + dc->max_packet_count = 1; + dc->max_packet_size = 64; + + // flush queue + dc_q_clear(dc); + + buf[0] = DAP_Info; + for (unsigned n = 0; n < 10; n++) { + int sz = dap_get_info(dc, n, buf, 0, 255); + if (sz > 0) { + buf[sz] = 0; + printf("0x%02x: '%s'\n", n, (char*) buf); + } + } + + buf[0] = 0; buf[1] = 0; + if (dap_get_info(dc, DI_Capabilities, buf, 1, 2) > 0) { + printf("Capabilities: 0x%02x 0x%02x\n", buf[0], buf[1]); + printf("Capabilities:"); + if (buf[0] & I0_SWD) printf(" SWD"); + if (buf[0] & I0_JTAG) printf(" JTAG"); + if (buf[0] & I0_SWO_UART) printf(" SWO(UART)"); + if (buf[0] & I0_SWO_Manchester) printf(" SWO(Manchester)"); + if (buf[0] & I0_Atomic_Commands) printf(" ATOMIC"); + if (buf[0] & I0_Test_Domain_Timer) printf(" TIMER"); + if (buf[0] & I0_SWO_Streaming_Trace) printf(" SWO(Streaming)"); + if (buf[0] & I0_UART_Comm_Port) printf(" UART"); + if (buf[1] & I1_USB_COM_Port) printf(" USBCOM"); + printf("\n"); + } + if (dap_get_info(dc, DI_UART_RX_Buffer_Size, &n32, 4, 4) == 4) { + printf("UART RX Buffer Size: %u\n", n32); + } + if (dap_get_info(dc, DI_UART_TX_Buffer_Size, &n32, 4, 4) == 4) { + printf("UART TX Buffer Size: %u\n", n32); + } + if (dap_get_info(dc, DI_SWO_Trace_Buffer_Size, &n32, 4, 4) == 4) { + printf("SWO Trace Buffer Size: %u\n", n32); + } + if (dap_get_info(dc, DI_Max_Packet_Count, &n8, 1, 1) == 1) { + printf("Max Packet Count: %u\n", n8); + dc->max_packet_count = n8; + } + if (dap_get_info(dc, DI_Max_Packet_Size, &n16, 2, 2) == 2) { + printf("Max Packet Size: %u\n", n16); + dc->max_packet_size = n16; + } + if ((dc->max_packet_count < 1) || (dc->max_packet_size < 64)) { + ERROR("dc_init() impossible packet configuration\n"); + return DC_ERR_PROTOCOL; + } + + // invalidate register cache + dc->dp_select_cache = INVALID; + + // clip to our buffer size + if (dc->max_packet_size > 1024) { + dc->max_packet_size = 1024; + } + + dap_connect(dc); + dap_swd_configure(dc, CFG_Turnaround_1); + dap_xfer_config(dc, 8, 64, 64); + return DC_OK; +} + +int dc_create(DC** out) { + DC* dc; + + if ((dc = calloc(1, sizeof(DC))) == NULL) { + return DC_ERR_FAILED; + } + + if ((dc->usb = usb_connect()) == NULL) { + free(dc); + return DC_ERR_OFFLINE; + } + + int r = dap_configure(dc); + if (r < 0) { + free(dc); + } else { + *out = dc; + } + return r; +} diff --git a/src/transport-private.h b/src/transport-private.h @@ -0,0 +1,78 @@ +// Copyright 2023, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#pragma once + +#include <stdint.h> + +#include "usb.h" + +struct debug_context { + usb_handle* usb; + unsigned status; + + // dap protocol info + uint32_t max_packet_count; + uint32_t max_packet_size; + + // dap internal state cache + uint32_t cfg_idle; + uint32_t cfg_wait; + uint32_t cfg_match; + uint32_t cfg_mask; + + // configured DP.SELECT register value + uint32_t dp_select; + // last known state of DP.SELECT on the target + uint32_t dp_select_cache; + + // MAP cached state + uint32_t map_csw_keep; + uint32_t map_csw_cache; + uint32_t map_tar_cache; + + // transfer queue state + uint8_t txbuf[1024]; + uint32_t* rxptr[256]; + uint8_t *txnext; + uint32_t** rxnext; + uint32_t txavail; + uint32_t rxavail; + int qerror; +}; + +typedef struct debug_context DC; + +#define DC_ATTACHED 0 // attached and ready to do txns +#define DC_FAILURE 1 // last txn failed, need to re-attach +#define DC_DETACHED 2 // have not yet attached +#define DC_OFFLINE 3 // usb connection not available + +#define INVALID 0xFFFFFFFFU + + +#define WITH_TRACE 0 + +#if WITH_TRACE +#define TRACE(fmt...) fprintf(stderr, fmt) +#else +#define TRACE(fmt...) do {} while (0) +#endif + +#define ERROR(fmt...) fprintf(stderr, fmt) + +#if WITH_TRACE +static void dump(const char* str, const void* ptr, unsigned len) { + const uint8_t* x = ptr; + TRACE("%s", str); + while (len > 0) { + TRACE(" %02x", *x++); + len--; + } + TRACE("\n"); +} +#else +#define dump(...) do {} while (0) +#endif + + diff --git a/src/transport.c b/src/transport.c @@ -1,582 +0,0 @@ -// Copyright 2023, Brian Swetland <swetland@frotz.net> -// Licensed under the Apache License, Version 2.0. - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> - -#include "usb.h" -#include "arm-debug.h" -#include "cmsis-dap-protocol.h" -#include "transport.h" - -#define WITH_TRACE 0 - -#if WITH_TRACE -#define TRACE(fmt...) fprintf(stderr, fmt) -#else -#define TRACE(fmt...) do {} while (0) -#endif - -#define ERROR(fmt...) fprintf(stderr, fmt) - -#if WITH_TRACE -static void dump(const char* str, const void* ptr, unsigned len) { - const uint8_t* x = ptr; - TRACE("%s", str); - while (len > 0) { - TRACE(" %02x", *x++); - len--; - } - TRACE("\n"); -} -#else -#define dump(...) do {} while (0) -#endif - -#define DC_ATTACHED 0 // attached and ready to do txns -#define DC_FAILURE 1 // last txn failed, need to re-attach -#define DC_DETACHED 2 // have not yet attached -#define DC_OFFLINE 3 // usb connection not available - -#define INVALID 0xFFFFFFFFU - -struct debug_context { - usb_handle* usb; - unsigned status; - - // dap protocol info - uint32_t max_packet_count; - uint32_t max_packet_size; - - // dap internal state cache - uint32_t cfg_idle; - uint32_t cfg_wait; - uint32_t cfg_match; - uint32_t cfg_mask; - - // configured DP.SELECT register value - uint32_t dp_select; - // last known state of DP.SELECT on the target - uint32_t dp_select_cache; - - // transfer queue state - uint8_t txbuf[1024]; - uint32_t* rxptr[256]; - uint8_t *txnext; - uint32_t** rxnext; - uint32_t txavail; - uint32_t rxavail; - int qerror; -}; - - -typedef struct debug_context DC; - - -int dap_get_info(DC* dc, unsigned di, void *out, unsigned minlen, unsigned maxlen) { - uint8_t buf[256 + 2]; - buf[0] = DAP_Info; - buf[1] = di; - if (usb_write(dc->usb, buf, 2) != 2) { - return DC_ERR_IO; - } - int sz = usb_read(dc->usb, buf, 256 + 2); - if ((sz < 2) || (buf[0] != DAP_Info)) { - return DC_ERR_PROTOCOL; - } - if ((buf[1] < minlen) || (buf[1] > maxlen)) { - return DC_ERR_PROTOCOL; - } - memcpy(out, buf + 2, buf[1]); - return buf[1]; -} - -int dap_cmd(DC* dc, const void* tx, unsigned txlen, void* rx, unsigned rxlen) { - uint8_t cmd = ((const uint8_t*) tx)[0]; - dump("TX>", tx, txlen); - if (usb_write(dc->usb, tx, txlen) != txlen) { - ERROR("dap_cmd(0x%02x): usb write error\n", cmd); - return DC_ERR_IO; - } - int sz = usb_read(dc->usb, rx, rxlen); - if (sz < 1) { - ERROR("dap_cmd(0x%02x): usb read error\n", cmd); - return DC_ERR_IO; - } - dump("RX>", rx, rxlen); - if (((uint8_t*) rx)[0] != cmd) { - ERROR("dap_cmd(0x%02x): unsupported (0x%02x)\n", - cmd, ((uint8_t*) rx)[0]); - return DC_ERR_UNSUPPORTED; - } - return sz; -} - -int dap_cmd_std(DC* dc, const char* name, uint8_t* io, - unsigned txlen, unsigned rxlen) { - int r = dap_cmd(dc, io, txlen, io, rxlen); - if (r < 0) { - return r; - } - if (io[1] != 0) { - ERROR("%s status 0x%02x\n", name, io[1]); - return DC_ERR_REMOTE; - } - return 0; -} - -int dap_connect(DC* dc) { - uint8_t io[2] = { DAP_Connect, PORT_SWD }; - return dap_cmd_std(dc, "dap_connect()", io, 2, 2); -} - -int dap_swd_configure(DC* dc, unsigned cfg) { - uint8_t io[2] = { DAP_SWD_Configure, cfg }; - return dap_cmd_std(dc, "dap_swd_configure()", io, 2, 2); -} - -int dc_set_clock(DC* dc, uint32_t hz) { - uint8_t io[5] = { DAP_SWJ_Clock, - hz, hz >> 8, hz >> 16, hz >> 24 }; - return dap_cmd_std(dc, "dap_swj_clock()", io, 5, 2); -} - -static int dap_xfer_config(DC* dc, unsigned idle, unsigned wait, unsigned match) { - // clamp to allowed max values - if (idle > 255) idle = 255; - if (wait > 65535) wait = 65535; - if (match > 65535) match = 65535; - - // do nothing if unchanged from last set values - if ((dc->cfg_idle == idle) && - (dc->cfg_wait == wait) && - (dc->cfg_match == match)) { - return 0; - } - - // cache new values - dc->cfg_idle = idle; - dc->cfg_wait = wait; - dc->cfg_match = match; - - // inform the probe - uint8_t io[6] = { DAP_TransferConfigure, idle, wait, wait >> 8, match, match >> 8}; - return dap_cmd_std(dc, "dap_transfer_configure()", io, 6, 2); -} - -static void dc_q_clear(DC* dc) { - dc->txnext = dc->txbuf + 3; - dc->rxnext = dc->rxptr; - dc->txavail = dc->max_packet_size - 3; - dc->rxavail = dc->max_packet_size - 3; - dc->qerror = 0; - // TODO: less conservative mode: don't always invalidate - dc->dp_select_cache = INVALID; - dc->cfg_mask = INVALID; - dc->txbuf[0] = DAP_Transfer; - dc->txbuf[1] = 0; // Index 0 for SWD - dc->txbuf[2] = 0; // Count 0 initially -} - -void dc_q_init(DC* dc) { - // TODO: handle error cleanup, re-attach, etc - dc_q_clear(dc); -} - -// unpack the status bits into a useful status code -static int dc_decode_status(unsigned n) { - unsigned ack = n & RSP_ACK_MASK; - if (n & RSP_ProtocolError) { - ERROR("DAP SWD Parity Error\n"); - return DC_ERR_SWD_PARITY; - } - switch (ack) { - case RSP_ACK_OK: - break; - case RSP_ACK_WAIT: - ERROR("DAP SWD WAIT (Timeout)\n"); - return DC_ERR_TIMEOUT; - case RSP_ACK_FAULT: - ERROR("DAP SWD FAULT\n"); - return DC_ERR_SWD_FAULT; - case RSP_ACK_MASK: // all bits set - ERROR("DAP SWD SILENT\n"); - return DC_ERR_SWD_SILENT; - default: - ERROR("DAP SWD BOGUS\n"); - return DC_ERR_SWD_BOGUS; - } - if (n & RSP_ValueMismatch) { - ERROR("DAP Value Mismatch\n"); - return DC_ERR_MATCH; - } - return DC_OK; -} - -int dc_q_exec(DC* dc) { - // if we're already in error, don't generate more usb traffic - if (dc->qerror) { - int r = dc->qerror; - dc_q_clear(dc); - return r; - } - // if we have no work to do, succeed - if (dc->txbuf[2] == 0) { - return 0; - } - int sz = dc->txnext - dc->txbuf; - dump("TX>", dc->txbuf, sz); - if (usb_write(dc->usb, dc->txbuf, sz) != sz) { - ERROR("dc_q_exec() usb write error\n"); - return DC_ERR_IO; - } - sz = 3 + (dc->rxnext - dc->rxptr) * 4; - uint8_t rxbuf[1024]; - memset(rxbuf, 0xEE, 1024); // DEBUG - int n = usb_read(dc->usb, rxbuf, sz); - if (n < 0) { - ERROR("dc_q_exec() usb read error\n"); - return DC_ERR_IO; - } - dump("RX>", rxbuf, sz); - if ((n < 3) || (rxbuf[0] != DAP_Transfer)) { - ERROR("dc_q_exec() bad response\n"); - return DC_ERR_PROTOCOL; - } - int r = dc_decode_status(rxbuf[2]); - if (r == DC_OK) { - // how many response words available? - n = (n - 3) / 4; - uint8_t* rxptr = rxbuf + 3; - for (unsigned i = 0; i < n; i++) { - memcpy(dc->rxptr[i], rxptr, 4); - rxptr += 4; - } - } - - dc_q_clear(dc); - return r; -} - -// internal use only -- queue raw dp reads and writes -// these do not check req for correctness -static void dc_q_raw_rd(DC* dc, unsigned req, uint32_t* val) { - if ((dc->txavail < 1) || (dc->rxavail < 4)) { - // exec q to make space for more work, - // but if there's an error, latch it - if ((dc->qerror = dc_q_exec(dc)) != DC_OK) { - return; - } - } - dc->txnext[0] = req; - dc->rxnext[0] = val; - dc->txnext += 1; - dc->rxnext += 1; - dc->txbuf[2] += 1; - dc->txavail -= 1; - dc->rxavail -= 4; -} - -static void dc_q_raw_wr(DC* dc, unsigned req, uint32_t val) { - if (dc->txavail < 5) { - // exec q to make space for more work, - // but if there's an error, latch it - if ((dc->qerror = dc_q_exec(dc)) != DC_OK) { - return; - } - } - dc->txnext[0] = req; - memcpy(dc->txnext + 1, &val, 4); - dc->txnext += 5; - dc->txavail -= 5; - dc->txbuf[2] += 1; -} - -// adjust DP.SELECT for desired DP access, if necessary -void dc_q_dp_sel(DC* dc, uint32_t dpaddr) { - // DP address is BANK:4 REG:4 - if (dpaddr & 0xFFFFFF03U) { - ERROR("invalid DP addr 0x%08x\n", dpaddr); - dc->qerror = DC_ERR_FAILED; - return; - } - // only register 4 cares about the value of DP.SELECT.DPBANK - // so do nothing unless we're setting a register 4 variant - if ((dpaddr & 0xF) != 0x4) { - return; - } - uint32_t mask = DP_SELECT_DPBANK(0xFU); - uint32_t addr = DP_SELECT_DPBANK((dpaddr >> 4)); - uint32_t select = (dc->dp_select & mask) | addr; - if (select != dc->dp_select_cache) { - dc->dp_select_cache = select; - dc_q_raw_wr(dc, XFER_DP | XFER_WR | DP_SELECT, select); - } -} - -// adjust DP.SELECT for desired AP access, if necessary -void dc_q_ap_sel(DC* dc, uint32_t apaddr) { - // AP address is AP:8 BANK:4 REG:4 - if (apaddr & 0xFFFF0003U) { - ERROR("invalid DP addr 0x%08x\n", apaddr); - dc->qerror = DC_ERR_FAILED; - return; - } - // we always return DPBANK to 0 when adjusting AP & APBANK - // since it preceeds an AP write which will need DPBANK at 0 - uint32_t select = - DP_SELECT_AP((apaddr & 0xFF00U) << 16) | - DP_SELECT_APBANK(apaddr >> 4); - if (select != dc->dp_select_cache) { - dc->dp_select_cache = select; - dc_q_raw_wr(dc, XFER_DP | XFER_WR | DP_SELECT, select); - } -} - -// DP and AP reads and writes -// DP.SELECT will be adjusted as necessary to ensure proper addressing -void dc_q_dp_rd(DC* dc, unsigned dpaddr, uint32_t* val) { - if (dc->qerror) return; - dc_q_dp_sel(dc, dpaddr); - dc_q_raw_rd(dc, XFER_DP | XFER_RD | (dpaddr & 0x0C), val); -} - -void dc_q_dp_wr(DC* dc, unsigned dpaddr, uint32_t val) { - if (dc->qerror) return; - dc_q_dp_sel(dc, dpaddr); - dc_q_raw_wr(dc, XFER_DP | XFER_WR | (dpaddr & 0x0C), val); -} - -void dc_q_ap_rd(DC* dc, unsigned apaddr, uint32_t* val) { - if (dc->qerror) return; - dc_q_ap_sel(dc, apaddr); - dc_q_raw_rd(dc, XFER_AP | XFER_RD | (apaddr & 0x0C), val); -} - -void dc_q_ap_wr(DC* dc, unsigned apaddr, uint32_t val) { - if (dc->qerror) return; - dc_q_ap_sel(dc, apaddr); - dc_q_raw_wr(dc, XFER_AP | XFER_WR | (apaddr & 0x0C), val); -} - -void dc_q_set_mask(DC* dc, uint32_t mask) { - if (dc->qerror) return; - if (dc->cfg_mask == mask) return; - dc->cfg_mask = mask; - dc_q_raw_wr(dc, XFER_WR | XFER_MatchMask, mask); -} - -void dc_set_match_retry(dctx_t* dc, unsigned num) { - if (dc->qerror) return; - dap_xfer_config(dc, dc->cfg_idle, dc->cfg_wait, num); -} - -void dc_q_ap_match(DC* dc, unsigned apaddr, uint32_t val) { - if (dc->qerror) return; - dc_q_ap_sel(dc, apaddr); - dc_q_raw_wr(dc, XFER_AP | XFER_RD | XFER_ValueMatch | (apaddr & 0x0C), val); -} - -void dc_q_dp_match(DC* dc, unsigned apaddr, uint32_t val) { - if (dc->qerror) return; - dc_q_ap_sel(dc, apaddr); - dc_q_raw_wr(dc, XFER_DP | XFER_RD | XFER_ValueMatch | (apaddr & 0x0C), val); -} - -// convenience wrappers for single reads and writes -int dc_dp_rd(DC* dc, unsigned dpaddr, uint32_t* val) { - dc_q_init(dc); - dc_q_dp_rd(dc, dpaddr, val); - return dc_q_exec(dc); -} -int dc_dp_wr(DC* dc, unsigned dpaddr, uint32_t val) { - dc_q_init(dc); - dc_q_dp_wr(dc, dpaddr, val); - return dc_q_exec(dc); -} -int dc_ap_rd(DC* dc, unsigned apaddr, uint32_t* val) { - dc_q_init(dc); - dc_q_ap_rd(dc, apaddr, val); - return dc_q_exec(dc); -} -int dc_ap_wr(DC* dc, unsigned apaddr, uint32_t val) { - dc_q_init(dc); - dc_q_ap_wr(dc, apaddr, val); - return dc_q_exec(dc); -} - -// SWD Attach Sequence: -// 1. Send >50 1s and then the JTAG to SWD escape code -// (in case this is a JTAG-SWD DAP in JTAG mode) -// 2. Send >8 1s and then the Selection Alert Sequence -// and then the SWD Activation Code -// (in case this is a SWD v2 DAP in Dormant State) -// 3. Send >50 1s and then 4 0s -- the Line Reset Sequence -// 4. If multidrop, issue a write to DP.TARGETSEL, but -// ignore the ACK response -// 5. Issue a read from DP.IDR - -static uint8_t attach_cmd[54] = { - DAP_SWD_Sequence, 5, - - // [--- 64 1s ----------------------------------] - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - // [JTAG2SWD] [- 16 1s ] [--------------------- - 0x00, 0x9E, 0xE7, 0xFF, 0xFF, 0x92, 0xF3, 0x09, 0x62, - // ----- Selection Alert Sequence --------------- - 0x00, 0x95, 0x2D, 0x85, 0x86, 0xE9, 0xAF, 0xDD, 0xE3, - // ---------------------] [Act Code] [--------- - 0x00, 0xA2, 0x0E, 0xBC, 0x19, 0xA0, 0xF1, 0xFF, 0xFF, - // ----- Line Reset Sequence -------] - 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - - // WR DP TARGETSEL - 0x08, 0x99, - // 5 bits idle - 0x85, - // WR VALUE:32, PARTY:1, ZEROs:7 - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -int dc_attach(DC* dc, unsigned flags, uint32_t tgt, uint32_t* idcode) { - uint8_t rsp[3]; - - if (flags & DC_MULTIDROP) { - // Copy and patch the attach sequence to include - // the DP.TARGETSEL write and insert the target - // id and parity - uint8_t cmd[54]; - memcpy(cmd, attach_cmd, 54); - cmd[1] = 8; - memcpy(cmd + 49, &tgt, sizeof(tgt)); - cmd[53] = __builtin_parity(tgt); - dap_cmd(dc, cmd, 54, rsp, 3); - } else { - // use the common part of the attach sequence, as-is - dap_cmd(dc, attach_cmd, 45, rsp, 2); - } - - // Issue a bare DP.IDR read, as required after a line reset - // or line reset + target select - dc_q_init(dc); - dc_q_raw_rd(dc, XFER_DP | XFER_RD | XFER_00, idcode); - int r = dc_q_exec(dc); - return r; -} - -static usb_handle* usb_connect(void) { - usb_handle *usb; - - usb = usb_open(0x1fc9, 0x0143, 0); - if (usb == 0) { - usb = usb_open(0x2e8a, 0x000c, 42); - if (usb == 0) { - ERROR("cannot find device\n"); - return NULL; - } - } - return usb; -} - -// setup a newly connected DAP device -static int dap_configure(DC* dc) { - uint8_t buf[256 + 2]; - uint32_t n32; - uint16_t n16; - uint8_t n8; - - // invalidate cached state - dc->cfg_idle = INVALID; - dc->cfg_wait = INVALID; - dc->cfg_match = INVALID; - dc->cfg_mask = INVALID; - - // setup default packet limits - dc->max_packet_count = 1; - dc->max_packet_size = 64; - - // flush queue - dc_q_clear(dc); - - buf[0] = DAP_Info; - for (unsigned n = 0; n < 10; n++) { - int sz = dap_get_info(dc, n, buf, 0, 255); - if (sz > 0) { - buf[sz] = 0; - printf("0x%02x: '%s'\n", n, (char*) buf); - } - } - - buf[0] = 0; buf[1] = 0; - if (dap_get_info(dc, DI_Capabilities, buf, 1, 2) > 0) { - printf("Capabilities: 0x%02x 0x%02x\n", buf[0], buf[1]); - printf("Capabilities:"); - if (buf[0] & I0_SWD) printf(" SWD"); - if (buf[0] & I0_JTAG) printf(" JTAG"); - if (buf[0] & I0_SWO_UART) printf(" SWO(UART)"); - if (buf[0] & I0_SWO_Manchester) printf(" SWO(Manchester)"); - if (buf[0] & I0_Atomic_Commands) printf(" ATOMIC"); - if (buf[0] & I0_Test_Domain_Timer) printf(" TIMER"); - if (buf[0] & I0_SWO_Streaming_Trace) printf(" SWO(Streaming)"); - if (buf[0] & I0_UART_Comm_Port) printf(" UART"); - if (buf[1] & I1_USB_COM_Port) printf(" USBCOM"); - printf("\n"); - } - if (dap_get_info(dc, DI_UART_RX_Buffer_Size, &n32, 4, 4) == 4) { - printf("UART RX Buffer Size: %u\n", n32); - } - if (dap_get_info(dc, DI_UART_TX_Buffer_Size, &n32, 4, 4) == 4) { - printf("UART TX Buffer Size: %u\n", n32); - } - if (dap_get_info(dc, DI_SWO_Trace_Buffer_Size, &n32, 4, 4) == 4) { - printf("SWO Trace Buffer Size: %u\n", n32); - } - if (dap_get_info(dc, DI_Max_Packet_Count, &n8, 1, 1) == 1) { - printf("Max Packet Count: %u\n", n8); - dc->max_packet_count = n8; - } - if (dap_get_info(dc, DI_Max_Packet_Size, &n16, 2, 2) == 2) { - printf("Max Packet Size: %u\n", n16); - dc->max_packet_size = n16; - } - if ((dc->max_packet_count < 1) || (dc->max_packet_size < 64)) { - ERROR("dc_init() impossible packet configuration\n"); - return DC_ERR_PROTOCOL; - } - - // invalidate register cache - dc->dp_select_cache = INVALID; - - // clip to our buffer size - if (dc->max_packet_size > 1024) { - dc->max_packet_size = 1024; - } - - dap_connect(dc); - dap_swd_configure(dc, CFG_Turnaround_1); - dap_xfer_config(dc, 8, 64, 64); - return DC_OK; -} - -int dc_create(DC** out) { - DC* dc; - - if ((dc = calloc(1, sizeof(DC))) == NULL) { - return DC_ERR_FAILED; - } - - if ((dc->usb = usb_connect()) == NULL) { - free(dc); - return DC_ERR_OFFLINE; - } - - int r = dap_configure(dc); - if (r < 0) { - free(dc); - } else { - *out = dc; - } - return r; -} diff --git a/src/transport.h b/src/transport.h @@ -5,6 +5,22 @@ #include <stdint.h> +#define DC_OK 0 +#define DC_ERR_FAILED -1 // generic internal failure +#define DC_ERR_BAD_PARAMS -2 // Invalid parameters +#define DC_ERR_IO -3 // IO error (USB read fail, etc) +#define DC_ERR_OFFLINE -4 // IO error (USB device offline) +#define DC_ERR_PROTOCOL -5 // Protocol error (bug in sw or fw) +#define DC_ERR_TIMEOUT -6 // WAIT response from DP (retries insufficient) +#define DC_ERR_SWD_FAULT -7 // FAULT response from DP +#define DC_ERR_SWD_PARITY -8 // Corrupt Data +#define DC_ERR_SWD_SILENT -9 // No Status Indicated +#define DC_ERR_SWD_BOGUS -10 // Nonsensical Status Indicated +#define DC_ERR_MATCH -11 // read match failure +#define DC_ERR_UNSUPPORTED -12 // unsupported operation +#define DC_ERR_REMOTE -13 // failure from debug probe +#define DC_ERR_DETACHED -14 // transport not connected to target + typedef struct debug_context dctx_t; int dc_set_clock(dctx_t* dc, uint32_t hz); @@ -29,7 +45,6 @@ void dc_q_set_mask(dctx_t* dc, uint32_t mask); void dc_q_ap_match(dctx_t* dc, unsigned apaddr, uint32_t val); void dc_q_dp_match(dctx_t* dc, unsigned apaddr, uint32_t val); - // prepare for a set of transactions void dc_q_init(dctx_t* dc); @@ -47,21 +62,17 @@ int dc_create(dctx_t** dc); // attempt to attach to the debug target int dc_attach(dctx_t* dc, unsigned flags, uint32_t tgt, uint32_t* idcode); +#define DC_MULTIDROP 1 + + +void dc_q_mem_rd32(dctx_t* dc, uint32_t addr, uint32_t* val); +void dc_q_mem_wr32(dctx_t* dc, uint32_t addr, uint32_t val); + +int dc_mem_rd32(dctx_t* dc, uint32_t addr, uint32_t* val); +int dc_mem_wr32(dctx_t* dc, uint32_t addr, uint32_t val); + +int dc_mem_rd_words(dctx_t* dc, uint32_t addr, uint32_t num, uint32_t* ptr); +int dc_mem_wr_words(dctx_t* dc, uint32_t addr, uint32_t num, const uint32_t* ptr); + -#define DC_OK 0 -#define DC_ERR_FAILED -1 // generic internal failure -#define DC_ERR_BAD_PARAMS -2 // Invalid parameters -#define DC_ERR_IO -3 // IO error (USB read fail, etc) -#define DC_ERR_OFFLINE -4 // IO error (USB device offline) -#define DC_ERR_PROTOCOL -5 // Protocol error (bug in sw or fw) -#define DC_ERR_TIMEOUT -6 // WAIT response from DP (retries insufficient) -#define DC_ERR_SWD_FAULT -7 // FAULT response from DP -#define DC_ERR_SWD_PARITY -8 // Corrupt Data -#define DC_ERR_SWD_SILENT -9 // No Status Indicated -#define DC_ERR_SWD_BOGUS -10 // Nonsensical Status Indicated -#define DC_ERR_MATCH -11 // read match failure -#define DC_ERR_UNSUPPORTED -12 // unsupported operation -#define DC_ERR_REMOTE -13 // failure from debug probe -#define DC_ERR_DETACHED -14 // transport not connected to target -#define DC_MULTIDROP 1 diff --git a/src/xdebug.c b/src/xdebug.c @@ -18,25 +18,11 @@ int main(int argc, char **argv) { return -1; } - dc_attach(dc, 0, 0, &n); - printf("IDCODE %08x\n", n); - - // If this is a RP2040, we need to connect in multidrop - // mode before doing anything else. - if (n == 0x0bc12477) { - dc_dp_rd(dc, DP_TARGETID, &n); - if (n == 0x01002927) { // RP2040 - dc_attach(dc, DC_MULTIDROP, 0x01002927, &n); - } - } + dc_set_clock(dc, 1000000); - // power up system & debug - dc_dp_rd(dc, DP_CS, &n); - printf("CTRL/STAT %08x\n", n); - dc_dp_wr(dc, DP_CS, DP_CS_CDBGPWRUPREQ | DP_CS_CSYSPWRUPREQ); - dc_dp_rd(dc, DP_CS, &n); - printf("CTRL/STAT %08x\n", n); + dc_attach(dc, 0, 0, &n); +#if 1 // dump some info dc_dp_rd(dc, DP_DPIDR, &n); printf("DP.DPIDR %08x\n", n); @@ -54,6 +40,16 @@ int main(int argc, char **argv) { printf("MAP.CFG1 %08x\n", n); dc_ap_rd(dc, MAP_BASE, &n); printf("MAP.BASE %08x\n", n); +#endif + + dc_mem_rd32(dc, 0, &n); + printf("%08x: %08x\n", 0, n); + + unsigned addr = 0x00000000; + for (unsigned a = addr; a < addr + 32; a += 4) { + dc_mem_rd32(dc, a, &n); + printf("%08x: %08x\n", a, n); + } return 0; }