commit b078c7fe3ea33d4a06ca81d3c2e5db3ae2e98815
parent 45ce633fd004850c01d325609e58d730f0383d3a
Author: Brian Swetland <swetland@frotz.net>
Date:   Mon,  1 Feb 2021 17:52:54 -0800
mdebug: initial rpi rp2040/pico support
- new lpclink ii firmware binary
  https://github.com/littlekernel/lk
  6e3edb87de03ab8f00fa45902ff89a1909e79927
- warn about pre 1.3 firmware (which will no longer work)
- new attach argument for multidrop selection:
  swd: plain swd, no multidrop (default)
  pico0: multidrop, select rp2040 cpu0
  pico1: multidrop, select rp2040 cpu1
  picor: multidrop, select rp2040 rescue dp
         (resets both CPUs and parks them in ROM)
- new --pico commandline argument to ensure we start in attach pico0
  mode and avoid trying to do a single-drop attach which confuses
  the rp2040
- some code to dump outbound RSWD packets (disabled)
Diffstat:
7 files changed, 145 insertions(+), 13 deletions(-)
diff --git a/firmware/lpclink2-mdebug.bin b/firmware/lpclink2-mdebug.bin
Binary files differ.
diff --git a/include/protocol/rswdp.h b/include/protocol/rswdp.h
@@ -60,6 +60,12 @@
 #define CMD_JTAG_IO	0x0C /* op=0, arg=bitcount, data x (count/32) * 2 */
 			     /* tms, tdi word pairs per 32bits */
 
+/* ATTACH ops */
+#define ATTACH_SWD_RESET 0
+#define ATTACH_JTAG_TO_SWD 1
+#define ATTACH_DORMANT_TO_SWD 2
+#define ATTACH_SWD_TO_DORMANT 3
+
 /* valid: target to host */
 #define CMD_STATUS	0x10 /* op=errorcode, arg=commands since last TXN_START */
 #define CMD_SWD_DATA	0x11 /* op=0 arg=count, payload: data x count */
