From ea92c8b8c8602a29a09782575d7860977acf2f65 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sun, 14 Feb 2021 22:48:41 +0100 Subject: [PATCH 1/6] cmsis-dap: Allow to use adiv5_swdp_scan. --- src/platforms/hosted/cmsis_dap.c | 90 +++++++++++++++++++++++++------- src/platforms/hosted/cmsis_dap.h | 2 - src/platforms/hosted/dap.c | 41 --------------- src/platforms/hosted/dap.h | 2 - src/target/adiv5.c | 2 +- src/target/adiv5_swdp.c | 6 ++- 6 files changed, 77 insertions(+), 66 deletions(-) diff --git a/src/platforms/hosted/cmsis_dap.c b/src/platforms/hosted/cmsis_dap.c index 68f8849a..b6ce2c4b 100644 --- a/src/platforms/hosted/cmsis_dap.c +++ b/src/platforms/hosted/cmsis_dap.c @@ -116,6 +116,7 @@ static void dap_dp_abort(ADIv5_DP_t *dp, uint32_t abort) static uint32_t dap_dp_error(ADIv5_DP_t *dp) { + /* Not used for SWD debugging, so no TARGETID switch needed!*/ uint32_t ctrlstat = dap_read_reg(dp, ADIV5_DP_CTRLSTAT); uint32_t err = ctrlstat & (ADIV5_DP_CTRLSTAT_STICKYORUN | ADIV5_DP_CTRLSTAT_STICKYCMP | @@ -180,7 +181,7 @@ int dbg_dap_cmd(uint8_t *data, int size, int rsize) memcpy(&hid_buffer[1], data, rsize); DEBUG_WIRE("cmd : "); - for(int i = 0; (i < 16) && (i < rsize + 1); i++) + for(int i = 0; (i < 32) && (i < rsize + 1); i++) DEBUG_WIRE("%02x.", hid_buffer[i]); DEBUG_WIRE("\n"); /* Write must be as long as we expect the result, at least @@ -284,16 +285,6 @@ static void dap_mem_write_sized( DEBUG_WIRE("memwrite done\n"); } -int dap_enter_debug_swd(ADIv5_DP_t *dp) -{ - dp->idcode = dap_read_idcode(dp); - dp->dp_read = dap_dp_read_reg; - dp->error = dap_dp_error; - dp->low_access = dap_dp_low_access; - dp->abort = dap_dp_abort; /* DP Write to Reg 0.*/ - return 0; -} - void dap_adiv5_dp_defaults(ADIv5_DP_t *dp) { if ((mode == DAP_CAP_JTAG) && dap_jtag_configure()) @@ -367,6 +358,66 @@ int dap_jtag_dp_init(ADIv5_DP_t *dp) return true; } +#define SWD_SEQUENCE_IN 0x80 +#define DAP_SWD_SEQUENCE 0x1d +/* DAP_SWD_SEQUENCE does not do auto turnaround*/ +static bool dap_dp_low_read(ADIv5_DP_t *dp, uint16_t addr, uint32_t *res) +{ + (void)dp; + unsigned int paket_request = make_packet_request(ADIV5_LOW_READ, addr); + uint8_t buf[32] = { + DAP_SWD_SEQUENCE, + 5, + 8, + paket_request, + 4 + SWD_SEQUENCE_IN, /* one turn-around + read 3 bit ACK */ + 32 + SWD_SEQUENCE_IN, /* read 32 bit data */ + 1 + SWD_SEQUENCE_IN, /* read parity bit */ + 1, /* one bit turn around to drive SWDIO */ + 0 + }; + dbg_dap_cmd(buf, sizeof(buf), 9); + if (buf[0]) + DEBUG_WARN("dap_dp_low_read failed\n"); + uint32_t ack = (buf[1] >> 1) & 7; + uint32_t data = (buf[2] << 0) + (buf[3] << 8) + (buf[4] << 16) + + (buf[5] << 24); + int parity = __builtin_parity(data); + bool ret = ((parity != buf[6]) || (ack != 1)); + *res = data; + DEBUG_PROBE("dap_dp_low_read ack %d, res %08" PRIx32 ", parity %s\n", ack, + data, (ret)? "ERR": "OK"); + return ret; +} + +static bool dap_dp_low_write(ADIv5_DP_t *dp, uint16_t addr, const uint32_t data) +{ + DEBUG_PROBE("dap_dp_low_write %08" PRIx32 "\n", data); + (void)dp; + unsigned int paket_request = make_packet_request(ADIV5_LOW_WRITE, addr); + uint8_t buf[32] = { + DAP_SWD_SEQUENCE, + 5, + 8, + paket_request, + 4 + SWD_SEQUENCE_IN, /* one turn-around + read 3 bit ACK */ + 1, /* one bit turn around to drive SWDIO */ + 0, + 32, /* write 32 bit data */ + (data >> 0) & 0xff, + (data >> 8) & 0xff, + (data >> 16) & 0xff, + (data >> 24) & 0xff, + 1, /* write parity biT */ + __builtin_parity(data) + }; + dbg_dap_cmd(buf, sizeof(buf), 14); + if (buf[0]) + DEBUG_WARN("dap_dp_low_write failed\n"); + uint32_t ack = (buf[1] >> 1) & 7; + return (ack != SWDP_ACK_OK); +} + int dap_swdptap_init(ADIv5_DP_t *dp) { if (!(dap_caps & DAP_CAP_SWD)) @@ -378,14 +429,15 @@ int dap_swdptap_init(ADIv5_DP_t *dp) dap_led(0, 1); dap_reset_link(false); if (has_swd_sequence) { - dp->seq_in = dap_swdptap_seq_in; - dp->seq_in_parity = dap_swdptap_seq_in_parity; - dp->seq_out = dap_swdptap_seq_out; - dp->seq_out_parity = dap_swdptap_seq_out_parity; - dp->dp_read = dap_dp_read_reg; - dp->error = dap_dp_error; - dp->low_access = dap_dp_low_access; - dp->abort = dap_dp_abort; + /* DAP_SWD_SEQUENCE does not do auto turnaround, use own!*/ + dp->dp_low_read = dap_dp_low_read; + dp->dp_low_write = dap_dp_low_write; } + dp->seq_out = dap_swdptap_seq_out; + dp->seq_out_parity = dap_swdptap_seq_out_parity; + dp->dp_read = dap_dp_read_reg; + /* For error() use the TARGETID switching firmware_swdp_error */ + dp->low_access = dap_dp_low_access; + dp->abort = dap_dp_abort; return 0; } diff --git a/src/platforms/hosted/cmsis_dap.h b/src/platforms/hosted/cmsis_dap.h index 45178e47..d23726db 100644 --- a/src/platforms/hosted/cmsis_dap.h +++ b/src/platforms/hosted/cmsis_dap.h @@ -24,7 +24,6 @@ #if defined(CMSIS_DAP) int dap_init(bmp_info_t *info); -int dap_enter_debug_swd(ADIv5_DP_t *dp); void dap_exit_function(void); void dap_adiv5_dp_defaults(ADIv5_DP_t *dp); int cmsis_dap_jtagtap_init(jtag_proc_t *jtag_proc); @@ -41,7 +40,6 @@ int dap_init(bmp_info_t *info) } # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-parameter" -int dap_enter_debug_swd(ADIv5_DP_t *dp) {return -1;} uint32_t dap_swj_clock(uint32_t clock) {return 0;} void dap_exit_function(void) {}; void dap_adiv5_dp_defaults(ADIv5_DP_t *dp) {}; diff --git a/src/platforms/hosted/dap.c b/src/platforms/hosted/dap.c index 0cf63e32..24ba7c70 100644 --- a/src/platforms/hosted/dap.c +++ b/src/platforms/hosted/dap.c @@ -782,44 +782,3 @@ void dap_swdptap_seq_out_parity(uint32_t MS, int ticks) if (buf[0]) DEBUG_WARN("dap_swdptap_seq_out error\n"); } - -#define SWD_SEQUENCE_IN 0x80 -uint32_t dap_swdptap_seq_in(int ticks) -{ - uint8_t buf[16] = { - ID_DAP_SWD_SEQUENCE, - 1, - ticks + SWD_SEQUENCE_IN - }; - dbg_dap_cmd(buf, 2 + ((ticks + 7) >> 3), 3); - uint32_t res = 0; - int len = (ticks + 7) >> 3; - while (len--) { - res <<= 8; - res += buf[len + 1]; - } - return res; -} - -bool dap_swdptap_seq_in_parity(uint32_t *ret, int ticks) -{ - (void)ticks; - uint8_t buf[16] = { - ID_DAP_SWD_SEQUENCE, - 2, - 32 + SWD_SEQUENCE_IN, - 1 + SWD_SEQUENCE_IN, - }; - dbg_dap_cmd(buf, 7, 4); - uint32_t res = 0; - int len = 4; - while (len--) { - res <<= 8; - res += buf[len + 1]; - } - *ret = res; - unsigned int parity = __builtin_parity(res) & 1; - parity ^= (buf[5] % 1); - DEBUG_WARN("Res %08" PRIx32" %d\n", *ret, parity & 1); - return (!parity & 1); -} diff --git a/src/platforms/hosted/dap.h b/src/platforms/hosted/dap.h index 470742ca..d7e11249 100644 --- a/src/platforms/hosted/dap.h +++ b/src/platforms/hosted/dap.h @@ -92,6 +92,4 @@ void dap_jtagtap_tdi_tdo_seq(uint8_t *DO, bool final_tms, const uint8_t *TMS, int dap_jtag_configure(void); void dap_swdptap_seq_out(uint32_t MS, int ticks); void dap_swdptap_seq_out_parity(uint32_t MS, int ticks); -uint32_t dap_swdptap_seq_in(int ticks); -bool dap_swdptap_seq_in_parity(uint32_t *ret, int ticks); #endif // _DAP_H_ diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 7e11de78..f535c25a 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -628,6 +628,7 @@ static void rp_rescue_setup(ADIv5_DP_t *dp) DEBUG_WARN("malloc: failed in %s\n", __func__); return; } + memset(ap, 0, sizeof(ADIv5_AP_t)); ap->dp = dp; extern void rp_rescue_probe(ADIv5_AP_t *); rp_rescue_probe(ap); @@ -646,7 +647,6 @@ void adiv5_dp_init(ADIv5_DP_t *dp) } if (dp->idcode == 0x10212927) { rp_rescue_setup(dp); - free(dp); return; } DEBUG_INFO("DPIDR 0x%08" PRIx32 " (v%d %srev%d)\n", dp->idcode, diff --git a/src/target/adiv5_swdp.c b/src/target/adiv5_swdp.c index b20ae271..ea105fe0 100644 --- a/src/target/adiv5_swdp.c +++ b/src/target/adiv5_swdp.c @@ -146,6 +146,9 @@ int adiv5_swdp_scan(uint32_t targetid) adiv5_dp_write(initial_dp, ADIV5_DP_CTRLSTAT, 0); break; } + if (!initial_dp->dp_low_read) + /* E.g. CMSIS_DAP < V1.2 can not handle multu-drop!*/ + is_v2 = false; } else { is_v2 = false; } @@ -161,8 +164,9 @@ int adiv5_swdp_scan(uint32_t targetid) initial_dp->dp_low_write(initial_dp, ADIV5_DP_TARGETSEL, dp_targetid); if (initial_dp->dp_low_read(initial_dp, ADIV5_DP_IDCODE, - &idcode)) + &idcode)) { continue; + } } else { dp_targetid = 0; } From fa5e69e3be505a4c7b95095620085dfd6e23b4ea Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 15 Feb 2021 12:05:48 +0100 Subject: [PATCH 2/6] RP 2040: Special handling for rescue DP As the rescue DP provided no AP, trigger the reset with attach(). However attach will indicate failure also reset/halt has succeeded. --- src/target/rp.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/target/rp.c b/src/target/rp.c index 0787c64e..4bae5f9a 100644 --- a/src/target/rp.c +++ b/src/target/rp.c @@ -42,21 +42,35 @@ #include "target_internal.h" #include "cortexm.h" +#define BOOTROM_MAGIC ('M' | ('u' << 8) | (0x01 << 16)) +#define BOOTROM_MAGIC_ADDR 0x00000010 + bool rp_probe(target *t) { + /* Check bootrom magic*/ + uint32_t boot_magic = target_mem_read32(t, BOOTROM_MAGIC_ADDR); + if ((boot_magic & 0x00ffffff) != BOOTROM_MAGIC) { + DEBUG_WARN("Wrong Bootmagic %08" PRIx32 " found\n!", boot_magic); + return false; + } t->driver = "Raspberry RP2040"; target_add_ram(t, 0x20000000, 0x40000); return true; } +static bool rp_rescue_do_reset(target *t) +{ + ADIv5_AP_t *ap = (ADIv5_AP_t *)t->priv; + ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, + ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ); + ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, 0); + return false; +} -static bool rp_rescue_reset(target *t, int argc, const char **argv); - -const struct command_s rp_rescue_cmd_list[] = { - {"rescue_reset", (cmd_handler)rp_rescue_reset, "Hard reset to bootrom and halt"}, - {NULL, NULL, NULL} -}; - +/* The RP Pico rescue DP provides no AP, so we need special handling + * + * Attach to this DP will do the reset, but will fail to attach! + */ void rp_rescue_probe(ADIv5_AP_t *ap) { target *t = target_new(); @@ -65,20 +79,9 @@ void rp_rescue_probe(ADIv5_AP_t *ap) } adiv5_ap_ref(ap); + t->attach = (void*)rp_rescue_do_reset; t->priv = ap; t->priv_free = (void*)adiv5_ap_unref; - t->driver = "Raspberry RP2040 Rescue"; + t->driver = "Raspberry RP2040 Rescue(Attach to reset!)"; - target_add_commands(t, rp_rescue_cmd_list, t->driver); -} - -static bool rp_rescue_reset(target *t, int argc, const char **argv) -{ - (void)argc; - (void)argv; - ADIv5_AP_t *ap = (ADIv5_AP_t *)t->priv; - ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, - ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ); - ap->dp->low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_DP_CTRLSTAT, 0); - return true; } From 61efe26348b8d4d3ae01c3b2ac68680a4ef68d89 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Tue, 16 Feb 2021 20:20:42 +0100 Subject: [PATCH 3/6] swdp_scan: Break infinite loop after Dormant->SWD transition. --- src/target/adiv5_swdp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/target/adiv5_swdp.c b/src/target/adiv5_swdp.c index ea105fe0..7085f864 100644 --- a/src/target/adiv5_swdp.c +++ b/src/target/adiv5_swdp.c @@ -116,13 +116,14 @@ int adiv5_swdp_scan(uint32_t targetid) idcode = initial_dp->low_access(initial_dp, ADIV5_LOW_READ, ADIV5_DP_IDCODE, 0); } - if (e.type) { + if (e.type || initial_dp->fault) { is_v2 = false; DEBUG_WARN("Trying old JTAG to SWD sequence\n"); initial_dp->seq_out(0xFFFFFFFF, 32); initial_dp->seq_out(0xFFFFFFFF, 32); initial_dp->seq_out(0xE79E, 16); /* 0b0111100111100111 */ dp_line_reset(initial_dp); + initial_dp->fault = 0; volatile struct exception e; TRY_CATCH (e, EXCEPTION_ALL) { idcode = initial_dp->low_access(initial_dp, ADIV5_LOW_READ, @@ -242,10 +243,8 @@ uint32_t firmware_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, dp->seq_out(request, 8); ack = dp->seq_in(3); if (ack == SWDP_ACK_FAULT) { - /* On fault, abort() and repeat the command once.*/ - dp->error(dp); - dp->seq_out(request, 8); - ack = dp->seq_in(3); + dp->fault = 1; + return 0; } } while (ack == SWDP_ACK_WAIT && !platform_timeout_is_expired(&timeout)); From 2b0e255c406b6cc82d7907169f3efeb8075a27ab Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sun, 18 Apr 2021 16:26:39 +0200 Subject: [PATCH 4/6] cortexm: timeout and debug for run_stub() --- src/target/cortexm.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 9fb89a31..1fec2a09 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -926,7 +926,7 @@ int cortexm_run_stub(target *t, uint32_t loadaddr, regs[2] = r2; regs[3] = r3; regs[15] = loadaddr; - regs[16] = 0x1000000; + regs[REG_XPSR] = CORTEXM_XPSR_THUMB; regs[19] = 0; cortexm_regs_write(t, regs); @@ -936,16 +936,36 @@ int cortexm_run_stub(target *t, uint32_t loadaddr, /* Execute the stub */ enum target_halt_reason reason; +#if defined(PLATFORM_HAS_DEBUG) + uint32_t arm_regs_start[t->regs_size]; + target_regs_read(t, arm_regs_start); +#endif cortexm_halt_resume(t, 0); - while ((reason = cortexm_halt_poll(t, NULL)) == TARGET_HALT_RUNNING) - ; + platform_timeout timeout; + platform_timeout_set(&timeout, 5000); + do { + if (platform_timeout_is_expired(&timeout)) { + cortexm_halt_request(t); +#if defined(PLATFORM_HAS_DEBUG) + DEBUG_WARN("Stub hangs\n"); + uint32_t arm_regs[t->regs_size]; + target_regs_read(t, arm_regs); + for (unsigned int i = 0; i < 20; i++) { + DEBUG_WARN("%2d: %08" PRIx32 ", %08" PRIx32 "\n", + i, arm_regs_start[i], arm_regs[i]); + } +#endif + return -3; + } + } while ((reason = cortexm_halt_poll(t, NULL)) == TARGET_HALT_RUNNING); if (reason == TARGET_HALT_ERROR) raise_exception(EXCEPTION_ERROR, "Target lost in stub"); - if (reason != TARGET_HALT_BREAKPOINT) + if (reason != TARGET_HALT_BREAKPOINT) { + DEBUG_WARN(" Reasone %d\n", reason); return -2; - + } uint32_t pc = cortexm_pc_read(t); uint16_t bkpt_instr = target_mem_read16(t, pc); if (bkpt_instr >> 8 != 0xbe) From 52bffa70cf6b8b3d1de8a440bfbcfbbe382dceaf Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Wed, 21 Apr 2021 10:01:05 +0200 Subject: [PATCH 5/6] rp: Handle flash. - Beware, after reset, only a valid flash payload get mapped to XIP! Only 0 is read from flash when flash image is not valid --- src/target/cortexm.h | 1 + src/target/rp.c | 290 ++++++++++++++++++++++++++++++++++++++++++- src/target/target.c | 1 + 3 files changed, 289 insertions(+), 3 deletions(-) diff --git a/src/target/cortexm.h b/src/target/cortexm.h index 812a683e..8bd95d36 100644 --- a/src/target/cortexm.h +++ b/src/target/cortexm.h @@ -168,6 +168,7 @@ extern unsigned cortexm_wait_timeout; #define REG_SPECIAL 19 #define ARM_THUMB_BREAKPOINT 0xBE00 +#define CORTEXM_XPSR_THUMB (1 << 24) #define CORTEXM_TOPT_INHIBIT_SRST (1 << 2) diff --git a/src/target/rp.c b/src/target/rp.c index 4bae5f9a..60db7e46 100644 --- a/src/target/rp.c +++ b/src/target/rp.c @@ -42,8 +42,247 @@ #include "target_internal.h" #include "cortexm.h" +#define RP_ID "Raspberry RP2040" #define BOOTROM_MAGIC ('M' | ('u' << 8) | (0x01 << 16)) #define BOOTROM_MAGIC_ADDR 0x00000010 +#define XIP_FLASH_START 0x10000000 +#define SRAM_START 0x20000000 + +struct rp_priv_s { + uint16_t _debug_trampoline; + uint16_t _debug_trampoline_end; + uint16_t _connect_internal_flash; + uint16_t _flash_exit_xip; + uint16_t _flash_range_erase; + uint16_t flash_range_program; + uint16_t _flash_flush_cache; + uint16_t _flash_enter_cmd_xip; + bool is_prepared; + uint32_t regs[0x20];/* Register playground*/ +}; + +static bool rp2040_fill_table(struct rp_priv_s *priv, uint16_t *table, int max) +{ + uint16_t tag = *table++; + int check = 0; + while ((tag != 0) && max) { + uint16_t data = *table++; + check++; + max -= 2; + switch (tag) { + case ('D' | ('T' << 8)): + priv->_debug_trampoline = data; + break; + case ('D' | ('E' << 8)): + priv->_debug_trampoline_end = data; + break; + case ('I' | ('F' << 8)): + priv->_connect_internal_flash = data; + break; + case ('E' | ('X' << 8)): + priv->_flash_exit_xip = data; + break; + case ('R' | ('E' << 8)): + priv->_flash_range_erase = data; + break; + case ('R' | ('P' << 8)): + priv->flash_range_program = data; + break; + case ('F' | ('C' << 8)): + priv->_flash_flush_cache = data; + break; + case ('C' | ('X' << 8)): + priv->_flash_enter_cmd_xip = data; + break; + default: + check--; + } + tag = *table++; + } + DEBUG_TARGET("connect %04x debug_trampoline %04x end %04x\n", + priv->_connect_internal_flash, priv->_debug_trampoline, + priv->_debug_trampoline_end); + return (check != 8); +} + +/* RP ROM functions for flash handling return void */ +static bool rp_rom_call(target *t, uint32_t *regs, uint32_t cmd, + uint32_t timeout) +{ + const char spinner[] = "|/-\\"; + int spinindex = 0; + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + regs[7] = cmd; + regs[REG_LR] = ps->_debug_trampoline_end; + regs[REG_PC] = ps->_debug_trampoline; + regs[REG_MSP] = 0x20042000; + regs[REG_XPSR] = CORTEXM_XPSR_THUMB; + uint32_t dbg_regs[t->regs_size / sizeof(uint32_t)]; + target_regs_write(t, regs); + /* start the target and wait for it to halt again */ + target_halt_resume(t, false); + DEBUG_INFO("Call cmd %04x\n", cmd); + platform_timeout to; + platform_timeout_set(&to, timeout); + do { + if (timeout > 400) + tc_printf(t, "\b%c", spinner[spinindex++ % 4]); + if (platform_timeout_is_expired(&to)) { + DEBUG_WARN("RP Run timout %d ms reached: ", timeout); + break; + } + } while (!target_halt_poll(t, NULL)); + /* Debug */ + target_regs_read(t, dbg_regs); + bool ret = ((dbg_regs[REG_PC] &~1) != (ps->_debug_trampoline_end & ~1)); + if (ret) { + DEBUG_WARN("rp_rom_call cmd %04x failed, PC %08" PRIx32 "\n", + cmd, dbg_regs[REG_PC]); + } + return ret; +} + +static void rp_flash_prepare(target *t) +{ + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + if (!ps->is_prepared) { + /* connect*/ + rp_rom_call(t, ps->regs, ps->_connect_internal_flash,100); + /* exit_xip */ + rp_rom_call(t, ps->regs, ps->_flash_exit_xip, 100); + ps->is_prepared = true; + } +} + +/* FLASHCMD_SECTOR_ERASE 45/ 400 ms + * 32k block erase 120/ 1600 ms + * 64k block erase 150/ 2000 ms + * chip erase 5000/25000 ms + * page programm 0.4/ 3 ms + */ +static int rp_flash_erase(struct target_flash *f, target_addr addr, + size_t len) +{ + if (addr & 0xfff) { + DEBUG_WARN("Unaligned erase\n"); + return -1; + } + if (len & 0xfff) { + DEBUG_WARN("Unaligned len\n"); + len = (len + 0xfff) & ~0xfff; + } + DEBUG_INFO("Erase addr %08" PRIx32 " len 0x%" PRIx32 "\n", addr, len); + target *t = f->t; + rp_flash_prepare(t); + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + /* Register playground*/ + /* erase */ +#define MAX_FLASH (2 * 1024 * 1024) +#define FLASHCMD_SECTOR_ERASE 0x20 +#define FLASHCMD_BLOCK32K_ERASE 0x52 +#define FLASHCMD_BLOCK64K_ERASE 0xd8 +#define FLASHCMD_CHIP_ERASE 0x72 + addr -= XIP_FLASH_START; + if (len > MAX_FLASH) + len = MAX_FLASH; + while (len) { + ps->regs[0] = addr; + ps->regs[2] = -1; + if (len >= MAX_FLASH) { /* Bulk erase */ + ps->regs[1] = MAX_FLASH; + ps->regs[3] = FLASHCMD_CHIP_ERASE; + DEBUG_WARN("BULK_ERASE\n"); + rp_rom_call(t, ps->regs, ps->_flash_range_erase, 25100); + len = 0; + } else if (len >= (64 * 1024)) { + uint32_t chunk = len & ~((1 << 16) - 1); + ps->regs[1] = chunk; + ps->regs[3] = FLASHCMD_BLOCK64K_ERASE; + DEBUG_WARN("64k_ERASE\n"); + rp_rom_call(t, ps->regs, ps->_flash_range_erase, 2100); + len -= chunk ; + addr += chunk; + } else if (len >= (32 * 1024)) { + uint32_t chunk = len & ~((1 << 15) - 1); + ps->regs[1] = chunk; + ps->regs[3] = FLASHCMD_BLOCK32K_ERASE; + DEBUG_WARN("32k_ERASE\n"); + rp_rom_call(t, ps->regs, ps->_flash_range_erase, 1700); + len -= chunk; + addr += chunk; + } else { + ps->regs[1] = len; + ps->regs[2] = MAX_FLASH; + ps->regs[3] = FLASHCMD_SECTOR_ERASE; + DEBUG_WARN("Sector_ERASE\n"); + rp_rom_call(t, ps->regs, ps->_flash_range_erase, 410); + len = 0; + } + } + DEBUG_INFO("\nErase done!\n"); + return 0; +} + +int rp_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len) +{ + DEBUG_INFO("RP Write %08" PRIx32 " len 0x%" PRIx32 "\n", dest, len); + if ((dest & 0xff) || (len & 0xff)) { + DEBUG_WARN("Unaligned erase\n"); + return -1; + } + target *t = f->t; + rp_flash_prepare(t); + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + /* Write payload to target ram */ + dest -= XIP_FLASH_START; +#define MAX_WRITE_CHUNK 0x1000 + while (len) { + uint32_t chunksize = (len <= MAX_WRITE_CHUNK) ? len : MAX_WRITE_CHUNK; + target_mem_write(t, SRAM_START, src, chunksize); + /* Programm range */ + ps->regs[0] = dest; + ps->regs[1] = SRAM_START; + ps->regs[2] = chunksize; + rp_rom_call(t, ps->regs, ps->flash_range_program, + (3 * chunksize) >> 8); /* 3 ms per 256 byte page */ + len -= chunksize; + src += chunksize; + dest += chunksize; + } + return 0; +} + +static bool rp_cmd_erase_mass(target *t, int argc, const char *argv[]) +{ + (void) argc; + (void) argv; + struct target_flash f; + f.t = t; + return (rp_flash_erase(&f, XIP_FLASH_START, MAX_FLASH)) ? false: true; +} + +const struct command_s rp_cmd_list[] = { + {"erase_mass", rp_cmd_erase_mass, "Erase entire flash memory"}, + {NULL, NULL, NULL} +}; + +static void rp_add_flash(target *t, uint32_t addr, size_t length) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + if (!f) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + f->start = addr; + f->length = length; + f->blocksize = 0x1000; + f->erase = rp_flash_erase; + f->write = rp_flash_write; + f->buf_size = 2048; /* Max buffer size used eotherwise */ + target_add_flash(t, f); +} bool rp_probe(target *t) { @@ -53,8 +292,54 @@ bool rp_probe(target *t) DEBUG_WARN("Wrong Bootmagic %08" PRIx32 " found\n!", boot_magic); return false; } - t->driver = "Raspberry RP2040"; - target_add_ram(t, 0x20000000, 0x40000); +#if defined(ENABLE_DEBUG) + if ((boot_magic >> 24) == 1) + DEBUG_WARN("Old Bootrom Version 1!\n"); +#endif +#define RP_MAX_TABLE_SIZE 0x80 + uint16_t *table = alloca(RP_MAX_TABLE_SIZE); + uint16_t table_offset = target_mem_read32( t, BOOTROM_MAGIC_ADDR + 4); + if (!table || target_mem_read(t, table, table_offset, RP_MAX_TABLE_SIZE)) + return false; + struct rp_priv_s *priv_storage = calloc(1, sizeof(struct rp_priv_s)); + if (!priv_storage) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return false; + } + if (rp2040_fill_table(priv_storage, table, RP_MAX_TABLE_SIZE)) { + free(priv_storage); + return false; + } + t->target_storage = (void*)priv_storage; + uint32_t bootsec[16]; + target_mem_read( t, bootsec, XIP_FLASH_START, sizeof( bootsec)); + int i; + for (i = 0; i < 16; i++) + if (bootsec[i]) + break; + uint32_t size = 8 * 1024 *1024; + if (i == 16) { + DEBUG_WARN("Use default size\n"); + } else { + /* Find out size of connected SPI Flash + * + * Flash needs valid content to be mapped + * Low flash is mirrored when flash size is exceeded + */ + while (size) { + uint32_t mirrorsec[16]; + target_mem_read(t, mirrorsec, XIP_FLASH_START + size, + sizeof( bootsec)); + if (memcmp(bootsec, mirrorsec, sizeof( bootsec))) + break; + size >>= 1; + } + } + rp_add_flash(t, XIP_FLASH_START, size << 1); + t->driver = RP_ID; + target_add_ram(t, SRAM_START, 0x40000); + target_add_ram(t, 0x51000000, 0x1000); + target_add_commands(t, rp_cmd_list, RP_ID); return true; } @@ -83,5 +368,4 @@ void rp_rescue_probe(ADIv5_AP_t *ap) t->priv = ap; t->priv_free = (void*)adiv5_ap_unref; t->driver = "Raspberry RP2040 Rescue(Attach to reset!)"; - } diff --git a/src/target/target.c b/src/target/target.c index eb43f375..c17e9158 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -584,6 +584,7 @@ void tc_printf(target *t, const char *fmt, ...) va_start(ap, fmt); t->tc->printf(t->tc, fmt, ap); + fflush(stdout); va_end(ap); } From 1b26ff560d0bc8b97c86d5bc0adb93535761eeb1 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Sat, 24 Apr 2021 14:48:58 +0200 Subject: [PATCH 6/6] rp.c: Add reset_usb_boot as monitor command --- src/target/rp.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/target/rp.c b/src/target/rp.c index 60db7e46..5d95085c 100644 --- a/src/target/rp.c +++ b/src/target/rp.c @@ -57,6 +57,7 @@ struct rp_priv_s { uint16_t flash_range_program; uint16_t _flash_flush_cache; uint16_t _flash_enter_cmd_xip; + uint16_t reset_usb_boot; bool is_prepared; uint32_t regs[0x20];/* Register playground*/ }; @@ -94,6 +95,9 @@ static bool rp2040_fill_table(struct rp_priv_s *priv, uint16_t *table, int max) case ('C' | ('X' << 8)): priv->_flash_enter_cmd_xip = data; break; + case ('U' | ('B' << 8)): + priv->reset_usb_boot = data; + break; default: check--; } @@ -102,10 +106,14 @@ static bool rp2040_fill_table(struct rp_priv_s *priv, uint16_t *table, int max) DEBUG_TARGET("connect %04x debug_trampoline %04x end %04x\n", priv->_connect_internal_flash, priv->_debug_trampoline, priv->_debug_trampoline_end); - return (check != 8); + return (check != 9); } -/* RP ROM functions for flash handling return void */ +/* RP ROM functions calls + * + * timout == 0: Do not wait for poll, use for reset_usb_boot() + * timeout > 400 (ms) : display spinner + */ static bool rp_rom_call(target *t, uint32_t *regs, uint32_t cmd, uint32_t timeout) { @@ -121,6 +129,8 @@ static bool rp_rom_call(target *t, uint32_t *regs, uint32_t cmd, target_regs_write(t, regs); /* start the target and wait for it to halt again */ target_halt_resume(t, false); + if (!timeout) + return false; DEBUG_INFO("Call cmd %04x\n", cmd); platform_timeout to; platform_timeout_set(&to, timeout); @@ -253,6 +263,21 @@ int rp_flash_write(struct target_flash *f, return 0; } +static bool rp_cmd_reset_usb_boot(target *t, int argc, const char *argv[]) +{ + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + if (argc > 2) { + ps->regs[1] = atoi(argv[2]); + } else if (argc < 3) { + ps->regs[0] = atoi(argv[1]); + } else { + ps->regs[0] = 0; + ps->regs[1] = 0; + } + rp_rom_call(t, ps->regs, ps->reset_usb_boot, 0); + return true; +} + static bool rp_cmd_erase_mass(target *t, int argc, const char *argv[]) { (void) argc; @@ -264,6 +289,7 @@ static bool rp_cmd_erase_mass(target *t, int argc, const char *argv[]) const struct command_s rp_cmd_list[] = { {"erase_mass", rp_cmd_erase_mass, "Erase entire flash memory"}, + {"reset_usb_boot", rp_cmd_reset_usb_boot, "Reboot the device into BOOTSEL mode"}, {NULL, NULL, NULL} };