xdebug

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

commit e2956ea711ef5b81f12ceaa51e945669bda97831
parent e6233ef4f875bd1420469a0fdfab36f3ba12336b
Author: Brian Swetland <swetland@frotz.net>
Date:   Wed,  1 Mar 2023 14:26:45 -0800

transport: read/match support and meaningful error codes

Diffstat:
Msrc/transport.c | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/transport.h | 27+++++++++++++++++++++++++++
2 files changed, 184 insertions(+), 69 deletions(-)

diff --git a/src/transport.c b/src/transport.c @@ -40,6 +40,8 @@ static void dump(const char* str, const void* ptr, unsigned len) { #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; @@ -48,6 +50,12 @@ struct debug_context { 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 @@ -60,7 +68,7 @@ struct debug_context { uint32_t** rxnext; uint32_t txavail; uint32_t rxavail; - uint32_t qerror; + int qerror; }; @@ -72,15 +80,14 @@ int dap_get_info(DC* dc, unsigned di, void *out, unsigned minlen, unsigned maxle buf[0] = DAP_Info; buf[1] = di; if (usb_write(dc->usb, buf, 2) != 2) { - return -1; + return DC_ERR_IO; } int sz = usb_read(dc->usb, buf, 256 + 2); if ((sz < 2) || (buf[0] != DAP_Info)) { - return -1; + return DC_ERR_PROTOCOL; } - //printf("0x%02x > 0x%02x 0x%02x\n", di, buf[0], buf[1]); if ((buf[1] < minlen) || (buf[1] > maxlen)) { - return -1; + return DC_ERR_PROTOCOL; } memcpy(out, buf + 2, buf[1]); return buf[1]; @@ -91,30 +98,31 @@ int dap_cmd(DC* dc, const void* tx, unsigned txlen, void* rx, unsigned rxlen) { dump("TX>", tx, txlen); if (usb_write(dc->usb, tx, txlen) != txlen) { ERROR("dap_cmd(0x%02x): usb write error\n", cmd); - return -1; + 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 -1; + 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 -1; + return DC_ERR_UNSUPPORTED; } return sz; } int dap_cmd_std(DC* dc, const char* name, uint8_t* io, unsigned txlen, unsigned rxlen) { - if (dap_cmd(dc, io, txlen, io, rxlen) < 0) { - return -1; + 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 -1; + return DC_ERR_REMOTE; } return 0; } @@ -129,21 +137,38 @@ int dap_swd_configure(DC* dc, unsigned cfg) { return dap_cmd_std(dc, "dap_swd_configure()", io, 2, 2); } -int dap_transfer_configure(DC* dc, unsigned idle, unsigned wait, unsigned match) { +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); + return dap_cmd_std(dc, "dap_transfer_configure()", io, 6, 2); } - 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; // TODO: less conservative mode: don't always invalidate - dc->dp_select_cache = 0xFFFFFFFFU; + 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 @@ -154,12 +179,42 @@ void dc_q_init(DC* dc) { 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) { - // fprintf(stderr, "Q EXEC\n"); // if we're already in error, don't generate more usb traffic if (dc->qerror) { + int r = dc->qerror; dc_q_clear(dc); - return -1; + return r; } // if we have no work to do, succeed if (dc->txbuf[2] == 0) { @@ -169,7 +224,7 @@ int dc_q_exec(DC* dc) { dump("TX>", dc->txbuf, sz); if (usb_write(dc->usb, dc->txbuf, sz) != sz) { ERROR("dc_q_exec() usb write error\n"); - return -1; + return DC_ERR_IO; } sz = 3 + (dc->rxnext - dc->rxptr) * 4; uint8_t rxbuf[1024]; @@ -177,39 +232,35 @@ int dc_q_exec(DC* dc) { int n = usb_read(dc->usb, rxbuf, sz); if (n < 0) { ERROR("dc_q_exec() usb read error\n"); - return -1; + return DC_ERR_IO; } dump("RX>", rxbuf, sz); if ((n < 3) || (rxbuf[0] != DAP_Transfer)) { ERROR("dc_q_exec() bad response\n"); - return -1; - } - if (rxbuf[2] != RSP_ACK_OK) { - ERROR("dc_q_exec() error response 0x%02x\n", rxbuf[2]); - return -1; + 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; + } } - // 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; + 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) { - // 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; + // 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; } } @@ -223,10 +274,10 @@ static void dc_q_raw_rd(DC* dc, unsigned req, uint32_t* val) { } static void dc_q_raw_wr(DC* dc, unsigned req, uint32_t val) { - // TRACE("Q RAW WR %08x %08x\n", req, val); if (dc->txavail < 5) { - if (dc_q_exec(dc) < 0) { - dc->qerror = 1; + // 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; } } @@ -239,11 +290,10 @@ static void dc_q_raw_wr(DC* dc, unsigned req, uint32_t val) { // adjust DP.SELECT for desired DP access, if necessary void dc_q_dp_sel(DC* dc, uint32_t dpaddr) { - // TRACE("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; + 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 @@ -265,7 +315,7 @@ 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 = 1; + dc->qerror = DC_ERR_FAILED; return; } // we always return DPBANK to 0 when adjusting AP & APBANK @@ -297,7 +347,6 @@ 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) { @@ -306,6 +355,29 @@ void dc_q_ap_wr(DC* dc, unsigned apaddr, uint32_t val) { 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) { @@ -384,7 +456,8 @@ int dc_attach(DC* dc, unsigned flags, uint32_t tgt, uint32_t* idcode) { // or line reset + target select dc_q_init(dc); dc_q_raw_rd(dc, XFER_DP | XFER_RD | XFER_00, idcode); - return dc_q_exec(dc); + int r = dc_q_exec(dc); + return r; } static usb_handle* usb_connect(void) { @@ -394,31 +467,31 @@ static usb_handle* usb_connect(void) { if (usb == 0) { usb = usb_open(0x2e8a, 0x000c, 42); if (usb == 0) { - fprintf(stderr, "cannot find device\n"); + ERROR("cannot find device\n"); return NULL; } } return usb; } -int dc_create(DC** out) { - DC* dc; +// 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; - if ((dc = calloc(1, sizeof(DC))) == NULL) { - return -1; - } - - if ((dc->usb = usb_connect()) == NULL) { - free(dc); - return -1; - } + // 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; @@ -463,26 +536,41 @@ int dc_create(DC** out) { dc->max_packet_size = n16; } if ((dc->max_packet_count < 1) || (dc->max_packet_size < 64)) { - fprintf(stderr, "dc_init() impossible packet configuration\n"); - free(dc); - return -1; + ERROR("dc_init() impossible packet configuration\n"); + return DC_ERR_PROTOCOL; } // invalidate register cache - dc->dp_select_cache = 0xFFFFFFFFU; + dc->dp_select_cache = INVALID; // 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); - dap_connect(dc); dap_swd_configure(dc, CFG_Turnaround_1); - dap_transfer_configure(dc, 8, 64, 0); + dap_xfer_config(dc, 8, 64, 0); + return DC_OK; +} - *out = dc; - return 0; +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 @@ -17,6 +17,17 @@ void dc_q_dp_wr(dctx_t* dc, unsigned dpaddr, uint32_t val); void dc_q_ap_rd(dctx_t* dc, unsigned apaddr, uint32_t* val); void dc_q_ap_wr(dctx_t* dc, unsigned apaddr, uint32_t val); +// set the max retry count for match operations +void dc_set_match_retry(dctx_t* dc, unsigned num); + +// set the mask pattern (in the probe, not the target) +void dc_q_set_mask(dctx_t* dc, uint32_t mask); + +// try to read until (readval & mask) == val or timeout +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); @@ -35,4 +46,20 @@ 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_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