diff --git a/tools/debugger-commands.c b/tools/debugger-commands.c
@@ -84,7 +84,20 @@ int do_exit(int argc, param *argv) {
 
 int do_attach(int argc, param *argv) {
 	if (argc > 0) {
-		if (!strcmp(argv[0].s, "swd")) {
+		if (!strcmp(argv[0].s, "pico0")) {
+			swdp_targetsel(0x01002927, 1);
+			ACTIVE_TRANSPORT = &SWDP_TRANSPORT;
+		} else if (!strcmp(argv[0].s, "pico1")) {
+			swdp_targetsel(0x11002927, 1);
+			ACTIVE_TRANSPORT = &SWDP_TRANSPORT;
+		} else if (!strcmp(argv[0].s, "picor")) {
+			swdp_targetsel(0xF1002927, 1);
+			ACTIVE_TRANSPORT = &SWDP_TRANSPORT;
+		} else if (!strcmp(argv[0].s, "swd")) {
+			swdp_targetsel(0, 0);
+			ACTIVE_TRANSPORT = &SWDP_TRANSPORT;
+		} else if (!strcmp(argv[0].s, "swdx")) {
+			swdp_targetsel(0xffffffff, 1);
 			ACTIVE_TRANSPORT = &SWDP_TRANSPORT;
 		} else if (!strcmp(argv[0].s, "jtag")) {
 			ACTIVE_TRANSPORT = &JTAG_TRANSPORT;
@@ -95,6 +108,19 @@ int do_attach(int argc, param *argv) {
 	return swdp_reset();
 }
 
+int debug_target(const char* name) {
+	if (!strcmp(name,"pico")) {
+		xprintf(XDATA, "target: RPxxxx MCUs\n");
+		xprintf(XDATA, "target: use 'attach pico0', 'attach pico1' to change cores\n");
+		xprintf(XDATA, "target: use 'attach picor' to enter rescue (reset-stop-in-rom)\n");
+		swdp_targetsel(0x01002927, 1);
+		return 0;
+	} else {
+		xprintf(XDATA, "target: unknown target '%s'\n", name);
+		return -1;
+	}
+}
+
 static u32 lastregs[19];
 
 int do_regs(int argc, param *argv) {
diff --git a/tools/debugger.c b/tools/debugger.c
@@ -72,6 +72,7 @@ int main(int argc, char **argv) {
 		static struct option long_options[] = {
 			{"help", 0, 0, 'h'},
 			{"script", 1, 0, 'f'},
+			{"pico", 0, 0, 'p'},
 			{0, 0, 0, 0},
 		};
 
@@ -86,6 +87,9 @@ int main(int argc, char **argv) {
 			case 'h':
 				usage(argc, argv);
 				break;
+			case 'p':
+				debug_target("pico");
+				break;
 			default:
 				usage(argc, argv);
 				break;
diff --git a/tools/debugger.h b/tools/debugger.h
@@ -131,5 +131,7 @@ extern debug_transport DUMMY_TRANSPORT;
 extern debug_transport SWDP_TRANSPORT;
 extern debug_transport JTAG_TRANSPORT;
 
+int debug_target(const char* name);
+
 #endif
 
diff --git a/tools/rswdp.c b/tools/rswdp.c
@@ -171,6 +171,9 @@ done:
 	xprintf(XSWD, "usb: protocol: %d.%d\n", version >> 8, version & 0xff);
 	xprintf(XSWD, "usb: max data: %d byte rx buffer\n", maxdata);
 
+	if (version < 0x0103) {
+		xprintf(XSWD, "usb: WARNING, FIRMWARE OUT OF DATE\n");
+	}
 	swd_version = version;
 	swd_maxwords = maxdata / 4;
 }
@@ -237,7 +240,7 @@ static int process_reply(struct txn *t, u32 *data, int count) {
 				return 0;
 			}
 		case CMD_CLOCK_KHZ:
-			xprintf(XSWD,"mdebug: SWD clock: %d KHz\n", n);
+			xprintf(XSWD,"mdebug: %s clock: %d KHz\n", op ? "SWO" : "SWD", n);
 			continue;
 		default:
 			xprintf(XSWD,"unknown command 0x%02x\n", RSWD_MSG_CMD(msg));
@@ -247,6 +250,61 @@ static int process_reply(struct txn *t, u32 *data, int count) {
 	return 0;
 }
 
+#define TRACE_RSWD_XMIT 0
+#if TRACE_RSWD_XMIT
+static const char* opstr(unsigned op) {
+	switch (op) {
+	case OP_RD|OP_DP|OP_X0: return "RD DP x0 (DPIDR)";
+	case OP_RD|OP_DP|OP_X4: return "RD DP x4 (DPCTRL)";
+	case OP_RD|OP_DP|OP_X8: return "RD DP x8 (RESEND)";
+	case OP_RD|OP_DP|OP_XC: return "RD DP xC";
+	case OP_WR|OP_DP|OP_X0: return "WR DP x0 (ABORT)";
+	case OP_WR|OP_DP|OP_X4: return "WR DP x4 (DPCTRL)";
+	case OP_WR|OP_DP|OP_X8: return "WR DP x8 (SELECT)";
+	case OP_WR|OP_DP|OP_XC: return "WR DP xC (TARGETSEL)";
+	case OP_RD|OP_AP|OP_X0: return "RD AP x0";
+	case OP_RD|OP_AP|OP_X4: return "RD AP x4";
+	case OP_RD|OP_AP|OP_X8: return "RD AP x8";
+	case OP_RD|OP_AP|OP_XC: return "RD AP xC";
+	case OP_WR|OP_AP|OP_X0: return "WR AP x0";
+	case OP_WR|OP_AP|OP_X4: return "WR AP x4";
+	case OP_WR|OP_AP|OP_X8: return "WR AP x8";
+	case OP_WR|OP_AP|OP_XC: return "WR AP xC";
+	default: return "???";
+	}
+}
+
+static void q_dump(u32* tx, unsigned count) {
+	while (count-- > 0) {
+		unsigned cmd = RSWD_MSG_CMD(*tx);
+		unsigned op = RSWD_MSG_OP(*tx);
+		unsigned arg = RSWD_MSG_ARG(*tx);
+		switch (cmd) {
+		case CMD_SWD_WRITE:
+			xprintf(XSWD, "CMD_SWD_WRITE %s 0x%08x n=%u\n", opstr(op), tx[1], arg);
+			while (arg > 0) { arg--; tx++; count--; }
+			break;
+		case CMD_SWD_READ:
+			xprintf(XSWD, "CMD_SWD_READ %s n=%u\n", opstr(op), arg);
+			break;
+		case CMD_SWD_DISCARD:
+			xprintf(XSWD, "CMD_SWD_DISCARD %s n=%u\n", opstr(op), arg);
+			break;
+		case CMD_ATTACH:
+			xprintf(XSWD, "CMD_ATTACH\n");
+			break;
+		case CMD_RESET:
+			xprintf(XSWD, "CMD_RESET n=%u\n", arg);
+			break;
+		default:
+			xprintf(XSWD, "CMD_%02x\n", cmd);
+		}
+		tx++;
+	}
+	xprintf(XSWD, "---\n");
+}
+#endif
+
 static int q_exec(struct txn *t) {
 	unsigned data[MAXWORDS];
 	unsigned seq;
@@ -265,6 +323,10 @@ static int q_exec(struct txn *t) {
 	if (((t->txc % 16) == 0) && (t->txc != swd_maxwords))
 		t->tx[t->txc++] = RSWD_MSG(CMD_NULL, 0, 0);
 
+#if TRACE_RSWD_XMIT
+	q_dump(t->tx + 1, t->txc - 1);
+#endif
+
 	pthread_mutex_lock(&swd_lock);
  	seq = sequence++;
 	id = RSWD_TXN_START(seq);
@@ -755,41 +817,71 @@ int swdp_bootloader(void) {
 	return q_exec(&t);
 }
 
+static uint32_t targetsel_val = 0;
+static unsigned targetsel_on = 0;
+
+void swdp_targetsel(uint32_t val, unsigned on) {
+	targetsel_val = val;
+	targetsel_on = on;
+}
+
 static int _swdp_reset(void) {
 	struct txn t;
 	u32 n, idcode;
 
 	swd_error = 0;
 	q_init(&t);
-	t.tx[t.txc++] = RSWD_MSG(CMD_ATTACH, 0, 0);
+	t.tx[t.txc++] = RSWD_MSG(CMD_ATTACH, ATTACH_JTAG_TO_SWD, 0);
+	t.tx[t.txc++] = RSWD_MSG(CMD_ATTACH, ATTACH_DORMANT_TO_SWD, 0);
+	t.tx[t.txc++] = RSWD_MSG(CMD_ATTACH, ATTACH_SWD_RESET, 0);
+
+	if (targetsel_on) {
+		t.tx[t.txc++] = SWD_WR(DP_BUFFER, 1);
+		t.tx[t.txc++] = targetsel_val;
+	}
+	
 	t.tx[t.txc++] = SWD_RD(DP_IDCODE, 1);
 	t.rx[t.rxc++] = &idcode;
 	if (q_exec(&t)) {
 		xprintf(XSWD, "attach: IDCODE: ????????\n");
 	} else {
-		xprintf(XSWD, "attach: IDCODE: %08x\n", idcode);
+		xprintf(XSWD, "attach: IDCODE: %08x\n");
 	}
 
 	swd_error = 0;
 	q_init(&t);
+
  	/* clear any stale errors */
 	t.tx[t.txc++] = SWD_WR(DP_ABORT, 1);
 	t.tx[t.txc++] = 0x1E;
 
-	/* power up */
-	t.tx[t.txc++] = SWD_WR(DP_DPCTRL, 1);
-	t.tx[t.txc++] = (1 << 28) | (1 << 30);
-	t.tx[t.txc++] = SWD_RD(DP_DPCTRL, 1);
-	t.rx[t.rxc++] = &n;
+	if (targetsel_on && (targetsel_val == 0xf1002927)) {
+		// for pico recovery dap, only valid action is clear
+		// debug power bits to put the chip in to recovery mode
+		t.tx[t.txc++] = SWD_WR(DP_DPCTRL, 1);
+		t.tx[t.txc++] = 0;
+		t.tx[t.txc++] = SWD_RD(DP_DPCTRL, 1);
+		t.rx[t.rxc++] = &n;
+	} else{
+		/* power up */
+		t.tx[t.txc++] = SWD_WR(DP_DPCTRL, 1);
+		t.tx[t.txc++] = (1 << 28) | (1 << 30);
+		t.tx[t.txc++] = SWD_RD(DP_DPCTRL, 1);
+		t.rx[t.rxc++] = &n;
+
+		/* configure for 32bit IO */
+		q_ap_write(&t, AHB_CSW,
+			AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+			AHB_CSW_PRIV | AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+	}
 
-	/* configure for 32bit IO */
-	q_ap_write(&t, AHB_CSW,
-		AHB_CSW_MDEBUG | AHB_CSW_PRIV |
-		AHB_CSW_PRIV | AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+	//u32 base;
+	//q_ap_read(&t, 0xF8, &base);
 	if (q_exec(&t))
 		return -1;
 
 	xprintf(XSWD, "attach: DPCTRL: %08x\n", n);
+	//xprintf(XSWD, "attach: BASE: %08x\n", base);
 	return 0;
 }
 
diff --git a/tools/rswdp.h b/tools/rswdp.h
@@ -55,6 +55,8 @@ int swdp_watchpoint_wr(unsigned n, u32 addr);
 int swdp_watchpoint_rw(unsigned n, u32 addr);
 int swdp_watchpoint_disable(unsigned n);
 
+void swdp_targetsel(u32 val, unsigned on);
+
 /* these are now provided by the transport layer */
 //int swdp_reset(void);
 //int swdp_error(void);