xdebug

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

commit 4280d3f76877bc695706f913bd1241f56838afa2
parent 81203d554763a279dfc53ff7daf9b13ac426fcc9
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat,  4 Mar 2023 15:22:26 -0800

start bolting the text UI on

- xtest exercises the transport by itself
- xdebug is the debugger
- start unifying logging
- minimal command parser

Diffstat:
MMakefile | 24++++++++++++++++++------
Asrc/commands.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/transport-dap.c | 44++++++++++++++++++++++----------------------
Msrc/transport-private.h | 14+++-----------
Msrc/xdebug.c | 213++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Asrc/xdebug.h | 36++++++++++++++++++++++++++++++++++++
Asrc/xtest.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 429 insertions(+), 73 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,16 +1,28 @@ -all: out/xdebug +all: out/xdebug out/xtest CFLAGS := -Wall -g -O1 - -SRCS := src/xdebug.c src/transport-arm-debug.c src/transport-dap.c src/usb.c +CFLAGS += -Itui -Itermbox -D_XOPEN_SOURCE LIBS := -lusb-1.0 -OBJS := $(addprefix out/,$(patsubst %.c,%.o,$(filter %.c,$(SRCS)))) +COMMON := src/transport-arm-debug.c src/transport-dap.c src/usb.c +XTESTSRCS := src/xtest.c $(COMMON) +XTESTOBJS := $(addprefix out/,$(patsubst %.c,%.o,$(filter %.c,$(XTESTSRCS)))) + +XDEBUGSRCS := src/xdebug.c src/commands.c $(COMMON) +XDEBUGSRCS += tui/tui.c termbox/termbox.c termbox/utf8.c +XDEBUGOBJS := $(addprefix out/,$(patsubst %.c,%.o,$(filter %.c,$(XDEBUGSRCS)))) -out/xdebug: $(OBJS) +out/xtest: $(XTESTOBJS) @mkdir -p $(dir $@) - gcc -o $@ -Wall -g -O1 $(OBJS) $(LIBS) + gcc -o $@ -Wall -g -O1 $(XTESTOBJS) $(LIBS) + +out/xdebug: $(XDEBUGOBJS) + @mkdir -p $(dir $@) + gcc -o $@ -Wall -g -O1 $(XDEBUGOBJS) $(LIBS) + +# remove dups +OBJS := $(sort $(XTESTOBJS) $(XDEBUGOBJS)) $(OBJS): out/%.o: %.c $(XDEPS) @mkdir -p $(dir $@) diff --git a/src/commands.c b/src/commands.c @@ -0,0 +1,54 @@ +// Copyright 2023, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <string.h> + +#include "xdebug.h" +#include "transport.h" +#include "arm-v7-debug.h" +#include "arm-v7-system-control.h" + + +int do_exit(DC* dc, CC* cc) { + debugger_exit(); + return 0; +} + +int do_help(DC* dc, CC* cc); + +struct { + const char* name; + int (*func)(DC* dc, CC* cc); + const char* help; +} CMDS[] = { + { "exit", do_exit, "exit debugger" }, + { "quit", do_exit, NULL }, + { "help", do_help, "list debugger commands" }, +}; + +int do_help(DC* dc, CC* cc) { + int w = 0; + for (int n = 0; n < sizeof(CMDS)/sizeof(CMDS[0]); n++) { + int len = strlen(CMDS[0].name); + if (len > w) w = len; + } + for (int n = 0; n < sizeof(CMDS)/sizeof(CMDS[0]); n++) { + if (CMDS[n].help == NULL) { + continue; + } + INFO("%-*s %s", w, CMDS[n].name, CMDS[n].help); + } + return 0; +} + +void debugger_command(DC* dc, CC* cc) { + const char* cmd = cmd_name(cc); + for (int n = 0; n < sizeof(CMDS)/sizeof(CMDS[0]); n++) { + if (!strcmp(cmd, CMDS[n].name)) { + CMDS[n].func(dc, cc); + return; + } + } + ERROR("unknown command '%s'\n", cmd); +} + diff --git a/src/transport-dap.c b/src/transport-dap.c @@ -426,7 +426,7 @@ 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); + INFO("IDCODE %08x\n", n); // If this is a RP2040, we need to connect in multidrop // mode before doing anything else. @@ -438,7 +438,7 @@ int dc_attach(DC* dc, unsigned flags, unsigned tgt, uint32_t* idcode) { } dc_dp_rd(dc, DP_CS, &n); - printf("CTRL/STAT %08x\n", n); + INFO("CTRL/STAT %08x\n", n); // clear all sticky errors dc_dp_wr(dc, DP_ABORT, DP_ABORT_ALLCLR); @@ -451,8 +451,8 @@ int dc_attach(DC* dc, unsigned flags, unsigned tgt, uint32_t* idcode) { 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); + INFO("CTRL/STAT %08x\n", n); + INFO("MAP.CSW %08x\n", dc->map_csw_keep); dc->map_csw_keep &= MAP_CSW_KEEP; @@ -503,40 +503,40 @@ static int dap_configure(DC* dc) { int sz = dap_get_info(dc, n, buf, 0, 255); if (sz > 0) { buf[sz] = 0; - printf("0x%02x: '%s'\n", n, (char*) buf); + INFO("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"); + INFO("Capabilities: 0x%02x 0x%02x\n", buf[0], buf[1]); + INFO("Capabilities:"); + if (buf[0] & I0_SWD) INFO(" SWD"); + if (buf[0] & I0_JTAG) INFO(" JTAG"); + if (buf[0] & I0_SWO_UART) INFO(" SWO(UART)"); + if (buf[0] & I0_SWO_Manchester) INFO(" SWO(Manchester)"); + if (buf[0] & I0_Atomic_Commands) INFO(" ATOMIC"); + if (buf[0] & I0_Test_Domain_Timer) INFO(" TIMER"); + if (buf[0] & I0_SWO_Streaming_Trace) INFO(" SWO(Streaming)"); + if (buf[0] & I0_UART_Comm_Port) INFO(" UART"); + if (buf[1] & I1_USB_COM_Port) INFO(" USBCOM"); + INFO("\n"); } if (dap_get_info(dc, DI_UART_RX_Buffer_Size, &n32, 4, 4) == 4) { - printf("UART RX Buffer Size: %u\n", n32); + INFO("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); + INFO("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); + INFO("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); + INFO("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); + INFO("Max Packet Size: %u\n", n16); dc->max_packet_size = n16; } if ((dc->max_packet_count < 1) || (dc->max_packet_size < 64)) { diff --git a/src/transport-private.h b/src/transport-private.h @@ -3,6 +3,8 @@ #pragma once +#include "xdebug.h" + #include <stdint.h> #include "usb.h" @@ -51,17 +53,7 @@ typedef struct debug_context DC; #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 +#if 0 static void dump(const char* str, const void* ptr, unsigned len) { const uint8_t* x = ptr; TRACE("%s", str); diff --git a/src/xdebug.c b/src/xdebug.c @@ -5,52 +5,197 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <stdarg.h> + +#include "xdebug.h" +#include "tui.h" -#include "arm-debug.h" -#include "cmsis-dap-protocol.h" #include "transport.h" -int main(int argc, char **argv) { - uint32_t n = 0; +#define MAX_ARGS 16 + +static const char* NTH(unsigned n) { + switch (n) { + case 1: return "1st "; + case 2: return "2nd "; + case 3: return "3rd "; + case 4: return "4th "; + case 5: return "5th "; + default: return ""; + } +} + +#define tSTRING 0x01 +#define tNUMBER 0x02 + +typedef struct token { + const char* s; + uint32_t n; + uint32_t info; +} TOKEN; + +struct command_context { + TOKEN tok[MAX_ARGS]; + unsigned count; +}; + +const char* cmd_name(CC* cc) { + return cc->tok[0].s; +} + +int cmd_arg_u32(CC* cc, unsigned nth, uint32_t* out) { + if (nth >= cc->count) { + ERROR("%s: missing %sargument\n", cc->tok[0].s, NTH(nth)); + return DBG_ERR; + } + if (!(cc->tok[nth].info & tNUMBER)) { + ERROR("%s: %sargument not a number\n", cc->tok[0].s, NTH(nth)); + return DBG_ERR; + } + *out = cc->tok[nth].n; + return 0; +} + +int cmd_arg_u32_opt(CC* cc, unsigned nth, uint32_t* out, uint32_t val) { + if (nth >= cc->count) { + *out = val; + return 0; + } + if (!(cc->tok[nth].info & tNUMBER)) { + ERROR("%s: %sargument not a number\n", cc->tok[0].s, NTH(nth)); + return DBG_ERR; + } + *out = cc->tok[nth].n; + return 0; +} + +int cmd_arg_str(CC* cc, unsigned nth, const char** out) { + if (nth >= cc->count) { + ERROR("%s: missing %sargument\n", cc->tok[0].s, NTH(nth)); + return DBG_ERR; + } + *out = cc->tok[nth].s; + return 0; +} + +int cmd_arg_str_opt(CC* cc, unsigned nth, const char** out, const char* str) { + if (nth >= cc->count) { + *out = str; + } else { + *out = cc->tok[nth].s; + } + return 0; +} + +static DC* dc; - dctx_t* dc; - if (dc_create(&dc) < 0) { - return -1; +int parse(TOKEN* tok) { + char *end; + if ((tok->s[0] == '.') && tok->s[1]) { + // decimal + tok->n = strtoul(tok->s + 1, &end, 10); + if (*end == 0) { + tok->info = tNUMBER; + return 0; + } } + tok->n = strtoul(tok->s, &end, 16); + if (*end == 0) { + tok->info = tNUMBER; + return 0; + } + tok->n = 0; + tok->info = tSTRING; + return 0; +} - dc_set_clock(dc, 1000000); +void handle_line(char *line, unsigned len) { + CC cc; - dc_attach(dc, 0, 0, &n); + while (*line && (*line <= ' ')) line++; + if (*line == '/') { + cc.count = 2; + cc.tok[0].s = "wconsole"; + cc.tok[0].info = tSTRING; + cc.tok[1].s = line + 1; + cc.tok[1].info = tSTRING; + cc.count = 2; + debugger_command(dc, &cc); + return; + } -#if 1 - // dump some info - dc_dp_rd(dc, DP_DPIDR, &n); - printf("DP.DPIDR %08x\n", n); - dc_dp_rd(dc, DP_TARGETID, &n); - printf("DP.TARGETID %08x\n", n); - dc_dp_rd(dc, DP_DLPIDR, &n); - printf("DP.DLPIDR %08x\n", n); - dc_ap_rd(dc, MAP_IDR, &n); - printf("MAP.IDR %08x\n", n); - dc_ap_rd(dc, MAP_CSW, &n); - printf("MAP.CSW %08x\n", n); - dc_ap_rd(dc, MAP_CFG, &n); - printf("MAP.CFG %08x\n", n); - dc_ap_rd(dc, MAP_CFG1, &n); - printf("MAP.CFG1 %08x\n", n); - dc_ap_rd(dc, MAP_BASE, &n); - printf("MAP.BASE %08x\n", n); -#endif + unsigned c, n = 0; + while ((c = *line)) { + if (c <= ' ') { + line++; + continue; + } + if (n == MAX_ARGS) { + ERROR("too many arguments\n"); + return; + } + cc.tok[n].s = line; + for (;;) { + if (c == 0) { + n++; + break; + } else if (c == '#') { + *line = 0; + break; + } else if (c <= ' ') { + *line++ = 0; + n++; + break; + } + c = *++line; + } + } - dc_mem_rd32(dc, 0, &n); - printf("%08x: %08x\n", 0, n); + if (n == 0) { + return; + } - unsigned addr = 0x00000000; - for (unsigned a = addr; a < addr + 32; a += 4) { - dc_mem_rd32(dc, a, &n); - printf("%08x: %08x\n", a, n); + cc.tok[0].info = tSTRING; + for (c = 1; c < n; n++) { + if (parse(cc.tok + n) < 0) { + return; + } } + debugger_command(dc, &cc); +} +static tui_ch_t* ch; + +int main(int argc, char** argv) { + tui_init(); + tui_ch_create(&ch, 0); + dc_create(&dc); + while (tui_handle_event(handle_line) == 0) ; + tui_exit(); return 0; } +void debugger_exit(void) { + tui_exit(); + exit(0); +} + +void MSG(uint32_t flags, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + switch (flags) { + case mDEBUG: + tui_ch_printf(ch, "debug: "); + break; + case mTRACE: + tui_ch_printf(ch, "trace: "); + break; + case mPANIC: + tui_exit(); + fprintf(stderr,"panic: "); + vfprintf(stderr, fmt, ap); + exit(-1); + } + tui_ch_vprintf(ch, fmt, ap); + va_end(ap); +} diff --git a/src/xdebug.h b/src/xdebug.h @@ -0,0 +1,36 @@ +// Copyright 2023, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#pragma once + +#include <stdint.h> +#include <stdarg.h> + +void MSG(uint32_t flags, const char* fmt, ...) + __attribute__ ((format (printf, 2, 3))); + +#define mINFO 1 +#define mDEBUG 2 +#define mTRACE 3 +#define mERROR 4 +#define mPANIC 5 + +#define DEBUG(fmt...) MSG(mDEBUG, fmt) +#define INFO(fmt...) MSG(mINFO, fmt) +#define TRACE(fmt...) MSG(mTRACE, fmt) +#define ERROR(fmt...) MSG(mERROR, fmt) +#define PANIC(fmt...) MSG(mPANIC, fmt) + +#define DBG_OK 0 +#define DBG_ERR -1 + +typedef struct command_context CC; +const char* cmd_name(CC* cc); +int cmd_arg_u32(CC* cc, unsigned nth, uint32_t* out); +int cmd_arg_u32_opt(CC* cc, unsigned nth, uint32_t* out, uint32_t val); +int cmd_arg_str(CC* cc, unsigned nth, const char** out); +int cmd_arg_str_opt(CC* cc, unsigned nth, const char** out, const char* str); + +typedef struct debug_context DC; +void debugger_command(DC* dc, CC* cc); +void debugger_exit(void); diff --git a/src/xtest.c b/src/xtest.c @@ -0,0 +1,117 @@ +// 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 <stdarg.h> + +#include "xdebug.h" +#include "transport.h" +#include "arm-debug.h" +#include "cmsis-dap-protocol.h" + +void MSG(uint32_t flags, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void dump(uint32_t* w, int count) { + int n = 0; + while (count > 0) { + INFO(" %08x", *w++); + count--; + n++; + if (n == 4) { + INFO("\n"); + n = 0; + } + } + if (n) INFO("\n"); +} + +int main(int argc, char **argv) { + uint32_t n = 0; + + dctx_t* dc; + if (dc_create(&dc) < 0) { + return -1; + } + + dc_set_clock(dc, 4000000); + + dc_attach(dc, 0, 0, &n); + +#if 1 + // dump some info + dc_dp_rd(dc, DP_DPIDR, &n); + INFO("DP.DPIDR %08x\n", n); + dc_dp_rd(dc, DP_TARGETID, &n); + INFO("DP.TARGETID %08x\n", n); + dc_dp_rd(dc, DP_DLPIDR, &n); + INFO("DP.DLPIDR %08x\n", n); + dc_ap_rd(dc, MAP_IDR, &n); + INFO("MAP.IDR %08x\n", n); + dc_ap_rd(dc, MAP_CSW, &n); + INFO("MAP.CSW %08x\n", n); + dc_ap_rd(dc, MAP_CFG, &n); + INFO("MAP.CFG %08x\n", n); + dc_ap_rd(dc, MAP_CFG1, &n); + INFO("MAP.CFG1 %08x\n", n); + dc_ap_rd(dc, MAP_BASE, &n); + INFO("MAP.BASE %08x\n", n); +#endif + +#if 0 + dc_mem_rd32(dc, 0, &n); + INFO("%08x: %08x\n", 0, n); + + dc_mem_rd32(dc, 0xc0000000, &n); + INFO("%08x: %08x\n", 0, n); + + dc_mem_rd32(dc, 0, &n); + INFO("%08x: %08x\n", 0, n); + + unsigned addr = 0x00000000; + for (unsigned a = addr; a < addr + 32; a += 4) { + dc_mem_rd32(dc, a, &n); + INFO("%08x: %08x\n", a, n); + } +#endif + +#if 0 + int r = dc_core_halt(dc); + INFO("halt ? %d\n", r); + r = dc_core_reg_rd(dc, 0, &n); + INFO("r0 %x ? %d\n", n, r); + r = dc_core_reg_wr(dc, 0, 0xe0a0b0c0); + INFO("wr ? %d\n", r); + r = dc_core_reg_rd(dc, 15, &n); + INFO("r15 %x ? %d\n", n, r); + r = dc_core_reg_rd(dc, 0, &n); + INFO("r0 %x ? %d\n", n, r); + r = dc_core_resume(dc); + INFO("resume ? %d\n", r); + r = dc_core_reg_rd(dc, 0, &n); + INFO("r0 %x ? %d\n", n, r); + r = dc_core_reg_rd(dc, 15, &n); + INFO("r15 %x ? %d\n", n, r); +#endif + +#if 0 + uint32_t w[1024]; + int r = dc_mem_rd_words(dc, 0x00000000, 1024, w); + if (r == 0) { + dump(w, 1024); + } + for (unsigned n = 0; n < 250; n++) { + uint32_t w[1024]; + dc_mem_rd_words(dc, 0x00000000, 1024, w); + } +#endif + return 0; +} +