xdebug

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

commit 14ca14d4020e6d8348049ea643af4e611495c073
parent 23d950c8e902562a5194fc9b7e2fa44b3bf4e1eb
Author: Brian Swetland <swetland@frotz.net>
Date:   Mon, 27 Feb 2023 02:19:43 -0800

build up more robust transport

- queue up multiple reads and writes into DAP_Transfer requests
- track the value of DP.SELECT and automatically update it as needed
- adjust definitions in headers
- remove old single read/write DAP_Transfer glue

Diffstat:
Msrc/arm-debug.h | 16++++++++++------
Msrc/cmsis-dap-protocol.h | 36+++++++++++++++++++-----------------
Msrc/xdebug.c | 260++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 247 insertions(+), 65 deletions(-)

diff --git a/src/arm-debug.h b/src/arm-debug.h @@ -1,6 +1,10 @@ // Copyright 2023, Brian Swetland <swetland@frotz.net> // Licensed under the Apache License, Version 2.0. +// see: ARM IHI 0031G +// Arm Debug Interface Architecture Specification +// ADIv5.0 to ADIv5.2 + #pragma once // high nybble is banksel @@ -9,7 +13,7 @@ #define DP_CS 0x04 // RW CTRL/STAT #define DP_DLCR 0x14 // RW Data Link Control #define DP_TARGETID 0x24 // RO v2 -#define DP_DLPIDR 0x34 // RO v2 Data Link ID +#define DP_DLPIDR 0x34 // RO v2 Data Link Protocol ID #define DP_EVENTSTAT 0x44 // RO v2 #define DP_SELECT 0x08 // WO #define DP_RESEND 0x08 // RO v2 Return last AP or RDBUFF read data @@ -33,8 +37,8 @@ #define DP_CS_STICKYERR 0x00000020U // RO/WI error occurred in AP txn #define DP_CS_READOK 0x00000040U // RO/WI last AP or RDBUFF RD was OK #define DP_CS_WDATAERR 0x00000080U // RO/WI -#define DP_CS_MASKLANE(n) (((n) & 0xF) << 8) // RW -#define DP_CS_TRNCNT(n) (((n) & 0xFFF) << 12) +#define DP_CS_MASKLANE(n) (((n) & 0xFU) << 8) // RW +#define DP_CS_TRNCNT(n) (((n) & 0xFFFU) << 12) #define DP_CS_CDBGRSTREQ 0x04000000U // RW or RAZ/WI #define DP_CS_CDBGRSTACK 0x08000000U // RO #define DP_CS_CDBGPWRUPREQ 0x10000000U // RW @@ -51,7 +55,7 @@ #define DP_EVENTSTAT_EA 0x000000001U -#define DP_SELECT_DPBANK(n) ((n) & 0xF) -#define DP_SELECT_APBANK(n) (((n) & 0xF) << 4) -#define DP_SELECT_AP(n) (((n) & 0xFF) << 24) +#define DP_SELECT_DPBANK(n) ((n) & 0xFU) +#define DP_SELECT_APBANK(n) (((n) & 0xFU) << 4) +#define DP_SELECT_AP(n) (((n) & 0xFFU) << 24) diff --git a/src/cmsis-dap-protocol.h b/src/cmsis-dap-protocol.h @@ -1,6 +1,8 @@ // Copyright 2023, Brian Swetland <swetland@frotz.net> // Licensed under the Apache License, Version 2.0. +// see: https://arm-software.github.io/CMSIS_5/DAP/html/index.html + #pragma once #define DAP_Info 0x00 // BYTE(DI_*) @@ -94,25 +96,25 @@ // wait retry - max number of retries after WAIT response // match retry - max number of retries on reads w/ value match -#define DAP_Transfer 0x05 +#define DAP_Transfer 0x05 // BYTE(Index) BYTE(Count) followed by Count instances of // BYTE(XferReq) WORD(Value) WORD(MatchMask) WORD(ValueMatch) -#define XFER_DebugPort 0x00 -#define XFER_AccessPort 0x01 -#define XFER_Write 0x00 -#define XFER_Read 0x02 -#define XFER_Addr_00 0x00 -#define XFER_Addr_04 0x04 -#define XFER_Addr_08 0x08 -#define XFER_Addr_0C 0x0C -#define XFER_ValueMatch 0x10 -#define XFER_MatchMask 0x20 -#define XFER_TimeStamp 0x80 - -#define RSP_ACK_MASK 0x07 -#define RSP_ACK_OK 0x01 -#define RSP_ACK_WAIT 0x02 -#define RSP_ACK_FAULT 0x04 +#define XFER_DP 0x00 +#define XFER_AP 0x01 +#define XFER_WR 0x00 +#define XFER_RD 0x02 +#define XFER_00 0x00 +#define XFER_04 0x04 +#define XFER_08 0x08 +#define XFER_0C 0x0C +#define XFER_ValueMatch 0x10 +#define XFER_MatchMask 0x20 +#define XFER_TimeStamp 0x80 + +#define RSP_ACK_MASK 0x07 +#define RSP_ACK_OK 0x01 +#define RSP_ACK_WAIT 0x02 +#define RSP_ACK_FAULT 0x04 #define RSP_ProtocolError 0x08 #define RSP_ValueMismatch 0x10 // Reponse BYTE(Count) BYTE(Response) WORD(TimeStamp)? WORD(Data)* diff --git a/src/xdebug.c b/src/xdebug.c @@ -7,17 +7,30 @@ #include <string.h> #include "usb.h" +#include "arm-debug.h" #include "cmsis-dap-protocol.h" struct debug_context { usb_handle* usb; unsigned connected; - // last dp select written - uint32_t dp_select; - + // dap protocol info uint32_t max_packet_count; uint32_t max_packet_size; + + // 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; + uint32_t qerror; }; @@ -71,7 +84,7 @@ int dap_cmd(DC* dc, const void* tx, unsigned txlen, void* rx, unsigned rxlen) { cmd, ((uint8_t*) rx)[0]); return -1; } - fprintf(stderr, "dap_cmd(0x%02x): sz %u\n", cmd, sz); + // fprintf(stderr, "dap_cmd(0x%02x): sz %u\n", cmd, sz); return sz; } @@ -102,59 +115,208 @@ int dap_transfer_configure(DC* dc, unsigned idle, unsigned wait, unsigned match) return dap_cmd_std(dc, "dap_transfer_configure()", io, 6, 2); } -int dap_xfer_wr1(DC* dc, unsigned cfg, uint32_t val) { - uint8_t io[8] = { - DAP_Transfer, 0, 1, cfg & 0x0D, - val, val >> 8, val >> 16, val >> 24 }; - if (dap_cmd(dc, io, 8, io, 3) < 0) { +int swd_init(DC* dc) { + uint8_t io[23] = { DAP_SWD_Sequence, 3, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 64 1s + 0x10, 0x9E, 0xE7, // JTAG to SWD magic sequence + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, // 60 1s, 4 0s + }; + if (dap_cmd(dc, io, 23, io, 2) < 0) { return -1; } if (io[1] != 0) { - fprintf(stderr, "dap_xfer_wr1() invalid count %u\n", io[1]); + fprintf(stderr, "swd_init() failure 0x%02x\n", io[1]); return -1; } - if (io[2] == RSP_ACK_OK) { - return 0; - } - fprintf(stderr, "dap_xfer_wr1() status 0x%02x\n", io[2]); - return -1; + return 0; } -int dap_xfer_rd1(DC* dc, unsigned cfg, uint32_t* val) { - uint8_t io[8] = { - DAP_Transfer, 0, 1, XFER_Read | (cfg & 0x0D) }; - if (dap_cmd(dc, io, 4, io, 7) < 0) { +static void dc_q_clear(DC* dc) { + // fprintf(stderr, "Q CLEAR\n"); + 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->dp_select_cache = 0xFFFFFFFFU; + dc->txbuf[0] = DAP_Transfer; + dc->txbuf[1] = 0; // Index 0 for SWD + dc->txbuf[2] = 0; // Count 0 initially +} + +int dc_q_exec(DC* dc) { + // fprintf(stderr, "Q EXEC\n"); + // if we're already in error, don't generate more usb traffic + if (dc->qerror) { + dc_q_clear(dc); return -1; } - if (io[1] != 1) { - fprintf(stderr, "dap_xfer_rd1() invalid count %u\n", io[1]); + // 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) { + fprintf(stderr, "dc_q_exec() usb write error\n"); return -1; } - if (io[2] == RSP_ACK_OK) { - memcpy(val, io + 3, 4); - return 0; + 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) { + fprintf(stderr, "dc_q_exec() usb read error\n"); + return -1; } - fprintf(stderr, "dap_xfer_rd1() status 0x%02x\n", io[2]); - return -1; -} - -int swd_init(DC* dc) { - uint8_t io[23] = { - DAP_SWD_Sequence, 3, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 64 1s - 0x10, 0x9E, 0xE7, // JTAG to SWD magic sequence - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, // 60 1s, 4 0s - }; - if (dap_cmd(dc, io, 23, io, 2) < 0) { + dump("RX>", rxbuf, sz); + if ((n < 3) || (rxbuf[0] != DAP_Transfer)) { + fprintf(stderr, "dc_q_exec() bad response\n"); return -1; } - if (io[1] != 0) { - fprintf(stderr, "swd_init() failure 0x%02x\n", io[1]); + if (rxbuf[2] != RSP_ACK_OK) { + fprintf(stderr, "dc_q_exec() error response 0x%02x\n", rxbuf[2]); return -1; } + + // how many response words available? + n = (n - 3) / 4; + uint8_t* rxptr = rxbuf + 3; + for (unsigned i = 0; i < n; i++) { + // fprintf(stderr, "RES %02x%02x%02x%02x @ %p\n", + // rxptr[3], rxptr[2], rxptr[1], rxptr[0], + // dc->rxptr[i]); + memcpy(dc->rxptr[i], rxptr, 4); + rxptr += 4; + } + dc_q_clear(dc); return 0; } +// 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) { + // fprintf(stderr, "Q RAW RD %08x @ %p\n", req, val); + if ((dc->txavail < 1) || (dc->rxavail < 4)) { + if (dc_q_exec(dc) < 0) { + dc->qerror = 1; + 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) { + // fprintf(stderr, "Q RAW WR %08x %08x\n", req, val); + if (dc->txavail < 5) { + if (dc_q_exec(dc) < 0) { + dc->qerror = 1; + 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) { + // fprintf(stderr, "DP SEL %08x (select is %08x)\n", dpaddr, dc->dp_select_cache); + // DP address is BANK:4 REG:4 + if (dpaddr & 0xFFFFFF03U) { + fprintf(stderr, "invalid DP addr 0x%08x\n", dpaddr); + dc->qerror = 1; + 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) { + fprintf(stderr, "invalid DP addr 0x%08x\n", apaddr); + dc->qerror = 1; + 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); +} + + +// convenience wrappers for single reads and writes +int dc_dp_rd(DC* dc, unsigned dpaddr, uint32_t* val) { + 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_dp_wr(dc, dpaddr, val); + return dc_q_exec(dc); +} +int dc_ap_rd(DC* dc, unsigned apaddr, uint32_t* val) { + 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_ap_wr(dc, apaddr, val); + return dc_q_exec(dc); +} + + + int dc_init(DC* dc, usb_handle* usb) { uint8_t buf[256 + 2]; uint32_t n32; @@ -165,6 +327,7 @@ int dc_init(DC* dc, usb_handle* usb) { dc->usb = usb; dc->max_packet_count = 1; dc->max_packet_size = 64; + dc_q_clear(dc); buf[0] = DAP_Info; for (unsigned n = 0; n < 10; n++) { @@ -207,12 +370,18 @@ int dc_init(DC* dc, usb_handle* usb) { printf("Max Packet Size: %u\n", n16); dc->max_packet_size = n16; } - if ((dc->max_packet_count < 1) || (dc->max_packet_size < 64)) { fprintf(stderr, "dc_init() impossible packet configuration\n"); return -1; } + // clip to our buffer size + if (dc->max_packet_size > 1024) { + dc->max_packet_size = 1024; + } + + // update based on queried state + dc_q_clear(dc); return 0; } @@ -236,12 +405,19 @@ int main(int argc, char **argv) { dap_connect(dc); dap_swd_configure(dc, CFG_Turnaround_1); dap_transfer_configure(dc, 8, 64, 0); - //dap_xfer_wr1(dc, XFER_DebugPort | XFER_Addr_04, 0x11553311); swd_init(dc); n = 0; - dap_xfer_rd1(dc, XFER_DebugPort, &n); + + dc_dp_rd(dc, DP_DPIDR, &n); printf("IDCODE %08x\n", n); - + + 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); return 0; }