From cae7a9a39bf0b2e66ac2fca029d0f62f5b20de7f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:20:52 +0000 Subject: [PATCH 001/130] riscv_debug: Begun building a scan handler for RISC-V debug v0.13 devices --- src/Makefile | 1 + src/target/jtag_devs.c | 8 +++--- src/target/riscv_debug.c | 53 ++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.h | 55 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/target/riscv_debug.c create mode 100644 src/target/riscv_debug.h diff --git a/src/Makefile b/src/Makefile index ca97790fa7b..fb6f2f5593c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,6 +63,7 @@ SRC = \ nrf91.c \ nxpke04.c \ remote.c \ + riscv_debug.c \ rp.c \ sam3x.c \ sam4l.c \ diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index a395ff35c75..d26eeb9239b 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -22,6 +22,7 @@ #include "general.h" #include "jtag_scan.h" #include "adiv5.h" +#include "riscv_debug.h" #include "jtag_devs.h" const jtag_dev_descr_s dev_descr[] = { @@ -119,9 +120,10 @@ const jtag_dev_descr_s dev_descr[] = { }, #ifdef ENABLE_DEBUG { - .idcode = 0x0000063dU, - .idmask = 0x00000fffU, - .descr = "Xambala: RVDBG013.", + .idcode = 0x0000563dU, + .idmask = 0x0fffffffU, + .descr = "RISC-V debug v0.13.", + .handler = riscv_debug_dtm_handler, }, { .idcode = 0x000007a3U, diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c new file mode 100644 index 00000000000..ac32110fa1a --- /dev/null +++ b/src/target/riscv_debug.c @@ -0,0 +1,53 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jtag_scan.h" +#include "riscv_debug.h" + +void riscv_debug_dtm_handler(const uint8_t dev_index) +{ + riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + dmi->idcode = jtag_devs[dev_index].jd_idcode; + dmi->version = RISCV_DEBUG_0_13; + dmi->dev_index = dev_index; + + DEBUG_INFO("RISC-V debug v0.13 DMI\n"); + + free(dmi); +} diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h new file mode 100644 index 00000000000..251bd413209 --- /dev/null +++ b/src/target/riscv_debug.h @@ -0,0 +1,55 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_RISCV_DEBUG_H +#define TARGET_RISCV_DEBUG_H + +#include + +typedef enum riscv_debug_version { + RISCV_DEBUG_0_11, + RISCV_DEBUG_0_13, + RISCV_DEBUG_1_0, +} riscv_debug_version_e; + +/* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ +typedef struct riscv_dmi { + uint32_t idcode; + riscv_debug_version_e version; + + uint8_t dev_index; +} riscv_dmi_s; + +void riscv_debug_dtm_handler(uint8_t dev_index); + +#endif /*TARGET_RISCV_DEBUG_H*/ From c748c6aabfa003b84e85e76b3972900d6c73ef25 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:27:57 +0000 Subject: [PATCH 002/130] riscv_debug: Created a stub for initialising a DMI --- src/target/riscv_debug.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index ac32110fa1a..f9df9e0853a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -35,6 +35,12 @@ #include "jtag_scan.h" #include "riscv_debug.h" +#define IR_DTMCS 0x10U +#define IR_DMI 0x11U +#define IR_BYPASS 0x1fU + +static bool riscv_dmi_init(riscv_dmi_s *dmi); + void riscv_debug_dtm_handler(const uint8_t dev_index) { riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); @@ -44,10 +50,15 @@ void riscv_debug_dtm_handler(const uint8_t dev_index) } dmi->idcode = jtag_devs[dev_index].jd_idcode; - dmi->version = RISCV_DEBUG_0_13; dmi->dev_index = dev_index; + if (!riscv_dmi_init(dmi)) + free(dmi); + jtag_dev_write_ir(dev_index, IR_BYPASS); +} +static bool riscv_dmi_init(riscv_dmi_s *const dmi) +{ + dmi->version = RISCV_DEBUG_0_13; DEBUG_INFO("RISC-V debug v0.13 DMI\n"); - - free(dmi); + return false; } From 9614e25cd0f04e0a834649805f68a63006445dfe Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:39:46 +0000 Subject: [PATCH 003/130] riscv_debug: Implemented a way to read/write the DTM control/status register --- src/Makefile | 121 ++++++++++++++++++------------------ src/target/riscv_debug.h | 2 + src/target/riscv_jtag_dtm.c | 47 ++++++++++++++ 3 files changed, 110 insertions(+), 60 deletions(-) create mode 100644 src/target/riscv_jtag_dtm.c diff --git a/src/Makefile b/src/Makefile index fb6f2f5593c..6f9a96578f1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,66 +23,67 @@ ifeq ($(ENABLE_DEBUG), 1) CFLAGS += -DENABLE_DEBUG endif -SRC = \ - adiv5.c \ - adiv5_jtag.c \ - adiv5_swd.c \ - command.c \ - cortex.c \ - cortexa.c \ - cortexm.c \ - crc32.c \ - efm32.c \ - exception.c \ - gdb_if.c \ - gdb_main.c \ - gdb_hostio.c \ - gdb_packet.c \ - gdb_reg.c \ - hex_utils.c \ - hc32l110.c \ - imxrt.c \ - jtag_devs.c \ - jtag_scan.c \ - lmi.c \ - lpc_common.c \ - lpc11xx.c \ - lpc17xx.c \ - lpc15xx.c \ - lpc40xx.c \ - lpc43xx.c \ - lpc546xx.c \ - lpc55xx.c \ - kinetis.c \ - main.c \ - maths_utils.c \ - morse.c \ - msp432e4.c \ - msp432p4.c \ - nrf51.c \ - nrf91.c \ - nxpke04.c \ - remote.c \ - riscv_debug.c \ - rp.c \ - sam3x.c \ - sam4l.c \ - samd.c \ - samx5x.c \ - sfdp.c \ - spi.c \ - stm32f1.c \ - ch32f1.c \ - stm32f4.c \ - stm32h5.c \ - stm32h7.c \ - stm32mp15.c \ - stm32l0.c \ - stm32l4.c \ - stm32g0.c \ - renesas.c \ - target.c \ - target_flash.c \ +SRC = \ + adiv5.c \ + adiv5_jtag.c \ + adiv5_swd.c \ + command.c \ + cortex.c \ + cortexa.c \ + cortexm.c \ + crc32.c \ + efm32.c \ + exception.c \ + gdb_if.c \ + gdb_main.c \ + gdb_hostio.c \ + gdb_packet.c \ + gdb_reg.c \ + hex_utils.c \ + hc32l110.c \ + imxrt.c \ + jtag_devs.c \ + jtag_scan.c \ + lmi.c \ + lpc_common.c \ + lpc11xx.c \ + lpc17xx.c \ + lpc15xx.c \ + lpc40xx.c \ + lpc43xx.c \ + lpc546xx.c \ + lpc55xx.c \ + kinetis.c \ + main.c \ + maths_utils.c \ + morse.c \ + msp432e4.c \ + msp432p4.c \ + nrf51.c \ + nrf91.c \ + nxpke04.c \ + remote.c \ + riscv_debug.c \ + riscv_jtag_dtm.c \ + rp.c \ + sam3x.c \ + sam4l.c \ + samd.c \ + samx5x.c \ + sfdp.c \ + spi.c \ + stm32f1.c \ + ch32f1.c \ + stm32f4.c \ + stm32h5.c \ + stm32h7.c \ + stm32mp15.c \ + stm32l0.c \ + stm32l4.c \ + stm32g0.c \ + renesas.c \ + target.c \ + target_flash.c \ target_probe.c ifeq (,$(filter all_platforms,$(MAKECMDGOALS))) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 251bd413209..03791447f5b 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -52,4 +52,6 @@ typedef struct riscv_dmi { void riscv_debug_dtm_handler(uint8_t dev_index); +uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); + #endif /*TARGET_RISCV_DEBUG_H*/ diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c new file mode 100644 index 00000000000..8c38476c37b --- /dev/null +++ b/src/target/riscv_jtag_dtm.c @@ -0,0 +1,47 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jtag_scan.h" +#include "riscv_debug.h" + +#define IR_DTMCS 0x10U + +/* Shift (read + write) the Debug Transport Module Control/Status (DTMCS) register */ +uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) +{ + jtag_dev_write_ir(dmi->dev_index, IR_DTMCS); + uint32_t status = 0; + jtag_dev_shift_dr(dmi->dev_index, (uint8_t *)&status, (const uint8_t *)&control, 32); + return status; +} From 65fc898d7a6bcc6b045bf502ac58a86ce79402e5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 21 Mar 2023 22:05:30 +0000 Subject: [PATCH 004/130] riscv_debug: Moved the JTAG-specific DTM handler into the JTAG DTM implementation file --- src/target/jtag_devs.c | 2 +- src/target/riscv_debug.c | 24 +----------------------- src/target/riscv_debug.h | 6 +++--- src/target/riscv_jtag_dtm.c | 19 ++++++++++++++++++- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index d26eeb9239b..7ae4b5f21bb 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -123,7 +123,7 @@ const jtag_dev_descr_s dev_descr[] = { .idcode = 0x0000563dU, .idmask = 0x0fffffffU, .descr = "RISC-V debug v0.13.", - .handler = riscv_debug_dtm_handler, + .handler = riscv_jtag_dtm_handler, }, { .idcode = 0x000007a3U, diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index f9df9e0853a..1a89fc75dd2 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -32,31 +32,9 @@ */ #include "general.h" -#include "jtag_scan.h" #include "riscv_debug.h" -#define IR_DTMCS 0x10U -#define IR_DMI 0x11U -#define IR_BYPASS 0x1fU - -static bool riscv_dmi_init(riscv_dmi_s *dmi); - -void riscv_debug_dtm_handler(const uint8_t dev_index) -{ - riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); - if (!dmi) { /* calloc failed: heap exhaustion */ - DEBUG_WARN("calloc: failed in %s\n", __func__); - return; - } - - dmi->idcode = jtag_devs[dev_index].jd_idcode; - dmi->dev_index = dev_index; - if (!riscv_dmi_init(dmi)) - free(dmi); - jtag_dev_write_ir(dev_index, IR_BYPASS); -} - -static bool riscv_dmi_init(riscv_dmi_s *const dmi) +bool riscv_dmi_init(riscv_dmi_s *const dmi) { dmi->version = RISCV_DEBUG_0_13; DEBUG_INFO("RISC-V debug v0.13 DMI\n"); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 03791447f5b..f1936f611db 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -35,6 +35,7 @@ #define TARGET_RISCV_DEBUG_H #include +#include typedef enum riscv_debug_version { RISCV_DEBUG_0_11, @@ -50,8 +51,7 @@ typedef struct riscv_dmi { uint8_t dev_index; } riscv_dmi_s; -void riscv_debug_dtm_handler(uint8_t dev_index); - -uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); +void riscv_jtag_dtm_handler(uint8_t dev_index); +bool riscv_dmi_init(riscv_dmi_s *dmi); #endif /*TARGET_RISCV_DEBUG_H*/ diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 8c38476c37b..15c5f83d60d 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -35,7 +35,24 @@ #include "jtag_scan.h" #include "riscv_debug.h" -#define IR_DTMCS 0x10U +#define IR_DTMCS 0x10U +#define IR_DMI 0x11U +#define IR_BYPASS 0x1fU + +void riscv_jtag_dtm_handler(const uint8_t dev_index) +{ + riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + dmi->idcode = jtag_devs[dev_index].jd_idcode; + dmi->dev_index = dev_index; + if (!riscv_dmi_init(dmi)) + free(dmi); + jtag_dev_write_ir(dev_index, IR_BYPASS); +} /* Shift (read + write) the Debug Transport Module Control/Status (DTMCS) register */ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) From cc6e1b322b69df388114d26992f0bf435e6c27bf Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:55:07 +0000 Subject: [PATCH 005/130] riscv_debug: Implemented support for reading and dispatching the DTM version --- src/target/riscv_debug.c | 10 ++++++++-- src/target/riscv_debug.h | 1 + src/target/riscv_jtag_dtm.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 1a89fc75dd2..e309453c8ac 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -36,7 +36,13 @@ bool riscv_dmi_init(riscv_dmi_s *const dmi) { - dmi->version = RISCV_DEBUG_0_13; - DEBUG_INFO("RISC-V debug v0.13 DMI\n"); + /* If we don't currently know how to talk to this DMI, warn and fail */ + if (dmi->version == RISCV_DEBUG_UNKNOWN) + return false; + if (dmi->version == RISCV_DEBUG_0_11) { + DEBUG_INFO("RISC-V debug v0.11 not presently supported\n"); + return false; + } + return false; } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index f1936f611db..d9975425137 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -38,6 +38,7 @@ #include typedef enum riscv_debug_version { + RISCV_DEBUG_UNKNOWN, RISCV_DEBUG_0_11, RISCV_DEBUG_0_13, RISCV_DEBUG_1_0, diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 15c5f83d60d..302171b259b 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -39,6 +39,15 @@ #define IR_DMI 0x11U #define IR_BYPASS 0x1fU +#define RV_DTMCS_NOOP 0x00000000U +#define RV_DTMCS_DMI_RESET 0x00010000U +#define RV_DTMCS_DMI_HARD_RESET 0x00020000U +#define RV_DTMCS_VERSION_MASK 0x0000000fU + +static bool riscv_jtag_dtm_init(riscv_dmi_s *dmi); +static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); +static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); + void riscv_jtag_dtm_handler(const uint8_t dev_index) { riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); @@ -49,11 +58,18 @@ void riscv_jtag_dtm_handler(const uint8_t dev_index) dmi->idcode = jtag_devs[dev_index].jd_idcode; dmi->dev_index = dev_index; - if (!riscv_dmi_init(dmi)) + if (!riscv_jtag_dtm_init(dmi)) free(dmi); jtag_dev_write_ir(dev_index, IR_BYPASS); } +static bool riscv_jtag_dtm_init(riscv_dmi_s *const dmi) +{ + const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); + dmi->version = riscv_dtmcs_version(dtmcs); + return riscv_dmi_init(dmi); +} + /* Shift (read + write) the Debug Transport Module Control/Status (DTMCS) register */ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) { @@ -62,3 +78,20 @@ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) jtag_dev_shift_dr(dmi->dev_index, (uint8_t *)&status, (const uint8_t *)&control, 32); return status; } + +static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) +{ + switch (dtmcs & RV_DTMCS_VERSION_MASK) { + case 0: + DEBUG_INFO("RISC-V debug v0.11 DMI\n"); + return RISCV_DEBUG_0_11; + case 1: + /* The stable version of the spec (v1.0) does not currently provide a way to distinguish between */ + /* a device built against v0.13 of the spec or v1.0 of the spec. They use the same value here. */ + DEBUG_INFO("RISC-V debug v0.13/v1.0 DMI\n"); + return RISCV_DEBUG_0_13; + } + DEBUG_INFO( + "Please report part with unknown RISC-V debug DMI version %x\n", (uint8_t)(dtmcs & RV_DTMCS_VERSION_MASK)); + return RISCV_DEBUG_UNKNOWN; +} From b8110046ab86bc0b103d9acc2aeffb1683c6c090 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:14:17 +0000 Subject: [PATCH 006/130] riscv_debug: Implemented reference counting and stub structures for the DMs and Harts --- src/target/riscv_debug.c | 28 ++++++++++++++++++++++++++++ src/target/riscv_debug.h | 17 +++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index e309453c8ac..dc6410b0a1a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -46,3 +46,31 @@ bool riscv_dmi_init(riscv_dmi_s *const dmi) return false; } + +static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) +{ + ++dmi->ref_count; +} + +static inline void riscv_dmi_unref(riscv_dmi_s *const dmi) +{ + --dmi->ref_count; + if (!dmi->ref_count) + free(dmi); +} + +void riscv_dm_ref(riscv_dm_s *const dbg_module) +{ + if (!dbg_module->ref_count) + riscv_dmi_ref(dbg_module->dmi_bus); + ++dbg_module->ref_count; +} + +void riscv_dm_unref(riscv_dm_s *const dbg_module) +{ + --dbg_module->ref_count; + if (!dbg_module->ref_count) { + riscv_dmi_unref(dbg_module->dmi_bus); + free(dbg_module); + } +} diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index d9975425137..4c22ec43a45 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -46,13 +46,30 @@ typedef enum riscv_debug_version { /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ typedef struct riscv_dmi { + uint32_t ref_count; + uint32_t idcode; riscv_debug_version_e version; uint8_t dev_index; } riscv_dmi_s; +/* This represents a specific Debug Module on the DMI bus */ +typedef struct riscv_dm { + uint32_t ref_count; + + riscv_dmi_s *dmi_bus; +} riscv_dm_s; + +/* This represents a specifc Hart on a DM */ +typedef struct riscv_hart { + riscv_dm_s *dbg_module; +} riscv_hart_s; + void riscv_jtag_dtm_handler(uint8_t dev_index); bool riscv_dmi_init(riscv_dmi_s *dmi); +void riscv_dm_ref(riscv_dm_s *dbg_module); +void riscv_dm_unref(riscv_dm_s *dbg_module); + #endif /*TARGET_RISCV_DEBUG_H*/ From ec311178da4e6c59e78a6a684e8f8d9f06e9a710 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:30:56 +0000 Subject: [PATCH 007/130] riscv_debug: Restructed riscv_dmi_init() and riscv_jtag_dtm_init() to prepare for scanning for DMs --- src/target/riscv_debug.c | 8 +++----- src/target/riscv_debug.h | 2 +- src/target/riscv_jtag_dtm.c | 10 ++++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index dc6410b0a1a..a4799b61dd6 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -34,17 +34,15 @@ #include "general.h" #include "riscv_debug.h" -bool riscv_dmi_init(riscv_dmi_s *const dmi) +void riscv_dmi_init(riscv_dmi_s *const dmi) { /* If we don't currently know how to talk to this DMI, warn and fail */ if (dmi->version == RISCV_DEBUG_UNKNOWN) - return false; + return; if (dmi->version == RISCV_DEBUG_0_11) { DEBUG_INFO("RISC-V debug v0.11 not presently supported\n"); - return false; + return; } - - return false; } static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 4c22ec43a45..ea8648fdf8f 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -67,7 +67,7 @@ typedef struct riscv_hart { } riscv_hart_s; void riscv_jtag_dtm_handler(uint8_t dev_index); -bool riscv_dmi_init(riscv_dmi_s *dmi); +void riscv_dmi_init(riscv_dmi_s *dmi); void riscv_dm_ref(riscv_dm_s *dbg_module); void riscv_dm_unref(riscv_dm_s *dbg_module); diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 302171b259b..25d6db7f64c 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -44,7 +44,7 @@ #define RV_DTMCS_DMI_HARD_RESET 0x00020000U #define RV_DTMCS_VERSION_MASK 0x0000000fU -static bool riscv_jtag_dtm_init(riscv_dmi_s *dmi); +static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); @@ -58,16 +58,18 @@ void riscv_jtag_dtm_handler(const uint8_t dev_index) dmi->idcode = jtag_devs[dev_index].jd_idcode; dmi->dev_index = dev_index; - if (!riscv_jtag_dtm_init(dmi)) + riscv_jtag_dtm_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) free(dmi); jtag_dev_write_ir(dev_index, IR_BYPASS); } -static bool riscv_jtag_dtm_init(riscv_dmi_s *const dmi) +static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) { const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); dmi->version = riscv_dtmcs_version(dtmcs); - return riscv_dmi_init(dmi); + riscv_dmi_init(dmi); } /* Shift (read + write) the Debug Transport Module Control/Status (DTMCS) register */ From a0d04ad00ec9b5cbbb9893c6783a23695a9a81d9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:49:58 +0000 Subject: [PATCH 008/130] riscv_debug: Handle the idle cycles component of the DTMCS register Co-Authored-By: Rafael Silva --- src/target/riscv_debug.h | 1 + src/target/riscv_jtag_dtm.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index ea8648fdf8f..4bb7cd2b0c4 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -52,6 +52,7 @@ typedef struct riscv_dmi { riscv_debug_version_e version; uint8_t dev_index; + uint8_t idle_cycles; } riscv_dmi_s; /* This represents a specific Debug Module on the DMI bus */ diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 25d6db7f64c..278ecb3130a 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -33,16 +33,24 @@ #include "general.h" #include "jtag_scan.h" +#include "jtagtap.h" #include "riscv_debug.h" #define IR_DTMCS 0x10U #define IR_DMI 0x11U #define IR_BYPASS 0x1fU -#define RV_DTMCS_NOOP 0x00000000U -#define RV_DTMCS_DMI_RESET 0x00010000U -#define RV_DTMCS_DMI_HARD_RESET 0x00020000U -#define RV_DTMCS_VERSION_MASK 0x0000000fU +#define RV_DTMCS_NOOP 0x00000000U +#define RV_DTMCS_DMI_RESET 0x00010000U +#define RV_DTMCS_DMI_HARD_RESET 0x00020000U +#define RV_DTMCS_IDLE_CYCLES_MASK 0x00007000U +#define RV_DTMCS_IDLE_CYCLES_SHIFT 12U +#define RV_DTMCS_DMI_STATUS_MASK 0x00000c00U +#define RV_DTMCS_DMI_STATUS_SHIFT 10U +#define RV_DTMCS_ADDRESS_MASK 0x000003f0U +#define RV_DTMCS_ADDRESS_SHIFT 4U + +#define RV_STATUS_VERSION_MASK 0x0000000fU static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); @@ -62,6 +70,7 @@ void riscv_jtag_dtm_handler(const uint8_t dev_index) /* If we failed to find any DMs or Harts, free the structure */ if (!dmi->ref_count) free(dmi); + /* Reset the JTAG machinery back to bypass to scan the next device in the chain */ jtag_dev_write_ir(dev_index, IR_BYPASS); } @@ -69,6 +78,7 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) { const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); dmi->version = riscv_dtmcs_version(dtmcs); + dmi->idle_cycles = (dtmcs & RV_DTMCS_IDLE_CYCLES_MASK) >> RV_DTMCS_IDLE_CYCLES_SHIFT; riscv_dmi_init(dmi); } @@ -83,7 +93,7 @@ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) { - switch (dtmcs & RV_DTMCS_VERSION_MASK) { + switch (dtmcs & RV_STATUS_VERSION_MASK) { case 0: DEBUG_INFO("RISC-V debug v0.11 DMI\n"); return RISCV_DEBUG_0_11; @@ -94,6 +104,6 @@ static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) return RISCV_DEBUG_0_13; } DEBUG_INFO( - "Please report part with unknown RISC-V debug DMI version %x\n", (uint8_t)(dtmcs & RV_DTMCS_VERSION_MASK)); + "Please report part with unknown RISC-V debug DMI version %x\n", (uint8_t)(dtmcs & RV_STATUS_VERSION_MASK)); return RISCV_DEBUG_UNKNOWN; } From ef82b6218a2a5c03b9f7e357a7ca970bef66a3f1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 07:02:33 +0000 Subject: [PATCH 009/130] riscv_debug: Implemented a way to read the DMI address space --- src/target/riscv_debug.c | 7 ++++++ src/target/riscv_debug.h | 10 +++++++-- src/target/riscv_jtag_dtm.c | 44 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index a4799b61dd6..03c1d08094c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -34,6 +34,8 @@ #include "general.h" #include "riscv_debug.h" +bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); + void riscv_dmi_init(riscv_dmi_s *const dmi) { /* If we don't currently know how to talk to this DMI, warn and fail */ @@ -45,6 +47,11 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) } } +bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + return dmi->read(dmi, address, value); +} + static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) { ++dmi->ref_count; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 4bb7cd2b0c4..c530609e183 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -44,8 +44,10 @@ typedef enum riscv_debug_version { RISCV_DEBUG_1_0, } riscv_debug_version_e; +typedef struct riscv_dmi riscv_dmi_s; + /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ -typedef struct riscv_dmi { +struct riscv_dmi { uint32_t ref_count; uint32_t idcode; @@ -53,7 +55,11 @@ typedef struct riscv_dmi { uint8_t dev_index; uint8_t idle_cycles; -} riscv_dmi_s; + uint8_t address_width; + uint8_t fault; + + bool (*read)(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +}; /* This represents a specific Debug Module on the DMI bus */ typedef struct riscv_dm { diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 278ecb3130a..7874da132b1 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -52,8 +52,16 @@ #define RV_STATUS_VERSION_MASK 0x0000000fU +#define RV_DMI_NOOP 0U +#define RV_DMI_READ 1U +#define RV_DMI_WRITE 2U +#define RV_DMI_SUCCESS 0U +#define RV_DMI_FAILURE 2U +#define RV_DMI_TOO_SOON 3U + static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); +static bool riscv_jtag_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); void riscv_jtag_dtm_handler(const uint8_t dev_index) @@ -79,6 +87,9 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); dmi->version = riscv_dtmcs_version(dtmcs); dmi->idle_cycles = (dtmcs & RV_DTMCS_IDLE_CYCLES_MASK) >> RV_DTMCS_IDLE_CYCLES_SHIFT; + + dmi->read = riscv_jtag_dmi_read; + riscv_dmi_init(dmi); } @@ -91,6 +102,39 @@ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) return status; } +static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, + const uint32_t data_in, uint32_t *const data_out) +{ + jtag_dev_s *device = &jtag_devs[dmi->dev_index]; + jtagtap_shift_dr(); + jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); + /* Shift out the 2 bits for the operation, and get the status bits for the previous back */ + uint8_t status = 0; + jtag_proc.jtagtap_tdi_tdo_seq(&status, false, &operation, 2); + /* Then the data component */ + if (data_out) + jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)data_out, false, (const uint8_t *)&data_in, 32); + else + jtag_proc.jtagtap_tdi_seq(false, (const uint8_t *)&data_in, 32); + /* And finally the address component */ + jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)&address, dmi->address_width); + jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); + jtagtap_return_idle(dmi->idle_cycles); + /* XXX: Need to deal with when status is 3. */ + dmi->fault = status; + return status == RV_DMI_SUCCESS; +} + +static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + bool result = riscv_shift_dmi(dmi, RV_DMI_READ, address, 0, 0); + if (result) + result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, value); + if (!result) + DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + return result; +} + static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) { switch (dtmcs & RV_STATUS_VERSION_MASK) { From f44170973a3d0cb8a376044af546f3d7d263e1fc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 07:05:21 +0000 Subject: [PATCH 010/130] riscv_debug: Implemented logic for iterating and discovering DMs --- src/target/riscv_debug.c | 72 +++++++++++++++++++++++++++++++++++-- src/target/riscv_debug.h | 5 +++ src/target/riscv_jtag_dtm.c | 8 +++-- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 03c1d08094c..028cc8a0755 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -34,7 +34,13 @@ #include "general.h" #include "riscv_debug.h" -bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU + +static void riscv_dm_init(riscv_dm_s *dbg_module); +static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static riscv_debug_version_e riscv_dm_version(uint32_t status); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -45,13 +51,75 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) DEBUG_INFO("RISC-V debug v0.11 not presently supported\n"); return; } + + /* Iterate through the possible DMs and probe implemented ones */ + /* The first DM is always at base address 0 */ + uint32_t base_addr = 0U; + do { + /* Read out the DM's status register */ + uint32_t dm_status = 0; + if (!riscv_dmi_read(dmi, base_addr + RV_DM_STATUS, &dm_status)) { + /* If we fail to read the status register, abort */ + break; + } + const riscv_debug_version_e dm_version = riscv_dm_version(dm_status); + + if (dm_version != RISCV_DEBUG_UNIMPL) { + riscv_dm_s *dbg_module = calloc(1, sizeof(*dbg_module)); + if (!dbg_module) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + /* Setup and try to discover the DM */ + dbg_module->dmi_bus = dmi; + dbg_module->base = base_addr; + dbg_module->version = dm_version; + riscv_dm_init(dbg_module); + /* If we failed to discover any Harts, free the structure */ + if (!dbg_module->ref_count) + free(dbg_module); + } + + /* Read out the address of the next DM */ + if (!riscv_dmi_read(dmi, base_addr + RV_DM_NEXT_DM, &base_addr)) { + /* If this fails then abort further scanning */ + DEBUG_INFO("Error while reading the next DM base address\n"); + break; + } + /* A new base address of 0 indicates this is the last one on the chain and we should stop. */ + } while (base_addr != 0U); } -bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +static void riscv_dm_init(riscv_dm_s *const dbg_module) +{ + (void)dbg_module; +} + +static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { return dmi->read(dmi, address, value); } +static riscv_debug_version_e riscv_dm_version(const uint32_t status) +{ + switch (status & RV_STATUS_VERSION_MASK) { + case 0: + return RISCV_DEBUG_UNIMPL; + case 1: + DEBUG_INFO("RISC-V debug v0.11 DM\n"); + return RISCV_DEBUG_0_11; + case 2: + DEBUG_INFO("RISC-V debug v0.13 DM\n"); + return RISCV_DEBUG_0_13; + case 3: + DEBUG_INFO("RISC-V debug v1.0 DM\n"); + return RISCV_DEBUG_1_0; + } + DEBUG_INFO( + "Please report part with unknown RISC-V debug DM version %x\n", (uint8_t)(status & RV_STATUS_VERSION_MASK)); + return RISCV_DEBUG_UNKNOWN; +} + static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) { ++dmi->ref_count; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index c530609e183..a9981f99c1e 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -39,6 +39,7 @@ typedef enum riscv_debug_version { RISCV_DEBUG_UNKNOWN, + RISCV_DEBUG_UNIMPL, RISCV_DEBUG_0_11, RISCV_DEBUG_0_13, RISCV_DEBUG_1_0, @@ -66,6 +67,8 @@ typedef struct riscv_dm { uint32_t ref_count; riscv_dmi_s *dmi_bus; + uint32_t base; + riscv_debug_version_e version; } riscv_dm_s; /* This represents a specifc Hart on a DM */ @@ -73,6 +76,8 @@ typedef struct riscv_hart { riscv_dm_s *dbg_module; } riscv_hart_s; +#define RV_STATUS_VERSION_MASK 0x0000000fU + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 7874da132b1..b708a656289 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -50,8 +50,6 @@ #define RV_DTMCS_ADDRESS_MASK 0x000003f0U #define RV_DTMCS_ADDRESS_SHIFT 4U -#define RV_STATUS_VERSION_MASK 0x0000000fU - #define RV_DMI_NOOP 0U #define RV_DMI_READ 1U #define RV_DMI_WRITE 2U @@ -72,6 +70,7 @@ void riscv_jtag_dtm_handler(const uint8_t dev_index) return; } + /* Setup and try to discover the DMI bus */ dmi->idcode = jtag_devs[dev_index].jd_idcode; dmi->dev_index = dev_index; riscv_jtag_dtm_init(dmi); @@ -86,7 +85,12 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) { const uint32_t dtmcs = riscv_shift_dtmcs(dmi, RV_DTMCS_NOOP); dmi->version = riscv_dtmcs_version(dtmcs); + /* Configure the TAP idle cylces based on what we've read */ dmi->idle_cycles = (dtmcs & RV_DTMCS_IDLE_CYCLES_MASK) >> RV_DTMCS_IDLE_CYCLES_SHIFT; + /* And figure out how many address bits the DMI address space has */ + dmi->address_width = (dtmcs & RV_DTMCS_ADDRESS_MASK) >> RV_DTMCS_ADDRESS_SHIFT; + /* Switch into DMI access mode for speed */ + jtag_dev_write_ir(dmi->dev_index, IR_DMI); dmi->read = riscv_jtag_dmi_read; From 322931cf01d1ded8b2923443067ae1bd832a94d1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 08:37:34 +0000 Subject: [PATCH 011/130] riscv_debug: Begun implementing the hart enumeration process --- src/target/riscv_debug.c | 34 +++++++++++++++++++++++++++++++++- src/target/riscv_debug.h | 1 + src/target/riscv_jtag_dtm.c | 18 +++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 028cc8a0755..1a51cb50e4b 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -38,8 +38,15 @@ #define RV_DM_STATUS 0x11U #define RV_DM_NEXT_DM 0x1dU +#define RV_DM_CTRL_ACTIVE 0x00000001U +#define RV_DM_CTRL_HARTSEL_MASK 0x0003ffc0U +#define RV_DM_CTRL_HARTSEL_SHIFT 6U + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); +static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); +static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); void riscv_dmi_init(riscv_dmi_s *const dmi) @@ -92,7 +99,17 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) static void riscv_dm_init(riscv_dm_s *const dbg_module) { - (void)dbg_module; + /* Attempt to activate the DM */ + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, RV_DM_CTRL_ACTIVE)) + return; + /* Now find out how many hartsel bits are present */ + uint32_t control = RV_DM_CTRL_ACTIVE | RV_DM_CTRL_HARTSEL_MASK; + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_CONTROL, &control)) + return; + /* Extract the maxinium number of harts present and iterate through the harts */ + const uint32_t harts_max = (control & RV_DM_CTRL_HARTSEL_MASK) >> RV_DM_CTRL_HARTSEL_SHIFT; + for (uint32_t hart = 0; hart <= harts_max; ++hart) { + } } static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) @@ -100,6 +117,21 @@ static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint3 return dmi->read(dmi, address, value); } +static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + return dmi->write(dmi, address, value); +} + +static inline bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) +{ + return riscv_dmi_read(dbg_module->dmi_bus, dbg_module->base + address, value); +} + +static inline bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, const uint32_t value) +{ + return riscv_dmi_write(dbg_module->dmi_bus, dbg_module->base + address, value); +} + static riscv_debug_version_e riscv_dm_version(const uint32_t status) { switch (status & RV_STATUS_VERSION_MASK) { diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index a9981f99c1e..38970b55a16 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -60,6 +60,7 @@ struct riscv_dmi { uint8_t fault; bool (*read)(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); + bool (*write)(riscv_dmi_s *dmi, uint32_t address, uint32_t value); }; /* This represents a specific Debug Module on the DMI bus */ diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index b708a656289..0641e1a865b 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -60,6 +60,7 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); static bool riscv_jtag_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool riscv_jtag_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); void riscv_jtag_dtm_handler(const uint8_t dev_index) @@ -93,6 +94,7 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) jtag_dev_write_ir(dmi->dev_index, IR_DMI); dmi->read = riscv_jtag_dmi_read; + dmi->write = riscv_jtag_dmi_write; riscv_dmi_init(dmi); } @@ -131,14 +133,28 @@ static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, con static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - bool result = riscv_shift_dmi(dmi, RV_DMI_READ, address, 0, 0); + /* Setup the location to read from */ + bool result = riscv_shift_dmi(dmi, RV_DMI_READ, address, 0, NULL); if (result) + /* If that worked, read back the value and check the operation status */ result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, value); if (!result) DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); return result; } +static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + /* Write a value to the requested register */ + bool result = riscv_shift_dmi(dmi, RV_DMI_WRITE, address, value, NULL); + if (result) + /* If that worked, read back the operation status to ensure the write actually worked */ + result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, NULL); + if (!result) + DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + return result; +} + static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) { switch (dtmcs & RV_STATUS_VERSION_MASK) { From 3d911d65ccf62e322eea5ff19d0427435f5cf6eb Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 11:04:37 +0000 Subject: [PATCH 012/130] riscv_debug: Implemented enumeration of the harts associated with a DM --- src/target/riscv_debug.c | 67 +++++++++++++++++++++++++++++++++++----- src/target/riscv_debug.h | 4 +-- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 1a51cb50e4b..981187aefd5 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -32,19 +32,29 @@ */ #include "general.h" +#include "target_internal.h" #include "riscv_debug.h" #define RV_DM_CONTROL 0x10U #define RV_DM_STATUS 0x11U #define RV_DM_NEXT_DM 0x1dU -#define RV_DM_CTRL_ACTIVE 0x00000001U -#define RV_DM_CTRL_HARTSEL_MASK 0x0003ffc0U -#define RV_DM_CTRL_HARTSEL_SHIFT 6U +#define RV_DM_CTRL_ACTIVE 0x00000001U +#define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U +#define RV_DM_CTRL_HARTSELLO_MASK 0x03ff0000U +#define RV_DM_CTRL_HARTSELHI_MASK 0x0000ffc0U +#define RV_DM_CTRL_HARTSELLO_SHIFT 16U +#define RV_DM_CTRL_HARTSELHI_SHIFT 4U + +#define RV_DM_STAT_NON_EXISTENT 0x00004000U static void riscv_dm_init(riscv_dm_s *dbg_module); +static bool riscv_hart_init(riscv_hart_s *hart); +static void riscv_hart_free(void *priv); static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); static bool riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); +static void riscv_dm_ref(riscv_dm_s *dbg_module); +static void riscv_dm_unref(riscv_dm_s *dbg_module); static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); @@ -106,12 +116,53 @@ static void riscv_dm_init(riscv_dm_s *const dbg_module) uint32_t control = RV_DM_CTRL_ACTIVE | RV_DM_CTRL_HARTSEL_MASK; if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_CONTROL, &control)) return; - /* Extract the maxinium number of harts present and iterate through the harts */ - const uint32_t harts_max = (control & RV_DM_CTRL_HARTSEL_MASK) >> RV_DM_CTRL_HARTSEL_SHIFT; - for (uint32_t hart = 0; hart <= harts_max; ++hart) { + /* Extract the maximum number of harts present and iterate through the harts */ + const uint32_t harts_max = ((control & RV_DM_CTRL_HARTSELLO_MASK) >> RV_DM_CTRL_HARTSELLO_SHIFT) | + ((control & RV_DM_CTRL_HARTSELHI_MASK) << RV_DM_CTRL_HARTSELHI_SHIFT); + for (uint32_t hart_idx = 0; hart_idx <= harts_max; ++hart_idx) { + /* Select the hart */ + control = ((hart_idx << RV_DM_CTRL_HARTSELLO_SHIFT) & RV_DM_CTRL_HARTSELLO_MASK) | + ((hart_idx >> RV_DM_CTRL_HARTSELHI_SHIFT) & RV_DM_CTRL_HARTSELHI_MASK) | RV_DM_CTRL_ACTIVE; + uint32_t status = 0; + if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_STATUS, &status)) + return; + /* If the hart doesn't exist, the spec says to terminate scan */ + if (status & RV_DM_STAT_NON_EXISTENT) + break; + + riscv_hart_s *hart = calloc(1, sizeof(*hart)); + if (!hart) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + /* Setup the hart structure and discover the target core */ + hart->dbg_module = dbg_module; + hart->hartsel = control; + if (!riscv_hart_init(hart)) + free(hart); } } +static bool riscv_hart_init(riscv_hart_s *const hart) +{ + target_s *target = target_new(); + if (!target) + return false; + + riscv_dm_ref(hart->dbg_module); + target->cpuid = hart->dbg_module->dmi_bus->idcode; + target->driver = "RISC-V"; + target->priv = hart; + target->priv_free = riscv_hart_free; + return true; +} + +static void riscv_hart_free(void *const priv) +{ + riscv_dm_unref(((riscv_hart_s *)priv)->dbg_module); + free(priv); +} + static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { return dmi->read(dmi, address, value); @@ -164,14 +215,14 @@ static inline void riscv_dmi_unref(riscv_dmi_s *const dmi) free(dmi); } -void riscv_dm_ref(riscv_dm_s *const dbg_module) +static void riscv_dm_ref(riscv_dm_s *const dbg_module) { if (!dbg_module->ref_count) riscv_dmi_ref(dbg_module->dmi_bus); ++dbg_module->ref_count; } -void riscv_dm_unref(riscv_dm_s *const dbg_module) +static void riscv_dm_unref(riscv_dm_s *const dbg_module) { --dbg_module->ref_count; if (!dbg_module->ref_count) { diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 38970b55a16..1a42f38daa1 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -75,6 +75,7 @@ typedef struct riscv_dm { /* This represents a specifc Hart on a DM */ typedef struct riscv_hart { riscv_dm_s *dbg_module; + uint32_t hartsel; } riscv_hart_s; #define RV_STATUS_VERSION_MASK 0x0000000fU @@ -82,7 +83,4 @@ typedef struct riscv_hart { void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); -void riscv_dm_ref(riscv_dm_s *dbg_module); -void riscv_dm_unref(riscv_dm_s *dbg_module); - #endif /*TARGET_RISCV_DEBUG_H*/ From 96285557d2897d5fcca2454ba4380e8586f68650 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 13:39:47 +0000 Subject: [PATCH 013/130] riscv_debug: Implemented support for grabbing the hart's address bus width and system bus version --- src/target/riscv_debug.c | 37 +++++++++++++++++++++++++++++++------ src/target/riscv_debug.h | 3 +++ src/target/riscv_jtag_dtm.c | 6 +++--- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 981187aefd5..a522356b98c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -35,9 +35,10 @@ #include "target_internal.h" #include "riscv_debug.h" -#define RV_DM_CONTROL 0x10U -#define RV_DM_STATUS 0x11U -#define RV_DM_NEXT_DM 0x1dU +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU +#define RV_DM_SYS_BUS_CTRLSTATUS 0x38U #define RV_DM_CTRL_ACTIVE 0x00000001U #define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U @@ -48,6 +49,9 @@ #define RV_DM_STAT_NON_EXISTENT 0x00004000U +#define RV_DM_SYS_BUS_ADDRESS_MASK 0x00000fe0U +#define RV_DM_SYS_BUS_ADDRESS_SHIFT 5U + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_hart_init(riscv_hart_s *hart); static void riscv_hart_free(void *priv); @@ -58,6 +62,7 @@ static void riscv_dm_unref(riscv_dm_s *dbg_module); static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); +static riscv_debug_version_e riscv_sys_bus_version(uint32_t status); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -137,6 +142,7 @@ static void riscv_dm_init(riscv_dm_s *const dbg_module) } /* Setup the hart structure and discover the target core */ hart->dbg_module = dbg_module; + hart->hart_idx = hart_idx; hart->hartsel = control; if (!riscv_hart_init(hart)) free(hart); @@ -145,6 +151,12 @@ static void riscv_dm_init(riscv_dm_s *const dbg_module) static bool riscv_hart_init(riscv_hart_s *const hart) { + uint32_t bus_status = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYS_BUS_CTRLSTATUS, &bus_status)) + return false; + hart->version = riscv_sys_bus_version(bus_status); + hart->address_width = (bus_status & RV_DM_SYS_BUS_ADDRESS_MASK) >> RV_DM_SYS_BUS_ADDRESS_SHIFT; + target_s *target = target_new(); if (!target) return false; @@ -185,7 +197,8 @@ static inline bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, static riscv_debug_version_e riscv_dm_version(const uint32_t status) { - switch (status & RV_STATUS_VERSION_MASK) { + uint8_t version = status & RV_STATUS_VERSION_MASK; + switch (version) { case 0: return RISCV_DEBUG_UNIMPL; case 1: @@ -198,8 +211,20 @@ static riscv_debug_version_e riscv_dm_version(const uint32_t status) DEBUG_INFO("RISC-V debug v1.0 DM\n"); return RISCV_DEBUG_1_0; } - DEBUG_INFO( - "Please report part with unknown RISC-V debug DM version %x\n", (uint8_t)(status & RV_STATUS_VERSION_MASK)); + DEBUG_INFO("Please report part with unknown RISC-V debug DM version %x\n", version); + return RISCV_DEBUG_UNKNOWN; +} + +static riscv_debug_version_e riscv_sys_bus_version(uint32_t status) +{ + uint8_t version = (status >> 29) & RV_STATUS_VERSION_MASK; + switch (version) { + case 0: + return RISCV_DEBUG_0_11; + case 1: + return RISCV_DEBUG_0_13; + } + DEBUG_INFO("Please report part with unknown RISC-V system bus version %x\n", version); return RISCV_DEBUG_UNKNOWN; } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 1a42f38daa1..ea6a1100644 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -75,7 +75,10 @@ typedef struct riscv_dm { /* This represents a specifc Hart on a DM */ typedef struct riscv_hart { riscv_dm_s *dbg_module; + uint32_t hart_idx; uint32_t hartsel; + riscv_debug_version_e version; + uint8_t address_width; } riscv_hart_s; #define RV_STATUS_VERSION_MASK 0x0000000fU diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 0641e1a865b..012f9802431 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -157,7 +157,8 @@ static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) { - switch (dtmcs & RV_STATUS_VERSION_MASK) { + uint8_t version = dtmcs & RV_STATUS_VERSION_MASK; + switch (version) { case 0: DEBUG_INFO("RISC-V debug v0.11 DMI\n"); return RISCV_DEBUG_0_11; @@ -167,7 +168,6 @@ static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) DEBUG_INFO("RISC-V debug v0.13/v1.0 DMI\n"); return RISCV_DEBUG_0_13; } - DEBUG_INFO( - "Please report part with unknown RISC-V debug DMI version %x\n", (uint8_t)(dtmcs & RV_STATUS_VERSION_MASK)); + DEBUG_INFO("Please report part with unknown RISC-V debug DMI version %x\n", version); return RISCV_DEBUG_UNKNOWN; } From dbb7446ff9f5a0501099cb159cc8b71c8082c32d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 13:59:19 +0000 Subject: [PATCH 014/130] riscv_debug: Implemented halt and resume support in preparation for reading CSRs and memory from a hart --- src/target/riscv_debug.c | 46 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index a522356b98c..2575df39425 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -44,10 +44,14 @@ #define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U #define RV_DM_CTRL_HARTSELLO_MASK 0x03ff0000U #define RV_DM_CTRL_HARTSELHI_MASK 0x0000ffc0U +#define RV_DM_CTRL_HALT_REQ 0x80000000U +#define RV_DM_CTRL_RESUME_REQ 0x40000000U #define RV_DM_CTRL_HARTSELLO_SHIFT 16U #define RV_DM_CTRL_HARTSELHI_SHIFT 4U -#define RV_DM_STAT_NON_EXISTENT 0x00004000U +#define RV_DM_STAT_ALL_RESUME_ACK 0x00020000U +#define RV_DM_STAT_NON_EXISTENT 0x00004000U +#define RV_DM_STAT_ALL_HALTED 0x00000200U #define RV_DM_SYS_BUS_ADDRESS_MASK 0x00000fe0U #define RV_DM_SYS_BUS_ADDRESS_SHIFT 5U @@ -64,6 +68,9 @@ static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint3 static riscv_debug_version_e riscv_dm_version(uint32_t status); static riscv_debug_version_e riscv_sys_bus_version(uint32_t status); +static void riscv_halt_request(target_s *target); +static void riscv_halt_resume(target_s *target, bool step); + void riscv_dmi_init(riscv_dmi_s *const dmi) { /* If we don't currently know how to talk to this DMI, warn and fail */ @@ -166,6 +173,10 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->driver = "RISC-V"; target->priv = hart; target->priv_free = riscv_hart_free; + + target->halt_request = riscv_halt_request; + target->halt_resume = riscv_halt_resume; + return true; } @@ -255,3 +266,36 @@ static void riscv_dm_unref(riscv_dm_s *const dbg_module) free(dbg_module); } } + +static void riscv_halt_request(target_s *const target) +{ + riscv_hart_s *const hart = (riscv_hart_s *)target->priv; + /* Request the hart to halt */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HALT_REQ)) + return; + uint32_t status = 0; + /* Poll for the hart to become halted */ + while (!(status & RV_DM_STAT_ALL_HALTED)) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) + return; + } + /* Clear the request now we've got it halted */ + (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); +} + +static void riscv_halt_resume(target_s *target, const bool step) +{ + (void)step; + riscv_hart_s *const hart = (riscv_hart_s *)target->priv; + /* Request the hart to resume */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) + return; + uint32_t status = 0; + /* Poll for the hart to become resumed */ + while (!(status & RV_DM_STAT_ALL_RESUME_ACK)) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) + return; + } + /* Clear the request now we've got it resumed */ + (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); +} From 324ba8b35f0286e7aec676d26f03bef6527cb618 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 14:23:44 +0000 Subject: [PATCH 015/130] riscv_debug: Implemented readout of CSRs and reading the 4 hart identification registers --- src/target/riscv_debug.c | 42 ++++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.h | 6 ++++++ 2 files changed, 48 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 2575df39425..b3724b7ac01 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -35,8 +35,11 @@ #include "target_internal.h" #include "riscv_debug.h" +#define RV_DM_DATA0 0x04U #define RV_DM_CONTROL 0x10U #define RV_DM_STATUS 0x11U +#define RV_DM_ABST_CTRLSTATUS 0x16U +#define RV_DM_ABST_COMMAND 0x17U #define RV_DM_NEXT_DM 0x1dU #define RV_DM_SYS_BUS_CTRLSTATUS 0x38U @@ -56,6 +59,21 @@ #define RV_DM_SYS_BUS_ADDRESS_MASK 0x00000fe0U #define RV_DM_SYS_BUS_ADDRESS_SHIFT 5U +#define RV_DM_ABST_STATUS_BUSY 0x00001000U +#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U + +#define RV_REG_READ 0x00000000U +#define RV_REG_WRITE 0x00010000U +#define RV_REG_XFER 0x00020000U +#define RV_REG_ACCESS_32_BIT 0x00200000U +#define RV_REG_ACCESS_64_BIT 0x00300000U +#define RV_REG_ACCESS_128_BIT 0x00400000U + +#define RV_VENDOR_ID 0xf11U +#define RV_ARCH_ID 0xf12U +#define RV_IMPL_ID 0xf13U +#define RV_HART_ID 0xf14U + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_hart_init(riscv_hart_s *hart); static void riscv_hart_free(void *priv); @@ -174,6 +192,17 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->priv = hart; target->priv_free = riscv_hart_free; + riscv_halt_request(target); + riscv_csr_read(hart, RV_VENDOR_ID, &hart->vendorid); + /* XXX: These will technically go wrong on rv64 - need some way to deal with that. */ + riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); + riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); + riscv_csr_read(hart, RV_HART_ID, &hart->hartid); + riscv_halt_resume(target, false); + + DEBUG_INFO("Hart %" PRIu32 ": %u-bit RISC-V (arch = %" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 "\n", + hart->hartid, hart->address_width, hart->archid, hart->vendorid, hart->implid); + target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -267,6 +296,19 @@ static void riscv_dm_unref(riscv_dm_s *const dbg_module) } } +bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) +{ + if (!riscv_dm_write( + hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_REG_XFER | RV_REG_ACCESS_32_BIT | reg)) + return false; + uint32_t status = RV_DM_ABST_STATUS_BUSY; + while (status & RV_DM_ABST_STATUS_BUSY) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &status)) + return false; + } + return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, (uint32_t *)data); +} + static void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = (riscv_hart_s *)target->priv; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index ea6a1100644..a5d1c937cbc 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -79,11 +79,17 @@ typedef struct riscv_hart { uint32_t hartsel; riscv_debug_version_e version; uint8_t address_width; + + uint32_t vendorid; + uint32_t archid; + uint32_t implid; + uint32_t hartid; } riscv_hart_s; #define RV_STATUS_VERSION_MASK 0x0000000fU void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); +bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); #endif /*TARGET_RISCV_DEBUG_H*/ From 741a865faea8ca2b0aaf60c7a0eca551ad72f407 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 14:44:25 +0000 Subject: [PATCH 016/130] riscv_debug: Implemented readout of the ISA register to determine address width --- src/target/riscv_debug.c | 59 +++++++++++++++++++--------------------- src/target/riscv_debug.h | 3 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index b3724b7ac01..b49b3171a3c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -35,13 +35,12 @@ #include "target_internal.h" #include "riscv_debug.h" -#define RV_DM_DATA0 0x04U -#define RV_DM_CONTROL 0x10U -#define RV_DM_STATUS 0x11U -#define RV_DM_ABST_CTRLSTATUS 0x16U -#define RV_DM_ABST_COMMAND 0x17U -#define RV_DM_NEXT_DM 0x1dU -#define RV_DM_SYS_BUS_CTRLSTATUS 0x38U +#define RV_DM_DATA0 0x04U +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_ABST_CTRLSTATUS 0x16U +#define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_NEXT_DM 0x1dU #define RV_DM_CTRL_ACTIVE 0x00000001U #define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U @@ -56,9 +55,6 @@ #define RV_DM_STAT_NON_EXISTENT 0x00004000U #define RV_DM_STAT_ALL_HALTED 0x00000200U -#define RV_DM_SYS_BUS_ADDRESS_MASK 0x00000fe0U -#define RV_DM_SYS_BUS_ADDRESS_SHIFT 5U - #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U @@ -69,6 +65,7 @@ #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_ISA 0x301U #define RV_VENDOR_ID 0xf11U #define RV_ARCH_ID 0xf12U #define RV_IMPL_ID 0xf13U @@ -84,7 +81,6 @@ static void riscv_dm_unref(riscv_dm_s *dbg_module); static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); -static riscv_debug_version_e riscv_sys_bus_version(uint32_t status); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); @@ -174,25 +170,39 @@ static void riscv_dm_init(riscv_dm_s *const dbg_module) } } -static bool riscv_hart_init(riscv_hart_s *const hart) +static uint8_t riscv_isa_address_width(const uint32_t isa) { - uint32_t bus_status = 0; - if (!riscv_dm_read(hart->dbg_module, RV_DM_SYS_BUS_CTRLSTATUS, &bus_status)) - return false; - hart->version = riscv_sys_bus_version(bus_status); - hart->address_width = (bus_status & RV_DM_SYS_BUS_ADDRESS_MASK) >> RV_DM_SYS_BUS_ADDRESS_SHIFT; + switch (isa >> 30U) { + case 1: + return 32U; + case 2: + return 64U; + case 3: + return 128U; + } + DEBUG_INFO("Unknown address width, defaulting to 32\n"); + return 32U; +} +static bool riscv_hart_init(riscv_hart_s *const hart) +{ + /* Allocate a new target */ target_s *target = target_new(); if (!target) return false; + /* Grab a reference to the DMI and DM structurues and do preliminary setup of the target structure */ riscv_dm_ref(hart->dbg_module); target->cpuid = hart->dbg_module->dmi_bus->idcode; target->driver = "RISC-V"; target->priv = hart; target->priv_free = riscv_hart_free; + /* Request halt and read certain key registers */ riscv_halt_request(target); + uint32_t isa = 0; + riscv_csr_read(hart, RV_ISA, &isa); + hart->access_width = riscv_isa_address_width(isa); riscv_csr_read(hart, RV_VENDOR_ID, &hart->vendorid); /* XXX: These will technically go wrong on rv64 - need some way to deal with that. */ riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); @@ -201,7 +211,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) riscv_halt_resume(target, false); DEBUG_INFO("Hart %" PRIu32 ": %u-bit RISC-V (arch = %" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 "\n", - hart->hartid, hart->address_width, hart->archid, hart->vendorid, hart->implid); + hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid); target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -255,19 +265,6 @@ static riscv_debug_version_e riscv_dm_version(const uint32_t status) return RISCV_DEBUG_UNKNOWN; } -static riscv_debug_version_e riscv_sys_bus_version(uint32_t status) -{ - uint8_t version = (status >> 29) & RV_STATUS_VERSION_MASK; - switch (version) { - case 0: - return RISCV_DEBUG_0_11; - case 1: - return RISCV_DEBUG_0_13; - } - DEBUG_INFO("Please report part with unknown RISC-V system bus version %x\n", version); - return RISCV_DEBUG_UNKNOWN; -} - static inline void riscv_dmi_ref(riscv_dmi_s *const dmi) { ++dmi->ref_count; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index a5d1c937cbc..7cf02a8b1ec 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -77,8 +77,7 @@ typedef struct riscv_hart { riscv_dm_s *dbg_module; uint32_t hart_idx; uint32_t hartsel; - riscv_debug_version_e version; - uint8_t address_width; + uint8_t access_width; uint32_t vendorid; uint32_t archid; From 09d8c12d5edd05fc2d01af82f426e6d8da07f291 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 01:48:03 +0000 Subject: [PATCH 017/130] riscv_debug: Implemented the ability for riscv_reg_read() to do 128-, 64-, and 32-bit reads as required --- src/target/riscv_debug.c | 44 +++++++++++++++++++++++++++++++++++----- src/target/riscv_debug.h | 19 +++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index b49b3171a3c..efca6abb7b1 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -36,6 +36,9 @@ #include "riscv_debug.h" #define RV_DM_DATA0 0x04U +#define RV_DM_DATA1 0x05U +#define RV_DM_DATA2 0x06U +#define RV_DM_DATA3 0x07U #define RV_DM_CONTROL 0x10U #define RV_DM_STATUS 0x11U #define RV_DM_ABST_CTRLSTATUS 0x16U @@ -293,17 +296,48 @@ static void riscv_dm_unref(riscv_dm_s *const dbg_module) } } -bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) +static uint32_t riscv_hart_access_width(const riscv_hart_s *const hart) +{ + if (hart->access_width == 128U) + return RV_REG_ACCESS_128_BIT; + if (hart->access_width == 64U) + return RV_REG_ACCESS_64_BIT; + return RV_REG_ACCESS_32_BIT; +} + +static bool riscv_csr_wait_complete(riscv_hart_s *const hart) { - if (!riscv_dm_write( - hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_REG_XFER | RV_REG_ACCESS_32_BIT | reg)) - return false; uint32_t status = RV_DM_ABST_STATUS_BUSY; while (status & RV_DM_ABST_STATUS_BUSY) { if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &status)) return false; } - return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, (uint32_t *)data); + /* Shift out and mask off the command status, then reset the status on the Hart */ + hart->status = (status >> 8U) & RISCV_HART_OTHER; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, RISCV_HART_OTHER << 8U)) + return false; + /* If the command failed, return the failure */ + return hart->status == RISCV_HART_NO_ERROR; +} + +bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) +{ + /* Set up the register read and wait for it to complete */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_REG_READ | RV_REG_XFER | riscv_hart_access_width(hart) | reg) || + !riscv_csr_wait_complete(hart)) + return false; + uint32_t *const value = (uint32_t *)data; + /* If we're doing a 128-bit read, grab the upper-most 2 uint32_t's */ + if (hart->access_width == 128U && + !(riscv_dm_read(hart->dbg_module, RV_DM_DATA3, value + 3) && + riscv_dm_read(hart->dbg_module, RV_DM_DATA2, value + 2))) + return false; + /* If we're doing at least a 64-bit read, grab the next uint32_t */ + if (hart->access_width >= 64U && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, value + 1)) + return false; + /* Finally grab the last and lowest uint32_t */ + return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, value); } static void riscv_halt_request(target_s *const target) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 7cf02a8b1ec..6b3f9b77650 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -45,6 +45,24 @@ typedef enum riscv_debug_version { RISCV_DEBUG_1_0, } riscv_debug_version_e; +/* This enum describes the Hart status (eg after a CSR read/write) */ +typedef enum riscv_hart_status { + /* The Hart is in a good state */ + RISCV_HART_NO_ERROR = 0, + /* The Hart was bust when the status was read */ + RISCV_HART_BUSY = 1, + /* The operation requested of the Hart was not supported */ + RISCV_HART_NOT_SUPP = 2, + /* An exception occured on the Hart while running the operation */ + RISCV_HART_EXCEPTION = 3, + /* The Hart is in the wrong state for the requested operation */ + RISCV_HART_WRONG_STATE = 4, + /* The operation triggered a Hart bus error (bad alignment, access size, or timeout) */ + RISCV_HART_BUS_ERROR = 5, + /* The operation failed for other (unknown) reasons */ + RISCV_HART_OTHER = 7, +} riscv_hart_status_e; + typedef struct riscv_dmi riscv_dmi_s; /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ @@ -78,6 +96,7 @@ typedef struct riscv_hart { uint32_t hart_idx; uint32_t hartsel; uint8_t access_width; + riscv_hart_status_e status; uint32_t vendorid; uint32_t archid; From d0fe0b31bf34fb6cd251e071d33754b321cfcefc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 01:57:44 +0000 Subject: [PATCH 018/130] riscv_debug: Implemented support for discovering the target Hart's ISA and access width --- src/target/riscv_debug.c | 69 ++++++++++++++++++++++++++++++++++++---- src/target/riscv_debug.h | 2 ++ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index efca6abb7b1..0f27b956961 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -58,8 +58,9 @@ #define RV_DM_STAT_NON_EXISTENT 0x00004000U #define RV_DM_STAT_ALL_HALTED 0x00000200U -#define RV_DM_ABST_STATUS_BUSY 0x00001000U -#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U +#define RV_DM_ABST_STATUS_BUSY 0x00001000U +#define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU +#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U #define RV_REG_READ 0x00000000U #define RV_REG_WRITE 0x00010000U @@ -74,6 +75,8 @@ #define RV_IMPL_ID 0xf13U #define RV_HART_ID 0xf14U +#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_hart_init(riscv_hart_s *hart); static void riscv_hart_free(void *priv); @@ -85,6 +88,8 @@ static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32 static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); +static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); + static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); @@ -203,9 +208,9 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* Request halt and read certain key registers */ riscv_halt_request(target); - uint32_t isa = 0; - riscv_csr_read(hart, RV_ISA, &isa); - hart->access_width = riscv_isa_address_width(isa); + uint32_t isa = riscv_hart_discover_isa(hart); + hart->address_width = riscv_isa_address_width(isa); + hart->extensions = isa & RV_ISA_EXTENSIONS_MASK; riscv_csr_read(hart, RV_VENDOR_ID, &hart->vendorid); /* XXX: These will technically go wrong on rv64 - need some way to deal with that. */ riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); @@ -213,8 +218,9 @@ static bool riscv_hart_init(riscv_hart_s *const hart) riscv_csr_read(hart, RV_HART_ID, &hart->hartid); riscv_halt_resume(target, false); - DEBUG_INFO("Hart %" PRIu32 ": %u-bit RISC-V (arch = %" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 "\n", - hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid); + DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %08" PRIx32 ", impl = %08" PRIx32 + ", exts = %08" PRIx32 "\n", + hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid, hart->extensions); target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -296,6 +302,55 @@ static void riscv_dm_unref(riscv_dm_s *const dbg_module) } } +static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) +{ + /* Read out the abstract command control/status register */ + uint32_t data_registers = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &data_registers)) + return 0U; + /* And use the data count bits to divine an initial guess on the platform width */ + data_registers &= RV_DM_ABST_STATUS_DATA_COUNT; + DEBUG_INFO("Hart has %" PRIu32 " data registers\n", data_registers); + /* Check we have at least enough data registers for arg0 */ + if (data_registers >= 4) + hart->access_width = 128U; + else if (data_registers >= 2) + hart->access_width = 64U; + else if (data_registers) + hart->access_width = 32U; + /* If the control/status register contains an invalid count, abort */ + else + return 0; + + do { + DEBUG_INFO("Attempting %u-bit read on misa\n", hart->access_width); + /* Try reading the register on the guessed width */ + uint32_t isa_data[4] = {}; + bool result = riscv_csr_read(hart, RV_ISA, isa_data); + if (result) { + if (hart->access_width == 128U) + return (isa_data[3] & 0xc0000000) | (isa_data[0] & 0x3fffffffU); + if (hart->access_width == 64U) + return (isa_data[1] & 0xc0000000) | (isa_data[0] & 0x3fffffffU); + return isa_data[0]; + } + /* If that failed, then find out why and instead try the next narrower width */ + if (hart->status != RISCV_HART_BUS_ERROR && hart->status != RISCV_HART_EXCEPTION) + return 0; + if (hart->access_width == 32U) { + hart->access_width = 0U; + return 0; /* We are unable to read the misa register */ + } + if (hart->access_width == 64U) + hart->access_width = 32U; + if (hart->access_width == 128U) + hart->access_width = 64U; + } while (hart->access_width != 0U); + DEBUG_WARN("Unable to read misa register\n"); + /* If the above loop failed, we're done.. */ + return 0U; +} + static uint32_t riscv_hart_access_width(const riscv_hart_s *const hart) { if (hart->access_width == 128U) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 6b3f9b77650..5c6c87c55b8 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -96,8 +96,10 @@ typedef struct riscv_hart { uint32_t hart_idx; uint32_t hartsel; uint8_t access_width; + uint8_t address_width; riscv_hart_status_e status; + uint32_t extensions; uint32_t vendorid; uint32_t archid; uint32_t implid; From 8a980e878659b1717ca35feb94f043ddcdf30c63 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 03:42:05 +0000 Subject: [PATCH 019/130] riscv_debug: Implemented writing to CSRs --- src/target/riscv_debug.c | 19 +++++++++++++++++++ src/target/riscv_debug.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 0f27b956961..92f4c35da9e 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -395,6 +395,25 @@ bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const da return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, value); } +bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) +{ + /* Set up the data registers based on the Hart native access size */ + const uint32_t *const value = (const uint32_t *)data; + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value[0])) + return false; + if (hart->access_width >= 64U && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, value[1])) + return false; + if (hart->access_width == 128 && + !(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, value[2]) && + riscv_dm_write(hart->dbg_module, RV_DM_DATA3, value[3]))) + return false; + /* Configure and run the write */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_REG_WRITE | RV_REG_XFER | riscv_hart_access_width(hart) | reg)) + return false; + return riscv_csr_wait_complete(hart); +} + static void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = (riscv_hart_s *)target->priv; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 5c6c87c55b8..a372afe28c2 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -111,5 +111,6 @@ typedef struct riscv_hart { void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); +bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); #endif /*TARGET_RISCV_DEBUG_H*/ From 3d29e9a83f9642042508696a869558fa47248d21 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 04:11:38 +0000 Subject: [PATCH 020/130] riscv_debug: Created probe stubs for probing RISC-V 32- and 64-bit targets --- src/Makefile | 2 ++ src/target/riscv32.c | 44 +++++++++++++++++++++++++++++++++++++++ src/target/riscv64.c | 44 +++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.c | 15 ++++++++++++- src/target/riscv_debug.h | 3 +++ src/target/target_probe.c | 3 +++ src/target/target_probe.h | 3 +++ 7 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/target/riscv32.c create mode 100644 src/target/riscv64.c diff --git a/src/Makefile b/src/Makefile index 6f9a96578f1..57ce7bc1069 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,6 +63,8 @@ SRC = \ nrf91.c \ nxpke04.c \ remote.c \ + riscv32.c \ + riscv64.c \ riscv_debug.c \ riscv_jtag_dtm.c \ rp.c \ diff --git a/src/target/riscv32.c b/src/target/riscv32.c new file mode 100644 index 00000000000..c84daf9302d --- /dev/null +++ b/src/target/riscv32.c @@ -0,0 +1,44 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "riscv_debug.h" + +bool riscv32_probe(target_s *const target) +{ + target->core = "rv32"; + return false; +} diff --git a/src/target/riscv64.c b/src/target/riscv64.c new file mode 100644 index 00000000000..96f3732d300 --- /dev/null +++ b/src/target/riscv64.c @@ -0,0 +1,44 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "riscv_debug.h" + +bool riscv64_probe(target_s *const target) +{ + target->core = "rv64"; + return false; +} diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 92f4c35da9e..b46e6b09916 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -32,6 +32,7 @@ */ #include "general.h" +#include "target_probe.h" #include "target_internal.h" #include "riscv_debug.h" @@ -201,7 +202,6 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* Grab a reference to the DMI and DM structurues and do preliminary setup of the target structure */ riscv_dm_ref(hart->dbg_module); - target->cpuid = hart->dbg_module->dmi_bus->idcode; target->driver = "RISC-V"; target->priv = hart; target->priv_free = riscv_hart_free; @@ -225,9 +225,22 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; + if (hart->access_width == 32U) { + DEBUG_INFO("-> riscv32_probe\n"); + return riscv32_probe(target); + } + if (hart->access_width == 64U) { + DEBUG_INFO("-> riscv64_probe\n"); + return riscv64_probe(target); + } return true; } +riscv_hart_s *riscv_hart_struct(target_s *const target) +{ + return (riscv_hart_s *)target->priv; +} + static void riscv_hart_free(void *const priv) { riscv_dm_unref(((riscv_hart_s *)priv)->dbg_module); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index a372afe28c2..fab9a923ed2 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -36,6 +36,7 @@ #include #include +#include "target.h" typedef enum riscv_debug_version { RISCV_DEBUG_UNKNOWN, @@ -110,6 +111,8 @@ typedef struct riscv_hart { void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); +riscv_hart_s *riscv_hart_struct(target_s *target); + bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 5edecacd92b..cc6d3ee5fbf 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -75,6 +75,9 @@ CORTEXA_PROBE_WEAK_NOP(cortexa_probe) CORTEXM_PROBE_WEAK_NOP(cortexm_probe) +TARGET_PROBE_WEAK_NOP(riscv32_probe) +TARGET_PROBE_WEAK_NOP(riscv64_probe) + CORTEXM_PROBE_WEAK_NOP(kinetis_mdm_probe) CORTEXM_PROBE_WEAK_NOP(nrf51_mdm_probe) CORTEXM_PROBE_WEAK_NOP(efm32_aap_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 8829f81edf0..1e3c4a1c3b3 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -42,6 +42,9 @@ bool cortexa_probe(adiv5_access_port_s *apb, uint32_t debug_base); bool cortexm_probe(adiv5_access_port_s *ap); +bool riscv32_probe(target_s *target); +bool riscv64_probe(target_s *target); + bool kinetis_mdm_probe(adiv5_access_port_s *ap); bool nrf51_mdm_probe(adiv5_access_port_s *ap); bool efm32_aap_probe(adiv5_access_port_s *ap); From 6175aa20eb89d8814d48347518cb0432ff185947 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 04:43:49 +0000 Subject: [PATCH 021/130] riscv_debug: Set up the target designer code field --- src/target/riscv_debug.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index b46e6b09916..7bebf03703a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -218,9 +218,13 @@ static bool riscv_hart_init(riscv_hart_s *const hart) riscv_csr_read(hart, RV_HART_ID, &hart->hartid); riscv_halt_resume(target, false); - DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %08" PRIx32 ", impl = %08" PRIx32 + DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 ", exts = %08" PRIx32 "\n", hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid, hart->extensions); + /* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the JTAG IDCode */ + target->designer_code = hart->vendorid ? + hart->vendorid : + ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; From abda4b1bfce57fdc4e5beb8861c6876854edde27 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:02:20 +0000 Subject: [PATCH 022/130] riscv_debug: Documented the used CSR addresses --- src/target/riscv_debug.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 7bebf03703a..f084f050d93 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -70,11 +70,17 @@ #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U -#define RV_ISA 0x301U +/* The following is a set of CSR address definitions */ +/* misa -> The Hart's machine ISA register */ +#define RV_ISA 0x301U +/* mvendorid -> The JEP-106 code for the vendor implementing this Hart */ #define RV_VENDOR_ID 0xf11U -#define RV_ARCH_ID 0xf12U -#define RV_IMPL_ID 0xf13U -#define RV_HART_ID 0xf14U +/* marchid -> The RISC-V International architecture ID code */ +#define RV_ARCH_ID 0xf12U +/* mimplid -> Hart's processor implementation ID */ +#define RV_IMPL_ID 0xf13U +/* mhartid -> machine ID of the Hart */ +#define RV_HART_ID 0xf14U #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU From 43f90a5e3591ee6da6c7c63e755343404630ec88 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:03:05 +0000 Subject: [PATCH 023/130] riscv_debug: Implemented the ability to force a CSR access to a specific width --- src/target/riscv_debug.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index f084f050d93..43f45df6bc4 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -70,6 +70,10 @@ #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_CSR_FORCE_MASK 0x3000U +#define RV_CSR_FORCE_32_BIT 0x1000U +#define RV_CSR_FORCE_64_BIT 0x2000U + /* The following is a set of CSR address definitions */ /* misa -> The Hart's machine ISA register */ #define RV_ISA 0x301U @@ -374,15 +378,25 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) return 0U; } -static uint32_t riscv_hart_access_width(const riscv_hart_s *const hart) +static uint32_t riscv_hart_access_width(uint8_t access_width) { - if (hart->access_width == 128U) + if (access_width == 128U) return RV_REG_ACCESS_128_BIT; - if (hart->access_width == 64U) + if (access_width == 64U) return RV_REG_ACCESS_64_BIT; return RV_REG_ACCESS_32_BIT; } +static uint32_t riscv_csr_access_width(const uint16_t reg) +{ + const uint16_t access_width = reg & RV_CSR_FORCE_MASK; + if (access_width == RV_CSR_FORCE_32_BIT) + return 32U; + if (access_width == RV_CSR_FORCE_64_BIT) + return 64U; + return 128U; +} + static bool riscv_csr_wait_complete(riscv_hart_s *const hart) { uint32_t status = RV_DM_ABST_STATUS_BUSY; @@ -400,19 +414,21 @@ static bool riscv_csr_wait_complete(riscv_hart_s *const hart) bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) { + const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; /* Set up the register read and wait for it to complete */ if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, - RV_DM_ABST_CMD_ACCESS_REG | RV_REG_READ | RV_REG_XFER | riscv_hart_access_width(hart) | reg) || + RV_DM_ABST_CMD_ACCESS_REG | RV_REG_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | + (reg & ~RV_CSR_FORCE_MASK)) || !riscv_csr_wait_complete(hart)) return false; uint32_t *const value = (uint32_t *)data; /* If we're doing a 128-bit read, grab the upper-most 2 uint32_t's */ - if (hart->access_width == 128U && + if (access_width == 128U && !(riscv_dm_read(hart->dbg_module, RV_DM_DATA3, value + 3) && riscv_dm_read(hart->dbg_module, RV_DM_DATA2, value + 2))) return false; /* If we're doing at least a 64-bit read, grab the next uint32_t */ - if (hart->access_width >= 64U && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, value + 1)) + if (access_width >= 64U && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, value + 1)) return false; /* Finally grab the last and lowest uint32_t */ return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, value); @@ -420,19 +436,21 @@ bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const da bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) { + const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; /* Set up the data registers based on the Hart native access size */ const uint32_t *const value = (const uint32_t *)data; if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value[0])) return false; - if (hart->access_width >= 64U && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, value[1])) + if (access_width >= 64U && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, value[1])) return false; - if (hart->access_width == 128 && + if (access_width == 128 && !(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, value[2]) && riscv_dm_write(hart->dbg_module, RV_DM_DATA3, value[3]))) return false; /* Configure and run the write */ if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, - RV_DM_ABST_CMD_ACCESS_REG | RV_REG_WRITE | RV_REG_XFER | riscv_hart_access_width(hart) | reg)) + RV_DM_ABST_CMD_ACCESS_REG | RV_REG_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) | + (reg & ~RV_CSR_FORCE_MASK))) return false; return riscv_csr_wait_complete(hart); } From c44d053f60fcf1b29dfadb0a2189cbf79cf3e2f6 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:03:38 +0000 Subject: [PATCH 024/130] riscv_debug: Implemented single-stepping support in riscv_halt_resume() --- src/target/riscv_debug.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 43f45df6bc4..c61e68ad74a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -77,6 +77,10 @@ /* The following is a set of CSR address definitions */ /* misa -> The Hart's machine ISA register */ #define RV_ISA 0x301U +/* dcsr -> Debug Control/Status Register */ +#define RV_DCSR 0x7b0U +/* dpc -> Debug Program Counter */ +#define RV_DPC 0x7b1U /* mvendorid -> The JEP-106 code for the vendor implementing this Hart */ #define RV_VENDOR_ID 0xf11U /* marchid -> The RISC-V International architecture ID code */ @@ -88,6 +92,9 @@ #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU +#define RV_DCSR_STEP 0x00000004U +#define RV_DCSR_STEPIE 0x00000800U + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_hart_init(riscv_hart_s *hart); static void riscv_hart_free(void *priv); @@ -473,8 +480,17 @@ static void riscv_halt_request(target_s *const target) static void riscv_halt_resume(target_s *target, const bool step) { - (void)step; riscv_hart_s *const hart = (riscv_hart_s *)target->priv; + /* Configure the debug controller for single-stepping as appropriate */ + uint32_t stepping_config = 0U; + if (!riscv_csr_read(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) + return; + if (step) + stepping_config |= RV_DCSR_STEP | RV_DCSR_STEPIE; + else + stepping_config &= ~(RV_DCSR_STEP | RV_DCSR_STEPIE); + if (!riscv_csr_write(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) + return; /* Request the hart to resume */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) return; From ee401ebb458339067fda2488eeff55ba750af34b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 06:03:04 +0000 Subject: [PATCH 025/130] riscv_debug: Added definitions for the abstract memory access command --- src/target/riscv_debug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index c61e68ad74a..8866a951d2d 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -62,14 +62,22 @@ #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU #define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U +#define RV_DM_ABST_CMD_ACCESS_MEM 0x02000000U -#define RV_REG_READ 0x00000000U -#define RV_REG_WRITE 0x00010000U +#define RV_ABST_READ 0x00000000U +#define RV_ABST_WRITE 0x00010000U #define RV_REG_XFER 0x00020000U #define RV_REG_ACCESS_32_BIT 0x00200000U #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_MEM_ACCESS_8_BIT 0x0U +#define RV_MEM_ACCESS_16_BIT 0x1U +#define RV_MEM_ACCESS_32_BIT 0x2U +#define RV_MEM_ACCESS_64_BIT 0x3U +#define RV_MEM_ACCESS_128_BIT 0x4U +#define RV_MEM_ACCESS_SHIFT 20U + #define RV_CSR_FORCE_MASK 0x3000U #define RV_CSR_FORCE_32_BIT 0x1000U #define RV_CSR_FORCE_64_BIT 0x2000U @@ -424,7 +432,7 @@ bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const da const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; /* Set up the register read and wait for it to complete */ if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, - RV_DM_ABST_CMD_ACCESS_REG | RV_REG_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | (reg & ~RV_CSR_FORCE_MASK)) || !riscv_csr_wait_complete(hart)) return false; @@ -456,7 +464,7 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c return false; /* Configure and run the write */ if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, - RV_DM_ABST_CMD_ACCESS_REG | RV_REG_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) | + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) | (reg & ~RV_CSR_FORCE_MASK))) return false; return riscv_csr_wait_complete(hart); From 6fc6d3605addaf0f28f755666d2199ab1a9c4398 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:18:57 +0000 Subject: [PATCH 026/130] riscv_debug: Implemented arbitrary memory read --- src/target/riscv_debug.c | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 8866a951d2d..67d45e59479 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -70,6 +70,7 @@ #define RV_REG_ACCESS_32_BIT 0x00200000U #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_MEM_ADDR_POST_INC 0x00080000U #define RV_MEM_ACCESS_8_BIT 0x0U #define RV_MEM_ACCESS_16_BIT 0x1U @@ -119,6 +120,8 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); +static void riscv_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); + void riscv_dmi_init(riscv_dmi_s *const dmi) { /* If we don't currently know how to talk to this DMI, warn and fail */ @@ -470,6 +473,63 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c return riscv_csr_wait_complete(hart); } +static uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) +{ + /* Grab the Hart's most maxmimally aligned possible write width */ + uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_MEM_ACCESS_SHIFT; + /* Convert the hart access width to a mask - for example, for 32-bit harts, this gives (1U << 2U) - 1U = 3U */ + uint8_t align_mask = (1U << access_width) - 1U; + /* Mask out the bottom bits of both the address and length - anything that determines the alignment */ + const uint8_t addr_bits = address & align_mask; + const uint8_t len_bits = length & align_mask; + /* bitwise-OR together the result so, for example, an odd address 2-byte read results in a pattern like 0bxx11 */ + const uint8_t align = addr_bits | len_bits; + /* Loop down through the possible access widths till we find one suitably aligned */ + for (; access_width; --access_width) { + if ((align & align_mask) == 0) + return access_width; + align_mask >>= 1U; + } + return access_width; +} + +/* XXX: target_addr_t supports only 32-bit addresses, artificially limiting this function to 32-bit targets */ +static void riscv_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to read to arg1 */ + /* XXX: This intentionally does not support 128-bit harts(!) */ + if (hart->address_width == 32 && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + return; + if (hart->address_width == 64 && + !(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, src) || riscv_dm_write(hart->dbg_module, RV_DM_DATA3, 0U))) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Execute the read */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_csr_wait_complete(hart)) + return; + /* Extract back the data from arg0 */ + uint32_t values[2] = {}; + if (access_width == RV_MEM_ACCESS_64_BIT && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, values + 1)) + return; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, values)) + return; + /* And copy the right part into data */ + memcpy(data + offset, values, access_length); + } +} + static void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = (riscv_hart_s *)target->priv; From e7b81ca760c302dc1765641eef964dcccd032791 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:19:44 +0000 Subject: [PATCH 027/130] riscv_debug: Fixed the target hart not being halted during probing --- src/target/riscv_debug.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 67d45e59479..e002c99188f 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -244,7 +244,6 @@ static bool riscv_hart_init(riscv_hart_s *const hart) riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); riscv_csr_read(hart, RV_HART_ID, &hart->hartid); - riscv_halt_resume(target, false); DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 ", exts = %08" PRIx32 "\n", @@ -254,17 +253,22 @@ static bool riscv_hart_init(riscv_hart_s *const hart) hart->vendorid : ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); + target->mem_read = riscv_mem_read; + target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; if (hart->access_width == 32U) { DEBUG_INFO("-> riscv32_probe\n"); - return riscv32_probe(target); + if (!riscv32_probe(target)) + DEBUG_INFO("Probing failed, please report unknown RISC-V 32 device\n"); } if (hart->access_width == 64U) { DEBUG_INFO("-> riscv64_probe\n"); - return riscv64_probe(target); + if (!riscv64_probe(target)) + DEBUG_INFO("Probing failed, please report unknown RISC-V 64 device\n"); } + riscv_halt_resume(target, false); return true; } From 25d500f87fd19ac0340bdee7c0ba4984e34f3ded Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:20:27 +0000 Subject: [PATCH 028/130] riscv_debug: Added some DEBUG_TARGET information to the CSR functions and a failure warning to the CSR wait complete function --- src/target/riscv_debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index e002c99188f..bedaaf959dd 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -430,6 +430,8 @@ static bool riscv_csr_wait_complete(riscv_hart_s *const hart) hart->status = (status >> 8U) & RISCV_HART_OTHER; if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, RISCV_HART_OTHER << 8U)) return false; + if (hart->status != RISCV_HART_NO_ERROR) + DEBUG_WARN("CSR access failed: %u\n", hart->status); /* If the command failed, return the failure */ return hart->status == RISCV_HART_NO_ERROR; } @@ -437,6 +439,7 @@ static bool riscv_csr_wait_complete(riscv_hart_s *const hart) bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data) { const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; + DEBUG_TARGET("Reading %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK); /* Set up the register read and wait for it to complete */ if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | @@ -459,6 +462,7 @@ bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const da bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) { const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width; + DEBUG_TARGET("Writing %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK); /* Set up the data registers based on the Hart native access size */ const uint32_t *const value = (const uint32_t *)data; if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value[0])) From 9f0682655fcafd477a284fdeb4a1f87559a54919 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:34:50 +0000 Subject: [PATCH 029/130] riscv_debug: Implemented the target check_error hook --- src/target/riscv_debug.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index bedaaf959dd..1d8f8684105 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -120,6 +120,7 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); +static bool riscv_check_error(target_s *target); static void riscv_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); void riscv_dmi_init(riscv_dmi_s *const dmi) @@ -253,6 +254,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) hart->vendorid : ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); + target->check_error = riscv_check_error; target->mem_read = riscv_mem_read; target->halt_request = riscv_halt_request; @@ -538,6 +540,11 @@ static void riscv_mem_read(target_s *const target, void *const dest, const targe } } +static bool riscv_check_error(target_s *const target) +{ + return riscv_hart_struct(target)->status != RISCV_HART_NO_ERROR; +} + static void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = (riscv_hart_s *)target->priv; From 1fca5464ee49ded82c636fd68b18db0e3d6a1545 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:35:02 +0000 Subject: [PATCH 030/130] riscv_debug: Made more of the DM and hart machinary available in the header --- src/target/riscv_debug.c | 43 +++++++++------------------------------- src/target/riscv_debug.h | 28 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 1d8f8684105..7f60794f3cb 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -36,15 +36,9 @@ #include "target_internal.h" #include "riscv_debug.h" -#define RV_DM_DATA0 0x04U -#define RV_DM_DATA1 0x05U -#define RV_DM_DATA2 0x06U -#define RV_DM_DATA3 0x07U -#define RV_DM_CONTROL 0x10U -#define RV_DM_STATUS 0x11U -#define RV_DM_ABST_CTRLSTATUS 0x16U -#define RV_DM_ABST_COMMAND 0x17U -#define RV_DM_NEXT_DM 0x1dU +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU #define RV_DM_CTRL_ACTIVE 0x00000001U #define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U @@ -61,23 +55,6 @@ #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU -#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U -#define RV_DM_ABST_CMD_ACCESS_MEM 0x02000000U - -#define RV_ABST_READ 0x00000000U -#define RV_ABST_WRITE 0x00010000U -#define RV_REG_XFER 0x00020000U -#define RV_REG_ACCESS_32_BIT 0x00200000U -#define RV_REG_ACCESS_64_BIT 0x00300000U -#define RV_REG_ACCESS_128_BIT 0x00400000U -#define RV_MEM_ADDR_POST_INC 0x00080000U - -#define RV_MEM_ACCESS_8_BIT 0x0U -#define RV_MEM_ACCESS_16_BIT 0x1U -#define RV_MEM_ACCESS_32_BIT 0x2U -#define RV_MEM_ACCESS_64_BIT 0x3U -#define RV_MEM_ACCESS_128_BIT 0x4U -#define RV_MEM_ACCESS_SHIFT 20U #define RV_CSR_FORCE_MASK 0x3000U #define RV_CSR_FORCE_32_BIT 0x1000U @@ -111,8 +88,6 @@ static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); static bool riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); static void riscv_dm_ref(riscv_dm_s *dbg_module); static void riscv_dm_unref(riscv_dm_s *dbg_module); -static inline bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); -static inline bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); static riscv_debug_version_e riscv_dm_version(uint32_t status); static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); @@ -295,12 +270,12 @@ static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, cons return dmi->write(dmi, address, value); } -static inline bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) +bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) { return riscv_dmi_read(dbg_module->dmi_bus, dbg_module->base + address, value); } -static inline bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, const uint32_t value) +bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, const uint32_t value) { return riscv_dmi_write(dbg_module->dmi_bus, dbg_module->base + address, value); } @@ -421,7 +396,7 @@ static uint32_t riscv_csr_access_width(const uint16_t reg) return 128U; } -static bool riscv_csr_wait_complete(riscv_hart_s *const hart) +bool riscv_command_wait_complete(riscv_hart_s *const hart) { uint32_t status = RV_DM_ABST_STATUS_BUSY; while (status & RV_DM_ABST_STATUS_BUSY) { @@ -446,7 +421,7 @@ bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const da if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | riscv_hart_access_width(access_width) | (reg & ~RV_CSR_FORCE_MASK)) || - !riscv_csr_wait_complete(hart)) + !riscv_command_wait_complete(hart)) return false; uint32_t *const value = (uint32_t *)data; /* If we're doing a 128-bit read, grab the upper-most 2 uint32_t's */ @@ -480,10 +455,10 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) | (reg & ~RV_CSR_FORCE_MASK))) return false; - return riscv_csr_wait_complete(hart); + return riscv_command_wait_complete(hart); } -static uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) +uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) { /* Grab the Hart's most maxmimally aligned possible write width */ uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_MEM_ACCESS_SHIFT; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index fab9a923ed2..339b4b5773a 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -109,10 +109,38 @@ typedef struct riscv_hart { #define RV_STATUS_VERSION_MASK 0x0000000fU +#define RV_DM_DATA0 0x04U +#define RV_DM_DATA1 0x05U +#define RV_DM_DATA2 0x06U +#define RV_DM_DATA3 0x07U +#define RV_DM_ABST_CTRLSTATUS 0x16U +#define RV_DM_ABST_COMMAND 0x17U + +#define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U +#define RV_DM_ABST_CMD_ACCESS_MEM 0x02000000U + +#define RV_ABST_READ 0x00000000U +#define RV_ABST_WRITE 0x00010000U +#define RV_REG_XFER 0x00020000U +#define RV_REG_ACCESS_32_BIT 0x00200000U +#define RV_REG_ACCESS_64_BIT 0x00300000U +#define RV_REG_ACCESS_128_BIT 0x00400000U +#define RV_MEM_ADDR_POST_INC 0x00080000U + +#define RV_MEM_ACCESS_8_BIT 0x0U +#define RV_MEM_ACCESS_16_BIT 0x1U +#define RV_MEM_ACCESS_32_BIT 0x2U +#define RV_MEM_ACCESS_64_BIT 0x3U +#define RV_MEM_ACCESS_128_BIT 0x4U +#define RV_MEM_ACCESS_SHIFT 20U + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); riscv_hart_s *riscv_hart_struct(target_s *target); +bool riscv_dm_read(riscv_dm_s *dbg_module, uint8_t address, uint32_t *value); +bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); +bool riscv_command_wait_complete(riscv_hart_s *hart); bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); From dab2cf100c4df12ed4c04b9b724333dd93d99f1c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:37:00 +0000 Subject: [PATCH 031/130] riscv_debug: Moved the mem_read implementation into the bitness-specific implementations as the access width depends on the address width for memory --- src/target/riscv32.c | 52 +++++++++++++++++++++++++++++++++++++++ src/target/riscv64.c | 53 ++++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.c | 39 ----------------------------- src/target/riscv_debug.h | 3 +++ 4 files changed, 108 insertions(+), 39 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index c84daf9302d..85d14de388c 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -37,8 +37,60 @@ #include "target_probe.h" #include "riscv_debug.h" +static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); + bool riscv32_probe(target_s *const target) { target->core = "rv32"; + target->mem_read = riscv32_mem_read; + return false; } + +void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) +{ + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: { + const uint8_t value = data & 0xffU; + memcpy(dest, &value, sizeof(value)); + break; + } + case RV_MEM_ACCESS_16_BIT: { + const uint16_t value = data & 0xffffU; + memcpy(dest, &value, sizeof(value)); + break; + } + case RV_MEM_ACCESS_32_BIT: + memcpy(dest, &data, sizeof(data)); + break; + } +} + +static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to read to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Execute the read */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + /* Extract back the data from arg0 */ + uint32_t value = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + return; + riscv32_unpack_data(data + offset, value, access_width); + } +} diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 96f3732d300..3b132de5abb 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -37,8 +37,61 @@ #include "target_probe.h" #include "riscv_debug.h" +static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); + bool riscv64_probe(target_s *const target) { target->core = "rv64"; + target->mem_read = riscv64_mem_read; + return false; } + +void riscv64_unpack_data( + void *const dest, const uint32_t data_low, const uint32_t data_high, const uint8_t access_width) +{ + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: + case RV_MEM_ACCESS_16_BIT: + case RV_MEM_ACCESS_32_BIT: + riscv32_unpack_data(dest, data_low, access_width); + break; + case RV_MEM_ACCESS_64_BIT: { + uint64_t value = ((uint64_t)data_high << 32U) | data_low; + memcpy(dest, &value, sizeof(value)); + break; + } + } +} + +/* XXX: target_addr_t supports only 32-bit addresses, artificially limiting this function */ +static void riscv64_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to read to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA2, src) || !riscv_dm_write(hart->dbg_module, RV_DM_DATA3, 0U)) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Execute the read */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + /* Extract back the data from arg0 */ + uint32_t value_low = 0; + uint32_t value_high = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value_low) || + !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, &value_high)) + return; + riscv64_unpack_data(data + offset, value_low, value_high, access_width); + } +} diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 7f60794f3cb..da685698c9b 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -96,7 +96,6 @@ static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); static bool riscv_check_error(target_s *target); -static void riscv_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -230,7 +229,6 @@ static bool riscv_hart_init(riscv_hart_s *const hart) ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); target->check_error = riscv_check_error; - target->mem_read = riscv_mem_read; target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -478,43 +476,6 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr return access_width; } -/* XXX: target_addr_t supports only 32-bit addresses, artificially limiting this function to 32-bit targets */ -static void riscv_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) -{ - DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); - /* If we're asked to do a 0-byte read, do nothing */ - if (!len) - return; - riscv_hart_s *const hart = riscv_hart_struct(target); - /* Figure out the maxmial width of access to perform, up to the bitness of the target */ - const uint8_t access_width = riscv_mem_access_width(hart, src, len); - const uint8_t access_length = 1U << access_width; - /* Build the access command */ - const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | - (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); - /* Write the address to read to arg1 */ - /* XXX: This intentionally does not support 128-bit harts(!) */ - if (hart->address_width == 32 && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) - return; - if (hart->address_width == 64 && - !(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, src) || riscv_dm_write(hart->dbg_module, RV_DM_DATA3, 0U))) - return; - uint8_t *const data = (uint8_t *)dest; - for (size_t offset = 0; offset < len; offset += access_length) { - /* Execute the read */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_csr_wait_complete(hart)) - return; - /* Extract back the data from arg0 */ - uint32_t values[2] = {}; - if (access_width == RV_MEM_ACCESS_64_BIT && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, values + 1)) - return; - if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, values)) - return; - /* And copy the right part into data */ - memcpy(data + offset, values, access_length); - } -} - static bool riscv_check_error(target_s *const target) { return riscv_hart_struct(target)->status != RISCV_HART_NO_ERROR; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 339b4b5773a..d8b8a6957c9 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -144,4 +144,7 @@ bool riscv_command_wait_complete(riscv_hart_s *hart); bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); +uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, size_t length); +void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); + #endif /*TARGET_RISCV_DEBUG_H*/ From 4b46d3555117d1f6841ef435a04e23980b0c379d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:37:27 +0000 Subject: [PATCH 032/130] riscv_debug: Forced the vendor ID to be read 32-bit per the privileged spec --- src/target/riscv_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index da685698c9b..026bf2b5807 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -214,7 +214,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) uint32_t isa = riscv_hart_discover_isa(hart); hart->address_width = riscv_isa_address_width(isa); hart->extensions = isa & RV_ISA_EXTENSIONS_MASK; - riscv_csr_read(hart, RV_VENDOR_ID, &hart->vendorid); + riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid); /* XXX: These will technically go wrong on rv64 - need some way to deal with that. */ riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); From 6948ff8bad1ca407c80b133844d5afe5e9674a10 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:09:06 +0000 Subject: [PATCH 033/130] stm32f1: Implemented a probe routine for GD32VF1 --- src/target/stm32f1.c | 25 +++++++++++++++++++++++++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 3 files changed, 27 insertions(+) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 049cd9c3f77..75d7a1c1907 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -183,6 +183,31 @@ bool gd32f1_probe(target_s *target) return true; } +/* Identify RISC-V GD32VF1 chips */ +bool gd32vf1_probe(target_s *const target) +{ + const uint16_t device_id = target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU; + switch (device_id) { + case 0x410U: /* GD32VF103 */ + target->driver = "GD32VF1"; + break; + default: + return false; + } + + const uint32_t signature = target_mem_read32(target, GD32Fx_FLASHSIZE); + const uint16_t flash_size = signature & 0xffffU; + const uint16_t ram_size = signature >> 16U; + + target->part_id = device_id; + target->mass_erase = stm32f1_mass_erase; + target_add_ram(target, 0x20000000, ram_size * 1024U); + stm32f1_add_flash(target, 0x8000000, (size_t)flash_size * 1024U, 0x400U); + target_add_commands(target, stm32f1_cmd_list, target->driver); + + return true; +} + static bool at32f40_detect(target_s *target, const uint16_t part_id) { // Current driver supports only *default* memory layout (256 KB Flash / 96 KB SRAM) diff --git a/src/target/target_probe.c b/src/target/target_probe.c index cc6d3ee5fbf..50944b64bd6 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -86,6 +86,7 @@ CORTEXM_PROBE_WEAK_NOP(lpc55_dmap_probe) TARGET_PROBE_WEAK_NOP(ch32f1_probe) TARGET_PROBE_WEAK_NOP(gd32f1_probe) +TARGET_PROBE_WEAK_NOP(gd32vf1_probe) TARGET_PROBE_WEAK_NOP(gd32f4_probe) TARGET_PROBE_WEAK_NOP(stm32f1_probe) TARGET_PROBE_WEAK_NOP(at32fxx_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 1e3c4a1c3b3..9a415f25def 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -56,6 +56,7 @@ bool at32fxx_probe(target_s *target); // STM32 clones from Artery bool mm32l0xx_probe(target_s *target); bool mm32f3xx_probe(target_s *target); bool gd32f1_probe(target_s *target); +bool gd32vf1_probe(target_s *target); bool gd32f4_probe(target_s *target); bool stm32f1_probe(target_s *target); bool stm32f4_probe(target_s *target); From 0feda264f6d4f8264d382f715d694caa908dd9fc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:09:47 +0000 Subject: [PATCH 034/130] riscv32: Implemented probing for the GD32VF1 --- src/target/cortexm.c | 119 +++++++++++++++++++------------------- src/target/jep106.h | 5 ++ src/target/riscv32.c | 13 +++++ src/target/target_probe.h | 4 +- 4 files changed, 80 insertions(+), 61 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 3c4f1d2698f..2df4d495ea0 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -483,25 +483,25 @@ const char *cortexm_regs_description(target_s *t) bool cortexm_probe(adiv5_access_port_s *ap) { - target_s *t = target_new(); - if (!t) + target_s *target = target_new(); + if (!target) return false; adiv5_ap_ref(ap); if (ap->dp->version >= 2 && ap->dp->target_designer_code != 0) { /* Use TARGETID register to identify target */ - t->designer_code = ap->dp->target_designer_code; - t->part_id = ap->dp->target_partno; + target->designer_code = ap->dp->target_designer_code; + target->part_id = ap->dp->target_partno; } else { /* Use AP DESIGNER and AP PARTNO to identify target */ - t->designer_code = ap->designer_code; - t->part_id = ap->partno; + target->designer_code = ap->designer_code; + target->part_id = ap->partno; } /* MM32F5xxx: part designer code is Arm China, target designer code uses forbidden continuation code */ - if (t->designer_code == JEP106_MANUFACTURER_ERRATA_ARM_CHINA && + if (target->designer_code == JEP106_MANUFACTURER_ERRATA_ARM_CHINA && ap->dp->designer_code == JEP106_MANUFACTURER_ARM_CHINA) - t->designer_code = JEP106_MANUFACTURER_ARM_CHINA; + target->designer_code = JEP106_MANUFACTURER_ARM_CHINA; cortexm_priv_s *priv = calloc(1, sizeof(*priv)); if (!priv) { /* calloc failed: heap exhaustion */ @@ -509,48 +509,48 @@ bool cortexm_probe(adiv5_access_port_s *ap) return false; } - t->priv = priv; - t->priv_free = cortex_priv_free; + target->priv = priv; + target->priv_free = cortex_priv_free; priv->base.ap = ap; priv->base.base_addr = CORTEXM_SCS_BASE; - t->check_error = cortex_check_error; - t->mem_read = cortexm_mem_read; - t->mem_write = cortexm_mem_write; + target->check_error = cortex_check_error; + target->mem_read = cortexm_mem_read; + target->mem_write = cortexm_mem_write; - t->driver = "ARM Cortex-M"; + target->driver = "ARM Cortex-M"; - cortex_read_cpuid(t); + cortex_read_cpuid(target); - t->attach = cortexm_attach; - t->detach = cortexm_detach; + target->attach = cortexm_attach; + target->detach = cortexm_detach; /* Probe for FP extension. */ - uint32_t cpacr = target_mem_read32(t, CORTEXM_CPACR); + uint32_t cpacr = target_mem_read32(target, CORTEXM_CPACR); cpacr |= 0x00f00000U; /* CP10 = 0b11, CP11 = 0b11 */ - target_mem_write32(t, CORTEXM_CPACR, cpacr); - bool is_cortexmf = target_mem_read32(t, CORTEXM_CPACR) == cpacr; + target_mem_write32(target, CORTEXM_CPACR, cpacr); + bool is_cortexmf = target_mem_read32(target, CORTEXM_CPACR) == cpacr; - t->regs_description = cortexm_regs_description; - t->regs_read = cortexm_regs_read; - t->regs_write = cortexm_regs_write; - t->reg_read = cortexm_reg_read; - t->reg_write = cortexm_reg_write; + target->regs_description = cortexm_regs_description; + target->regs_read = cortexm_regs_read; + target->regs_write = cortexm_regs_write; + target->reg_read = cortexm_reg_read; + target->reg_write = cortexm_reg_write; - t->reset = cortexm_reset; - t->halt_request = cortexm_halt_request; - t->halt_poll = cortexm_halt_poll; - t->halt_resume = cortexm_halt_resume; - t->regs_size = sizeof(uint32_t) * CORTEXM_GENERAL_REG_COUNT; + target->reset = cortexm_reset; + target->halt_request = cortexm_halt_request; + target->halt_poll = cortexm_halt_poll; + target->halt_resume = cortexm_halt_resume; + target->regs_size = sizeof(uint32_t) * CORTEXM_GENERAL_REG_COUNT; - t->breakwatch_set = cortexm_breakwatch_set; - t->breakwatch_clear = cortexm_breakwatch_clear; + target->breakwatch_set = cortexm_breakwatch_set; + target->breakwatch_clear = cortexm_breakwatch_clear; - target_add_commands(t, cortexm_cmd_list, t->driver); + target_add_commands(target, cortexm_cmd_list, target->driver); if (is_cortexmf) { - t->target_options |= CORTEXM_TOPT_FLAVOUR_V7MF; - t->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; + target->target_options |= CORTEXM_TOPT_FLAVOUR_V7MF; + target->regs_size += sizeof(uint32_t) * CORTEX_FLOAT_REG_COUNT; } /* Default vectors to catch */ @@ -568,15 +568,15 @@ bool cortexm_probe(adiv5_access_port_s *ap) conn_reset = true; /* Request halt when reset is de-asseted */ - target_mem_write32(t, CORTEXM_DEMCR, priv->demcr); + target_mem_write32(target, CORTEXM_DEMCR, priv->demcr); /* Force a halt */ - cortexm_halt_request(t); + cortexm_halt_request(target); /* Release reset */ platform_nrst_set_val(false); /* Poll for release from reset */ platform_timeout_s timeout; platform_timeout_set(&timeout, 1000); - while (target_mem_read32(t, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST) { + while (target_mem_read32(target, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST) { if (platform_timeout_is_expired(&timeout)) { DEBUG_ERROR("Error releasing from reset\n"); /* Go on and try to detect the target anyways */ @@ -587,18 +587,18 @@ bool cortexm_probe(adiv5_access_port_s *ap) } /* Check cache type */ - const uint32_t cache_type = target_mem_read32(t, CORTEXM_CTR); + const uint32_t cache_type = target_mem_read32(target, CORTEXM_CTR); if (cache_type >> CORTEX_CTR_FORMAT_SHIFT == CORTEX_CTR_FORMAT_ARMv7) { priv->base.icache_line_length = CORTEX_CTR_ICACHE_LINE(cache_type); priv->base.dcache_line_length = CORTEX_CTR_DCACHE_LINE(cache_type); } else - target_check_error(t); + target_check_error(target); /* If we set the interrupt catch vector earlier, clear it. */ if (conn_reset) - target_mem_write32(t, CORTEXM_DEMCR, 0); + target_mem_write32(target, CORTEXM_DEMCR, 0); - switch (t->designer_code) { + switch (target->designer_code) { case JEP106_MANUFACTURER_FREESCALE: PROBE(imxrt_probe); PROBE(kinetis_probe); @@ -649,7 +649,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) PROBE(renesas_probe); break; case JEP106_MANUFACTURER_NXP: - if ((t->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M33) + if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M33) PROBE(lpc55xx_probe); else DEBUG_WARN("Unhandled NXP device\n"); @@ -662,22 +662,22 @@ bool cortexm_probe(adiv5_access_port_s *ap) * All of these have braces as a brake from the standard so they're completely * consistent and easier to add new probe calls to. */ - if (t->part_id == 0x4c0U) { /* Cortex-M0+ ROM */ - PROBE(lpc11xx_probe); /* LPC8 */ - PROBE(hc32l110_probe); /* HDSC HC32L110 */ - } else if (t->part_id == 0x4c1U) { /* NXP Cortex-M0+ ROM */ - PROBE(lpc11xx_probe); /* newer LPC11U6x */ - } else if (t->part_id == 0x4c3U) { /* Cortex-M3 ROM */ + if (target->part_id == 0x4c0U) { /* Cortex-M0+ ROM */ + PROBE(lpc11xx_probe); /* LPC8 */ + PROBE(hc32l110_probe); /* HDSC HC32L110 */ + } else if (target->part_id == 0x4c1U) { /* NXP Cortex-M0+ ROM */ + PROBE(lpc11xx_probe); /* newer LPC11U6x */ + } else if (target->part_id == 0x4c3U) { /* Cortex-M3 ROM */ PROBE(lmi_probe); PROBE(ch32f1_probe); - PROBE(stm32f1_probe); /* Care for other STM32F1 clones (?) */ - PROBE(lpc15xx_probe); /* Thanks to JojoS for testing */ - PROBE(mm32f3xx_probe); /* MindMotion MM32 */ - } else if (t->part_id == 0x471U) { /* Cortex-M0 ROM */ - PROBE(lpc11xx_probe); /* LPC24C11 */ + PROBE(stm32f1_probe); /* Care for other STM32F1 clones (?) */ + PROBE(lpc15xx_probe); /* Thanks to JojoS for testing */ + PROBE(mm32f3xx_probe); /* MindMotion MM32 */ + } else if (target->part_id == 0x471U) { /* Cortex-M0 ROM */ + PROBE(lpc11xx_probe); /* LPC24C11 */ PROBE(lpc43xx_probe); - PROBE(mm32l0xx_probe); /* MindMotion MM32 */ - } else if (t->part_id == 0x4c4U) { /* Cortex-M4 ROM */ + PROBE(mm32l0xx_probe); /* MindMotion MM32 */ + } else if (target->part_id == 0x4c4U) { /* Cortex-M4 ROM */ PROBE(sam3x_probe); PROBE(lmi_probe); /* @@ -694,8 +694,8 @@ bool cortexm_probe(adiv5_access_port_s *ap) PROBE(kinetis_probe); /* Older K-series */ PROBE(at32fxx_probe); PROBE(msp432e4_probe); - } else if (t->part_id == 0x4cbU) { /* Cortex-M23 ROM */ - PROBE(gd32f1_probe); /* GD32E23x uses GD32F1 peripherals */ + } else if (target->part_id == 0x4cbU) { /* Cortex-M23 ROM */ + PROBE(gd32f1_probe); /* GD32E23x uses GD32F1 peripherals */ } break; case ASCII_CODE_FLAG: @@ -712,7 +712,8 @@ bool cortexm_probe(adiv5_access_port_s *ap) #if PC_HOSTED == 0 gdb_outf("Please report unknown device with Designer 0x%x Part ID 0x%x\n", t->designer_code, t->part_id); #else - DEBUG_WARN("Please report unknown device with Designer 0x%x Part ID 0x%x\n", t->designer_code, t->part_id); + DEBUG_WARN( + "Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); #endif return true; } diff --git a/src/target/jep106.h b/src/target/jep106.h index 83317b9f9dd..bc410a982ec 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -68,6 +68,11 @@ #define JEP106_MANUFACTURER_RASPBERRY 0x913U /* Raspberry Pi */ #define JEP106_MANUFACTURER_RENESAS 0x423U /* Renesas */ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx */ +/* + * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, + * so in the unlikely event we need to support chips by them, here be dragons. + */ +#define JEP106_MANUFACTURER_RV_GIGADEVICE 0x61eU /* * This code is not listed in the JEP106 standard, but is used by some stm32f1 clones diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 85d14de388c..011d806f13a 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -35,7 +35,9 @@ #include "target.h" #include "target_internal.h" #include "target_probe.h" +#include "jep106.h" #include "riscv_debug.h" +#include "gdb_packet.h" static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); @@ -44,6 +46,17 @@ bool riscv32_probe(target_s *const target) target->core = "rv32"; target->mem_read = riscv32_mem_read; + switch (target->designer_code) { + case JEP106_MANUFACTURER_RV_GIGADEVICE: + PROBE(gd32vf1_probe); + break; + } +#if PC_HOSTED == 0 + gdb_outf("Please report unknown device with Designer 0x%x\n", target->designer_code); +#else + DEBUG_WARN("Please report unknown device with Designer 0x%x\n", target->designer_code); +#endif +#undef PROBE return false; } diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 9a415f25def..02dc72874ff 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -28,9 +28,9 @@ #define PROBE(x) \ do { \ DEBUG_TARGET("Calling " STRINGIFY(x) "\n"); \ - if ((x)(t)) \ + if ((x)(target)) \ return true; \ - target_check_error(t); \ + target_check_error(target); \ } while (0) /* From 141b0846cc4b2777015effa2c68e5d695051f062 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:16:29 +0000 Subject: [PATCH 035/130] riscv_debug: Handle the rv128 case by complaining to the user we don't support the hart and exiting fast from riscv_hart_init() --- src/target/riscv_debug.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 026bf2b5807..d87eb6834c4 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -223,13 +223,20 @@ static bool riscv_hart_init(riscv_hart_s *const hart) DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 ", exts = %08" PRIx32 "\n", hart->hartid, hart->access_width, hart->archid, hart->vendorid, hart->implid, hart->extensions); + + /* We don't support rv128, so tell the user and fast-quit on this target. */ + if (hart->access_width == 128U) { + target->core = "(unsup) rv128"; + DEBUG_WARN("rv128 is unsupported, ignoring this hart\n"); + return true; + } + /* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the JTAG IDCode */ target->designer_code = hart->vendorid ? hart->vendorid : ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); target->check_error = riscv_check_error; - target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; From 50989dd9acbdab9069930c16b085e12a611f7db0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:22:37 +0000 Subject: [PATCH 036/130] riscv_debug: Begun implementing attach/detach --- src/target/riscv_debug.c | 35 +++++++++++++++++++++++++++++++---- src/target/riscv_debug.h | 2 ++ src/target/riscv_jtag_dtm.c | 19 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d87eb6834c4..db4e34f1178 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -92,10 +92,12 @@ static riscv_debug_version_e riscv_dm_version(uint32_t status); static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); -static void riscv_halt_request(target_s *target); -static void riscv_halt_resume(target_s *target, bool step); +static bool riscv_attach(target_s *target); +static void riscv_detach(target_s *target); static bool riscv_check_error(target_s *target); +static void riscv_halt_request(target_s *target); +static void riscv_halt_resume(target_s *target, bool step); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -236,6 +238,10 @@ static bool riscv_hart_init(riscv_hart_s *const hart) hart->vendorid : ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); + /* Setup core-agnostic target functions */ + target->attach = riscv_attach; + target->detach = riscv_detach; + target->check_error = riscv_check_error; target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -483,6 +489,27 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr return access_width; } +static bool riscv_attach(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* If the DMI requires special preparation, do that first */ + if (hart->dbg_module->dmi_bus->prepare) + hart->dbg_module->dmi_bus->prepare(target); + /* We then also need to select the Hart again so we're poking with the right one on the target */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel)) + return false; + + return true; +} + +static void riscv_detach(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* If the DMI needs steps done to quiesce it, finsh up with that */ + if (hart->dbg_module->dmi_bus->quiesce) + hart->dbg_module->dmi_bus->quiesce(target); +} + static bool riscv_check_error(target_s *const target) { return riscv_hart_struct(target)->status != RISCV_HART_NO_ERROR; @@ -490,7 +517,7 @@ static bool riscv_check_error(target_s *const target) static void riscv_halt_request(target_s *const target) { - riscv_hart_s *const hart = (riscv_hart_s *)target->priv; + riscv_hart_s *const hart = riscv_hart_struct(target); /* Request the hart to halt */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HALT_REQ)) return; @@ -506,7 +533,7 @@ static void riscv_halt_request(target_s *const target) static void riscv_halt_resume(target_s *target, const bool step) { - riscv_hart_s *const hart = (riscv_hart_s *)target->priv; + riscv_hart_s *const hart = riscv_hart_struct(target); /* Configure the debug controller for single-stepping as appropriate */ uint32_t stepping_config = 0U; if (!riscv_csr_read(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index d8b8a6957c9..75b7c77f643 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -78,6 +78,8 @@ struct riscv_dmi { uint8_t address_width; uint8_t fault; + void (*prepare)(target_s *target); + void (*quiesce)(target_s *target); bool (*read)(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); bool (*write)(riscv_dmi_s *dmi, uint32_t address, uint32_t value); }; diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 012f9802431..2661b8d9352 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -63,6 +63,9 @@ static bool riscv_jtag_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *va static bool riscv_jtag_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); static riscv_debug_version_e riscv_dtmcs_version(uint32_t dtmcs); +static void riscv_jtag_prepare(target_s *target); +static void riscv_jtag_quiesce(target_s *target); + void riscv_jtag_dtm_handler(const uint8_t dev_index) { riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); @@ -93,6 +96,8 @@ static void riscv_jtag_dtm_init(riscv_dmi_s *const dmi) /* Switch into DMI access mode for speed */ jtag_dev_write_ir(dmi->dev_index, IR_DMI); + dmi->prepare = riscv_jtag_prepare; + dmi->quiesce = riscv_jtag_quiesce; dmi->read = riscv_jtag_dmi_read; dmi->write = riscv_jtag_dmi_write; @@ -171,3 +176,17 @@ static riscv_debug_version_e riscv_dtmcs_version(const uint32_t dtmcs) DEBUG_INFO("Please report part with unknown RISC-V debug DMI version %x\n", version); return RISCV_DEBUG_UNKNOWN; } + +static void riscv_jtag_prepare(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* We put the TAP into bypass at the end of the JTAG handler, so put it back into DMI */ + jtag_dev_write_ir(hart->dbg_module->dmi_bus->dev_index, IR_DMI); +} + +static void riscv_jtag_quiesce(target_s *const target) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + /* On detaching, stick the TAP back into bypass */ + jtag_dev_write_ir(hart->dbg_module->dmi_bus->dev_index, IR_BYPASS); +} From 0b7ccee1810f2b41d633a66c19d5eb97c605ee7b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:38:44 +0000 Subject: [PATCH 037/130] riscv_debug: Populated target->cpuid and made use of it in gd32vf1_probe() for an even more positive part identification Co-Authored-By: Rafael Silva --- src/target/riscv_debug.c | 1 + src/target/stm32f1.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index db4e34f1178..afd6e00dbec 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -237,6 +237,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->designer_code = hart->vendorid ? hart->vendorid : ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); + target->cpuid = hart->archid; /* Setup core-agnostic target functions */ target->attach = riscv_attach; diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 75d7a1c1907..5c2bcf54c6c 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -186,6 +186,10 @@ bool gd32f1_probe(target_s *target) /* Identify RISC-V GD32VF1 chips */ bool gd32vf1_probe(target_s *const target) { + /* Make sure the architecture ID matches */ + if (target->cpuid != 0x80000022U) + return false; + /* Then read out the device ID */ const uint16_t device_id = target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU; switch (device_id) { case 0x410U: /* GD32VF103 */ From dbcd0fb41a37be3db27137c60fb152850d3e1679 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 08:13:33 +0000 Subject: [PATCH 038/130] riscv_debug: Implemented building the target description XML --- src/target/riscv_debug.c | 186 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index afd6e00dbec..ff4a8ce118b 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -34,8 +34,11 @@ #include "general.h" #include "target_probe.h" #include "target_internal.h" +#include "gdb_reg.h" #include "riscv_debug.h" +#include + #define RV_DM_CONTROL 0x10U #define RV_DM_STATUS 0x11U #define RV_DM_NEXT_DM 0x1dU @@ -76,11 +79,74 @@ /* mhartid -> machine ID of the Hart */ #define RV_HART_ID 0xf14U -#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU +#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU +#define RV_ISA_EXT_EMBEDDED 0x00000010U +#define RV_ISA_EXT_ANY_FLOAT 0x00010028U +#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U +#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U +#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U #define RV_DCSR_STEP 0x00000004U #define RV_DCSR_STEPIE 0x00000800U +#define RV_GPRS_COUNT 32U + +// clang-format off +/* General-purpose register name strings */ +static const char *const riscv_gpr_names[RV_GPRS_COUNT] = { + "zero", "ra", "sp", "gp", + "tp", "t0", "t1", "t2", + "fp", "s1", "a0", "a1", + "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", + "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", + "t3", "t4", "t5", "t6", +}; +// clang-format on + +/* General-purpose register types */ +static const gdb_reg_type_e riscv_gpr_types[RV_GPRS_COUNT] = { + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_CODE_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_DATA_PTR, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_DATA_PTR, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, + GDB_TYPE_UNSPECIFIED, +}; + +// clang-format off +static_assert(ARRAY_LENGTH(riscv_gpr_names) == ARRAY_LENGTH(riscv_gpr_types), + "GPR array length mismatch! GPR type array should have the same length as GPR name array." +); +// clang-format on + static void riscv_dm_init(riscv_dm_s *dbg_module); static bool riscv_hart_init(riscv_hart_s *hart); static void riscv_hart_free(void *priv); @@ -95,6 +161,8 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); static bool riscv_attach(target_s *target); static void riscv_detach(target_s *target); +static const char *riscv_target_description(target_s *target); + static bool riscv_check_error(target_s *target); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); @@ -243,6 +311,8 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->attach = riscv_attach; target->detach = riscv_detach; + target->regs_description = riscv_target_description; + target->check_error = riscv_check_error; target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; @@ -557,3 +627,117 @@ static void riscv_halt_resume(target_s *target, const bool step) /* Clear the request now we've got it resumed */ (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } + +static const char *riscv_fpu_ext_string(const uint32_t extensions) +{ + if (extensions & RV_ISA_EXT_QUAD_FLOAT) + return "q"; + if (extensions & RV_ISA_EXT_DOUBLE_FLOAT) + return "d"; + if (extensions & RV_ISA_EXT_SINGLE_FLOAT) + return "f"; + return ""; +} + +/* + * This function creates the target description XML string for a RISC-V part. + * This is done this way to decrease string duplication and thus code size, making it + * unfortunately much less readable than the string literal it is equivalent to. + * + * This string it creates is the XML-equivalent to the following: + * "" + * "" + * "" + * " riscv:rv[address_width][exts]" + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * The following are only generated for an I core, not an E: + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * Both are then continued with: + * " " + * " " + * "" + */ +static size_t riscv_build_target_description( + char *const buffer, size_t max_length, const uint8_t address_width, const uint32_t extensions) +{ + const bool embedded = extensions & RV_ISA_EXT_EMBEDDED; + const uint32_t fpu = extensions & RV_ISA_EXT_ANY_FLOAT; + + size_t print_size = max_length; + /* Start with the "preamble" chunks, which are mostly common across targets save for 2 words. */ + int offset = snprintf(buffer, print_size, "%s target %sriscv:rv%u%c%s%s ", + gdb_xml_preamble_first, gdb_xml_preamble_second, address_width, embedded ? 'e' : 'i', riscv_fpu_ext_string(fpu), + gdb_xml_preamble_third); + + const uint8_t gprs = embedded ? 16U : 32U; + /* Then build the general purpose register descriptions using the arrays at top of file */ + /* Note that in a device using the embedded (E) extension, we only generate the first 16. */ + for (uint8_t i = 0; i < gprs; ++i) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + + const char *const name = riscv_gpr_names[i]; + const gdb_reg_type_e type = riscv_gpr_types[i]; + + offset += snprintf(buffer + offset, print_size, "", name, address_width, + gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); + } + + /* Then build the program counter register description, which has the same bitsize as the GPRs. */ + if (max_length != 0) + print_size = max_length - (size_t)offset; + offset += snprintf(buffer + offset, print_size, "", address_width, + gdb_reg_type_strings[GDB_TYPE_CODE_PTR]); + + /* XXX: TODO - implement generation of the FPU feature and registers */ + + /* Add the closing tags required */ + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, ""); + /* offset is now the total length of the string created, discard the sign and return it. */ + return (size_t)offset; +} + +static const char *riscv_target_description(target_s *const target) +{ + const riscv_hart_s *const hart = riscv_hart_struct(target); + const size_t description_length = + riscv_build_target_description(NULL, 0, hart->address_width, hart->extensions) + 1U; + char *const description = malloc(description_length); + if (description) + (void)riscv_build_target_description(description, description_length, hart->address_width, hart->extensions); + return description; +} From 1ca5e44a2fbe70edf25356f85cc7d383e622bba0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 09:51:34 +0000 Subject: [PATCH 039/130] riscv_debug: Implemented regs_read for both rv32 and rv64 --- src/target/riscv32.c | 21 +++++++++++++++++++++ src/target/riscv64.c | 21 +++++++++++++++++++++ src/target/riscv_debug.c | 15 ++++----------- src/target/riscv_debug.h | 13 +++++++++++++ 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 011d806f13a..14d928d39c9 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -39,11 +39,20 @@ #include "riscv_debug.h" #include "gdb_packet.h" +typedef struct riscv32_regs { + uint32_t gprs[32]; + uint32_t pc; +} riscv32_regs_s; + +static void riscv32_regs_read(target_s *target, void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); bool riscv32_probe(target_s *const target) { target->core = "rv32"; + /* Provide the length of a suitable registers structure */ + target->regs_size = sizeof(riscv32_regs_s); + target->regs_read = riscv32_regs_read; target->mem_read = riscv32_mem_read; switch (target->designer_code) { @@ -60,6 +69,18 @@ bool riscv32_probe(target_s *const target) return false; } +static void riscv32_regs_read(target_s *const target, void *const data) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv32_regs_s *const regs = (riscv32_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + for (size_t gpr = 0; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + riscv_csr_read(hart, RV_DPC, ®s->pc); +} + void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) { switch (access_width) { diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 3b132de5abb..4f599c068e3 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -37,16 +37,37 @@ #include "target_probe.h" #include "riscv_debug.h" +typedef struct riscv64_regs { + uint64_t gprs[32]; + uint64_t pc; +} riscv64_regs_s; + +static void riscv64_regs_read(target_s *target, void *data); static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); bool riscv64_probe(target_s *const target) { target->core = "rv64"; + /* Provide the length of a suitable registers structure */ + target->regs_size = sizeof(riscv64_regs_s); + target->regs_read = riscv64_regs_read; target->mem_read = riscv64_mem_read; return false; } +static void riscv64_regs_read(target_s *const target, void *const data) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv64_regs_s *const regs = (riscv64_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + for (size_t gpr = 0; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + riscv_csr_read(hart, RV_DPC, ®s->pc); +} + void riscv64_unpack_data( void *const dest, const uint32_t data_low, const uint32_t data_high, const uint8_t access_width) { diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index ff4a8ce118b..c8a86882e01 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -59,17 +59,15 @@ #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU -#define RV_CSR_FORCE_MASK 0x3000U -#define RV_CSR_FORCE_32_BIT 0x1000U -#define RV_CSR_FORCE_64_BIT 0x2000U +#define RV_CSR_FORCE_MASK 0xc000U +#define RV_CSR_FORCE_32_BIT 0x4000U +#define RV_CSR_FORCE_64_BIT 0x8000U /* The following is a set of CSR address definitions */ /* misa -> The Hart's machine ISA register */ #define RV_ISA 0x301U /* dcsr -> Debug Control/Status Register */ #define RV_DCSR 0x7b0U -/* dpc -> Debug Program Counter */ -#define RV_DPC 0x7b1U /* mvendorid -> The JEP-106 code for the vendor implementing this Hart */ #define RV_VENDOR_ID 0xf11U /* marchid -> The RISC-V International architecture ID code */ @@ -79,12 +77,7 @@ /* mhartid -> machine ID of the Hart */ #define RV_HART_ID 0xf14U -#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU -#define RV_ISA_EXT_EMBEDDED 0x00000010U -#define RV_ISA_EXT_ANY_FLOAT 0x00010028U -#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U -#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U -#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U +#define RV_ISA_EXTENSIONS_MASK 0x03ffffffU #define RV_DCSR_STEP 0x00000004U #define RV_DCSR_STEPIE 0x00000800U diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 75b7c77f643..8de5a074886 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -136,6 +136,19 @@ typedef struct riscv_hart { #define RV_MEM_ACCESS_128_BIT 0x4U #define RV_MEM_ACCESS_SHIFT 20U +/* dpc -> Debug Program Counter */ +#define RV_DPC 0x7b1U +/* The GPR base defines the starting register space address for the CPU state registers */ +#define RV_GPR_BASE 0x1000U +/* The FP base defines the starting register space address for the floating point registers */ +#define RV_FP_BASE 0x1020U + +#define RV_ISA_EXT_EMBEDDED 0x00000010U +#define RV_ISA_EXT_ANY_FLOAT 0x00010028U +#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U +#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U +#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); riscv_hart_s *riscv_hart_struct(target_s *target); From 338d206fe4168784c7abb867b118ccdf30530856 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 09:52:29 +0000 Subject: [PATCH 040/130] riscv_debug: Halt the hart we're looking at to on attach, and resume it on detach --- src/target/riscv_debug.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index c8a86882e01..3d012d7ef7a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -562,13 +562,16 @@ static bool riscv_attach(target_s *const target) /* We then also need to select the Hart again so we're poking with the right one on the target */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel)) return false; - + /* We then need to halt the hart so the attach process can function */ + riscv_halt_request(target); return true; } static void riscv_detach(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); + /* Once we get done and the user's asked us to detach, we need to resume the hart */ + riscv_halt_resume(target, false); /* If the DMI needs steps done to quiesce it, finsh up with that */ if (hart->dbg_module->dmi_bus->quiesce) hart->dbg_module->dmi_bus->quiesce(target); From 402b677d35c365fdcdf63c4fce4356b778f7115b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:05:42 +0000 Subject: [PATCH 041/130] riscv32: Added some better documentation for what various things do --- src/target/riscv32.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 14d928d39c9..a8ce462c0fa 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -49,6 +49,7 @@ static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, si bool riscv32_probe(target_s *const target) { + /* Finish setting up the target structure with generic rv32 functions */ target->core = "rv32"; /* Provide the length of a suitable registers structure */ target->regs_size = sizeof(riscv32_regs_s); @@ -71,16 +72,20 @@ bool riscv32_probe(target_s *const target) static void riscv32_regs_read(target_s *const target, void *const data) { + /* Grab the hart structure and figure out how many registers need reading out */ riscv_hart_s *const hart = riscv_hart_struct(target); riscv32_regs_s *const regs = (riscv32_regs_s *)data; const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through reading out the GPRs */ for (size_t gpr = 0; gpr < gprs_count; ++gpr) { // TODO: handle when this fails.. riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); } + /* Special access to grab the program counter that would be executed on resuming the hart */ riscv_csr_read(hart, RV_DPC, ®s->pc); } +/* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) { switch (access_width) { From ca9bd0cd420f151ee754c2f264ea8910f8d73232 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:07:45 +0000 Subject: [PATCH 042/130] riscv64: Added some better documentation for what various things do --- src/target/riscv64.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 4f599c068e3..7bf04beb2aa 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -47,6 +47,7 @@ static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, si bool riscv64_probe(target_s *const target) { + /* Finish setting up the target structure with generic rv64 functions */ target->core = "rv64"; /* Provide the length of a suitable registers structure */ target->regs_size = sizeof(riscv64_regs_s); @@ -58,26 +59,32 @@ bool riscv64_probe(target_s *const target) static void riscv64_regs_read(target_s *const target, void *const data) { + /* Grab the hart structure and figure out how many registers need reading out */ riscv_hart_s *const hart = riscv_hart_struct(target); riscv64_regs_s *const regs = (riscv64_regs_s *)data; const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through reading out the GPRs */ for (size_t gpr = 0; gpr < gprs_count; ++gpr) { // TODO: handle when this fails.. riscv_csr_read(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); } + /* Special access to grab the program counter that would be executed on resuming the hart */ riscv_csr_read(hart, RV_DPC, ®s->pc); } +/* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ void riscv64_unpack_data( void *const dest, const uint32_t data_low, const uint32_t data_high, const uint8_t access_width) { switch (access_width) { + /* If the access was 32-bit or less, ignore data_high and delegate to the RV32 version of this function */ case RV_MEM_ACCESS_8_BIT: case RV_MEM_ACCESS_16_BIT: case RV_MEM_ACCESS_32_BIT: riscv32_unpack_data(dest, data_low, access_width); break; case RV_MEM_ACCESS_64_BIT: { + /* Reconstruct the 64-bit value and copy it into the destination */ uint64_t value = ((uint64_t)data_high << 32U) | data_low; memcpy(dest, &value, sizeof(value)); break; From 28e26b3386be1a634a188d1c8d7a226b313ba727 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:17:09 +0000 Subject: [PATCH 043/130] riscv_debug: Fixed a couple of mistakes in the comments for riscv_dmi_init() --- src/target/riscv_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 3d012d7ef7a..89b7466d1a6 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -182,13 +182,14 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) } const riscv_debug_version_e dm_version = riscv_dm_version(dm_status); + /* If the DM is not unimplemented, allocate a structure for it and do further processing */ if (dm_version != RISCV_DEBUG_UNIMPL) { riscv_dm_s *dbg_module = calloc(1, sizeof(*dbg_module)); if (!dbg_module) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return; } - /* Setup and try to discover the DM */ + /* Setup and try to discover the DM's Harts */ dbg_module->dmi_bus = dmi; dbg_module->base = base_addr; dbg_module->version = dm_version; From 2a36eee74491f361addacdc1cffa52e69c51f435 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:43:41 +0000 Subject: [PATCH 044/130] riscv_debug: Implemented the target halt_poll hook --- src/target/riscv_debug.c | 49 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 89b7466d1a6..a45144fcd32 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -79,11 +79,26 @@ #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU -#define RV_DCSR_STEP 0x00000004U -#define RV_DCSR_STEPIE 0x00000800U +#define RV_DCSR_STEP 0x00000004U +#define RV_DCSR_CAUSE_MASK 0x000001c0U +#define RV_DCSR_STEPIE 0x00000800U #define RV_GPRS_COUNT 32U +/* This enum defines the set of currently known and valid halt causes */ +typedef enum riscv_halt_cause { + /* Halt was caused by an `ebreak` instruction executing */ + RV_HALT_CAUSE_EBREAK = (1U << 6U), + /* Halt was caused by a breakpoint (set in the trigger module) */ + RV_HALT_CAUSE_BREAKPOINT = (2U << 6U), + /* Halt was caused by debugger request (haltreq) */ + RV_HALT_CAUSE_REQUEST = (3U << 6U), + /* Halt was caused by single-step execution */ + RV_HALT_CAUSE_STEP = (4U << 6U), + /* Halt was caused by request out of reset (resethaltreq) */ + RV_HALT_CAUSE_RESET = (5U << 6U), +} riscv_halt_cause_e; + // clang-format off /* General-purpose register name strings */ static const char *const riscv_gpr_names[RV_GPRS_COUNT] = { @@ -159,6 +174,7 @@ static const char *riscv_target_description(target_s *target); static bool riscv_check_error(target_s *target); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); +static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -310,6 +326,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->check_error = riscv_check_error; target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; + target->halt_poll = riscv_halt_poll; if (hart->access_width == 32U) { DEBUG_INFO("-> riscv32_probe\n"); @@ -625,6 +642,34 @@ static void riscv_halt_resume(target_s *target, const bool step) (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } +static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) +{ + (void)watch; + riscv_hart_s *const hart = riscv_hart_struct(target); + uint32_t status = 0; + /* Check if the hart is currently halted */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) + return TARGET_HALT_ERROR; + /* If the hart is currently running, exit out early */ + if (!(status & RV_DM_STAT_ALL_HALTED)) + return TARGET_HALT_RUNNING; + /* Read out DCSR to find out why we're halted */ + if (!riscv_csr_read(hart, RV_DCSR, &status)) + return TARGET_HALT_ERROR; + status &= RV_DCSR_CAUSE_MASK; + /* Dispatch on the cause code */ + switch (status) { + case RV_HALT_CAUSE_BREAKPOINT: + return TARGET_HALT_BREAKPOINT; + case RV_HALT_CAUSE_STEP: + return TARGET_HALT_STEPPING; + default: + break; + } + /* In the default case, assume it was by request (ebreak, haltreq, resethaltreq) */ + return TARGET_HALT_REQUEST; +} + static const char *riscv_fpu_ext_string(const uint32_t extensions) { if (extensions & RV_ISA_EXT_QUAD_FLOAT) From 4aad1b4caf9934dc5b96446b13b4e7d4b4625dc8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 17:18:11 +0000 Subject: [PATCH 045/130] riscv_debug: Implemented discovery of the available "trigger" slots and what kinds of triggers they each support --- src/target/riscv_debug.c | 62 ++++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.h | 5 ++++ 2 files changed, 67 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index a45144fcd32..f90bb09fbb6 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -77,6 +77,13 @@ /* mhartid -> machine ID of the Hart */ #define RV_HART_ID 0xf14U +/* tselect -> Trigger selection register */ +#define RV_TRIG_SELECT 0x7a0U +/* tinfo -> selected trigger information register */ +#define RV_TRIG_INFO 0x7a4U +/* tdata1 -> selected trigger configuration register 1 */ +#define RV_TRIG_DATA_1 0x7a1U + #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU #define RV_DCSR_STEP 0x00000004U @@ -165,6 +172,7 @@ static void riscv_dm_unref(riscv_dm_s *dbg_module); static riscv_debug_version_e riscv_dm_version(uint32_t status); static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); +static void riscv_hart_discover_triggers(riscv_hart_s *hart); static bool riscv_attach(target_s *target); static void riscv_detach(target_s *target); @@ -317,6 +325,8 @@ static bool riscv_hart_init(riscv_hart_s *const hart) ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); target->cpuid = hart->archid; + riscv_hart_discover_triggers(hart); + /* Setup core-agnostic target functions */ target->attach = riscv_attach; target->detach = riscv_detach; @@ -571,6 +581,58 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr return access_width; } +static void riscv_hart_discover_triggers(riscv_hart_s *const hart) +{ + /* Discover how many breakpoints this hart supports */ + hart->triggers = UINT32_MAX; + if (!riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers) || + !riscv_csr_read(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers)) { + hart->triggers = 0; + return; + } + /* + * The value we read back will always be one less than the actual number supported + * as it represents the last valid index, rather than the last valid breakpoint. + */ + ++hart->triggers; + DEBUG_INFO("Hart has %" PRIu32 " trigger slots available\n", hart->triggers); + /* If the hardware supports more slots than we do, cap it. */ + if (hart->triggers > RV_TRIGGERS_MAX) + hart->triggers = RV_TRIGGERS_MAX; + + /* Next, go through each one and map what it supports out into the trigger_uses slots */ + for (uint32_t trigger = 0; trigger < hart->triggers; ++trigger) { + /* Select the trigger */ + riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger); + /* Try reading the trigger info */ + uint32_t info = 0; + if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info)) { + /* + * If that fails, it's probably because the tinfo register isn't implemented, so read + * the tdata1 register instead and extract the type from the MSb and build the info bitset from that + */ + if (hart->access_width == 32U) { + uint32_t data = 0; + riscv_csr_read(hart, RV_TRIG_DATA_1, &data); + /* The last 4 bits contain the trigger info */ + info = data >> 28U; + } else { + uint64_t data = 0; + riscv_csr_read(hart, RV_TRIG_DATA_1, &data); + /* The last 4 bits contain the trigger info */ + info = data >> 60U; + } + /* Info now needs converting from a value from 0 to 15 to having the correct bit set */ + info = 1U << info; + } + /* If the 0th bit is set, this means the trigger is unsupported. Clear it to make testing easy */ + info &= 0x0000fffeU; + /* Now info's bottom 16 bits contain the supported trigger modes, so write this info to the slot in the hart */ + hart->trigger_uses[trigger] = info; + DEBUG_TARGET("Hart trigger slot %" PRIu32 " modes: %04" PRIx32 "\n", trigger, info); + } +} + static bool riscv_attach(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 8de5a074886..cf258ab4128 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -93,6 +93,8 @@ typedef struct riscv_dm { riscv_debug_version_e version; } riscv_dm_s; +#define RV_TRIGGERS_MAX 8U + /* This represents a specifc Hart on a DM */ typedef struct riscv_hart { riscv_dm_s *dbg_module; @@ -107,6 +109,9 @@ typedef struct riscv_hart { uint32_t archid; uint32_t implid; uint32_t hartid; + + uint32_t triggers; + uint32_t trigger_uses[RV_TRIGGERS_MAX]; } riscv_hart_s; #define RV_STATUS_VERSION_MASK 0x0000000fU From 7db2ed3983e71d213f1d612374d1669c92892c8d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:15:54 +0000 Subject: [PATCH 046/130] riscv_debug: Implemented support machinary for being able to set watch and breakpoints --- src/target/riscv_debug.c | 22 +++++++++++++++++++++- src/target/riscv_debug.h | 12 ++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index f90bb09fbb6..427a0e3a90d 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -83,6 +83,8 @@ #define RV_TRIG_INFO 0x7a4U /* tdata1 -> selected trigger configuration register 1 */ #define RV_TRIG_DATA_1 0x7a1U +/* tdata2 -> selected trigger configuration register 2 */ +#define RV_TRIG_DATA_2 0x7a2U #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU @@ -626,13 +628,31 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) info = 1U << info; } /* If the 0th bit is set, this means the trigger is unsupported. Clear it to make testing easy */ - info &= 0x0000fffeU; + info &= RV_TRIGGER_SUPPORT_MASK; /* Now info's bottom 16 bits contain the supported trigger modes, so write this info to the slot in the hart */ hart->trigger_uses[trigger] = info; DEBUG_TARGET("Hart trigger slot %" PRIu32 " modes: %04" PRIx32 "\n", trigger, info); } } +bool riscv_config_trigger(riscv_hart_s *const hart, const uint32_t trigger, const riscv_trigger_state_e mode, + const void *const config, const void *const address) +{ + /* + * Select the trigger and write the new configuration to it provided by config. + * tdata1 (RV_TRIG_DATA_1) becomes mcontrol (match control) for this - + * see §5.2.9 pg53 of the RISC-V debug spec v0.13.2 for more details. + */ + const bool result = riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger) && + riscv_csr_write(hart, RV_TRIG_DATA_1, config) && riscv_csr_write(hart, RV_TRIG_DATA_2, address); + if (result) { + /* If that succeeds, update the slot with the new mode it's in */ + hart->trigger_uses[trigger] &= ~RV_TRIGGER_MODE_MASK; + hart->trigger_uses[trigger] |= mode; + } + return result; +} + static bool riscv_attach(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index cf258ab4128..01c313269ca 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -64,6 +64,13 @@ typedef enum riscv_hart_status { RISCV_HART_OTHER = 7, } riscv_hart_status_e; +/* This enum describes the current state of a trigger in the TM */ +typedef enum riscv_trigger_state { + RISCV_TRIGGER_MODE_UNUSED = 0x00000000U, + RISCV_TRIGGER_MODE_BREAKPOINT = 0x00010000U, + RISCV_TRIGGER_MODE_WATCHPOINT = 0x00020000U, +} riscv_trigger_state_e; + typedef struct riscv_dmi riscv_dmi_s; /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ @@ -154,6 +161,9 @@ typedef struct riscv_hart { #define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U #define RV_ISA_EXT_QUAD_FLOAT 0x00010000U +#define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU +#define RV_TRIGGER_MODE_MASK 0xffff0000U + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); riscv_hart_s *riscv_hart_struct(target_s *target); @@ -163,6 +173,8 @@ bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); bool riscv_command_wait_complete(riscv_hart_s *hart); bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); +bool riscv_config_trigger( + riscv_hart_s *hart, uint32_t trigger, riscv_trigger_state_e mode, const void *config, const void *address); uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, size_t length); void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); From da69c708fcb8f288d089c9f2899e162faf239fd5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:16:27 +0000 Subject: [PATCH 047/130] riscv_debug: Added documentation on where to find the debug specs used --- src/target/riscv_debug.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 427a0e3a90d..d905c05b92a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -39,6 +39,16 @@ #include +/* + * Links to exact specifications used in this code are listed here for ease: + * riscv-debug-spec v0.11 as used by SiFive: + * https://static.dev.sifive.com/riscv-debug-spec-0.11nov12.pdf + * riscv-debug-spec v0.13.2 (release): + * https://raw.githubusercontent.com/riscv/riscv-debug-spec/v0.13-release/riscv-debug-release.pdf + * riscv-debug-spec v1.0 (stable): + * https://github.com/riscv/riscv-debug-spec/blob/master/riscv-debug-stable.pdf + */ + #define RV_DM_CONTROL 0x10U #define RV_DM_STATUS 0x11U #define RV_DM_NEXT_DM 0x1dU From e821868716d00f1e36b273bc0e1daaab8f1afd84 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:46:57 +0000 Subject: [PATCH 048/130] riscv_debug: Implemented a translator for a breakwatch size to match size --- src/target/riscv_debug.c | 25 +++++++++++++++++++++++++ src/target/riscv_debug.h | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d905c05b92a..3d3ea082e19 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -645,6 +645,31 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) } } +riscv_match_size_e riscv_breakwatch_match_size(const size_t size) +{ + switch (size) { + case 8U: + return RV_MATCH_SIZE_8_BIT; + case 16U: + return RV_MATCH_SIZE_16_BIT; + case 32U: + return RV_MATCH_SIZE_32_BIT; + case 48U: + return RV_MATCH_SIZE_48_BIT; + case 64U: + return RV_MATCH_SIZE_64_BIT; + case 80U: + return RV_MATCH_SIZE_80_BIT; + case 96U: + return RV_MATCH_SIZE_96_BIT; + case 112U: + return RV_MATCH_SIZE_112_BIT; + case 128U: + return RV_MATCH_SIZE_128_BIT; + } + return 0; +} + bool riscv_config_trigger(riscv_hart_s *const hart, const uint32_t trigger, const riscv_trigger_state_e mode, const void *const config, const void *const address) { diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 01c313269ca..3a4a40c1bdd 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -71,6 +71,19 @@ typedef enum riscv_trigger_state { RISCV_TRIGGER_MODE_WATCHPOINT = 0x00020000U, } riscv_trigger_state_e; +/* The size bits are 22:21 + 17:16, but the upper 2 are only valid on rv64 */ +typedef enum riscv_match_size { + RV_MATCH_SIZE_8_BIT = 0x00010000U, + RV_MATCH_SIZE_16_BIT = 0x00020000U, + RV_MATCH_SIZE_32_BIT = 0x00030000U, + RV_MATCH_SIZE_48_BIT = 0x00200000U, + RV_MATCH_SIZE_64_BIT = 0x00210000U, + RV_MATCH_SIZE_80_BIT = 0x00220000U, + RV_MATCH_SIZE_96_BIT = 0x00230000U, + RV_MATCH_SIZE_112_BIT = 0x00400000U, + RV_MATCH_SIZE_128_BIT = 0x00410000U, +} riscv_match_size_e; + typedef struct riscv_dmi riscv_dmi_s; /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ @@ -173,6 +186,7 @@ bool riscv_dm_write(riscv_dm_s *dbg_module, uint8_t address, uint32_t value); bool riscv_command_wait_complete(riscv_hart_s *hart); bool riscv_csr_read(riscv_hart_s *hart, uint16_t reg, void *data); bool riscv_csr_write(riscv_hart_s *hart, uint16_t reg, const void *data); +riscv_match_size_e riscv_breakwatch_match_size(size_t size); bool riscv_config_trigger( riscv_hart_s *hart, uint32_t trigger, riscv_trigger_state_e mode, const void *config, const void *address); From 91bcc41f32a101a2e34c20cd285b021184a04c85 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:58:45 +0000 Subject: [PATCH 049/130] riscv32: Implemented the target breakwatch_set hook --- src/target/riscv32.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index a8ce462c0fa..b92215ddda4 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -44,9 +44,34 @@ typedef struct riscv32_regs { uint32_t pc; } riscv32_regs_s; +/* This defines a match trigger that's for an address or data location */ +#define RV32_MATCH_ADDR_DATA_TRIGGER 0x20000000U +/* A dmode of 1 restricts the writability of the trigger to debug mode only */ +#define RV32_MATCH_DMODE_DEBUG 0x08000000U +/* Match when the processor tries to execute the location */ +#define RV32_MATCH_EXECUTE 0x00000004U +/* Match when the processor tries to read the location */ +#define RV32_MATCH_READ 0x00000001U +/* Match when the processor tries to write the location */ +#define RV32_MATCH_WRITE 0x00000002U +/* Define that the match should occur in all/any mode */ +#define RV32_MATCH_ANY_MODE 0x00000058U +/* Set the match action to raise a breakpoint exception */ +#define RV32_MATCH_ACTION_EXCEPTION 0x00000000U +/* Set the match action to enter debug mode */ +#define RV32_MATCH_ACTION_DEBUG_MODE 0x00001000U +/* These two define whether the match should be performed on the address, or specific data */ +#define RV32_MATCH_ADDR 0x00000000U +#define RV32_MATCH_DATA 0x00080000U +/* These two define the match timing (before-or-after operation execution) */ +#define RV32_MATCH_BEFORE 0x00000000U +#define RV32_MATCH_AFTER 0x00040000U + static void riscv32_regs_read(target_s *target, void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); +static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); + bool riscv32_probe(target_s *const target) { /* Finish setting up the target structure with generic rv32 functions */ @@ -56,6 +81,8 @@ bool riscv32_probe(target_s *const target) target->regs_read = riscv32_regs_read; target->mem_read = riscv32_mem_read; + target->breakwatch_set = riscv32_breakwatch_set; + switch (target->designer_code) { case JEP106_MANUFACTURER_RV_GIGADEVICE: PROBE(gd32vf1_probe); @@ -133,3 +160,56 @@ static void riscv32_mem_read(target_s *const target, void *const dest, const tar riscv32_unpack_data(data + offset, value, access_width); } } + +/* + * The following can be used as a key for understanding the various return results from the breakwatch functions: + * 0 -> success + * 1 -> not supported + * -1 -> an error occured + */ + +static int riscv32_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + size_t trigger = 0; + /* Find the first unused trigger slot */ + for (; trigger < hart->triggers; ++trigger) { + if ((hart->trigger_uses[trigger] & RV_TRIGGER_MODE_MASK) == RISCV_TRIGGER_MODE_UNUSED) + break; + } + /* If none was available, return an error */ + if (trigger == hart->triggers) + return -1; + + /* Build the mcontrol config for the requested breakwatch type */ + uint32_t config = RV32_MATCH_ADDR_DATA_TRIGGER | RV32_MATCH_DMODE_DEBUG | RV32_MATCH_ANY_MODE | + RV32_MATCH_ACTION_DEBUG_MODE | RV32_MATCH_ADDR | riscv_breakwatch_match_size(breakwatch->size); + // RV32_MATCH_DATA (bit 19) + riscv_trigger_state_e mode = RISCV_TRIGGER_MODE_WATCHPOINT; + switch (breakwatch->type) { + case TARGET_BREAK_HARD: + config |= RV32_MATCH_EXECUTE | RV32_MATCH_BEFORE; + mode = RISCV_TRIGGER_MODE_BREAKPOINT; + break; + case TARGET_WATCH_READ: + config |= RV32_MATCH_READ | RV32_MATCH_AFTER; + break; + case TARGET_WATCH_WRITE: + config |= RV32_MATCH_WRITE | RV32_MATCH_BEFORE; + break; + case TARGET_WATCH_ACCESS: + config |= RV32_MATCH_READ | RV32_MATCH_WRITE | RV32_MATCH_AFTER; + break; + default: + /* If the breakwatch type is not one of the above, tell the debugger we don't support it */ + return 1; + } + /* Grab the address to set the breakwatch on and configure the hardware */ + const uint32_t address = breakwatch->addr; + const bool result = riscv_config_trigger(hart, trigger, mode, &config, &address); + /* If configuration succeeds, store the trigger index in the breakwatch structure */ + if (result) + breakwatch->reserved[0] = trigger; + /* Return based on whether setting up the hardware worked or not */ + return result ? 0 : -1; +} From 6267b84c1bc10d916c8961fbba2394a98692d3be Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:48:28 +0000 Subject: [PATCH 050/130] riscv32: Implemented the target breakwatch_clear hook --- src/target/riscv32.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index b92215ddda4..b991e833ef4 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -71,6 +71,7 @@ static void riscv32_regs_read(target_s *target, void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); +static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); bool riscv32_probe(target_s *const target) { @@ -82,6 +83,7 @@ bool riscv32_probe(target_s *const target) target->mem_read = riscv32_mem_read; target->breakwatch_set = riscv32_breakwatch_set; + target->breakwatch_clear = riscv32_breakwatch_clear; switch (target->designer_code) { case JEP106_MANUFACTURER_RV_GIGADEVICE: @@ -213,3 +215,11 @@ static int riscv32_breakwatch_set(target_s *const target, breakwatch_s *const br /* Return based on whether setting up the hardware worked or not */ return result ? 0 : -1; } + +static int riscv32_breakwatch_clear(target_s *const target, breakwatch_s *const breakwatch) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + const uint32_t config = RV32_MATCH_ADDR_DATA_TRIGGER; + const uint32_t address = 0; + return riscv_config_trigger(hart, breakwatch->reserved[0], RISCV_TRIGGER_MODE_UNUSED, &config, &address) ? 0 : -1; +} From 067526a57839191d42b18b4e303e22302f6474cf Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:48:54 +0000 Subject: [PATCH 051/130] gdb_main: Added some comments to aid with understanding handle_z_packet() --- src/gdb_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gdb_main.c b/src/gdb_main.c index 8af934921b9..d8d39d5f1a7 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -722,10 +722,13 @@ static void handle_z_packet(char *packet, const size_t plen) else ret = target_breakwatch_clear(cur_target, type, addr, len); + /* If the target handler was unable to set/clear the break/watch-point, return an error */ if (ret < 0) gdb_putpacketz("E01"); + /* If the handler does not support the kind requested, return empty string */ else if (ret > 0) gdb_putpacketz(""); + /* Otherwise let GDB know that everything went well */ else gdb_putpacketz("OK"); } From 3ec8a749bf82b0812d360f49a06598a2956c22e4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 05:52:53 +0000 Subject: [PATCH 052/130] stm32f1: Modified the Flash routines to work with GD32VF103 parts too --- src/target/stm32f1.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 5c2bcf54c6c..4f634e303ac 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -41,6 +41,7 @@ #include "target.h" #include "target_internal.h" #include "cortexm.h" +#include "jep106.h" static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv); @@ -729,7 +730,11 @@ static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET); target_mem_write32(target, FLASH_CR, FLASH_CR_PG); - cortexm_mem_write_sized(target, dest, src, offset, ALIGN_16BIT); + /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ + if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) + target_mem_write(target, dest, src, offset); + else + cortexm_mem_write_sized(target, dest, src, offset, ALIGN_16BIT); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL)) @@ -743,7 +748,11 @@ static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const stm32f1_flash_clear_eop(target, FLASH_BANK2_OFFSET); target_mem_write32(target, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG); - cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_16BIT); + /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ + if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) + target_mem_write(target, dest + offset, data + offset, remainder); + else + cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_16BIT); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK2_OFFSET, NULL)) From c53a3736aade13baa02742221722852c8a5314c7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 05:57:00 +0000 Subject: [PATCH 053/130] riscv_debug: Moved the part ID readout into its own function, fixing operation on rv64 --- src/target/riscv_debug.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 3d3ea082e19..50c96c797b4 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -296,6 +296,26 @@ static uint8_t riscv_isa_address_width(const uint32_t isa) return 32U; } +static void riscv_hart_read_ids(riscv_hart_s *const hart) +{ + riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid); + if (hart->access_width == 32U) { + riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); + riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); + riscv_csr_read(hart, RV_HART_ID, &hart->hartid); + } else if (hart->access_width == 64U) { + /* For now, on rv64, we just truncate these down after read */ + uint64_t ident = 0; + riscv_csr_read(hart, RV_ARCH_ID, &ident); + hart->archid = ident & 0xffffffffU; + riscv_csr_read(hart, RV_IMPL_ID, &ident); + hart->implid = ident & 0xffffffffU; + riscv_csr_read(hart, RV_HART_ID, &ident); + hart->hartid = ident & 0xffffffffU; + } + /* rv128 is unimpl. */ +} + static bool riscv_hart_init(riscv_hart_s *const hart) { /* Allocate a new target */ @@ -314,11 +334,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) uint32_t isa = riscv_hart_discover_isa(hart); hart->address_width = riscv_isa_address_width(isa); hart->extensions = isa & RV_ISA_EXTENSIONS_MASK; - riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid); - /* XXX: These will technically go wrong on rv64 - need some way to deal with that. */ - riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); - riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); - riscv_csr_read(hart, RV_HART_ID, &hart->hartid); + riscv_hart_read_ids(hart); DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 ", exts = %08" PRIx32 "\n", From 71e87258d37a6049f522ee3d284fbdedbd66fab7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 06:38:03 +0000 Subject: [PATCH 054/130] riscv_debug: Implemented target reset --- src/target/riscv_debug.c | 25 +++++++++++++++++++++++++ src/target/riscv_debug.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 50c96c797b4..2b837f4328c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -59,6 +59,9 @@ #define RV_DM_CTRL_HARTSELHI_MASK 0x0000ffc0U #define RV_DM_CTRL_HALT_REQ 0x80000000U #define RV_DM_CTRL_RESUME_REQ 0x40000000U +#define RV_DM_CTRL_HART_RESET 0x20000000U +#define RV_DM_CTRL_HART_ACK_RESET 0x10000000U +#define RV_DM_CTRL_SYSTEM_RESET 0x00000002U #define RV_DM_CTRL_HARTSELLO_SHIFT 16U #define RV_DM_CTRL_HARTSELHI_SHIFT 4U @@ -195,6 +198,7 @@ static bool riscv_check_error(target_s *target); static void riscv_halt_request(target_s *target); static void riscv_halt_resume(target_s *target, bool step); static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); +static void riscv_reset(target_s *target); void riscv_dmi_init(riscv_dmi_s *const dmi) { @@ -365,6 +369,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->halt_request = riscv_halt_request; target->halt_resume = riscv_halt_resume; target->halt_poll = riscv_halt_poll; + target->reset = riscv_reset; if (hart->access_width == 32U) { DEBUG_INFO("-> riscv32_probe\n"); @@ -803,6 +808,26 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_ return TARGET_HALT_REQUEST; } +/* Do note that this can be used with a riscv_halt_request() call to initiate halt-on-reset debugging */ +static void riscv_reset(target_s *const target) +{ + /* If the target does not have the nRST pin inhibited, use that to initiate reset */ + if (!(target->target_options & RV_TOPT_INHIBIT_NRST)) { + platform_nrst_set_val(true); + platform_nrst_set_val(false); + /* In theory we're done at this point and no debug state was perturbed */ + } else { + /* + * Otherwise, if nRST is not usable, use instead reset via dmcontrol. In this case, + * when reset is requested, use the ndmreset bit to perform a system reset + */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_SYSTEM_RESET); + /* Complete the reset by resetting ndmreset */ + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); + } +} + static const char *riscv_fpu_ext_string(const uint32_t extensions) { if (extensions & RV_ISA_EXT_QUAD_FLOAT) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 3a4a40c1bdd..34c68218802 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -177,6 +177,8 @@ typedef struct riscv_hart { #define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU #define RV_TRIGGER_MODE_MASK 0xffff0000U +#define RV_TOPT_INHIBIT_NRST 0x00000001U + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); riscv_hart_s *riscv_hart_struct(target_s *target); From b5349fced6c322b70beaca70f59f1fa1fb9f9a78 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:02:07 +0000 Subject: [PATCH 055/130] riscv_debug: Refactored out the DM state polling code into a new function --- src/target/riscv_debug.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 2b837f4328c..0faca592d58 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -738,18 +738,26 @@ static bool riscv_check_error(target_s *const target) return riscv_hart_struct(target)->status != RISCV_HART_NO_ERROR; } +static bool riscv_dm_poll_state(riscv_dm_s *const dbg_module, const uint32_t state) +{ + /* Poll for the requested state to become set */ + uint32_t status = 0; + while (!(status & state)) { + if (!riscv_dm_read(dbg_module, RV_DM_STATUS, &status)) + return false; + } + return true; +} + static void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); /* Request the hart to halt */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HALT_REQ)) return; - uint32_t status = 0; /* Poll for the hart to become halted */ - while (!(status & RV_DM_STAT_ALL_HALTED)) { - if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) - return; - } + if (!riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_HALTED)) + return; /* Clear the request now we've got it halted */ (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } @@ -770,12 +778,9 @@ static void riscv_halt_resume(target_s *target, const bool step) /* Request the hart to resume */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) return; - uint32_t status = 0; /* Poll for the hart to become resumed */ - while (!(status & RV_DM_STAT_ALL_RESUME_ACK)) { - if (!riscv_dm_read(hart->dbg_module, RV_DM_STATUS, &status)) - return; - } + if (!riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESUME_ACK)) + return; /* Clear the request now we've got it resumed */ (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } From 6483abf6f586485e791a84dcad49e3c62de9f58f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:02:40 +0000 Subject: [PATCH 056/130] riscv_debug: Implemented polling for reset succeeding and then acknowledgement of the reset --- src/target/riscv_debug.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 0faca592d58..d2e1b92c5af 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -68,6 +68,7 @@ #define RV_DM_STAT_ALL_RESUME_ACK 0x00020000U #define RV_DM_STAT_NON_EXISTENT 0x00004000U #define RV_DM_STAT_ALL_HALTED 0x00000200U +#define RV_DM_STAT_ALL_RESET 0x00080000U #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU @@ -816,9 +817,11 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_ /* Do note that this can be used with a riscv_halt_request() call to initiate halt-on-reset debugging */ static void riscv_reset(target_s *const target) { + riscv_hart_s *const hart = riscv_hart_struct(target); /* If the target does not have the nRST pin inhibited, use that to initiate reset */ if (!(target->target_options & RV_TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); + riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); platform_nrst_set_val(false); /* In theory we're done at this point and no debug state was perturbed */ } else { @@ -826,11 +829,14 @@ static void riscv_reset(target_s *const target) * Otherwise, if nRST is not usable, use instead reset via dmcontrol. In this case, * when reset is requested, use the ndmreset bit to perform a system reset */ - riscv_hart_s *const hart = riscv_hart_struct(target); riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_SYSTEM_RESET); + riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); /* Complete the reset by resetting ndmreset */ riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } + /* Acknowledge the reset */ + riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HART_ACK_RESET); + target_check_error(target); } static const char *riscv_fpu_ext_string(const uint32_t extensions) From 5528a8d38ca01fbf4932a19ce17af160de17f092 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:22:39 +0000 Subject: [PATCH 057/130] riscv32: Improved the checks in riscv32_breakwatch_set() to avoid using unsuitable trigger slots --- src/target/riscv32.c | 5 ++++- src/target/riscv_debug.h | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index b991e833ef4..3b9549c4b3a 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -176,7 +176,10 @@ static int riscv32_breakwatch_set(target_s *const target, breakwatch_s *const br size_t trigger = 0; /* Find the first unused trigger slot */ for (; trigger < hart->triggers; ++trigger) { - if ((hart->trigger_uses[trigger] & RV_TRIGGER_MODE_MASK) == RISCV_TRIGGER_MODE_UNUSED) + const uint32_t trigger_use = hart->trigger_uses[trigger]; + /* Make sure it's unused and that it supports breakwatch mode */ + if ((trigger_use & RV_TRIGGER_MODE_MASK) == RISCV_TRIGGER_MODE_UNUSED && + (trigger_use & RV_TRIGGER_SUPPORT_BREAKWATCH)) break; } /* If none was available, return an error */ diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 34c68218802..efb2e056059 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -174,8 +174,9 @@ typedef struct riscv_hart { #define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U #define RV_ISA_EXT_QUAD_FLOAT 0x00010000U -#define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU -#define RV_TRIGGER_MODE_MASK 0xffff0000U +#define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU +#define RV_TRIGGER_MODE_MASK 0xffff0000U +#define RV_TRIGGER_SUPPORT_BREAKWATCH 0x00000004U #define RV_TOPT_INHIBIT_NRST 0x00000001U From 840cbe65fefb5f4f0a9423c9ce7d084f0d402ef6 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:23:09 +0000 Subject: [PATCH 058/130] riscv_debug: Cleaned up the nomenclature of riscv_halt_poll()'s halt causes enum --- src/target/riscv_debug.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d2e1b92c5af..5df661140b6 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -112,8 +112,8 @@ typedef enum riscv_halt_cause { /* Halt was caused by an `ebreak` instruction executing */ RV_HALT_CAUSE_EBREAK = (1U << 6U), - /* Halt was caused by a breakpoint (set in the trigger module) */ - RV_HALT_CAUSE_BREAKPOINT = (2U << 6U), + /* Halt was caused by a breakpoint or watchpoint (set in the trigger module) */ + RV_HALT_CAUSE_TRIGGER = (2U << 6U), /* Halt was caused by debugger request (haltreq) */ RV_HALT_CAUSE_REQUEST = (3U << 6U), /* Halt was caused by single-step execution */ @@ -803,7 +803,8 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_ status &= RV_DCSR_CAUSE_MASK; /* Dispatch on the cause code */ switch (status) { - case RV_HALT_CAUSE_BREAKPOINT: + case RV_HALT_CAUSE_TRIGGER: + /* XXX: Need to read out the triggers to find the one causing this, and grab the watch value */ return TARGET_HALT_BREAKPOINT; case RV_HALT_CAUSE_STEP: return TARGET_HALT_STEPPING; From 55d50a95bdc94b1e1cc72a9c645f1dddb78d7fcc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 09:02:25 +0000 Subject: [PATCH 059/130] riscv_debug: Implemented regs_write for both rv32 and rv64 --- src/target/riscv32.c | 17 +++++++++++++++++ src/target/riscv64.c | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 3b9549c4b3a..5d08275ff84 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -68,6 +68,7 @@ typedef struct riscv32_regs { #define RV32_MATCH_AFTER 0x00040000U static void riscv32_regs_read(target_s *target, void *data); +static void riscv32_regs_write(target_s *target, const void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); @@ -80,6 +81,7 @@ bool riscv32_probe(target_s *const target) /* Provide the length of a suitable registers structure */ target->regs_size = sizeof(riscv32_regs_s); target->regs_read = riscv32_regs_read; + target->regs_write = riscv32_regs_write; target->mem_read = riscv32_mem_read; target->breakwatch_set = riscv32_breakwatch_set; @@ -114,6 +116,21 @@ static void riscv32_regs_read(target_s *const target, void *const data) riscv_csr_read(hart, RV_DPC, ®s->pc); } +static void riscv32_regs_write(target_s *const target, const void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv32_regs_s *const regs = (riscv32_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through writing out the GPRs, except for the first which is always 0 */ + for (size_t gpr = 1; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to poke in the program counter that will be executed on resuming the hart */ + riscv_csr_write(hart, RV_DPC, ®s->pc); +} + /* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) { diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 7bf04beb2aa..54ab5aa500a 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -43,6 +43,7 @@ typedef struct riscv64_regs { } riscv64_regs_s; static void riscv64_regs_read(target_s *target, void *data); +static void riscv64_regs_write(target_s *target, const void *data); static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); bool riscv64_probe(target_s *const target) @@ -52,6 +53,7 @@ bool riscv64_probe(target_s *const target) /* Provide the length of a suitable registers structure */ target->regs_size = sizeof(riscv64_regs_s); target->regs_read = riscv64_regs_read; + target->regs_write = riscv64_regs_write; target->mem_read = riscv64_mem_read; return false; @@ -72,6 +74,21 @@ static void riscv64_regs_read(target_s *const target, void *const data) riscv_csr_read(hart, RV_DPC, ®s->pc); } +static void riscv64_regs_write(target_s *const target, const void *const data) +{ + /* Grab the hart structure and figure out how many registers need reading out */ + riscv_hart_s *const hart = riscv_hart_struct(target); + riscv64_regs_s *const regs = (riscv64_regs_s *)data; + const size_t gprs_count = hart->extensions & RV_ISA_EXT_EMBEDDED ? 16U : 32U; + /* Loop through writing out the GPRs, except for the first which is always 0 */ + for (size_t gpr = 1; gpr < gprs_count; ++gpr) { + // TODO: handle when this fails.. + riscv_csr_write(hart, RV_GPR_BASE + gpr, ®s->gprs[gpr]); + } + /* Special access to poke in the program counter that will be executed on resuming the hart */ + riscv_csr_write(hart, RV_DPC, ®s->pc); +} + /* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ void riscv64_unpack_data( void *const dest, const uint32_t data_low, const uint32_t data_high, const uint8_t access_width) From 84c7c53839d7be79e50c0ef1f7afb56ca94fc549 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 10:00:43 +0000 Subject: [PATCH 060/130] riscv32: Implemented mem_write and a data packing helper --- src/target/riscv32.c | 53 ++++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.h | 1 + 2 files changed, 54 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 5d08275ff84..f0d8df02179 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -70,6 +70,7 @@ typedef struct riscv32_regs { static void riscv32_regs_read(target_s *target, void *data); static void riscv32_regs_write(target_s *target, const void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); +static void riscv32_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); @@ -83,6 +84,7 @@ bool riscv32_probe(target_s *const target) target->regs_read = riscv32_regs_read; target->regs_write = riscv32_regs_write; target->mem_read = riscv32_mem_read; + target->mem_write = riscv32_mem_write; target->breakwatch_set = riscv32_breakwatch_set; target->breakwatch_clear = riscv32_breakwatch_clear; @@ -151,6 +153,29 @@ void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t ac } } +/* Takes in data from src, based on the access width, to be written to abstract command arg0 and packs it */ +uint32_t riscv32_pack_data(const void *const src, const uint8_t access_width) +{ + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: { + uint8_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + case RV_MEM_ACCESS_16_BIT: { + uint16_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + case RV_MEM_ACCESS_32_BIT: { + uint32_t value = 0; + memcpy(&value, src, sizeof(value)); + return value; + } + } + return 0; +} + static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); @@ -180,6 +205,34 @@ static void riscv32_mem_read(target_s *const target, void *const dest, const tar } } +static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + /* Write the address to write to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest)) + return; + const uint8_t *const data = (const uint8_t *)src; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Pack the data to write into arg0 */ + uint32_t value = riscv32_pack_data(data + offset, access_width); + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) + return; + /* Execute the write */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + return; + } +} + /* * The following can be used as a key for understanding the various return results from the breakwatch functions: * 0 -> success diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index efb2e056059..9cdeee6d7ac 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -195,5 +195,6 @@ bool riscv_config_trigger( uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, size_t length); void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); +uint32_t riscv32_pack_data(const void *src, uint8_t access_width); #endif /*TARGET_RISCV_DEBUG_H*/ From 96591fff6956a139f6942d02fe72feeae1a41509 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Sun, 19 Feb 2023 03:58:47 +0000 Subject: [PATCH 061/130] riscv_debug: Transform JTAG ID Code designer into JEP-106 internal format and store it as a DMI designer code --- src/target/adiv5.h | 5 +++++ src/target/riscv_debug.c | 6 ++---- src/target/riscv_debug.h | 2 +- src/target/riscv_jtag_dtm.c | 14 +++++++++++++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/target/adiv5.h b/src/target/adiv5.h index ed9ddb1025f..891a4d728f0 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -207,6 +207,11 @@ #define JTAG_IDCODE_PARTNO_MASK (0xffffU << JTAG_IDCODE_PARTNO_OFFSET) #define JTAG_IDCODE_DESIGNER_OFFSET 1U #define JTAG_IDCODE_DESIGNER_MASK (0x7ffU << JTAG_IDCODE_DESIGNER_OFFSET) +/* Bits 10:7 - JEP-106 Continuation code */ +/* Bits 6:0 - JEP-106 Identity code */ +#define JTAG_IDCODE_DESIGNER_JEP106_CONT_OFFSET 7U +#define JTAG_IDCODE_DESIGNER_JEP106_CONT_MASK (0xfU << ADIV5_DP_DESIGNER_JEP106_CONT_OFFSET) +#define JTAG_IDCODE_DESIGNER_JEP106_CODE_MASK (0x7fU) #define JTAG_IDCODE_PARTNO_DPv0 0xba00U diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 5df661140b6..b2708224799 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -352,10 +352,8 @@ static bool riscv_hart_init(riscv_hart_s *const hart) return true; } - /* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the JTAG IDCode */ - target->designer_code = hart->vendorid ? - hart->vendorid : - ((hart->dbg_module->dmi_bus->idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET); + /* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the DTM designer code */ + target->designer_code = hart->vendorid ? hart->vendorid : hart->dbg_module->dmi_bus->designer_code; target->cpuid = hart->archid; riscv_hart_discover_triggers(hart); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 9cdeee6d7ac..c1b71790c78 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -90,7 +90,7 @@ typedef struct riscv_dmi riscv_dmi_s; struct riscv_dmi { uint32_t ref_count; - uint32_t idcode; + uint16_t designer_code; riscv_debug_version_e version; uint8_t dev_index; diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 2661b8d9352..678f1a33ac6 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -35,6 +35,7 @@ #include "jtag_scan.h" #include "jtagtap.h" #include "riscv_debug.h" +#include "adiv5.h" #define IR_DTMCS 0x10U #define IR_DMI 0x11U @@ -75,12 +76,23 @@ void riscv_jtag_dtm_handler(const uint8_t dev_index) } /* Setup and try to discover the DMI bus */ - dmi->idcode = jtag_devs[dev_index].jd_idcode; dmi->dev_index = dev_index; + /* + * The code in JTAG_IDCODE_DESIGNER is in the form + * Bits 10:7 - JEP-106 Continuation code + * Bits 6:0 - JEP-106 Identity code + * here we convert it to our internal representation, See JEP-106 code list + */ + const uint16_t designer = + (jtag_devs[dev_index].jd_idcode & JTAG_IDCODE_DESIGNER_MASK) >> JTAG_IDCODE_DESIGNER_OFFSET; + dmi->designer_code = + (designer & JTAG_IDCODE_DESIGNER_JEP106_CONT_MASK) << 1U | (designer & JTAG_IDCODE_DESIGNER_JEP106_CODE_MASK); + riscv_jtag_dtm_init(dmi); /* If we failed to find any DMs or Harts, free the structure */ if (!dmi->ref_count) free(dmi); + /* Reset the JTAG machinery back to bypass to scan the next device in the chain */ jtag_dev_write_ir(dev_index, IR_BYPASS); } From 3e667cdbec9dee745511300b3ec427d138273879 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 02:55:13 +0100 Subject: [PATCH 062/130] riscv_debug: Fixed the vendor ID decode in riscv_hart_read_ids() to adjust for BMD's representation of JEP-106 codes --- src/target/riscv_debug.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index b2708224799..bfe98611f69 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -102,6 +102,9 @@ #define RV_ISA_EXTENSIONS_MASK 0x03ffffffU +#define RV_VENDOR_JEP106_CONT_MASK 0x7fffff80U +#define RV_VENDOR_JEP106_CODE_MASK 0x7fU + #define RV_DCSR_STEP 0x00000004U #define RV_DCSR_CAUSE_MASK 0x000001c0U #define RV_DCSR_STEPIE 0x00000800U @@ -303,7 +306,12 @@ static uint8_t riscv_isa_address_width(const uint32_t isa) static void riscv_hart_read_ids(riscv_hart_s *const hart) { + /* Read out the vendor ID */ riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid); + /* Adjust the value to fit our view of JEP-106 codes */ + hart->vendorid = + ((hart->vendorid & RV_VENDOR_JEP106_CONT_MASK) << 1U) | (hart->vendorid & RV_VENDOR_JEP106_CODE_MASK); + /* Depending on the bus width, read out the other IDs suitably */ if (hart->access_width == 32U) { riscv_csr_read(hart, RV_ARCH_ID, &hart->archid); riscv_csr_read(hart, RV_IMPL_ID, &hart->implid); From 341a3b6a29415d8d11637c2a4e9ddc1ec8cabb8b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 16:45:17 +0100 Subject: [PATCH 063/130] riscv_debug: Implemented poll timeouts for halt/resume --- src/target/riscv_debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index bfe98611f69..1d5747a7d17 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -747,10 +747,12 @@ static bool riscv_check_error(target_s *const target) static bool riscv_dm_poll_state(riscv_dm_s *const dbg_module, const uint32_t state) { + platform_timeout_s timeout; + platform_timeout_set(&timeout, 500U); /* Poll for the requested state to become set */ uint32_t status = 0; while (!(status & state)) { - if (!riscv_dm_read(dbg_module, RV_DM_STATUS, &status)) + if (!riscv_dm_read(dbg_module, RV_DM_STATUS, &status) || platform_timeout_is_expired(&timeout)) return false; } return true; From df68a75efd5c256f6a84159d8c31cc40a8df0362 Mon Sep 17 00:00:00 2001 From: mean Date: Sun, 13 Aug 2023 19:53:22 +0200 Subject: [PATCH 064/130] riscv: check if hw reset worked, if not do a DM reset --- src/target/riscv_debug.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 1d5747a7d17..ed9172598d7 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -827,17 +827,20 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_ static void riscv_reset(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); + bool has_reset = false; /* If the target does not have the nRST pin inhibited, use that to initiate reset */ if (!(target->target_options & RV_TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); - riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); + has_reset = riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); platform_nrst_set_val(false); /* In theory we're done at this point and no debug state was perturbed */ - } else { - /* - * Otherwise, if nRST is not usable, use instead reset via dmcontrol. In this case, - * when reset is requested, use the ndmreset bit to perform a system reset - */ + } + + /* + * Otherwise, if nRST is not usable (or failed), use instead reset via dmcontrol. In this case, + * when reset is requested, use the ndmreset bit to perform a system reset + */ + if (!has_reset) { riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_SYSTEM_RESET); riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); /* Complete the reset by resetting ndmreset */ From 21b5b301ed2730252508ae5f09edb91b8b7a8ec6 Mon Sep 17 00:00:00 2001 From: mean Date: Tue, 15 Aug 2023 09:08:06 +0200 Subject: [PATCH 065/130] riscv: make sure the hart is stopped when exiting reset --- src/target/riscv_debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index ed9172598d7..ca4dffe125c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -848,6 +848,7 @@ static void riscv_reset(target_s *const target) } /* Acknowledge the reset */ riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_HART_ACK_RESET); + riscv_halt_request(target); target_check_error(target); } From 6e1fd84b6cae89e8b1c7170ecbc2e6c737476ed7 Mon Sep 17 00:00:00 2001 From: mean Date: Tue, 15 Aug 2023 12:01:31 +0200 Subject: [PATCH 066/130] riscv: add single register access, add csr access with an offset, expose some CSRs register in the xml description --- src/target/riscv32.c | 39 +++++++++++++++++++++++++++++++++++++++ src/target/riscv_debug.c | 29 +++++++++++++++++++++++++++++ src/target/riscv_debug.h | 14 ++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f0d8df02179..bfafec67655 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -67,6 +67,8 @@ typedef struct riscv32_regs { #define RV32_MATCH_BEFORE 0x00000000U #define RV32_MATCH_AFTER 0x00040000U +static ssize_t riscv32_reg_read(target_s *target, uint32_t c, void *data, size_t max); +static ssize_t riscv32_reg_write(target_s *target, uint32_t c, const void *data, size_t max); static void riscv32_regs_read(target_s *target, void *data); static void riscv32_regs_write(target_s *target, const void *data); static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); @@ -83,6 +85,8 @@ bool riscv32_probe(target_s *const target) target->regs_size = sizeof(riscv32_regs_s); target->regs_read = riscv32_regs_read; target->regs_write = riscv32_regs_write; + target->reg_write = riscv32_reg_write; + target->reg_read = riscv32_reg_read; target->mem_read = riscv32_mem_read; target->mem_write = riscv32_mem_write; @@ -133,6 +137,41 @@ static void riscv32_regs_write(target_s *const target, const void *const data) riscv_csr_write(hart, RV_DPC, ®s->pc); } +static inline ssize_t riscv32_bool_to_4(const bool ret) +{ + return ret ? 4 : -1; +} + +static ssize_t riscv32_reg_read(target_s *target, const uint32_t reg, void *data, const size_t max) +{ + if (max != 4) + return -1; + /* Grab the hart structure */ + riscv_hart_s *const hart = riscv_hart_struct(target); + if (reg < 32) + return riscv32_bool_to_4(riscv_csr_read(hart, RV_GPR_BASE + reg, data)); + if (reg == 32) + return riscv32_bool_to_4(riscv_csr_read(hart, RV_DPC, data)); + if (reg >= RV_CSR_GDB_OFFSET) + return riscv32_bool_to_4(riscv_csr_read(hart, reg - RV_CSR_GDB_OFFSET, data)); + return -1; +} + +static ssize_t riscv32_reg_write(target_s *const target, const uint32_t reg, const void *data, const size_t max) +{ + if (max != 4) + return -1; + /* Grab the hart structure */ + riscv_hart_s *const hart = riscv_hart_struct(target); + if (reg < 32) + return riscv32_bool_to_4(riscv_csr_write(hart, RV_GPR_BASE + reg, data)); + if (reg == 32) + return riscv32_bool_to_4(riscv_csr_write(hart, RV_DPC, data)); + if (reg >= RV_CSR_GDB_OFFSET) + return riscv32_bool_to_4(riscv_csr_write(hart, reg - RV_CSR_GDB_OFFSET, data)); + return -1; +} + /* Takes in data from abstract command arg0 and, based on the access width, unpacks it to dest */ void riscv32_unpack_data(void *const dest, const uint32_t data, const uint8_t access_width) { diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index ca4dffe125c..44ec9e42c8b 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -137,6 +137,24 @@ static const char *const riscv_gpr_names[RV_GPRS_COUNT] = { "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", }; + +typedef struct riscv_csr_descriptor { + const char *name; + const uint32_t csr_number; // fits in 16 bits actually (?) +} riscv_csr_descriptor_s; + +static const riscv_csr_descriptor_s riscv_csrs[]={ + {"mstatus",RV_CSR_STATUS}, + {"misa",RV_CSR_MISA}, + {"mie", RV_CSR_MIE}, + {"mtvec", RV_CSR_MTVEC}, + {"tscratch",RV_CSR_MSCRATCH}, + {"mepc", RV_CSR_MEPC}, + {"mcause",RV_CSR_MCAUSE}, + {"mtval", RV_CSR_MTVAL}, + {"mip", RV_CSR_MIP}, +}; + // clang-format on /* General-purpose register types */ @@ -946,6 +964,17 @@ static size_t riscv_build_target_description( /* XXX: TODO - implement generation of the FPU feature and registers */ + /* Add main CSR registers*/ + if (max_length != 0) + print_size = max_length - (size_t)offset; + offset += snprintf(buffer + offset, print_size, ""); + for (size_t i = 0; i < ARRAY_LENGTH(riscv_csrs); i++) { + if (max_length != 0) + print_size = max_length - (size_t)offset; + offset += snprintf(buffer + offset, print_size, " ", + riscv_csrs[i].name, address_width, riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, + gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); + } /* Add the closing tags required */ if (max_length != 0) print_size = max_length - (size_t)offset; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index c1b71790c78..1b96257ff00 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -178,6 +178,20 @@ typedef struct riscv_hart { #define RV_TRIGGER_MODE_MASK 0xffff0000U #define RV_TRIGGER_SUPPORT_BREAKWATCH 0x00000004U +// The CSR id when reported by GDB is shifted by RV_CSR_GDB_OFFSET +// so they cannot collide with GPR registers, so you have to substract +// RV_CSR_GDB_OFFSET from the value received from gdb +#define RV_CSR_GDB_OFFSET 128 +#define RV_CSR_STATUS 0x300 +#define RV_CSR_MISA 0x301 +#define RV_CSR_MIE 0x304 +#define RV_CSR_MTVEC 0x305 +#define RV_CSR_MSCRATCH 0x340 +#define RV_CSR_MEPC 0x341 +#define RV_CSR_MCAUSE 0x342 +#define RV_CSR_MTVAL 0x343 +#define RV_CSR_MIP 0x344 + #define RV_TOPT_INHIBIT_NRST 0x00000001U void riscv_jtag_dtm_handler(uint8_t dev_index); From bf319569f2b4820d757720c11c48ff1717a25b2c Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Tue, 3 Oct 2023 16:35:52 +0100 Subject: [PATCH 067/130] fixup! riscv32: Implemented probing for the GD32VF1 --- src/target/cortexm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 2df4d495ea0..cc597cf82fa 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -710,7 +710,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) break; } #if PC_HOSTED == 0 - gdb_outf("Please report unknown device with Designer 0x%x Part ID 0x%x\n", t->designer_code, t->part_id); + gdb_outf("Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); #else DEBUG_WARN( "Please report unknown device with Designer 0x%x Part ID 0x%x\n", target->designer_code, target->part_id); From e97c366575849be5d1c355defc46a2ed9b5d9663 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Feb 2023 21:19:35 +0000 Subject: [PATCH 068/130] jtag_devs: Added the ESP32-C3's IDCode to the devices table --- src/target/jtag_devs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 7ae4b5f21bb..2a141dd2b5d 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -125,6 +125,12 @@ const jtag_dev_descr_s dev_descr[] = { .descr = "RISC-V debug v0.13.", .handler = riscv_jtag_dtm_handler, }, + { + .idcode = 0x00005c25U, + .idmask = 0x0fffffffU, + .descr = "RISC-V debug v0.13.", + .handler = riscv_jtag_dtm_handler, + }, { .idcode = 0x000007a3U, .idmask = 0x00000fffU, From 48fe16097b30e00b0df6c6afb4dd5ee3c4d52c87 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Feb 2023 21:20:45 +0000 Subject: [PATCH 069/130] riscv_debug: Too wide a CSR access triggers a not-supported error on ESP32-C3 --- src/target/riscv_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 44ec9e42c8b..e63e037c76c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -522,7 +522,8 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) return isa_data[0]; } /* If that failed, then find out why and instead try the next narrower width */ - if (hart->status != RISCV_HART_BUS_ERROR && hart->status != RISCV_HART_EXCEPTION) + if (hart->status != RISCV_HART_BUS_ERROR && hart->status != RISCV_HART_EXCEPTION && + hart->status != RISCV_HART_NOT_SUPP) return 0; if (hart->access_width == 32U) { hart->access_width = 0U; From c238e58f0b7df51de6dbcdf758787623cd95d672 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Feb 2023 21:21:14 +0000 Subject: [PATCH 070/130] jep106: Added the manufacturer code for Espressif --- src/target/jep106.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/jep106.h b/src/target/jep106.h index bc410a982ec..8eb24da4b5d 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -68,6 +68,7 @@ #define JEP106_MANUFACTURER_RASPBERRY 0x913U /* Raspberry Pi */ #define JEP106_MANUFACTURER_RENESAS 0x423U /* Renesas */ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx */ +#define JEP106_MANUFACTURER_ESPRESSIF 0xc12U /* Espressif */ /* * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, * so in the unlikely event we need to support chips by them, here be dragons. From 8d7ebaf277fd0d8452caa56cbc25e77872a5a773 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Feb 2023 21:22:58 +0000 Subject: [PATCH 071/130] riscv_debug: Implemented DMI sticky error recovery --- src/target/riscv_jtag_dtm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 678f1a33ac6..459e3af1c54 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -125,6 +125,12 @@ uint32_t riscv_shift_dtmcs(const riscv_dmi_s *const dmi, const uint32_t control) return status; } +static void riscv_dmi_reset(const riscv_dmi_s *const dmi) +{ + riscv_shift_dtmcs(dmi, RV_DTMCS_DMI_RESET); + jtag_dev_write_ir(dmi->dev_index, IR_DMI); +} + static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, const uint32_t data_in, uint32_t *const data_out) { @@ -145,6 +151,8 @@ static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, con jtagtap_return_idle(dmi->idle_cycles); /* XXX: Need to deal with when status is 3. */ dmi->fault = status; + if (status == RV_DMI_FAILURE) + riscv_dmi_reset(dmi); return status == RV_DMI_SUCCESS; } From 3a85a6f257492f2b2bff20c84dccf7f3ee38640b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 14 Feb 2023 11:27:48 +0000 Subject: [PATCH 072/130] jtag_devs: Added IR quirks for the ESP32-C3 --- src/target/jtag_devs.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 2a141dd2b5d..7b38ad390fc 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -130,6 +130,11 @@ const jtag_dev_descr_s dev_descr[] = { .idmask = 0x0fffffffU, .descr = "RISC-V debug v0.13.", .handler = riscv_jtag_dtm_handler, + .ir_quirks = + { + .ir_length = 5, + .ir_value = 0x0005U, + }, }, { .idcode = 0x000007a3U, From bc80f189bfa36b4c184cd88381a1650311ce9262 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 01:37:30 +0100 Subject: [PATCH 073/130] riscv_jtag_dtm: Fixed riscv_shift_dmi() not implementing handling for RV_DMI_TOO_SOON --- src/target/riscv_jtag_dtm.c | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 459e3af1c54..cbfc1895b95 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -131,7 +131,7 @@ static void riscv_dmi_reset(const riscv_dmi_s *const dmi) jtag_dev_write_ir(dmi->dev_index, IR_DMI); } -static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, +static uint8_t riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, const uint32_t data_in, uint32_t *const data_out) { jtag_dev_s *device = &jtag_devs[dmi->dev_index]; @@ -149,20 +149,52 @@ static bool riscv_shift_dmi(riscv_dmi_s *const dmi, const uint8_t operation, con jtag_proc.jtagtap_tdi_seq(!device->dr_postscan, (const uint8_t *)&address, dmi->address_width); jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); jtagtap_return_idle(dmi->idle_cycles); - /* XXX: Need to deal with when status is 3. */ + /* Translate error 1 into RV_DMI_FAILURE per the spec */ + if (status == 1U) + return RV_DMI_FAILURE; + return status; +} + +static bool riscv_dmi_transfer(riscv_dmi_s *const dmi, const uint8_t operation, const uint32_t address, + const uint32_t data_in, uint32_t *const data_out) +{ + /* Try the transfer */ + uint8_t status = riscv_shift_dmi(dmi, operation, address, data_in, data_out); + + /* Handle status == 3 (RV_DMI_TOO_SOON) */ + if (status == RV_DMI_TOO_SOON) { + /* + * If we got RV_DMI_TOO_SOON and we're under 8 idle cycles, increase the number + * of idle cycles used to compensate and have the outer code re-run the transnfers + */ + if (dmi->idle_cycles < 8) + ++dmi->idle_cycles; + /* + * Otherwise we've hit 8 idle cycles, it doesn't matter if we get another + * RV_DMI_TOO_SOON, treat that as a hard error and bail out. + */ + else + status = RV_DMI_FAILURE; + } + dmi->fault = status; - if (status == RV_DMI_FAILURE) + /* If we get straight failure, do a DMI reset */ + if (status == RV_DMI_FAILURE || status == RV_DMI_TOO_SOON) riscv_dmi_reset(dmi); return status == RV_DMI_SUCCESS; } static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - /* Setup the location to read from */ - bool result = riscv_shift_dmi(dmi, RV_DMI_READ, address, 0, NULL); - if (result) - /* If that worked, read back the value and check the operation status */ - result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, value); + bool result = true; + do { + /* Setup the location to read from */ + result = riscv_dmi_transfer(dmi, RV_DMI_READ, address, 0, NULL); + if (result) + /* If that worked, read back the value and check the operation status */ + result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, value); + } while (dmi->fault == RV_DMI_TOO_SOON); + if (!result) DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); return result; @@ -171,10 +203,10 @@ static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { /* Write a value to the requested register */ - bool result = riscv_shift_dmi(dmi, RV_DMI_WRITE, address, value, NULL); + bool result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); if (result) /* If that worked, read back the operation status to ensure the write actually worked */ - result = riscv_shift_dmi(dmi, RV_DMI_NOOP, 0, 0, NULL); + result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, NULL); if (!result) DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); return result; From 1f2f86ad4a94851be9f3c6162ab57b9ac1043a7a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 02:28:23 +0100 Subject: [PATCH 074/130] riscv_debug: Added in some additional target-level logging in the DMI read/write routines --- src/target/riscv_debug.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index e63e037c76c..972e276aab7 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -423,11 +423,15 @@ static void riscv_hart_free(void *const priv) static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - return dmi->read(dmi, address, value); + const bool result = dmi->read(dmi, address, value); + if (result) + DEBUG_TARGET("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); + return result; } static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { + DEBUG_TARGET("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value); return dmi->write(dmi, address, value); } From 7d63254bf14a52200a889acae5ab1655023ab9bb Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 02:33:37 +0100 Subject: [PATCH 075/130] riscv_jtag_dtm: Adjusted riscv_jtag_dmi_write() with handling for RV_DMI_TOO_SOON --- src/target/riscv_jtag_dtm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index cbfc1895b95..144e907bb74 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -202,11 +202,15 @@ static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { - /* Write a value to the requested register */ - bool result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); - if (result) - /* If that worked, read back the operation status to ensure the write actually worked */ - result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, NULL); + bool result = true; + do { + /* Write a value to the requested register */ + result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); + if (result) + /* If that worked, read back the operation status to ensure the write actually worked */ + result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, NULL); + } while (dmi->fault == RV_DMI_TOO_SOON); + if (!result) DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); return result; From 094a4939896ceef659af45ba293a4ca7fe5570f0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 04:06:07 +0100 Subject: [PATCH 076/130] esp32c3: Began implementing support and memory maps for the ESP32-C3 --- src/Makefile | 1 + src/target/esp32c3.c | 60 +++++++++++++++++++++++++++++++++++++++ src/target/riscv32.c | 3 ++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 5 files changed, 66 insertions(+) create mode 100644 src/target/esp32c3.c diff --git a/src/Makefile b/src/Makefile index 57ce7bc1069..d04f15282b5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,6 +33,7 @@ SRC = \ cortexm.c \ crc32.c \ efm32.c \ + esp32c3.c \ exception.c \ gdb_if.c \ gdb_main.c \ diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c new file mode 100644 index 00000000000..19e231d7a1d --- /dev/null +++ b/src/target/esp32c3.c @@ -0,0 +1,60 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target_internal.h" +#include "target_probe.h" +#include "riscv_debug.h" + +#define ESP32_C3_ARCH_ID 0x80000001U +#define ESP32_C3_IMPL_ID 0x00000001U + +bool esp32c3_probe(target_s *target) +{ + const riscv_hart_s *const hart = riscv_hart_struct(target); + /* Seems that the best we can do is check the marchid and mimplid register values */ + if (hart->archid != ESP32_C3_ARCH_ID || hart->implid != ESP32_C3_IMPL_ID) + return false; + + target->driver = "ESP32-C3"; + + /* I-bus mapping for SRAM 0 + 1 */ + target_add_ram(target, 0x4037c000U, 0x4000U); + target_add_ram(target, 0x40380000U, 0x60000U); + /* D-bus mapping for SRAM 1 */ + target_add_ram(target, 0x3fc80000U, 0x60000U); + /* Mapping for the RTC SRAM */ + target_add_ram(target, 0x50000000U, 0x2000U); + + return true; +} diff --git a/src/target/riscv32.c b/src/target/riscv32.c index bfafec67655..f664f24f81b 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -97,6 +97,9 @@ bool riscv32_probe(target_s *const target) case JEP106_MANUFACTURER_RV_GIGADEVICE: PROBE(gd32vf1_probe); break; + case JEP106_MANUFACTURER_ESPRESSIF: + PROBE(esp32c3_probe); + break; } #if PC_HOSTED == 0 gdb_outf("Please report unknown device with Designer 0x%x\n", target->designer_code); diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 50944b64bd6..8224d65739b 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -124,5 +124,6 @@ TARGET_PROBE_WEAK_NOP(renesas_probe) TARGET_PROBE_WEAK_NOP(mm32l0xx_probe) TARGET_PROBE_WEAK_NOP(mm32f3xx_probe) TARGET_PROBE_WEAK_NOP(imxrt_probe) +TARGET_PROBE_WEAK_NOP(esp32c3_probe) LPC55_DP_PREPARE_WEAK_NOP(lpc55_dp_prepare) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 02dc72874ff..0246fd0603b 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -91,6 +91,7 @@ bool ke04_probe(target_s *target); bool rp_probe(target_s *target); bool renesas_probe(target_s *target); bool imxrt_probe(target_s *target); +bool esp32c3_probe(target_s *target); void lpc55_dp_prepare(adiv5_debug_port_s *dp); From 43866844218e1f10f6b0eb0dd4ebf8e134d37f7f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 07:06:56 +0100 Subject: [PATCH 077/130] esp32c3: Began the process of implementing support for the off-chip Flash, starting with the mappings --- src/target/esp32c3.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 19e231d7a1d..5be1923db11 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -39,7 +39,35 @@ #define ESP32_C3_ARCH_ID 0x80000001U #define ESP32_C3_IMPL_ID 0x00000001U -bool esp32c3_probe(target_s *target) +#define ESP32_C3_DBUS_SRAM1_BASE 0x3fc80000U +#define ESP32_C3_DBUS_SRAM1_SIZE 0x00060000U +#define ESP32_C3_IBUS_SRAM0_BASE 0x4037c000U +#define ESP32_C3_IBUS_SRAM0_SIZE 0x00004000U +#define ESP32_C3_IBUS_SRAM1_BASE 0x40380000U +#define ESP32_C3_IBUS_SRAM1_SIZE 0x00060000U +#define ESP32_C3_RTC_SRAM_BASE 0x50000000U +#define ESP32_C3_RTC_SRAM_SIZE 0x00002000U + +#define ESP32_C3_IBUS_FLASH_BASE 0x42000000U +#define ESP32_C3_IBUS_FLASH_SIZE 0x00800000U + +static void esp32c3_add_flash(target_s *const target) +{ + target_flash_s *const flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + flash->start = ESP32_C3_IBUS_FLASH_BASE; + flash->length = ESP32_C3_IBUS_FLASH_SIZE; + flash->blocksize = 256U; + flash->writesize = 4096U; + flash->erased = 0xffU; + target_add_flash(target, flash); +} + +bool esp32c3_probe(target_s *const target) { const riscv_hart_s *const hart = riscv_hart_struct(target); /* Seems that the best we can do is check the marchid and mimplid register values */ @@ -47,14 +75,14 @@ bool esp32c3_probe(target_s *target) return false; target->driver = "ESP32-C3"; + /* Establish the target RAM mappings */ + target_add_ram(target, ESP32_C3_IBUS_SRAM0_BASE, ESP32_C3_IBUS_SRAM0_SIZE); + target_add_ram(target, ESP32_C3_IBUS_SRAM1_BASE, ESP32_C3_IBUS_SRAM1_SIZE); + target_add_ram(target, ESP32_C3_DBUS_SRAM1_BASE, ESP32_C3_DBUS_SRAM1_SIZE); + target_add_ram(target, ESP32_C3_RTC_SRAM_BASE, ESP32_C3_RTC_SRAM_SIZE); - /* I-bus mapping for SRAM 0 + 1 */ - target_add_ram(target, 0x4037c000U, 0x4000U); - target_add_ram(target, 0x40380000U, 0x60000U); - /* D-bus mapping for SRAM 1 */ - target_add_ram(target, 0x3fc80000U, 0x60000U); - /* Mapping for the RTC SRAM */ - target_add_ram(target, 0x50000000U, 0x2000U); + /* Establish the target Flash mappings */ + esp32c3_add_flash(target); return true; } From 32669c3491e76356fb01b90332e900147000f6e0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 08:32:43 +0100 Subject: [PATCH 078/130] riscv32: Refactored the memory access routines to allow a target to specify use of system bus instead of abstract command for memory access --- src/target/riscv32.c | 42 ++++++++++++++++++++++++++++++++++++---- src/target/riscv_debug.h | 5 +++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f664f24f81b..660df5f337d 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -218,13 +218,13 @@ uint32_t riscv32_pack_data(const void *const src, const uint8_t access_width) return 0; } -static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +static void riscv32_abstract_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); /* If we're asked to do a 0-byte read, do nothing */ if (!len) return; - riscv_hart_s *const hart = riscv_hart_struct(target); /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; @@ -247,13 +247,13 @@ static void riscv32_mem_read(target_s *const target, void *const dest, const tar } } -static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +static void riscv32_abstract_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) { DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); /* If we're asked to do a 0-byte read, do nothing */ if (!len) return; - riscv_hart_s *const hart = riscv_hart_struct(target); /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, dest, len); const uint8_t access_length = 1U << access_width; @@ -275,6 +275,40 @@ static void riscv32_mem_write(target_s *const target, const target_addr_t dest, } } +static void riscv32_sysbus_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +{ + (void)hart; + (void)dest; + (void)src; + (void)len; +} + +static void riscv32_sysbus_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + (void)hart; + (void)dest; + (void)src; + (void)len; +} + +static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) + return riscv32_sysbus_mem_read(hart, dest, src, len); + return riscv32_abstract_mem_read(hart, dest, src, len); +} + +static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) + return riscv32_sysbus_mem_write(hart, dest, src, len); + return riscv32_abstract_mem_write(hart, dest, src, len); +} + /* * The following can be used as a key for understanding the various return results from the breakwatch functions: * 0 -> success diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 1b96257ff00..73a499cbd1b 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -84,6 +84,10 @@ typedef enum riscv_match_size { RV_MATCH_SIZE_128_BIT = 0x00410000U, } riscv_match_size_e; +/* These defines specify Hart-specific information such as which memory access style to use */ +#define RV_HART_FLAG_MEMORY_ABSTRACT 0x00U +#define RV_HART_FLAG_MEMORY_SYSBUS 0x01U + typedef struct riscv_dmi riscv_dmi_s; /* This structure represents a version-agnostic Debug Module Interface on a RISC-V device */ @@ -122,6 +126,7 @@ typedef struct riscv_hart { uint32_t hartsel; uint8_t access_width; uint8_t address_width; + uint8_t flags; riscv_hart_status_e status; uint32_t extensions; From 6eb56cebc55fe044c82a46e596c57384e100b8f2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 08:41:35 +0100 Subject: [PATCH 079/130] riscv32: Moved the length check and target-level debugging for memory access into the overarching routines --- src/target/riscv32.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 660df5f337d..084ec7231f7 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -221,10 +221,6 @@ uint32_t riscv32_pack_data(const void *const src, const uint8_t access_width) static void riscv32_abstract_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { - DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); - /* If we're asked to do a 0-byte read, do nothing */ - if (!len) - return; /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; @@ -250,10 +246,6 @@ static void riscv32_abstract_mem_read( static void riscv32_abstract_mem_write( riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) { - DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); - /* If we're asked to do a 0-byte read, do nothing */ - if (!len) - return; /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, dest, len); const uint8_t access_length = 1U << access_width; @@ -295,6 +287,11 @@ static void riscv32_sysbus_mem_write( static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) return riscv32_sysbus_mem_read(hart, dest, src, len); @@ -303,6 +300,11 @@ static void riscv32_mem_read(target_s *const target, void *const dest, const tar static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) { + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; + riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) return riscv32_sysbus_mem_write(hart, dest, src, len); From 055d7444f8d22515c72252bcecfb0d2df53a4de1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 08:54:04 +0100 Subject: [PATCH 080/130] riscv_debug: Implemented support for detecting and switching over to the system bus interface for memory access --- src/target/riscv_debug.c | 19 +++++++++++++++++++ src/target/riscv_debug.h | 17 +++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 972e276aab7..cfeca66ecea 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -73,6 +73,8 @@ #define RV_DM_ABST_STATUS_BUSY 0x00001000U #define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU +#define RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK 0x00000fe0U + #define RV_CSR_FORCE_MASK 0xc000U #define RV_CSR_FORCE_32_BIT 0x4000U #define RV_CSR_FORCE_64_BIT 0x8000U @@ -210,6 +212,7 @@ static riscv_debug_version_e riscv_dm_version(uint32_t status); static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart); static void riscv_hart_discover_triggers(riscv_hart_s *hart); +static void riscv_hart_memory_access_type(riscv_hart_s *hart); static bool riscv_attach(target_s *target); static void riscv_detach(target_s *target); @@ -383,6 +386,7 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->cpuid = hart->archid; riscv_hart_discover_triggers(hart); + riscv_hart_memory_access_type(hart); /* Setup core-agnostic target functions */ target->attach = riscv_attach; @@ -696,6 +700,21 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) } } +static void riscv_hart_memory_access_type(riscv_hart_s *const hart) +{ + uint32_t sysbus_status; + hart->flags &= ~RV_HART_FLAG_MEMORY_SYSBUS; + /* + * Try reading the system bus access control and status register. + * Check if the value read back is non-zero for the sbasize field + */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) || + !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) + return; + /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ + hart->flags = RV_HART_FLAG_MEMORY_SYSBUS; +} + riscv_match_size_e riscv_breakwatch_match_size(const size_t size) { switch (size) { diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 73a499cbd1b..4d038c984dc 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -141,12 +141,17 @@ typedef struct riscv_hart { #define RV_STATUS_VERSION_MASK 0x0000000fU -#define RV_DM_DATA0 0x04U -#define RV_DM_DATA1 0x05U -#define RV_DM_DATA2 0x06U -#define RV_DM_DATA3 0x07U -#define RV_DM_ABST_CTRLSTATUS 0x16U -#define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_DATA0 0x04U +#define RV_DM_DATA1 0x05U +#define RV_DM_DATA2 0x06U +#define RV_DM_DATA3 0x07U +#define RV_DM_ABST_CTRLSTATUS 0x16U +#define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_SYSBUS_CTRLSTATUS 0x38U +#define RV_DM_SYSBUS_ADDR0 0x39U +#define RV_DM_SYSBUS_ADDR1 0x3aU +#define RV_DM_SYSBUS_DATA0 0x3cU +#define RV_DM_SYSBUS_DATA1 0x3dU #define RV_DM_ABST_CMD_ACCESS_REG 0x00000000U #define RV_DM_ABST_CMD_ACCESS_MEM 0x02000000U From 47138f866045e7320cc540945de6e785db279cb8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 09:12:35 +0100 Subject: [PATCH 081/130] riscv_debug: Naming cleanup of the constants involved in abstract command memory access --- src/target/riscv32.c | 8 ++++---- src/target/riscv64.c | 4 ++-- src/target/riscv_debug.c | 2 +- src/target/riscv_debug.h | 5 +++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 084ec7231f7..b1beae9a784 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -225,8 +225,8 @@ static void riscv32_abstract_mem_read( const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; /* Build the access command */ - const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | - (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); /* Write the address to read to arg1 */ if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) return; @@ -250,8 +250,8 @@ static void riscv32_abstract_mem_write( const uint8_t access_width = riscv_mem_access_width(hart, dest, len); const uint8_t access_length = 1U << access_width; /* Build the access command */ - const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_MEM_ACCESS_SHIFT) | - (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); /* Write the address to write to arg1 */ if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest)) return; diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 54ab5aa500a..2e25bd17b42 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -121,8 +121,8 @@ static void riscv64_mem_read(target_s *const target, void *const dest, const tar const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; /* Build the access command */ - const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_MEM_ACCESS_SHIFT) | - (access_length < len ? RV_MEM_ADDR_POST_INC : 0U); + const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); /* Write the address to read to arg1 */ if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA2, src) || !riscv_dm_write(hart->dbg_module, RV_DM_DATA3, 0U)) return; diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index cfeca66ecea..38281fa73bb 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -631,7 +631,7 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) { /* Grab the Hart's most maxmimally aligned possible write width */ - uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_MEM_ACCESS_SHIFT; + uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_ABST_MEM_ACCESS_SHIFT; /* Convert the hart access width to a mask - for example, for 32-bit harts, this gives (1U << 2U) - 1U = 3U */ uint8_t align_mask = (1U << access_width) - 1U; /* Mask out the bottom bits of both the address and length - anything that determines the alignment */ diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 4d038c984dc..8febd584834 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -162,14 +162,15 @@ typedef struct riscv_hart { #define RV_REG_ACCESS_32_BIT 0x00200000U #define RV_REG_ACCESS_64_BIT 0x00300000U #define RV_REG_ACCESS_128_BIT 0x00400000U -#define RV_MEM_ADDR_POST_INC 0x00080000U #define RV_MEM_ACCESS_8_BIT 0x0U #define RV_MEM_ACCESS_16_BIT 0x1U #define RV_MEM_ACCESS_32_BIT 0x2U #define RV_MEM_ACCESS_64_BIT 0x3U #define RV_MEM_ACCESS_128_BIT 0x4U -#define RV_MEM_ACCESS_SHIFT 20U + +#define RV_ABST_MEM_ADDR_POST_INC 0x00080000U +#define RV_ABST_MEM_ACCESS_SHIFT 20U /* dpc -> Debug Program Counter */ #define RV_DPC 0x7b1U From 26dd2bde26b7233825d50cd97b714f8bfd78e3b6 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 09:13:10 +0100 Subject: [PATCH 082/130] riscv32: Implemented memory read via system bus --- src/target/riscv32.c | 30 ++++++++++++++++++++++++++---- src/target/riscv_debug.h | 5 +++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index b1beae9a784..ad956fcfe81 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -270,10 +270,32 @@ static void riscv32_abstract_mem_write( static void riscv32_sysbus_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { - (void)hart; - (void)dest; - (void)src; - (void)len; + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = (access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | RV_SYSBUS_MEM_READ_ON_ADDR | + (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC | RV_SYSBUS_MEM_READ_ON_DATA : 0U); + /* + * Write the command setup to the access control register + * Then set up the read by writing the address to the address register + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, command) || + !riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_ADDR0, src)) + return; + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* If this would be the last read, clean up the access control register */ + if (offset + access_length == len && (command & RV_SYSBUS_MEM_ADDR_POST_INC)) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0)) + return; + } + uint32_t value = 0; + /* Read back and extract the data for this block */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_DATA0, &value)) + return; + riscv32_unpack_data(data + offset, value, access_width); + } } static void riscv32_sysbus_mem_write( diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 8febd584834..a40847f4ef5 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -172,6 +172,11 @@ typedef struct riscv_hart { #define RV_ABST_MEM_ADDR_POST_INC 0x00080000U #define RV_ABST_MEM_ACCESS_SHIFT 20U +#define RV_SYSBUS_MEM_ADDR_POST_INC 0x00010000U +#define RV_SYSBUS_MEM_READ_ON_ADDR 0x00100000U +#define RV_SYSBUS_MEM_READ_ON_DATA 0x00008000U +#define RV_SYSBUS_MEM_ACCESS_SHIFT 17U + /* dpc -> Debug Program Counter */ #define RV_DPC 0x7b1U /* The GPR base defines the starting register space address for the CPU state registers */ From 94ae3acaff2b7fac4151befa2c224e4ba289bcde Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 10:22:40 +0100 Subject: [PATCH 083/130] riscv32: Fixed riscv32_sysbus_mem_read() not checking the system bus status after completion --- src/target/riscv32.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index ad956fcfe81..b1ef374735e 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -296,6 +296,15 @@ static void riscv32_sysbus_mem_read( return; riscv32_unpack_data(data + offset, value, access_width); } + uint32_t status = 0; + /* Read back the system bus status */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &status)) + return; + hart->status = (status >> 12U) & RISCV_HART_OTHER; + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, RISCV_HART_OTHER << 12U)) + return; + if (hart->status != RISCV_HART_NO_ERROR) + DEBUG_WARN("memory read failed: %u\n", hart->status); } static void riscv32_sysbus_mem_write( From 56eb6c0229d2e93f1bb8116bd87c342ae64383c8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 13:13:15 +0100 Subject: [PATCH 084/130] riscv32: Implemented memory write via sytem bus --- src/target/riscv32.c | 52 ++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index b1ef374735e..55304cb1c82 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -267,6 +267,21 @@ static void riscv32_abstract_mem_write( } } +static void riscv_sysbus_check(riscv_hart_s *const hart) +{ + uint32_t status = 0; + /* Read back the system bus status */ + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &status)) + return; + /* Store the result and reset the value in the control/status register */ + hart->status = (status >> 12U) & RISCV_HART_OTHER; + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, RISCV_HART_OTHER << 12U)) + return; + /* If something goes wrong, tell the user */ + if (hart->status != RISCV_HART_NO_ERROR) + DEBUG_WARN("memory access failed: %u\n", hart->status); +} + static void riscv32_sysbus_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { @@ -291,29 +306,38 @@ static void riscv32_sysbus_mem_read( return; } uint32_t value = 0; - /* Read back and extract the data for this block */ + /* Read back and unpack the data for this block */ if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_DATA0, &value)) return; riscv32_unpack_data(data + offset, value, access_width); } - uint32_t status = 0; - /* Read back the system bus status */ - if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &status)) - return; - hart->status = (status >> 12U) & RISCV_HART_OTHER; - if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, RISCV_HART_OTHER << 12U)) - return; - if (hart->status != RISCV_HART_NO_ERROR) - DEBUG_WARN("memory read failed: %u\n", hart->status); + riscv_sysbus_check(hart); } static void riscv32_sysbus_mem_write( riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) { - (void)hart; - (void)dest; - (void)src; - (void)len; + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + /* Build the access command */ + const uint32_t command = + (access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC : 0U); + /* + * Write the command setup to the access control register + * Then set up the write by writing the address to the address register + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, command) || + !riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_ADDR0, dest)) + return; + const uint8_t *const data = (const uint8_t *)src; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Pack the data for this block and write it */ + const uint32_t value = riscv32_pack_data(data + offset, access_width); + if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_DATA0, value)) + return; + } + riscv_sysbus_check(hart); } static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) From edf1dc01a95eaff2a41bf0ddeca45f5591196a8b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 29 Mar 2023 08:11:35 +0100 Subject: [PATCH 085/130] riscv_debug: Grabbed the supported system bus access widths in riscv_hart_memory_access_type() --- src/target/riscv_debug.c | 4 ++-- src/target/riscv_debug.h | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 38281fa73bb..be7686bd4ce 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -703,7 +703,7 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) static void riscv_hart_memory_access_type(riscv_hart_s *const hart) { uint32_t sysbus_status; - hart->flags &= ~RV_HART_FLAG_MEMORY_SYSBUS; + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_SYSBUS; /* * Try reading the system bus access control and status register. * Check if the value read back is non-zero for the sbasize field @@ -712,7 +712,7 @@ static void riscv_hart_memory_access_type(riscv_hart_s *const hart) !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) return; /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ - hart->flags = RV_HART_FLAG_MEMORY_SYSBUS; + hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); } riscv_match_size_e riscv_breakwatch_match_size(const size_t size) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index a40847f4ef5..7a19e07680f 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -85,8 +85,13 @@ typedef enum riscv_match_size { } riscv_match_size_e; /* These defines specify Hart-specific information such as which memory access style to use */ -#define RV_HART_FLAG_MEMORY_ABSTRACT 0x00U -#define RV_HART_FLAG_MEMORY_SYSBUS 0x01U +#define RV_HART_FLAG_MEMORY_ABSTRACT 0x00U +#define RV_HART_FLAG_MEMORY_SYSBUS 0x10U +#define RV_HART_FLAG_ACCESS_WIDTH_MASK 0x0fU +#define RV_HART_FLAG_ACCESS_WIDTH_8BIT 0x01U +#define RV_HART_FLAG_ACCESS_WIDTH_16BIT 0x02U +#define RV_HART_FLAG_ACCESS_WIDTH_32BIT 0x04U +#define RV_HART_FLAG_ACCESS_WIDTH_64BIT 0x08U typedef struct riscv_dmi riscv_dmi_s; From 5bab152648f5428b8249e43295dfeba78ee7cd83 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 29 Mar 2023 08:12:24 +0100 Subject: [PATCH 086/130] riscv32: Refactored out the native-width system bus access logic into a new function --- src/target/riscv32.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 55304cb1c82..f93bbc05ec8 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -282,14 +282,12 @@ static void riscv_sysbus_check(riscv_hart_s *const hart) DEBUG_WARN("memory access failed: %u\n", hart->status); } -static void riscv32_sysbus_mem_read( - riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +static void riscv32_sysbus_mem_native_read(riscv_hart_s *const hart, void *const dest, const target_addr_t src, + const size_t len, const uint8_t access_width, const uint8_t access_length) { - /* Figure out the maxmial width of access to perform, up to the bitness of the target */ - const uint8_t access_width = riscv_mem_access_width(hart, src, len); - const uint8_t access_length = 1U << access_width; + DEBUG_TARGET("%s: %zu byte read at %08" PRIx32 " in %u byte blocks\n", __func__, len, src, access_length); /* Build the access command */ - const uint32_t command = (access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | RV_SYSBUS_MEM_READ_ON_ADDR | + const uint32_t command = ((uint32_t)access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | RV_SYSBUS_MEM_READ_ON_ADDR | (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC | RV_SYSBUS_MEM_READ_ON_DATA : 0U); /* * Write the command setup to the access control register @@ -314,6 +312,15 @@ static void riscv32_sysbus_mem_read( riscv_sysbus_check(hart); } +static void riscv32_sysbus_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +{ + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = (uint8_t)(1U << access_width); + riscv32_sysbus_mem_native_read(hart, dest, src, len, access_width, access_length); +} + static void riscv32_sysbus_mem_write( riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) { From d07f8029235a21b89206f3d0196b1e1a1daff213 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 29 Mar 2023 08:18:59 +0100 Subject: [PATCH 087/130] riscv32: Implemented support for non-native-width system bus memory reads --- src/target/riscv32.c | 68 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f93bbc05ec8..77cf58d0b4c 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -38,6 +38,7 @@ #include "jep106.h" #include "riscv_debug.h" #include "gdb_packet.h" +#include "adiv5.h" typedef struct riscv32_regs { uint32_t gprs[32]; @@ -312,13 +313,78 @@ static void riscv32_sysbus_mem_native_read(riscv_hart_s *const hart, void *const riscv_sysbus_check(hart); } +static void riscv32_sysbus_mem_adjusted_read(riscv_hart_s *const hart, void *const dest, const target_addr_t src, + const uint8_t access_width, const uint8_t native_access_width, const uint8_t access_length) +{ + DEBUG_PROBE("%s: %u read for %u bytes\n", __func__, access_length, 1U << access_width); + const target_addr_t alignment = ~(access_length - 1U); + /* + * On a 32-bit target the only possible widths are 8- 16- and 32-bit, so after the adjustment loop, + * there are only and exactly 2 possible cases to handle here: 16- and 32-bit access. + */ + switch (native_access_width) { + case RV_MEM_ACCESS_16_BIT: { + uint16_t value = 0; + riscv32_sysbus_mem_native_read( + hart, &value, src & alignment, access_length, RV_MEM_ACCESS_16_BIT, access_length); + adiv5_unpack_data(dest, src & 1U, value, access_width); + break; + } + case RV_MEM_ACCESS_32_BIT: { + uint32_t value = 0; + riscv32_sysbus_mem_native_read( + hart, &value, src & alignment, access_length, RV_MEM_ACCESS_32_BIT, access_length); + adiv5_unpack_data(dest, src & 3U, value, access_width); + break; + } + } +} + static void riscv32_sysbus_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = (uint8_t)(1U << access_width); - riscv32_sysbus_mem_native_read(hart, dest, src, len, access_width, access_length); + /* Check if the access is a natural/native width */ + if (hart->flags & access_length) { + riscv32_sysbus_mem_native_read(hart, dest, src, len, access_width, access_length); + return; + } + + /* If we were unable to do this using a native access, find the next largest supported access width */ + uint8_t native_access_width = access_width; + while (!((hart->flags >> native_access_width) & 1U) && native_access_width < RV_MEM_ACCESS_32_BIT) + ++native_access_width; + const uint8_t native_access_length = (uint8_t)(1U << native_access_width); + + /* Do the initial adjusted access */ + size_t remainder = len; + target_addr_t address = src; + uint8_t *data = (uint8_t *)dest; + riscv32_sysbus_mem_adjusted_read(hart, data, address, access_width, native_access_width, native_access_length); + + /* After doing the initial access, adjust the location of the next and do any follow-up accesses required */ + remainder -= access_length; + address += access_length; + data += access_length; + + /* + * Now we're aligned to the wider access width, do another set of reads if there's + * any remainder. Do this till we either reach nothing left, or we have another small left-over amount + */ + if (!remainder) + return; + const size_t amount = remainder & ~(native_access_length - 1U); + if (amount) + riscv32_sysbus_mem_native_read(hart, data, address, amount, native_access_width, native_access_length); + remainder -= amount; + address += (uint32_t)amount; + data += amount; + + /* If there's any data left to read, do another adjusted access to grab it */ + if (remainder) + riscv32_sysbus_mem_adjusted_read(hart, data, address, access_width, native_access_width, native_access_length); } static void riscv32_sysbus_mem_write( From 1c5aef0be1dd5c4355e9e69296b687298443b667 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 30 Mar 2023 00:44:26 +0100 Subject: [PATCH 088/130] riscv32: Added in a busy check to the native memory read loop to keep the target's system bus controller happy --- src/target/riscv32.c | 6 ++++++ src/target/riscv_debug.c | 2 +- src/target/riscv_debug.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 77cf58d0b4c..120b73cc1d7 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -299,6 +299,12 @@ static void riscv32_sysbus_mem_native_read(riscv_hart_s *const hart, void *const return; uint8_t *const data = (uint8_t *)dest; for (size_t offset = 0; offset < len; offset += access_length) { + uint32_t status = RV_SYSBUS_STATUS_BUSY; + /* Wait for the current read cycle to complete */ + while (status & RV_SYSBUS_STATUS_BUSY) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &status)) + return; + } /* If this would be the last read, clean up the access control register */ if (offset + access_length == len && (command & RV_SYSBUS_MEM_ADDR_POST_INC)) { if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0)) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index be7686bd4ce..4adf3a3147f 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -429,7 +429,7 @@ static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint3 { const bool result = dmi->read(dmi, address, value); if (result) - DEBUG_TARGET("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); + DEBUG_TARGET("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); return result; } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 7a19e07680f..f130090fbaa 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -180,6 +180,7 @@ typedef struct riscv_hart { #define RV_SYSBUS_MEM_ADDR_POST_INC 0x00010000U #define RV_SYSBUS_MEM_READ_ON_ADDR 0x00100000U #define RV_SYSBUS_MEM_READ_ON_DATA 0x00008000U +#define RV_SYSBUS_STATUS_BUSY 0x00200000U #define RV_SYSBUS_MEM_ACCESS_SHIFT 17U /* dpc -> Debug Program Counter */ From 471e7edea568d6bde971b8000e65291760e2fbe5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 30 Mar 2023 05:22:32 +0100 Subject: [PATCH 089/130] riscv_debug: Added some insurance that the system bus is in a good state before use --- src/target/riscv_debug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 4adf3a3147f..3cf4712c624 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -713,6 +713,8 @@ static void riscv_hart_memory_access_type(riscv_hart_s *const hart) return; /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + /* Make sure the system bus is not in any kind of error state */ + (void)riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0x00407000U); } riscv_match_size_e riscv_breakwatch_match_size(const size_t size) From 2a195a8a352ff893771cdffaa84e9d5b8d88fefb Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Mar 2023 11:36:14 +0100 Subject: [PATCH 090/130] esp32c3: Implemented support for performing SPI reads and reading out the SFDP data of the target --- src/target/esp32c3.c | 133 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 3 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 5be1923db11..acc48dcc2c3 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -35,6 +35,7 @@ #include "target_internal.h" #include "target_probe.h" #include "riscv_debug.h" +#include "sfdp.h" #define ESP32_C3_ARCH_ID 0x80000001U #define ESP32_C3_IMPL_ID 0x00000001U @@ -51,6 +52,65 @@ #define ESP32_C3_IBUS_FLASH_BASE 0x42000000U #define ESP32_C3_IBUS_FLASH_SIZE 0x00800000U +#define ESP32_C3_SPI1_BASE 0x60002000U +#define ESP32_C3_SPI1_CMD (ESP32_C3_SPI1_BASE + 0x000U) +#define ESP32_C3_SPI1_ADDR (ESP32_C3_SPI1_BASE + 0x004U) +#define ESP32_C3_SPI1_USER0 (ESP32_C3_SPI1_BASE + 0x018U) +#define ESP32_C3_SPI1_USER1 (ESP32_C3_SPI1_BASE + 0x01cU) +#define ESP32_C3_SPI1_USER2 (ESP32_C3_SPI1_BASE + 0x020U) +#define ESP32_C3_SPI1_DATA_OUT_LEN (ESP32_C3_SPI1_BASE + 0x024U) +#define ESP32_C3_SPI1_DATA_IN_LEN (ESP32_C3_SPI1_BASE + 0x028U) +#define ESP32_C3_SPI1_DATA (ESP32_C3_SPI1_BASE + 0x058U) + +/* These define the various stages of a SPI transaction that we can choose to enable */ +#define ESP32_C3_SPI_CMD_EXEC_XFER 0x00040000U +#define ESP32_C3_SPI_USER0_CMD 0x80000000U +#define ESP32_C3_SPI_USER0_ADDR 0x40000000U +#define ESP32_C3_SPI_USER0_DUMMY 0x20000000U +#define ESP32_C3_SPI_USER0_DATA_IN 0x10000000U +#define ESP32_C3_SPI_USER0_DATA_OUT 0x08000000U + +/* These define the various bit ranges used to store the cycle counts for the enabled stages */ +#define ESP32_C3_SPI_USER2_CMD_LEN_MASK 0xf0000000U +#define ESP32_C3_SPI_USER2_CMD_LEN_SHIFT 28U +#define ESP32_C3_SPI_USER2_CMD_LEN(x) ((((x)-1U) << ESP32_C3_SPI_USER2_CMD_LEN_SHIFT) & ESP32_C3_SPI_USER2_CMD_LEN_MASK) +#define ESP32_C3_SPI_USER1_ADDR_LEN_MASK 0xfc000000U +#define ESP32_C3_SPI_USER1_ADDR_LEN_SHIFT 26U +#define ESP32_C3_SPI_USER1_ADDR_LEN(x) \ + ((((x)-1U) << ESP32_C3_SPI_USER1_ADDR_LEN_SHIFT) & ESP32_C3_SPI_USER1_ADDR_LEN_MASK) +#define ESP32_C3_SPI_USER1_DUMMY_LEN_MASK 0x0000003fU +#define ESP32_C3_SPI_USER1_DUMMY_LEN(x) (((x)-1U) & ESP32_C3_SPI_USER1_DUMMY_LEN_MASK) +#define ESP32_C3_SPI_DATA_BIT_LEN_MASK 0x000003ffU +#define ESP32_C3_SPI_DATA_BIT_LEN(x) ((((x)*8U) - 1U) & ESP32_C3_SPI_DATA_BIT_LEN_MASK) + +#define ESP32_C3_SPI_FLASH_OPCODE_MASK 0x000000ffU +#define ESP32_C3_SPI_FLASH_OPCODE(x) ((x)&ESP32_C3_SPI_FLASH_OPCODE_MASK) +#define ESP32_C3_SPI_FLASH_DUMMY_MASK 0x0000ff00U +#define ESP32_C3_SPI_FLASH_DUMMY_SHIFT 8U +#define ESP32_C3_SPI_FLASH_DUMMY_LEN(x) (((x) << ESP32_C3_SPI_FLASH_DUMMY_SHIFT) & ESP32_C3_SPI_FLASH_DUMMY_MASK) +#define ESP32_C3_SPI_FLASH_OPCODE_MODE_MASK 0x00010000U +#define ESP32_C3_SPI_FLASH_OPCODE_ONLY (0U << 16U) +#define ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR (1U << 16U) +#define ESP32_C3_SPI_FLASH_DATA_IN (0U << 17U) +#define ESP32_C3_SPI_FLASH_DATA_OUT (1U << 17U) +#define ESP32_C3_SPI_FLASH_COMMAND_MASK 0x0003ffffU + +#define SPI_FLASH_OPCODE_SECTOR_ERASE 0x20U +#define SPI_FLASH_CMD_READ_JEDEC_ID \ + (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | \ + ESP32_C3_SPI_FLASH_OPCODE(0x9fU)) +#define SPI_FLASH_CMD_READ_SFDP \ + (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(8) | \ + ESP32_C3_SPI_FLASH_OPCODE(0x5aU)) + +static void esp32c3_spi_read(target_s *target, uint32_t command, target_addr_t address, void *buffer, size_t length); + +static void esp32c3_spi_read_sfdp( + target_s *const target, const uint32_t address, void *const buffer, const size_t length) +{ + esp32c3_spi_read(target, SPI_FLASH_CMD_READ_SFDP, address, buffer, length); +} + static void esp32c3_add_flash(target_s *const target) { target_flash_s *const flash = calloc(1, sizeof(*flash)); @@ -59,10 +119,18 @@ static void esp32c3_add_flash(target_s *const target) return; } + spi_parameters_s spi_parameters; + if (!sfdp_read_parameters(target, &spi_parameters, esp32c3_spi_read_sfdp)) { + /* SFDP readout failed, so make some assumptions and hope for the best. */ + spi_parameters.page_size = 256U; + spi_parameters.sector_size = 4096U; + spi_parameters.capacity = ESP32_C3_IBUS_FLASH_SIZE; + spi_parameters.sector_erase_opcode = SPI_FLASH_OPCODE_SECTOR_ERASE; + } + flash->start = ESP32_C3_IBUS_FLASH_BASE; - flash->length = ESP32_C3_IBUS_FLASH_SIZE; - flash->blocksize = 256U; - flash->writesize = 4096U; + flash->length = MIN(spi_parameters.capacity, ESP32_C3_IBUS_FLASH_SIZE); + flash->blocksize = spi_parameters.sector_size; flash->erased = 0xffU; target_add_flash(target, flash); } @@ -86,3 +154,62 @@ bool esp32c3_probe(target_s *const target) return true; } + +static void esp32c3_spi_config( + target_s *const target, const uint32_t command, const target_addr_t address, size_t length) +{ + uint32_t enabled_stages = ESP32_C3_SPI_USER0_CMD; + uint32_t user1_value = 0; + + /* Set up the command phase */ + const uint8_t spi_command = command & ESP32_C3_SPI_FLASH_OPCODE_MASK; + target_mem_write32(target, ESP32_C3_SPI1_USER2, ESP32_C3_SPI_USER2_CMD_LEN(8) | spi_command); + + /* Configure the address to send */ + if ((command & ESP32_C3_SPI_FLASH_OPCODE_MODE_MASK) == ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR) { + enabled_stages |= ESP32_C3_SPI_USER0_ADDR; + target_mem_write32(target, ESP32_C3_SPI1_ADDR, address); + user1_value |= ESP32_C3_SPI_USER1_ADDR_LEN(24U); + } + + /* Configure the number of dummy cycles required */ + if (command & ESP32_C3_SPI_FLASH_DUMMY_MASK) { + enabled_stages |= ESP32_C3_SPI_USER0_DUMMY; + uint8_t dummy_cycles = (command & ESP32_C3_SPI_FLASH_DUMMY_MASK) >> ESP32_C3_SPI_FLASH_DUMMY_SHIFT; + user1_value |= ESP32_C3_SPI_USER1_DUMMY_LEN(dummy_cycles); + } + + /* Configure the data phase */ + if (length) { + if (command & ESP32_C3_SPI_FLASH_DATA_OUT) { + enabled_stages |= ESP32_C3_SPI_USER0_DATA_OUT; + target_mem_write32(target, ESP32_C3_SPI1_DATA_OUT_LEN, ESP32_C3_SPI_DATA_BIT_LEN(length)); + } else { + enabled_stages |= ESP32_C3_SPI_USER0_DATA_IN; + target_mem_write32(target, ESP32_C3_SPI1_DATA_IN_LEN, ESP32_C3_SPI_DATA_BIT_LEN(length)); + } + } + + /* Now we've defined all the information needed for user0 and user1, send it */ + target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); + target_mem_write32(target, ESP32_C3_SPI1_USER1, user1_value); +} + +static void esp32c3_spi_read( + target_s *const target, const uint32_t command, const target_addr_t address, void *const buffer, size_t length) +{ + /* Start by setting up the common components of the transaction */ + esp32c3_spi_config(target, command, address, length); + /* Now trigger the configured transaction */ + target_mem_write32(target, ESP32_C3_SPI1_CMD, ESP32_C3_SPI_CMD_EXEC_XFER); + /* And wait for the transaction to complete */ + while (target_mem_read32(target, ESP32_C3_SPI1_CMD) & ESP32_C3_SPI_CMD_EXEC_XFER) + continue; + + uint8_t *const data = (uint8_t *)buffer; + for (size_t offset = 0; offset < length; offset += 4U) { + const uint32_t value = target_mem_read32(target, ESP32_C3_SPI1_DATA + offset); + const size_t amount = MIN(4U, length - offset); + memcpy(data + offset, &value, amount); + } +} From 10da94b2da9c329aab16a7441f3c8779e8ba6b97 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Mar 2023 11:38:32 +0100 Subject: [PATCH 091/130] esp32c3: Improved the fallback path in esp32c3_add_flash() when the SFDP readout fails --- src/target/esp32c3.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index acc48dcc2c3..c5247424cd9 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -121,10 +121,15 @@ static void esp32c3_add_flash(target_s *const target) spi_parameters_s spi_parameters; if (!sfdp_read_parameters(target, &spi_parameters, esp32c3_spi_read_sfdp)) { - /* SFDP readout failed, so make some assumptions and hope for the best. */ + /* SFDP readout failed, so read the JTAG ID of the device next */ + spi_flash_id_s flash_id; + esp32c3_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); + const uint32_t capacity = 1U << flash_id.capacity; + + /* Now make some assumptions and hope for the best. */ spi_parameters.page_size = 256U; spi_parameters.sector_size = 4096U; - spi_parameters.capacity = ESP32_C3_IBUS_FLASH_SIZE; + spi_parameters.capacity = MIN(capacity, ESP32_C3_IBUS_FLASH_SIZE); spi_parameters.sector_erase_opcode = SPI_FLASH_OPCODE_SECTOR_ERASE; } From c6da1d05325375323df2409b457602d9d253ddd1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Mar 2023 11:40:36 +0100 Subject: [PATCH 092/130] riscv32: Added a loop to riscv32_sysbus_mem_write() to make sure each write completes before issuing the next --- src/target/riscv32.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 120b73cc1d7..41a1aa5b1f4 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -415,6 +415,13 @@ static void riscv32_sysbus_mem_write( const uint32_t value = riscv32_pack_data(data + offset, access_width); if (!riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_DATA0, value)) return; + + uint32_t status = RV_SYSBUS_STATUS_BUSY; + /* Wait for the current write cycle to complete */ + while (status & RV_SYSBUS_STATUS_BUSY) { + if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &status)) + return; + } } riscv_sysbus_check(hart); } From 2cb86c91cd966c7a2ce2d8904e50a1118cc555f1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Mar 2023 15:11:10 +0100 Subject: [PATCH 093/130] esp32c3: Implemented handling for chunking reads to handle the fact the buffer's not otherwise big enough to handle all possible reads --- src/target/esp32c3.c | 58 ++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index c5247424cd9..03348c96744 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -60,6 +60,7 @@ #define ESP32_C3_SPI1_USER2 (ESP32_C3_SPI1_BASE + 0x020U) #define ESP32_C3_SPI1_DATA_OUT_LEN (ESP32_C3_SPI1_BASE + 0x024U) #define ESP32_C3_SPI1_DATA_IN_LEN (ESP32_C3_SPI1_BASE + 0x028U) +#define ESP32_C3_SPI1_MISC (ESP32_C3_SPI1_BASE + 0x034U) #define ESP32_C3_SPI1_DATA (ESP32_C3_SPI1_BASE + 0x058U) /* These define the various stages of a SPI transaction that we can choose to enable */ @@ -69,6 +70,7 @@ #define ESP32_C3_SPI_USER0_DUMMY 0x20000000U #define ESP32_C3_SPI_USER0_DATA_IN 0x10000000U #define ESP32_C3_SPI_USER0_DATA_OUT 0x08000000U +#define ESP32_C3_SPI_MISC_CS_HOLD 0x00000400U /* These define the various bit ranges used to store the cycle counts for the enabled stages */ #define ESP32_C3_SPI_USER2_CMD_LEN_MASK 0xf0000000U @@ -160,7 +162,7 @@ bool esp32c3_probe(target_s *const target) return true; } -static void esp32c3_spi_config( +static uint32_t esp32c3_spi_config( target_s *const target, const uint32_t command, const target_addr_t address, size_t length) { uint32_t enabled_stages = ESP32_C3_SPI_USER0_CMD; @@ -186,35 +188,61 @@ static void esp32c3_spi_config( /* Configure the data phase */ if (length) { - if (command & ESP32_C3_SPI_FLASH_DATA_OUT) { + if (command & ESP32_C3_SPI_FLASH_DATA_OUT) enabled_stages |= ESP32_C3_SPI_USER0_DATA_OUT; - target_mem_write32(target, ESP32_C3_SPI1_DATA_OUT_LEN, ESP32_C3_SPI_DATA_BIT_LEN(length)); - } else { + else enabled_stages |= ESP32_C3_SPI_USER0_DATA_IN; - target_mem_write32(target, ESP32_C3_SPI1_DATA_IN_LEN, ESP32_C3_SPI_DATA_BIT_LEN(length)); - } } /* Now we've defined all the information needed for user0 and user1, send it */ - target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); target_mem_write32(target, ESP32_C3_SPI1_USER1, user1_value); + return enabled_stages; } -static void esp32c3_spi_read( - target_s *const target, const uint32_t command, const target_addr_t address, void *const buffer, size_t length) +static void esp32c3_spi_wait_complete(target_s *const target) { - /* Start by setting up the common components of the transaction */ - esp32c3_spi_config(target, command, address, length); /* Now trigger the configured transaction */ target_mem_write32(target, ESP32_C3_SPI1_CMD, ESP32_C3_SPI_CMD_EXEC_XFER); /* And wait for the transaction to complete */ while (target_mem_read32(target, ESP32_C3_SPI1_CMD) & ESP32_C3_SPI_CMD_EXEC_XFER) continue; +} +static void esp32c3_spi_read( + target_s *const target, const uint32_t command, const target_addr_t address, void *const buffer, size_t length) +{ + /* Start by setting up the common components of the transaction */ + const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, length); uint8_t *const data = (uint8_t *)buffer; - for (size_t offset = 0; offset < length; offset += 4U) { - const uint32_t value = target_mem_read32(target, ESP32_C3_SPI1_DATA + offset); - const size_t amount = MIN(4U, length - offset); - memcpy(data + offset, &value, amount); + /* + * The transfer has to proceed in no more than 64 bytes at a time because that's + * how many data registers are available in the SPI peripheral + */ + for (size_t offset = 0U; offset < length; offset += 64U) { + const uint32_t amount = MIN(length - offset, 64U); + /* Tell the controller how many bytes we want received in this transaction */ + target_mem_write32(target, ESP32_C3_SPI1_DATA_IN_LEN, ESP32_C3_SPI_DATA_BIT_LEN(amount)); + /* Configure which transaction stages to use */ + if (offset) + target_mem_write32(target, ESP32_C3_SPI1_USER0, ESP32_C3_SPI_USER0_DATA_IN); + else + target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); + + /* On the final transfer, clear the chip select hold bit, otherwise set it */ + const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC); + if (length - offset == amount) + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg & ~ESP32_C3_SPI_MISC_CS_HOLD); + else + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg | ESP32_C3_SPI_MISC_CS_HOLD); + + /* Run the transaction */ + esp32c3_spi_wait_complete(target); + + /* Extract and unpack the received data */ + for (size_t idx = 0; idx < amount; idx += 4U) { + const uint32_t value = target_mem_read32(target, ESP32_C3_SPI1_DATA + idx); + const size_t bytes = MIN(4U, amount - idx); + memcpy(data + offset + idx, &value, bytes); + } } } From 610509afc58c2cd662a05bb91d5b05a5f8b4fe79 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 04:18:26 +0100 Subject: [PATCH 094/130] esp32c3: Implemented handling for disabling and restoring the WDTs so they stop destroying the debug environment esp32c3: Implemented customised halt/resume functions to take care of the WDTs properly --- src/target/esp32c3.c | 105 ++++++++++++++++++++++++++++++++++++++- src/target/riscv_debug.c | 9 ++-- src/target/riscv_debug.h | 4 ++ 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 03348c96744..adc4fbcdf2f 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -85,6 +85,28 @@ #define ESP32_C3_SPI_DATA_BIT_LEN_MASK 0x000003ffU #define ESP32_C3_SPI_DATA_BIT_LEN(x) ((((x)*8U) - 1U) & ESP32_C3_SPI_DATA_BIT_LEN_MASK) +#define ESP32_C3_RTC_BASE 0x60008000U +#define ESP32_C3_RTC_WDT_CONFIG0 (ESP32_C3_RTC_BASE + 0x090U) +#define ESP32_C3_RTC_WDT_FEED (ESP32_C3_RTC_BASE + 0x0a4U) +#define ESP32_C3_RTC_WDT_WRITE_PROT (ESP32_C3_RTC_BASE + 0x0a8U) +#define ESP32_C3_RTC_SWD_CONFIG (ESP32_C3_RTC_BASE + 0x0acU) +#define ESP32_C3_RTC_SWD_WRITE_PROT (ESP32_C3_RTC_BASE + 0x0b0U) + +#define ESP32_C3_WDT_WRITE_PROT_KEY 0x50d83aa1U +#define ESP32_C3_RTC_SWD_WRITE_PROT_KEY 0x8f1d312aU +#define ESP32_C3_RTC_SWD_CONFIG_DISABLE 0x40000002U +#define ESP32_C3_RTC_SWD_CONFIG_FEED 0x60000002U + +#define ESP32_C3_TIMG0_BASE 0x6001f000U +#define ESP32_C3_TIMG0_WDT_CONFIG0 (ESP32_C3_TIMG0_BASE + 0x048U) +#define ESP32_C3_TIMG0_WDT_FEED (ESP32_C3_TIMG0_BASE + 0x060U) +#define ESP32_C3_TIMG0_WDT_WRITE_PROT (ESP32_C3_TIMG0_BASE + 0x064U) + +#define ESP32_C3_TIMG1_BASE 0x60020000U +#define ESP32_C3_TIMG1_WDT_CONFIG0 (ESP32_C3_TIMG1_BASE + 0x048U) +#define ESP32_C3_TIMG1_WDT_FEED (ESP32_C3_TIMG1_BASE + 0x060U) +#define ESP32_C3_TIMG1_WDT_WRITE_PROT (ESP32_C3_TIMG1_BASE + 0x064U) + #define ESP32_C3_SPI_FLASH_OPCODE_MASK 0x000000ffU #define ESP32_C3_SPI_FLASH_OPCODE(x) ((x)&ESP32_C3_SPI_FLASH_OPCODE_MASK) #define ESP32_C3_SPI_FLASH_DUMMY_MASK 0x0000ff00U @@ -105,6 +127,16 @@ (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(8) | \ ESP32_C3_SPI_FLASH_OPCODE(0x5aU)) +typedef struct esp32c3_priv { + uint32_t wdt_config[4]; +} esp32c3_priv_s; + +static void esp32c3_disable_wdts(target_s *target); +static void esp32c3_restore_wdts(target_s *target); +static void esp32c3_halt_request(target_s *target); +static void esp32c3_halt_resume(target_s *target, bool step); +static target_halt_reason_e esp32c3_halt_poll(target_s *target, target_addr_t *watch); + static void esp32c3_spi_read(target_s *target, uint32_t command, target_addr_t address, void *buffer, size_t length); static void esp32c3_spi_read_sfdp( @@ -149,7 +181,20 @@ bool esp32c3_probe(target_s *const target) if (hart->archid != ESP32_C3_ARCH_ID || hart->implid != ESP32_C3_IMPL_ID) return false; + esp32c3_priv_s *const priv = calloc(1, sizeof(esp32c3_priv_s)); + if (!priv) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return false; + } + target->target_storage = priv; target->driver = "ESP32-C3"; + esp32c3_disable_wdts(target); + + /* We have to provide our own halt/resume functions to take care of the WDTs as they cause Problems */ + target->halt_request = esp32c3_halt_request; + target->halt_resume = esp32c3_halt_resume; + target->halt_poll = esp32c3_halt_poll; + /* Establish the target RAM mappings */ target_add_ram(target, ESP32_C3_IBUS_SRAM0_BASE, ESP32_C3_IBUS_SRAM0_SIZE); target_add_ram(target, ESP32_C3_IBUS_SRAM1_BASE, ESP32_C3_IBUS_SRAM1_SIZE); @@ -158,10 +203,68 @@ bool esp32c3_probe(target_s *const target) /* Establish the target Flash mappings */ esp32c3_add_flash(target); - return true; } +static void esp32c3_disable_wdts(target_s *const target) +{ + esp32c3_priv_s *const priv = (esp32c3_priv_s *)target->target_storage; + /* Disable Timer Group 0's WDT */ + target_mem_write32(target, ESP32_C3_TIMG0_WDT_WRITE_PROT, ESP32_C3_WDT_WRITE_PROT_KEY); + priv->wdt_config[0] = target_mem_read32(target, ESP32_C3_TIMG0_WDT_CONFIG0); + target_mem_write32(target, ESP32_C3_TIMG0_WDT_CONFIG0, 0U); + /* Disable Timer Group 1's WDT */ + target_mem_write32(target, ESP32_C3_TIMG1_WDT_WRITE_PROT, ESP32_C3_WDT_WRITE_PROT_KEY); + priv->wdt_config[1] = target_mem_read32(target, ESP32_C3_TIMG1_WDT_CONFIG0); + target_mem_write32(target, ESP32_C3_TIMG1_WDT_CONFIG0, 0U); + /* Disable the RTC WDT */ + target_mem_write32(target, ESP32_C3_RTC_WDT_WRITE_PROT, ESP32_C3_WDT_WRITE_PROT_KEY); + priv->wdt_config[2] = target_mem_read32(target, ESP32_C3_RTC_WDT_CONFIG0); + target_mem_write32(target, ESP32_C3_RTC_WDT_CONFIG0, 0U); + /* Disable the "super" WDT */ + target_mem_write32(target, ESP32_C3_RTC_SWD_WRITE_PROT, ESP32_C3_RTC_SWD_WRITE_PROT_KEY); + priv->wdt_config[3] = target_mem_read32(target, ESP32_C3_RTC_SWD_CONFIG); + target_mem_write32(target, ESP32_C3_RTC_SWD_CONFIG, ESP32_C3_RTC_SWD_CONFIG_DISABLE); +} + +static void esp32c3_restore_wdts(target_s *const target) +{ + esp32c3_priv_s *const priv = (esp32c3_priv_s *)target->target_storage; + /* Restore Timger Group 0's WDT */ + target_mem_write32(target, ESP32_C3_TIMG0_WDT_CONFIG0, priv->wdt_config[0]); + target_mem_write32(target, ESP32_C3_TIMG0_WDT_WRITE_PROT, 0U); + /* Restore Timger Group 1's WDT */ + target_mem_write32(target, ESP32_C3_TIMG1_WDT_CONFIG0, priv->wdt_config[1]); + target_mem_write32(target, ESP32_C3_TIMG1_WDT_WRITE_PROT, 0U); + /* Restore the RTC WDT */ + target_mem_write32(target, ESP32_C3_RTC_WDT_CONFIG0, priv->wdt_config[2]); + target_mem_write32(target, ESP32_C3_RTC_WDT_WRITE_PROT, 0U); + /* Restore the "super" WDT */ + target_mem_write32(target, ESP32_C3_RTC_SWD_CONFIG, priv->wdt_config[2]); + target_mem_write32(target, ESP32_C3_RTC_SWD_WRITE_PROT, 0U); +} + +static void esp32c3_halt_request(target_s *const target) +{ + riscv_halt_request(target); + esp32c3_disable_wdts(target); +} + +static void esp32c3_halt_resume(target_s *const target, const bool step) +{ + if (!step) + esp32c3_restore_wdts(target); + riscv_halt_resume(target, step); +} + +static target_halt_reason_e esp32c3_halt_poll(target_s *const target, target_addr_t *const watch) +{ + const target_halt_reason_e reason = riscv_halt_poll(target, watch); + if (reason == TARGET_HALT_BREAKPOINT) + esp32c3_disable_wdts(target); + return reason; +} + static uint32_t esp32c3_spi_config( target_s *const target, const uint32_t command, const target_addr_t address, size_t length) { diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 3cf4712c624..02376c2d96a 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -220,9 +220,6 @@ static void riscv_detach(target_s *target); static const char *riscv_target_description(target_s *target); static bool riscv_check_error(target_s *target); -static void riscv_halt_request(target_s *target); -static void riscv_halt_resume(target_s *target, bool step); -static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); static void riscv_reset(target_s *target); void riscv_dmi_init(riscv_dmi_s *const dmi) @@ -802,7 +799,7 @@ static bool riscv_dm_poll_state(riscv_dm_s *const dbg_module, const uint32_t sta return true; } -static void riscv_halt_request(target_s *const target) +void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); /* Request the hart to halt */ @@ -815,7 +812,7 @@ static void riscv_halt_request(target_s *const target) (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } -static void riscv_halt_resume(target_s *target, const bool step) +void riscv_halt_resume(target_s *const target, const bool step) { riscv_hart_s *const hart = riscv_hart_struct(target); /* Configure the debug controller for single-stepping as appropriate */ @@ -838,7 +835,7 @@ static void riscv_halt_resume(target_s *target, const bool step) (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } -static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) +target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) { (void)watch; riscv_hart_s *const hart = riscv_hart_struct(target); diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index f130090fbaa..f5a9b1f42d1 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -233,4 +233,8 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); uint32_t riscv32_pack_data(const void *src, uint8_t access_width); +void riscv_halt_request(target_s *target); +void riscv_halt_resume(target_s *target, bool step); +target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); + #endif /*TARGET_RISCV_DEBUG_H*/ From f92ce464101f20121fff629065728421523197a0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 05:06:59 +0100 Subject: [PATCH 095/130] riscv32: Refactored out the native-width system bus write logic into a new function --- src/target/riscv32.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 41a1aa5b1f4..253f7d04322 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -393,15 +393,13 @@ static void riscv32_sysbus_mem_read( riscv32_sysbus_mem_adjusted_read(hart, data, address, access_width, native_access_width, native_access_length); } -static void riscv32_sysbus_mem_write( - riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +static void riscv32_sysbus_mem_native_write(riscv_hart_s *const hart, const target_addr_t dest, const void *const src, + const size_t len, const uint8_t access_width, const uint8_t access_length) { - /* Figure out the maxmial width of access to perform, up to the bitness of the target */ - const uint8_t access_width = riscv_mem_access_width(hart, dest, len); - const uint8_t access_length = 1U << access_width; + DEBUG_TARGET("%s: %zu byte write at %08" PRIx32 " in %u byte blocks\n", __func__, len, dest, access_length); /* Build the access command */ - const uint32_t command = - (access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC : 0U); + const uint32_t command = ((uint32_t)access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | + (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC : 0U); /* * Write the command setup to the access control register * Then set up the write by writing the address to the address register @@ -426,6 +424,16 @@ static void riscv32_sysbus_mem_write( riscv_sysbus_check(hart); } +static void riscv32_sysbus_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + /* Check if the access is a natural/native width */ + riscv32_sysbus_mem_native_write(hart, dest, src, len, access_width, access_length); +} + static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) { DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); From 98c9d63825acdcba19a430d5301d06041553cddb Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 05:43:28 +0100 Subject: [PATCH 096/130] riscv32: Implemented support for non-native-width system bus memory writes --- src/target/riscv32.c | 81 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 253f7d04322..b8fe5855e2e 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -424,6 +424,48 @@ static void riscv32_sysbus_mem_native_write(riscv_hart_s *const hart, const targ riscv_sysbus_check(hart); } +static void riscv32_sysbus_mem_adjusted_write(riscv_hart_s *const hart, const target_addr_t dest, const void *const src, + const uint8_t access_width, const uint8_t native_access_width, const uint8_t access_length) +{ + DEBUG_PROBE("%s: %u write for %u bytes\n", __func__, access_length, 1U << access_width); + const target_addr_t alignment = ~(access_length - 1U); + /* + * On a 32-bit target the only possible widths are 8- 16- and 32-bit, so after the adjustment loop, + * there are only and exactly 2 possible cases to handle here: 16- and 32-bit access. + * The basic premise here is that we have to read to correctly write - to do a N bit write with a + * wider access primitive, we first have to read back what's at the target aligned location, replace + * the correct set of bits in the target value, then write the new combined value back + */ + switch (native_access_width) { + case RV_MEM_ACCESS_16_BIT: { + uint16_t value = 0; + /* Start by reading 16 bits */ + riscv32_sysbus_mem_native_read( + hart, &value, dest & alignment, access_length, RV_MEM_ACCESS_16_BIT, access_length); + /* Now replace the part to write (must be done on the widened version of the value) */ + uint32_t widened_value = value; + adiv5_pack_data(dest & 1U, src, &widened_value, access_width); + value = (uint16_t)widened_value; + /* And finally write the new value back */ + riscv32_sysbus_mem_native_write( + hart, dest & alignment, &value, access_length, RV_MEM_ACCESS_16_BIT, access_length); + break; + } + case RV_MEM_ACCESS_32_BIT: { + uint32_t value = 0; + /* Start by reading 32 bits */ + riscv32_sysbus_mem_native_read( + hart, &value, dest & alignment, access_length, RV_MEM_ACCESS_32_BIT, access_length); + /* Now replace the part to write */ + adiv5_pack_data(dest & 3U, src, &value, access_width); + /* And finally write the new value back */ + riscv32_sysbus_mem_native_write( + hart, dest & alignment, &value, access_length, RV_MEM_ACCESS_32_BIT, access_length); + break; + } + } +} + static void riscv32_sysbus_mem_write( riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) { @@ -431,7 +473,44 @@ static void riscv32_sysbus_mem_write( const uint8_t access_width = riscv_mem_access_width(hart, dest, len); const uint8_t access_length = 1U << access_width; /* Check if the access is a natural/native width */ - riscv32_sysbus_mem_native_write(hart, dest, src, len, access_width, access_length); + if (hart->flags & access_length) { + riscv32_sysbus_mem_native_write(hart, dest, src, len, access_width, access_length); + return; + } + + /* If we were unable to do this using a native access, find the next largest supported access width */ + uint8_t native_access_width = access_width; + while (!((hart->flags >> native_access_width) & 1U) && native_access_width < RV_MEM_ACCESS_32_BIT) + ++native_access_width; + const uint8_t native_access_length = (uint8_t)(1U << native_access_width); + + /* Do the initial adjusted access */ + size_t remainder = len; + target_addr_t address = dest; + const uint8_t *data = (const uint8_t *)src; + riscv32_sysbus_mem_adjusted_write(hart, address, data, access_width, native_access_width, native_access_length); + + /* After doing the initial access, adjust the location of the next and do any follow-up accesses required */ + remainder -= access_length; + address += access_length; + data += access_length; + + /* + * Now we're aligned to the wider access width, do another set of writes if there's + * any remainder. Do this till we either reach nothing left, or we have another small left-over amount + */ + if (!remainder) + return; + const size_t amount = remainder & ~(native_access_length - 1U); + if (amount) + riscv32_sysbus_mem_native_write(hart, address, data, amount, native_access_width, native_access_length); + remainder -= amount; + address += (uint32_t)amount; + data += amount; + + /* If there's any data left to write, do another adjusted access to perform it */ + if (remainder) + riscv32_sysbus_mem_adjusted_write(hart, address, data, access_width, native_access_width, native_access_length); } static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) From a1932c890ef7049be49d16545d653eb577ec155e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 08:58:09 +0100 Subject: [PATCH 097/130] esp32c3: Implemented SPI write support and mass erase --- src/target/esp32c3.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index adc4fbcdf2f..3073fac6999 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -120,6 +120,13 @@ #define ESP32_C3_SPI_FLASH_COMMAND_MASK 0x0003ffffU #define SPI_FLASH_OPCODE_SECTOR_ERASE 0x20U +#define SPI_FLASH_CMD_WRITE_ENABLE \ + (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x06U)) +#define SPI_FLASH_CMD_CHIP_ERASE \ + (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x60U)) +#define SPI_FLASH_CMD_READ_STATUS \ + (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | \ + ESP32_C3_SPI_FLASH_OPCODE(0x05U)) #define SPI_FLASH_CMD_READ_JEDEC_ID \ (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | \ ESP32_C3_SPI_FLASH_OPCODE(0x9fU)) @@ -127,6 +134,9 @@ (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DATA_IN | ESP32_C3_SPI_FLASH_DUMMY_LEN(8) | \ ESP32_C3_SPI_FLASH_OPCODE(0x5aU)) +#define SPI_FLASH_STATUS_BUSY 0x01U +#define SPI_FLASH_STATUS_WRITE_ENABLED 0x02U + typedef struct esp32c3_priv { uint32_t wdt_config[4]; } esp32c3_priv_s; @@ -138,6 +148,10 @@ static void esp32c3_halt_resume(target_s *target, bool step); static target_halt_reason_e esp32c3_halt_poll(target_s *target, target_addr_t *watch); static void esp32c3_spi_read(target_s *target, uint32_t command, target_addr_t address, void *buffer, size_t length); +static void esp32c3_spi_write( + target_s *target, uint32_t command, target_addr_t address, const void *buffer, size_t length); + +static bool esp32c3_mass_erase(target_s *target); static void esp32c3_spi_read_sfdp( target_s *const target, const uint32_t address, void *const buffer, const size_t length) @@ -194,6 +208,8 @@ bool esp32c3_probe(target_s *const target) target->halt_request = esp32c3_halt_request; target->halt_resume = esp32c3_halt_resume; target->halt_poll = esp32c3_halt_poll; + /* Provide an implementation of the mass erase command */ + target->mass_erase = esp32c3_mass_erase; /* Establish the target RAM mappings */ target_add_ram(target, ESP32_C3_IBUS_SRAM0_BASE, ESP32_C3_IBUS_SRAM0_SIZE); @@ -349,3 +365,78 @@ static void esp32c3_spi_read( } } } + +static void esp32c3_spi_write(target_s *const target, const uint32_t command, const target_addr_t address, + const void *const buffer, const size_t length) +{ + /* Start by setting up the common components of the transaction */ + const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, length); + const uint8_t *const data = (const uint8_t *)buffer; + /* Handle the case where we have nothing to write (execute command) */ + if (!length) { + /* Write the stages to execute and run the transaction */ + target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); + esp32c3_spi_wait_complete(target); + /* The for loop below will do nothing so we do nothing */ + } + + /* + * The transfer has to proceed in no more than 64 bytes at a time because that's + * how many data registers are available in the SPI peripheral + */ + for (size_t offset = 0U; offset < length; offset += 64U) { + const uint32_t amount = MIN(length - offset, 64U); + /* Tell the controller how many bytes we want sent in this transaction */ + target_mem_write32(target, ESP32_C3_SPI1_DATA_OUT_LEN, ESP32_C3_SPI_DATA_BIT_LEN(amount)); + /* Configure which transaction stages to use */ + if (offset) + target_mem_write32(target, ESP32_C3_SPI1_USER0, ESP32_C3_SPI_USER0_DATA_OUT); + else + target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); + + /* On the final transfer, clear the chip select hold bit, otherwise set it */ + const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC); + if (length - offset == amount) + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg & ~ESP32_C3_SPI_MISC_CS_HOLD); + else + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg | ESP32_C3_SPI_MISC_CS_HOLD); + + /* Pack and stage the data to transmit */ + for (size_t idx = 0; idx < amount; idx += 4U) { + const size_t bytes = MIN(4U, amount - idx); + uint32_t value = 0; + memcpy(&value, data + offset + idx, bytes); + target_mem_write32(target, ESP32_C3_SPI1_DATA + idx, value); + } + + /* Run the transaction */ + esp32c3_spi_wait_complete(target); + } +} + +static inline uint8_t esp32c3_spi_read_status(target_s *const target) +{ + uint8_t status = 0; + esp32c3_spi_read(target, SPI_FLASH_CMD_READ_STATUS, 0, &status, sizeof(status)); + return status; +} + +static inline void esp32c3_spi_run_command(target_s *const target, const uint32_t command) +{ + esp32c3_spi_write(target, command, 0U, NULL, 0U); +} + +static bool esp32c3_mass_erase(target_s *const target) +{ + platform_timeout_s timeout; + platform_timeout_set(&timeout, 500U); + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) + return false; + + esp32c3_spi_run_command(target, SPI_FLASH_CMD_CHIP_ERASE); + while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) + target_print_progress(&timeout); + + return true; +} From a73e3bbcdef55f84bf2a5dc0dbcd4444e5887ade Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 17:25:38 +0100 Subject: [PATCH 098/130] esp32c3: Implemented a type for holding SPI Flash specific information --- src/target/esp32c3.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 3073fac6999..e2bb3468796 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -141,6 +141,12 @@ typedef struct esp32c3_priv { uint32_t wdt_config[4]; } esp32c3_priv_s; +typedef struct esp32c3_spi_flash { + target_flash_s flash; + uint32_t page_size; + uint8_t sector_erase_opcode; +} esp32c3_spi_flash_s; + static void esp32c3_disable_wdts(target_s *target); static void esp32c3_restore_wdts(target_s *target); static void esp32c3_halt_request(target_s *target); @@ -161,8 +167,8 @@ static void esp32c3_spi_read_sfdp( static void esp32c3_add_flash(target_s *const target) { - target_flash_s *const flash = calloc(1, sizeof(*flash)); - if (!flash) { /* calloc failed: heap exhaustion */ + esp32c3_spi_flash_s *const spi_flash = calloc(1, sizeof(*spi_flash)); + if (!spi_flash) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return; } @@ -181,11 +187,15 @@ static void esp32c3_add_flash(target_s *const target) spi_parameters.sector_erase_opcode = SPI_FLASH_OPCODE_SECTOR_ERASE; } + target_flash_s *const flash = &spi_flash->flash; flash->start = ESP32_C3_IBUS_FLASH_BASE; flash->length = MIN(spi_parameters.capacity, ESP32_C3_IBUS_FLASH_SIZE); flash->blocksize = spi_parameters.sector_size; flash->erased = 0xffU; target_add_flash(target, flash); + + spi_flash->page_size = spi_parameters.page_size; + spi_flash->sector_erase_opcode = spi_parameters.sector_erase_opcode; } bool esp32c3_probe(target_s *const target) From 6fecd3d5f302c48416ce0567900ca7b6870c81b6 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 17:26:33 +0100 Subject: [PATCH 099/130] esp32c3: Implemented Flash erase --- src/target/esp32c3.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index e2bb3468796..6c26aba0f68 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -122,6 +122,7 @@ #define SPI_FLASH_OPCODE_SECTOR_ERASE 0x20U #define SPI_FLASH_CMD_WRITE_ENABLE \ (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x06U)) +#define SPI_FLASH_CMD_SECTOR_ERASE (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DUMMY_LEN(0)) #define SPI_FLASH_CMD_CHIP_ERASE \ (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x60U)) #define SPI_FLASH_CMD_READ_STATUS \ @@ -158,6 +159,7 @@ static void esp32c3_spi_write( target_s *target, uint32_t command, target_addr_t address, const void *buffer, size_t length); static bool esp32c3_mass_erase(target_s *target); +static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static void esp32c3_spi_read_sfdp( target_s *const target, const uint32_t address, void *const buffer, const size_t length) @@ -191,6 +193,7 @@ static void esp32c3_add_flash(target_s *const target) flash->start = ESP32_C3_IBUS_FLASH_BASE; flash->length = MIN(spi_parameters.capacity, ESP32_C3_IBUS_FLASH_SIZE); flash->blocksize = spi_parameters.sector_size; + flash->erase = esp32c3_spi_flash_erase; flash->erased = 0xffU; target_add_flash(target, flash); @@ -450,3 +453,22 @@ static bool esp32c3_mass_erase(target_s *const target) return true; } + +static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + target_s *const target = flash->t; + const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; + const target_addr_t begin = addr - flash->start; + for (size_t offset = 0; offset < length; offset += flash->blocksize) { + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) + return false; + + esp32c3_spi_write(target, + SPI_FLASH_CMD_SECTOR_ERASE | ESP32_C3_SPI_FLASH_OPCODE(spi_flash->sector_erase_opcode), begin + offset, + NULL, 0); + while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) + continue; + } + return true; +} From 3e11ae2a488e0a523bd802b7160dc307e4fe9782 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 17:48:46 +0100 Subject: [PATCH 100/130] esp32c3: Implemented Flash write --- src/target/esp32c3.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 6c26aba0f68..9615835a197 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -122,6 +122,9 @@ #define SPI_FLASH_OPCODE_SECTOR_ERASE 0x20U #define SPI_FLASH_CMD_WRITE_ENABLE \ (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x06U)) +#define SPI_FLASH_CMD_PAGE_PROGRAM \ + (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DATA_OUT | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | \ + ESP32_C3_SPI_FLASH_OPCODE(0x02)) #define SPI_FLASH_CMD_SECTOR_ERASE (ESP32_C3_SPI_FLASH_OPCODE_3B_ADDR | ESP32_C3_SPI_FLASH_DUMMY_LEN(0)) #define SPI_FLASH_CMD_CHIP_ERASE \ (ESP32_C3_SPI_FLASH_OPCODE_ONLY | ESP32_C3_SPI_FLASH_DUMMY_LEN(0) | ESP32_C3_SPI_FLASH_OPCODE(0x60U)) @@ -160,6 +163,7 @@ static void esp32c3_spi_write( static bool esp32c3_mass_erase(target_s *target); static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); +static bool esp32c3_spi_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); static void esp32c3_spi_read_sfdp( target_s *const target, const uint32_t address, void *const buffer, const size_t length) @@ -193,6 +197,7 @@ static void esp32c3_add_flash(target_s *const target) flash->start = ESP32_C3_IBUS_FLASH_BASE; flash->length = MIN(spi_parameters.capacity, ESP32_C3_IBUS_FLASH_SIZE); flash->blocksize = spi_parameters.sector_size; + flash->write = esp32c3_spi_flash_write; flash->erase = esp32c3_spi_flash_erase; flash->erased = 0xffU; target_add_flash(target, flash); @@ -472,3 +477,23 @@ static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_ad } return true; } + +static bool esp32c3_spi_flash_write( + target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t length) +{ + target_s *const target = flash->t; + const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; + const target_addr_t begin = dest - flash->start; + const char *const buffer = src; + for (size_t offset = 0; offset < length; offset += spi_flash->page_size) { + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) + return false; + + const size_t amount = MIN(length - offset, spi_flash->page_size); + esp32c3_spi_write(target, SPI_FLASH_CMD_PAGE_PROGRAM, begin + offset, buffer + offset, amount); + while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) + continue; + } + return true; +} From dbfc7e999bcfe492d330e95b2f7dc736bdbe26ca Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 2 Apr 2023 16:11:00 +0100 Subject: [PATCH 101/130] esp32c3: Added handling for correctly entering Flash mode --- src/target/esp32c3.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 9615835a197..2a6da9c363a 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -162,6 +162,7 @@ static void esp32c3_spi_write( target_s *target, uint32_t command, target_addr_t address, const void *buffer, size_t length); static bool esp32c3_mass_erase(target_s *target); +static bool esp32c3_enter_flash_mode(target_s *target); static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool esp32c3_spi_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); @@ -228,6 +229,8 @@ bool esp32c3_probe(target_s *const target) target->halt_poll = esp32c3_halt_poll; /* Provide an implementation of the mass erase command */ target->mass_erase = esp32c3_mass_erase; + /* Special care must be taken during Flash programming */ + target->enter_flash_mode = esp32c3_enter_flash_mode; /* Establish the target RAM mappings */ target_add_ram(target, ESP32_C3_IBUS_SRAM0_BASE, ESP32_C3_IBUS_SRAM0_SIZE); @@ -459,6 +462,12 @@ static bool esp32c3_mass_erase(target_s *const target) return true; } +static bool esp32c3_enter_flash_mode(target_s *const target) +{ + esp32c3_disable_wdts(target); + return true; +} + static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) { target_s *const target = flash->t; From d14ed5cb441c32594260997304ed71432be6c362 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 3 Apr 2023 21:24:15 +0100 Subject: [PATCH 102/130] esp32c3: Implemented i-cache invalidation and reload for post-completion of Flash operations --- src/target/esp32c3.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 2a6da9c363a..2c2b965fe67 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -107,6 +107,19 @@ #define ESP32_C3_TIMG1_WDT_FEED (ESP32_C3_TIMG1_BASE + 0x060U) #define ESP32_C3_TIMG1_WDT_WRITE_PROT (ESP32_C3_TIMG1_BASE + 0x064U) +#define ESP32_C3_EXTMEM_BASE 0x600c4000U +#define ESP32_C3_EXTMEM_ICACHE_SYNC_CTRL (ESP32_C3_EXTMEM_BASE + 0x028U) +#define ESP32_C3_EXTMEM_ICACHE_SYNC_ADDR (ESP32_C3_EXTMEM_BASE + 0x02cU) +#define ESP32_C3_EXTMEM_ICACHE_SYNC_SIZE (ESP32_C3_EXTMEM_BASE + 0x030U) +#define ESP32_C3_EXTMEM_ICACHE_PRELOAD_CTRL (ESP32_C3_EXTMEM_BASE + 0x034U) +#define ESP32_C3_EXTMEM_ICACHE_PRELOAD_ADDR (ESP32_C3_EXTMEM_BASE + 0x038U) +#define ESP32_C3_EXTMEM_ICACHE_PRELOAD_SIZE (ESP32_C3_EXTMEM_BASE + 0x03cU) + +#define ESP32_C3_EXTMEM_ICACHE_INVALIDATE 0x00000001U +#define ESP32_C3_EXTMEM_ICACHE_SYNC_DONE 0x00000002U +#define ESP32_C3_EXTMEM_ICACHE_PRELOAD 0x00000001U +#define ESP32_C3_EXTMEM_ICACHE_PRELOAD_DONE 0x00000002U + #define ESP32_C3_SPI_FLASH_OPCODE_MASK 0x000000ffU #define ESP32_C3_SPI_FLASH_OPCODE(x) ((x)&ESP32_C3_SPI_FLASH_OPCODE_MASK) #define ESP32_C3_SPI_FLASH_DUMMY_MASK 0x0000ff00U @@ -143,6 +156,7 @@ typedef struct esp32c3_priv { uint32_t wdt_config[4]; + target_addr_t last_invalidated_sector; } esp32c3_priv_s; typedef struct esp32c3_spi_flash { @@ -163,6 +177,7 @@ static void esp32c3_spi_write( static bool esp32c3_mass_erase(target_s *target); static bool esp32c3_enter_flash_mode(target_s *target); +static bool esp32c3_exit_flash_mode(target_s *target); static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool esp32c3_spi_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); @@ -231,6 +246,7 @@ bool esp32c3_probe(target_s *const target) target->mass_erase = esp32c3_mass_erase; /* Special care must be taken during Flash programming */ target->enter_flash_mode = esp32c3_enter_flash_mode; + target->exit_flash_mode = esp32c3_exit_flash_mode; /* Establish the target RAM mappings */ target_add_ram(target, ESP32_C3_IBUS_SRAM0_BASE, ESP32_C3_IBUS_SRAM0_SIZE); @@ -468,6 +484,29 @@ static bool esp32c3_enter_flash_mode(target_s *const target) return true; } +static bool esp32c3_exit_flash_mode(target_s *const target) +{ + esp32c3_priv_s *const priv = (esp32c3_priv_s *)target->target_storage; + /* Calculate the length of the region to invalidate and reload */ + const uint32_t region_length = priv->last_invalidated_sector - ESP32_C3_IBUS_FLASH_BASE; + /* Invalidate the i-cache for the required length */ + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_SYNC_ADDR, ESP32_C3_IBUS_FLASH_BASE); + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_SYNC_SIZE, region_length); + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_SYNC_CTRL, ESP32_C3_EXTMEM_ICACHE_INVALIDATE); + /* Wait for invalidation to complete */ + while (!(target_mem_read32(target, ESP32_C3_EXTMEM_ICACHE_SYNC_CTRL) & ESP32_C3_EXTMEM_ICACHE_SYNC_DONE)) + continue; + /* Now preload the cache with the new data */ + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_PRELOAD_ADDR, ESP32_C3_IBUS_FLASH_BASE); + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_PRELOAD_SIZE, region_length); + target_mem_write32(target, ESP32_C3_EXTMEM_ICACHE_PRELOAD_CTRL, ESP32_C3_EXTMEM_ICACHE_PRELOAD); + /* Wait for preload to complete */ + while (!(target_mem_read32(target, ESP32_C3_EXTMEM_ICACHE_PRELOAD_CTRL) & ESP32_C3_EXTMEM_ICACHE_PRELOAD_DONE)) + continue; + target_reset(target); + return true; +} + static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) { target_s *const target = flash->t; @@ -484,6 +523,9 @@ static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_ad while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) continue; } + /* Update the address of the last invalidated sector so we can correctly invalidate the i-cache and reload it */ + esp32c3_priv_s *const priv = (esp32c3_priv_s *)target->target_storage; + priv->last_invalidated_sector = addr + length; return true; } From 38ffd3e235c1f4dbef78b1023c4627f3599c4744 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 5 Apr 2023 22:30:17 +0100 Subject: [PATCH 103/130] esp32c3: Improved the setup for esp32c3_spu_run_command() --- src/target/esp32c3.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 2c2b965fe67..6e89729a71f 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -458,20 +458,20 @@ static inline uint8_t esp32c3_spi_read_status(target_s *const target) return status; } -static inline void esp32c3_spi_run_command(target_s *const target, const uint32_t command) +static inline void esp32c3_spi_run_command(target_s *const target, const uint32_t command, const target_addr_t address) { - esp32c3_spi_write(target, command, 0U, NULL, 0U); + esp32c3_spi_write(target, command, address, NULL, 0U); } static bool esp32c3_mass_erase(target_s *const target) { platform_timeout_s timeout; platform_timeout_set(&timeout, 500U); - esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; - esp32c3_spi_run_command(target, SPI_FLASH_CMD_CHIP_ERASE); + esp32c3_spi_run_command(target, SPI_FLASH_CMD_CHIP_ERASE, 0U); while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) target_print_progress(&timeout); @@ -513,13 +513,12 @@ static bool esp32c3_spi_flash_erase(target_flash_s *const flash, const target_ad const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; const target_addr_t begin = addr - flash->start; for (size_t offset = 0; offset < length; offset += flash->blocksize) { - esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; - esp32c3_spi_write(target, - SPI_FLASH_CMD_SECTOR_ERASE | ESP32_C3_SPI_FLASH_OPCODE(spi_flash->sector_erase_opcode), begin + offset, - NULL, 0); + esp32c3_spi_run_command(target, + SPI_FLASH_CMD_SECTOR_ERASE | ESP32_C3_SPI_FLASH_OPCODE(spi_flash->sector_erase_opcode), begin + offset); while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) continue; } @@ -537,7 +536,7 @@ static bool esp32c3_spi_flash_write( const target_addr_t begin = dest - flash->start; const char *const buffer = src; for (size_t offset = 0; offset < length; offset += spi_flash->page_size) { - esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE); + esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; From 9b6a041908cab76a926c6da74163c0ee7b99dca4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 5 Apr 2023 22:32:34 +0100 Subject: [PATCH 104/130] esp32c3: Refactored out the innards of esp32c3_spi_run_command() from esp32c3_spi_write() --- src/target/esp32c3.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 6e89729a71f..614706f9aab 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -409,13 +409,6 @@ static void esp32c3_spi_write(target_s *const target, const uint32_t command, co /* Start by setting up the common components of the transaction */ const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, length); const uint8_t *const data = (const uint8_t *)buffer; - /* Handle the case where we have nothing to write (execute command) */ - if (!length) { - /* Write the stages to execute and run the transaction */ - target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); - esp32c3_spi_wait_complete(target); - /* The for loop below will do nothing so we do nothing */ - } /* * The transfer has to proceed in no more than 64 bytes at a time because that's @@ -460,7 +453,11 @@ static inline uint8_t esp32c3_spi_read_status(target_s *const target) static inline void esp32c3_spi_run_command(target_s *const target, const uint32_t command, const target_addr_t address) { - esp32c3_spi_write(target, command, address, NULL, 0U); + /* Start by setting up the common components of the transaction */ + const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, 0U); + /* Write the stages to execute and run the transaction */ + target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); + esp32c3_spi_wait_complete(target); } static bool esp32c3_mass_erase(target_s *const target) From dc4925efe98e2856ab7cbdd9c207c7af967e0cf8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 5 Apr 2023 22:33:35 +0100 Subject: [PATCH 105/130] esp32c3: Improved our handling of chip select hold in esp32c3_spi_write() --- src/target/esp32c3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 614706f9aab..45e30de66d4 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -409,6 +409,7 @@ static void esp32c3_spi_write(target_s *const target, const uint32_t command, co /* Start by setting up the common components of the transaction */ const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, length); const uint8_t *const data = (const uint8_t *)buffer; + const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC) & ~ESP32_C3_SPI_MISC_CS_HOLD; /* * The transfer has to proceed in no more than 64 bytes at a time because that's @@ -425,9 +426,8 @@ static void esp32c3_spi_write(target_s *const target, const uint32_t command, co target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); /* On the final transfer, clear the chip select hold bit, otherwise set it */ - const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC); if (length - offset == amount) - target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg & ~ESP32_C3_SPI_MISC_CS_HOLD); + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg); else target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg | ESP32_C3_SPI_MISC_CS_HOLD); From 8ebe4d3970b086f9814963e2763e2be4bfbd03cf Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 6 Apr 2023 23:59:00 +0100 Subject: [PATCH 106/130] esp32c3: Cleaned up the handling of the SPI1 misc register in esp32c3_spi_read() --- src/target/esp32c3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 45e30de66d4..ff7cde2ff9c 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -370,6 +370,7 @@ static void esp32c3_spi_read( /* Start by setting up the common components of the transaction */ const uint32_t enabled_stages = esp32c3_spi_config(target, command, address, length); uint8_t *const data = (uint8_t *)buffer; + const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC) & ~ESP32_C3_SPI_MISC_CS_HOLD; /* * The transfer has to proceed in no more than 64 bytes at a time because that's * how many data registers are available in the SPI peripheral @@ -385,9 +386,8 @@ static void esp32c3_spi_read( target_mem_write32(target, ESP32_C3_SPI1_USER0, enabled_stages); /* On the final transfer, clear the chip select hold bit, otherwise set it */ - const uint32_t misc_reg = target_mem_read32(target, ESP32_C3_SPI1_MISC); if (length - offset == amount) - target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg & ~ESP32_C3_SPI_MISC_CS_HOLD); + target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg); else target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg | ESP32_C3_SPI_MISC_CS_HOLD); From 971bfaaac142f179307356b12e7a43d4662d76c4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 6 Apr 2023 23:59:28 +0100 Subject: [PATCH 107/130] esp32c3: Cleaned up and improved the data handling in esp32c3_spi_read() and esp32c3_spi_write() --- src/target/esp32c3.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index ff7cde2ff9c..f10e2abea1a 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -395,11 +395,7 @@ static void esp32c3_spi_read( esp32c3_spi_wait_complete(target); /* Extract and unpack the received data */ - for (size_t idx = 0; idx < amount; idx += 4U) { - const uint32_t value = target_mem_read32(target, ESP32_C3_SPI1_DATA + idx); - const size_t bytes = MIN(4U, amount - idx); - memcpy(data + offset + idx, &value, bytes); - } + target_mem_read(target, data + offset, ESP32_C3_SPI1_DATA, amount); } } @@ -432,12 +428,7 @@ static void esp32c3_spi_write(target_s *const target, const uint32_t command, co target_mem_write32(target, ESP32_C3_SPI1_MISC, misc_reg | ESP32_C3_SPI_MISC_CS_HOLD); /* Pack and stage the data to transmit */ - for (size_t idx = 0; idx < amount; idx += 4U) { - const size_t bytes = MIN(4U, amount - idx); - uint32_t value = 0; - memcpy(&value, data + offset + idx, bytes); - target_mem_write32(target, ESP32_C3_SPI1_DATA + idx, value); - } + target_mem_write(target, ESP32_C3_SPI1_DATA, data + offset, amount); /* Run the transaction */ esp32c3_spi_wait_complete(target); From 82af43f26b3f90bc90dc3830518e1c862cf82f31 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 7 Apr 2023 00:00:09 +0100 Subject: [PATCH 108/130] esp32c3: Hacky patch to make Flash write almost work correctly at the loss of a huge amount of speed (25% the speed) --- src/target/esp32c3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index f10e2abea1a..3dfc5278fda 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -520,15 +520,15 @@ static bool esp32c3_spi_flash_write( target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t length) { target_s *const target = flash->t; - const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; + // const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; const target_addr_t begin = dest - flash->start; const char *const buffer = src; - for (size_t offset = 0; offset < length; offset += spi_flash->page_size) { + for (size_t offset = 0; offset < length; offset += 64U) { esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; - const size_t amount = MIN(length - offset, spi_flash->page_size); + const size_t amount = MIN(length - offset, 64U); esp32c3_spi_write(target, SPI_FLASH_CMD_PAGE_PROGRAM, begin + offset, buffer + offset, amount); while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) continue; From fe722de64ec6c0812a8d2ecbbf0a60d14a658c60 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 15 Apr 2023 06:49:54 +0100 Subject: [PATCH 109/130] esp32c3: Switched errors over to using `DEBUG_ERROR` instead of abusing `DEBUG_WARN` --- src/target/esp32c3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 3dfc5278fda..822b953ec2b 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -191,7 +191,7 @@ static void esp32c3_add_flash(target_s *const target) { esp32c3_spi_flash_s *const spi_flash = calloc(1, sizeof(*spi_flash)); if (!spi_flash) { /* calloc failed: heap exhaustion */ - DEBUG_WARN("calloc: failed in %s\n", __func__); + DEBUG_ERROR("calloc: failed in %s\n", __func__); return; } @@ -231,7 +231,7 @@ bool esp32c3_probe(target_s *const target) esp32c3_priv_s *const priv = calloc(1, sizeof(esp32c3_priv_s)); if (!priv) { /* calloc failed: heap exhaustion */ - DEBUG_WARN("calloc: failed in %s\n", __func__); + DEBUG_ERROR("calloc: failed in %s\n", __func__); return false; } target->target_storage = priv; @@ -522,7 +522,7 @@ static bool esp32c3_spi_flash_write( target_s *const target = flash->t; // const esp32c3_spi_flash_s *const spi_flash = (esp32c3_spi_flash_s *)flash; const target_addr_t begin = dest - flash->start; - const char *const buffer = src; + const char *const buffer = (const char *)src; for (size_t offset = 0; offset < length; offset += 64U) { esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) From 93b7ab60ff1aa49679767d6f23207ecf1b1a40bd Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 10 Aug 2023 16:54:22 +0100 Subject: [PATCH 110/130] target/esp32c3: fix sfdp function signature --- src/target/esp32c3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 822b953ec2b..1f23e062490 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -182,9 +182,9 @@ static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, s static bool esp32c3_spi_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); static void esp32c3_spi_read_sfdp( - target_s *const target, const uint32_t address, void *const buffer, const size_t length) + target_s *const target, const uint16_t command, const uint32_t address, void *const buffer, const size_t length) { - esp32c3_spi_read(target, SPI_FLASH_CMD_READ_SFDP, address, buffer, length); + esp32c3_spi_read(target, command, address, buffer, length); } static void esp32c3_add_flash(target_s *const target) From c0d6105422e0194d3935c270901e9bc3d38a65c6 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 10 Aug 2023 17:25:34 +0100 Subject: [PATCH 111/130] hosted: barebones wch-link implementation --- src/platforms/hosted/Makefile.inc | 1 + src/platforms/hosted/bmp_libusb.c | 3 +- src/platforms/hosted/platform.c | 9 + src/platforms/hosted/platform.h | 8 +- src/platforms/hosted/wchlink.c | 240 ++++++++++++++++++ src/platforms/hosted/wchlink.h | 40 +++ src/platforms/hosted/wchlink_protocol.h | 321 ++++++++++++++++++++++++ 7 files changed, 620 insertions(+), 2 deletions(-) create mode 100644 src/platforms/hosted/wchlink.c create mode 100644 src/platforms/hosted/wchlink.h create mode 100644 src/platforms/hosted/wchlink_protocol.h diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index b02cb721b18..bd31c1010e0 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -126,6 +126,7 @@ ifneq ($(HOSTED_BMP_ONLY), 1) SRC += bmp_libusb.c stlinkv2.c stlinkv2_jtag.c stlinkv2_swd.c SRC += ftdi_bmp.c ftdi_jtag.c ftdi_swd.c SRC += jlink.c jlink_jtag.c jlink_swd.c + SRC += wchlink.c else SRC += bmp_serial.c endif diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index 6c04a1f3924..2c6d3ddabb7 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -74,6 +74,7 @@ static const debugger_device_s debugger_devices[] = { {VENDOR_ID_STLINK, PRODUCT_ID_STLINKV3, PROBE_TYPE_STLINK_V2, NULL, "ST-Link v3"}, {VENDOR_ID_STLINK, PRODUCT_ID_STLINKV3E, PROBE_TYPE_STLINK_V2, NULL, "ST-Link v3E"}, {VENDOR_ID_SEGGER, PRODUCT_ID_ANY, PROBE_TYPE_JLINK, NULL, "Segger JLink"}, + {VENDOR_ID_WCH, PRODUCT_ID_WCHLINK_RV, PROBE_TYPE_WCHLINK, NULL, "WCH-Link"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT2232, PROBE_TYPE_FTDI, NULL, "FTDI FT2232"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT4232, PROBE_TYPE_FTDI, NULL, "FTDI FT4232"}, {VENDOR_ID_FTDI, PRODUCT_ID_FTDI_FT232, PROBE_TYPE_FTDI, NULL, "FTDI FT232"}, @@ -99,7 +100,7 @@ const debugger_device_s *get_debugger_device_from_vid_pid(const uint16_t probe_v void bmp_ident(bmda_probe_s *info) { DEBUG_INFO("Black Magic Debug App " FIRMWARE_VERSION "\n for Black Magic Probe, ST-Link v2 and v3, CMSIS-DAP, " - "J-Link and FTDI (MPSSE)\n"); + "J-Link, FTDI (MPSSE) and WCH-Link\n"); if (info && info->vid && info->pid) { DEBUG_INFO("Using %04x:%04x %s %s\n %s %s\n", info->vid, info->pid, (info->serial[0]) ? info->serial : NO_SERIAL_NUMBER, info->manufacturer, info->product, info->version); diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index fb82206d485..c97fcc38ca8 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -50,6 +50,7 @@ #include "stlinkv2.h" #include "ftdi_bmp.h" #include "jlink.h" +#include "wchlink.h" #include "cmsis_dap.h" #endif @@ -148,6 +149,11 @@ void platform_init(int argc, char **argv) if (!jlink_init()) exit(1); break; + + case PROBE_TYPE_WCHLINK: + if (!wchlink_init()) + exit(-1); + break; #endif default: @@ -334,6 +340,9 @@ char *bmda_adaptor_ident(void) case PROBE_TYPE_JLINK: return "J-Link"; + case PROBE_TYPE_WCHLINK: + return "WCH-Link"; + default: return NULL; } diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index 968fad1ad94..2f4ff47e81d 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -77,13 +77,19 @@ void platform_buffer_flush(void); #define VENDOR_ID_ORBCODE 0x1209U #define PRODUCT_ID_ORBTRACE 0x3443U +#define VENDOR_ID_WCH 0x1a86U +#define PRODUCT_ID_WCHLINK_RV 0x8010U /* WCH-Link and WCH-LinkE in mode RV */ +#define PRODUCT_ID_WCHLINK_DAP 0x8011U /* WCH-Link in mode DAP */ +#define PRODUCT_ID_WCHLINKE_DAP 0x8012U /* WCH-LinkE in mode DAP */ + typedef enum probe_type { PROBE_TYPE_NONE = 0, PROBE_TYPE_BMP, PROBE_TYPE_STLINK_V2, PROBE_TYPE_FTDI, PROBE_TYPE_CMSIS_DAP, - PROBE_TYPE_JLINK + PROBE_TYPE_JLINK, + PROBE_TYPE_WCHLINK, } probe_type_e; void gdb_ident(char *p, int count); diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c new file mode 100644 index 00000000000..e9e5c0db1a1 --- /dev/null +++ b/src/platforms/hosted/wchlink.c @@ -0,0 +1,240 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "wchlink.h" +#include "wchlink_protocol.h" + +typedef struct wchlink { + struct wchlink_fw_version { + uint8_t major; + uint8_t minor; + } fw_version; /* Firmware version */ + + uint8_t hw_type; /* Hardware type */ +} wchlink_s; + +static wchlink_s wchlink; + +/* WCH-Link USB protocol functions */ + +static char *wchlink_command_error(const uint8_t command, const uint8_t subcommand, const uint8_t error) +{ + /* Only this error is formaly known, so we hack it's identification a bit for now */ + return command == WCH_CMD_CONTROL && subcommand == WCH_CONTROL_SUBCMD_ATTACH && error == WCH_ERR_ATTACH ? + "Failed to attach to target" : + "Unknown"; +} + +/* + * Send a command to the WCH-Link. + * + * ┌─────────────┬────────┬─────────┬──────────────┬──────────────────────────────┐ + * │ Byte │ 0 │ 1 │ 2 │ 3:End │ + * ├─────────────┼────────┼─────────┼──────────────┼──────────────────────────────┤ + * │ Description │ Header │ Command │ Payload Size │ Payload (Sub-command + Data) │ + * └─────────────┴────────┴─────────┴──────────────┴──────────────────────────────┘ + * See wchlink_protocol.h for more information. + * + * Returns true for success, false for failure. + */ +bool wchlink_command_send_recv(const uint8_t command, const uint8_t subcommand, const void *const payload, + const size_t payload_length, void *const response, const size_t response_length) +{ + /* + * Total request size is packet header + command + payload size + payload (for which we always add the subcommand byte) + * Total response size is packet header + command/error + payload size + payload + */ + const size_t request_size = 4U + payload_length; + const size_t response_size = 3U + response_length; + + /* Stack buffer for the transfer, this is much larger than we need */ + uint8_t buffer[256U] = {0}; + if (request_size > sizeof(buffer) || response_size > sizeof(buffer)) + return false; + + /* Prepare the command packet */ + buffer[WCH_CMD_PACKET_HEADER_OFFSET] = WCH_CMD_PACKET_HEADER_OUT; /* Command packet header */ + buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] = command; /* Command */ + buffer[WCH_CMD_PACKET_SIZE_OFFSET] = payload_length + 1U; /* Payload size */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET] = subcommand; /* Subcommand as the first byte of the payload */ + + /* Payload if any */ + if (payload_length && payload) + memcpy(buffer + WCH_CMD_PACKET_PAYLOAD_OFFSET + 1U, payload, payload_length); + + /* Send the command and receive the response */ + if (bmda_usb_transfer(bmda_probe_info.usb_link, buffer, request_size, buffer, response_size, WCH_USB_TIMEOUT) < 0) + return false; + + /* Check the response */ + if (buffer[WCH_CMD_PACKET_HEADER_OFFSET] != WCH_CMD_PACKET_HEADER_IN) { + DEBUG_ERROR("wchlink protocol error: malformed response\n"); + return false; + } + if (buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] != command) { + DEBUG_ERROR("wchlink protocol error: 0x%02x - %s\n", buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET], + wchlink_command_error(command, subcommand, buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET])); + return false; + } + if (buffer[WCH_CMD_PACKET_SIZE_OFFSET] != response_length) { + DEBUG_ERROR("wchlink protocol error: response payload size mismatch\n"); + return false; + } + + /* Copy the response payload if requested */ + if (response_length && response) + memcpy(response, buffer + WCH_CMD_PACKET_PAYLOAD_OFFSET, response_length); + + return true; +} + +/* + * Try to claim the debugging interface of a WCH-Link. + * On success this copies the command endpoint addresses identified into the + * usb_link_s sub-structure of bmda_probe_s (bmda_probe_info.usb_link) for later use. + * Returns true for success, false for failure. + */ +static bool wchlink_claim_interface(void) +{ + libusb_config_descriptor_s *config; + const int result = libusb_get_active_config_descriptor(bmda_probe_info.libusb_dev, &config); + if (result != LIBUSB_SUCCESS) { + DEBUG_ERROR("Failed to get configuration descriptor: %s\n", libusb_error_name(result)); + return false; + } + const libusb_interface_descriptor_s *descriptor = NULL; + for (size_t idx = 0; idx < config->bNumInterfaces; ++idx) { + const libusb_interface_s *const interface = &config->interface[idx]; + const libusb_interface_descriptor_s *const interface_desc = &interface->altsetting[0]; + if (interface_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + interface_desc->bInterfaceSubClass == WCH_USB_INTERFACE_SUBCLASS && interface_desc->bNumEndpoints > 1U) { + const int result = libusb_claim_interface(bmda_probe_info.usb_link->device_handle, (int)idx); + if (result) { + DEBUG_ERROR("Can not claim handle: %s\n", libusb_error_name(result)); + break; + } + bmda_probe_info.usb_link->interface = idx; + descriptor = interface_desc; + } + } + if (!descriptor) { + DEBUG_ERROR("No suitable interface found\n"); + libusb_free_config_descriptor(config); + return false; + } + for (size_t i = 0; i < descriptor->bNumEndpoints; i++) { + const libusb_endpoint_descriptor_s *endpoint = &descriptor->endpoint[i]; + if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK) == WCH_USB_MODE_RV_CMD_EPT_ADDR) { + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + bmda_probe_info.usb_link->ep_rx = endpoint->bEndpointAddress; + else + bmda_probe_info.usb_link->ep_tx = endpoint->bEndpointAddress; + } + } + libusb_free_config_descriptor(config); + return true; +} + +/* WCH-Link command functions */ + +static char *wchlink_hw_type_to_string(const uint8_t hardware_id) +{ + switch (hardware_id) { + case WCH_HARDWARE_TYPE_WCHLINK: + return "WCH-Link (CH549)"; + case WCH_HARDWARE_TYPE_WCHLINKE2: + case WCH_HARDWARE_TYPE_WCHLINKE: + return "WCH-LinkE (CH32V305)"; + case WCH_HARDWARE_TYPE_WCHLINKS: + return "WCH-LinkS (CH32V203)"; + case WCH_HARDWARE_TYPE_WCHLINKB: + return "WCH-LinkB"; + case WCH_HARDWARE_TYPE_WCHLINKW: + return "WCH-LinkW (CH32V208)"; + default: + return "Unknown"; + } +} + +static bool wchlink_get_version(void) +{ + uint8_t response[4U]; + if (!wchlink_command_send_recv( + WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_GET_PROBE_INFO, NULL, 0, response, sizeof(response))) + return false; + + wchlink.fw_version.major = response[WCH_VERSION_MAJOR_OFFSET]; + wchlink.fw_version.minor = response[WCH_VERSION_MINOR_OFFSET]; + DEBUG_INFO("Firmware version: v%" PRIu32 ".%" PRIu32 "\n", wchlink.fw_version.major, wchlink.fw_version.minor); + + const uint8_t hardware_type = response[WCH_HARDWARE_TYPE_OFFSET]; + DEBUG_INFO("Hardware type: %s\n", wchlink_hw_type_to_string(hardware_type)); + + /* Build version string onto info struct for version command */ + snprintf(bmda_probe_info.version, sizeof(bmda_probe_info.version), "%s v%" PRIu32 ".%" PRIu32, + wchlink_hw_type_to_string(hardware_type), wchlink.fw_version.major, wchlink.fw_version.minor); + + return true; +} + +bool wchlink_init(void) +{ + usb_link_s *link = calloc(1U, sizeof(usb_link_s)); + if (!link) + return false; + bmda_probe_info.usb_link = link; + link->context = bmda_probe_info.libusb_ctx; + const int result = libusb_open(bmda_probe_info.libusb_dev, &link->device_handle); + if (result != LIBUSB_SUCCESS) { + DEBUG_ERROR("libusb_open() failed (%d): %s\n", result, libusb_error_name(result)); + return false; + } + if (!wchlink_claim_interface()) { + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + if (!link->ep_tx || !link->ep_rx) { + DEBUG_ERROR("Device setup failed\n"); + libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface); + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + if (!wchlink_get_version()) { + DEBUG_ERROR("Failed to read WCH-Link information\n"); + libusb_release_interface(bmda_probe_info.usb_link->device_handle, bmda_probe_info.usb_link->interface); + libusb_close(bmda_probe_info.usb_link->device_handle); + return false; + } + return true; +} diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h new file mode 100644 index 00000000000..c653801d2ef --- /dev/null +++ b/src/platforms/hosted/wchlink.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PLATFORMS_HOSTED_WCHLINK_H +#define PLATFORMS_HOSTED_WCHLINK_H + +#include "bmp_hosted.h" + +bool wchlink_init(void); + +#endif /* PLATFORMS_HOSTED_WCHLINK_H */ diff --git a/src/platforms/hosted/wchlink_protocol.h b/src/platforms/hosted/wchlink_protocol.h new file mode 100644 index 00000000000..0fa46e27736 --- /dev/null +++ b/src/platforms/hosted/wchlink_protocol.h @@ -0,0 +1,321 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H +#define PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H + +#include +#include +#include + +/* + * This file contains the definitions for the WCH-Link USB Protocol, no public documentation is available + * so these definitions are the result of reverse engineering the protocol and trial and error. + * + * !!! THIS IS LARGELY INCOMPLETE AND UNTESTED, DO NOT TAKE THIS AS A DEFINITIVE SOURCE OF INFORMATION !!! + * + * The WCH-Link has two modes of operation, DAPLink and RV (i.e. RISC-V). + * This refers to the RV mode of operation only, changing the mode of operation is also out of scope. + * This was based on probes with firmware v2.5 and v2.8, differences are expected on untested/future versions. + * + * Overview + * + * WCH-Link uses USB Bulk Transfers to communicate with the host + * + * The WCH-Link exposes 4 endpoints through a Vendor interface: + * 0x82: EP 2 IN (Raw data) + * 0x02: EP 2 OUT (Raw data) + * 0x81: EP 1 IN (Command packets) + * 0x01: EP 1 OUT (Command packets) + * EP 1 IN/OUT is used for most of the communication, EP 2 IN/OUT is used for some flash related operations. + * + * Command packet format: + * ┌─────────────┬────────┬─────────┬──────────────┬──────────────────────────────┐ + * │ Byte │ 0 │ 1 │ 2 │ 3:End │ + * ├─────────────┼────────┼─────────┼──────────────┼──────────────────────────────┤ + * │ Description │ Header │ Command │ Payload Size │ Payload (Sub-command + Data) │ + * └─────────────┴────────┴─────────┴──────────────┴──────────────────────────────┘ + * + * Header: + * - 0x81 for host command packets + * - 0x82 for device response packets + * + * Command: command, used to identify how the payload will be interpreted. + * Payload Size: length in bytes of the remaining command data. + * Payload: command data, interpreted according to the command, most command have a subcommand as the 1st byte. + * + * Responses are sent in the same format, with the header set to 0x82 and the same command. + * In case of an error, the response will contain the error value instead of the requested command in the command field. + */ + +/* USB protocol */ +#define WCH_USB_MODE_RV_CMD_EPT_ADDR 0x1U +#define WCH_USB_MODE_RV_RAW_EPT_ADDR 0x2U +#define WCH_USB_MODE_DAP_OUT_EPT_ADDR 0x2U +#define WCH_USB_MODE_DAP_IN_EPT_ADDR 0x3U + +#define WCH_USB_TIMEOUT 5000U + +#define WCH_USB_INTERFACE_SUBCLASS 0x80U + +/* Command packet */ +#define WCH_CMD_PACKET_HEADER_OFFSET 0U +#define WCH_CMD_PACKET_HEADER_OUT 0x81U +#define WCH_CMD_PACKET_HEADER_IN 0x82U +#define WCH_CMD_PACKET_CMD_ERROR_OFFSET 1U +#define WCH_CMD_PACKET_SIZE_OFFSET 2U +#define WCH_CMD_PACKET_PAYLOAD_OFFSET 3U + +/* Error */ +#define WCH_ERR_ATTACH 0x55U /* Failed to attach to target */ + +/* RISC-V targets AKA "riscvchip" */ +#define WCH_RISCVCHIP_CH32V103 0x01U /* CH32V103 RISC-V3A series */ +#define WCH_RISCVCHIP_CH57X 0x02U /* CH571/CH573 RISC-V3A BLE 4.2 series */ +#define WCH_RISCVCHIP_CH56X 0x03U /* CH565/CH569 RISC-V3A series */ +#define WCH_RISCVCHIP_CH32V20X 0x05U /* CH32V20X RISC-V4B/V4C series */ +#define WCH_RISCVCHIP_CH32V30X 0x06U /* CH32V30X RISC-V4C/V4F series */ +#define WCH_RISCVCHIP_CH58X 0x07U /* CH581/CH582/CH583 RISC-V4A BLE 5.3 series */ +#define WCH_RISCVCHIP_CH32V003 0x09U /* CH32V003 RISC-V2A series */ +#define WCH_RISCVCHIP_CH59X 0x0bU /* CH59x RISC-V4C BLE 5.4 series */ +#define WCH_RISCVCHIP_CH32X035 0x0dU /* CH32X035 RISC-V4C series */ + +/* Commands */ +#define WCH_CMD_ADDR_N_SIZE 0x01U /* Set address and size command */ +#define WCH_CMD_FLASH 0x02U /* Flash command */ +#define WCH_CMD_READ_MEM 0x03U /* Memory read command */ +#define WCH_CMD_PROTECT 0x06U /* Flash potection command */ +#define WCH_CMD_DMI 0x08U /* DMI transfer command */ +#define WCH_CMD_RESET 0x0bU /* Reset command */ +#define WCH_CMD_PROBE_CONTROL 0x0cU /* Probe control command */ +#define WCH_CMD_CONTROL 0x0dU /* Control command */ +#define WCH_CMD_RV_DIS_DBG 0x0eU /* RV disable debug command */ +#define WCH_CMD_VERIFY 0x0fU /* Verify command */ +#define WCH_CMD_UID 0x11U /* Chip UID command */ +#define WCH_CMD_MODDE_SWITCH 0xffU /* Switch probe mode command */ + +/* + * Set address and size command - WCH_CMD_ADDR_N_SIZE + * + * This command does not have a sub-command byte, + * the payload is the address followed by the size + * + * ┌──────┬──────┐ + * │ 0:4 │ 5:8 │ + * ├──────┼──────┤ + * │ ADDR │ SIZE │ + * └──────┴──────┘ + */ + +/* Flash command - WCH_CMD_FLASH */ +#define WCH_FLASH_SUBCMD_CHIPERASE 0x01U /* Chip erase */ +#define WCH_FLASH_SUBCMD_BEGIN_WRITE_FLASH 0x02U /* Begin write flash - ?? */ +#define WCH_FLASH_SUBCMD_EXEC_RAM 0x03U /* Execute ram - ?? */ +#define WCH_FLASH_SUBCMD_BEGIN_WRITE_MEM 0x05U /* Begin transfer - ?? */ +#define WCH_FLASH_SUBCMD_PREPARE 0x06U /* ?? */ +#define WCH_FLASH_SUBCMD_EXEC_MEM 0x07U /* ?? */ +#define WCH_FLASH_SUBCMD_TERMINATE 0x08U /* ?? */ +#define WCH_FLASH_SUBCMD_READY_WRITE 0x09U /* End transfer - ?? */ +#define WCH_FLASH_SUBCMD_VERIFY2 0x0aU /* Verify - ?? */ +#define WCH_FLASH_SUBCMD_RV_VERIFY 0x0bU /* Verify - ?? */ +#define WCH_FLASH_SUBCMD_BEGIN_READ_MEM 0x0cU /* ?? */ + +/* + * Memory read commands - WCH_CMD_READ_MEM + * + * This command does not have a sub-command byte, + * the payload is the address to read from followed by the number of bytes to read + * + * ┌──────┬────────┐ + * │ 0:4 │ 5:8 │ + * ├──────┼────────┤ + * │ ADDR │ LENGTH │ + * └──────┴────────┘ + */ + +/* + * Flash potection command - WCH_CMD_PROTECT + * + * Not supported on riscvchip: + * - 0x01: CH32V103 + * - 0x09: CH32V003 + * - 0x05: CH32V20X + * - 0x06: CH32V30X + * - 0x0d: CH32X035 + */ +#define WCH_PROTECT_SUBCMD_CHECK 0x01U /* Check if flash is read-protected */ +#define WCH_PROTECT_SUBCMD_FLASH_UNPROTECT 0x02U /* Set flash read unprotected */ +#define WCH_PROTECT_SUBCMD_FLASH_PROTECT 0x03U /* Set flash read protected */ +#define WCH_PROTECT_SUBCMD_CHECK_V2 0x04U /* Check if flash is read-protected - Firmware >= v2.9 */ +/* PROTECT_V2 and UNPROTECT_V2 require `0xbf ff ff ff ff ff ff` as payload */ +#define WCH_PROTECT_SUBCMD_FLASH_UNPROTECT_V2 0xf2U /* Set flash read unprotected - Firmware >= v2.9 */ +#define WCH_PROTECT_SUBCMD_FLASH_PROTECT_V2 0xf3U /* Set flash read protected - Firmware >= v2.9 */ + +#define WCH_PROTECTED 0x01U /* Protected, memory read returns random data */ +#define WCH_UNPROTECTED 0x02U /* Unprotected */ +#define WCH_PROTECTED_V2 0x01U /* Protected, memory read returns random data */ +#define WCH_UNPROTECTED_V2 0x00U /* Unprotected */ + +/* + * DMI transfer command - WCH_CMD_DMI + * + * This command does not have a sub-command byte + * + * ┌────────────────────────────┐ + * │ Payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Operation │ + * └─────────┴──────┴───────────┘ + * ┌────────────────────────────┐ + * │ Response payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Status │ + * └─────────┴──────┴───────────┘ + * + * Operation and Status correspond to the same values + * found in the JTAG implementation of RISC-V DMI: + * + * Operation: + * - 0x00: no-op + * - 0x01: read + * - 0x02: write + * + * Status: + * - 0x00: success + * - 0x01: error + * - 0x03: busy + */ +#define WCH_DMI_ADDR_OFFSET 0U +#define WCH_DMI_DATA_OFFSET 1U +#define WCH_DMI_OP_STATUS_OFFSET 5U + +/* Reset command - WCH_CMD_RESET */ +#define WCH_RESET_SUBCMD_RELEASE 0x01U /* Release reset (after 300ms delay) */ +/* + * There are two _SUBCMD_ASSERT sub-commands, used depending on the riscvchip: + * + * ASSERT2 used for riscvchip: + * - 0x02: CH57X + * - 0x07: CH58X + * - 0x0b: CH59X + */ +#define WCH_RESET_SUBCMD_ASSERT 0x03U /* Reset */ +#define WCH_RESET_SUBCMD_ASSERT2 0x02U /* Reset */ + +/* + * Probe constrol command - WCH_CMD_PROBE_CONTROL + * + * This command does not have a sub-command byte, + * the payload is the the riscvchip number followed by the speed + * + * ┌───────────┬───────┐ + * │ 0 │ 1 │ + * ├───────────┼───────┤ + * │ RISCVCHIP │ Speed │ + * └───────────┴───────┘ + * + * Response is one byte, 0x01 meaning success + */ +#define WCH_SPEED_LOW 0x03U +#define WCH_SPEED_MEDIUM 0x02U +#define WCH_SPEED_HIGH 0x01U +#define WCH_SPEED_VERYHIGH 0x00U + +#define WCH_PROBE_CONTROL_OK 0x01U /* Success response */ + +/* Control command - WCH_CMD_CONTROL */ +#define WCH_CONTROL_SUBCMD_GET_PROBE_INFO 0x01U /* Firmware version and hardware type */ +#define WCH_CONTROL_SUBCMD_ATTACH 0x02U /* Attach to target */ +/* + * On some riscvchip targets, a _SUBCMD_UNKNOWN is issued after attach + * + * Issued on riscvchip: + * - 0x01: CH32V103 + * - 0x05: CH32V20X + * - 0x06: CH32V30X + * - 0x09: CH32V003 + */ +#define WCH_CONTROL_SUBCMD_UNKNOWN 0x03U /* ?? - issued after attach */ +/* + * _GET_MEMORY_INFO + * + * Supported on riscvchip: + * - 0x05: CH32V20X + * - 0x06: CH32V30X + */ +#define WCH_CONTROL_SUBCMD_GET_MEMORY_INFO 0x04U /* RAM size, flash size and addr */ +#define WCH_CONTROL_SUBCMD_CLOSE 0xffU /* Terminate connection (unsure what this entails) */ + +/* Probe info subcommand - WCH_SYS_SUBCMD_GET_PROBE_INFO */ +#define WCH_VERSION_MAJOR_OFFSET 0U /* 8 bit */ +#define WCH_VERSION_MINOR_OFFSET 1U /* 8 bit */ + +#define WCH_HARDWARE_TYPE_OFFSET 2U +#define WCH_HARDWARE_TYPE_WCHLINK 1U /* WCH-Link (CH549) *does not support SDIO (single wire debug) */ +#define WCH_HARDWARE_TYPE_WCHLINKE 2U /* WCH-LinkE (CH32V305) */ +#define WCH_HARDWARE_TYPE_WCHLINKS 3U /* WCH-LinkS (CH32V203) */ +#define WCH_HARDWARE_TYPE_WCHLINKB 4U /* WCH-LinkB */ +#define WCH_HARDWARE_TYPE_WCHLINKW 5U /* WCH-LinkW (CH32V208) *wireless */ +#define WCH_HARDWARE_TYPE_WCHLINKE2 18U /* WCH-LinkE (CH32V305) - ?? */ + +/* Attach to target subcommand - WCH_CONTROL_SUBCMD_ATTACH */ +#define WCH_RISCVCHIP_OFFSET 0U /* 8 bit */ +#define WCH_IDCODDE_OFFSET 1U /* 32 bit */ + +/* + * RV disable debug command - WCH_CMD_RV_DIS_DBG + * + * Supported on riscvchip: + * - 0x02: CH57X + * - 0x03: CH56X + * - 0x07: CH58X + * - 0x0b: CH59X + */ +#define WCH_RV_DIS_DBG_SUBCMD_DISABLE 0x01U /* Disable debug */ + +/* + * Verify command - WCH_CMD_VERIFY + * FIXME: TBD + */ + +/* + * Chip UID command - WCH_CMD_UID + * + * The reply does not use the standard format. + * + * Raw response: ffff00 20 aeb4abcd 16c6bc45 e339e339e339e339 + * Corresponding UID: 0xcdabb4ae45bcc616 + * Unknown value: e339e339e339e339 -> inital value for erased flash + */ +#define WCH_UID_SUBCMD_GET 0x09U /* Get chip UID */ +#define WCH_UID_SUBCMD_GET_V2 0x06U /* Get chip UID - Firmware >= v2.9 */ + +/* Switch probe mode command - WCH_CMD_MODDE_SWITCH */ +#define WCH_MODDE_SWITCH_SUBCMD_SUPPORTED 0x41U /* Check if mode switching is supported - ?? */ +#define WCH_MODDE_SWITCH_SUBCMD_DAP_TO_RV 0x52U /* Switch to RV mode - ?? */ + +bool wchlink_command_send_recv(uint8_t command, uint8_t subcommand, const void *payload, size_t payload_length, + void *response, size_t response_length); + +#endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ From 192dfe8be48b86d3c7d41d6cea0864b3fce6c9ef Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 16:29:13 +0100 Subject: [PATCH 112/130] buffer_utils: Implemented a big endian 32-bit write --- src/include/buffer_utils.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index 085d75af9ad..0bad5e4904e 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -51,6 +51,14 @@ static inline void write_le4(uint8_t *const buffer, const size_t offset, const u buffer[offset + 3U] = (value >> 24U) & 0xffU; } +static inline void write_be4(uint8_t *const buffer, const size_t offset, const uint32_t value) +{ + buffer[offset + 0U] = (value >> 24U) & 0xffU; + buffer[offset + 1U] = (value >> 16U) & 0xffU; + buffer[offset + 2U] = (value >> 8U) & 0xffU; + buffer[offset + 3U] = value & 0xffU; +} + static inline uint16_t read_le2(const uint8_t *const buffer, const size_t offset) { return buffer[offset + 0U] | ((uint16_t)buffer[offset + 1U] << 8U); From 6ea9e73e0dc81b55ba2264f64a3ebdda4128816a Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 16:54:44 +0100 Subject: [PATCH 113/130] hosted/wchlink: implement DMI transfer --- src/platforms/hosted/wchlink.c | 67 +++++++++++++++++++++++++ src/platforms/hosted/wchlink_protocol.h | 1 + 2 files changed, 68 insertions(+) diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c index e9e5c0db1a1..0cc0871c250 100644 --- a/src/platforms/hosted/wchlink.c +++ b/src/platforms/hosted/wchlink.c @@ -34,6 +34,7 @@ #include "wchlink.h" #include "wchlink_protocol.h" +#include "buffer_utils.h" typedef struct wchlink { struct wchlink_fw_version { @@ -119,6 +120,72 @@ bool wchlink_command_send_recv(const uint8_t command, const uint8_t subcommand, return true; } +/* + * Do a DMI transfer. + * + * ┌────────────────────────────┐ + * │ Payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Operation │ + * └─────────┴──────┴───────────┘ + * ┌────────────────────────────┐ + * │ Response payload │ + * ├─────────┬──────┬───────────┤ + * │ 0 │ 1:4 │ 5 │ + * ├─────────┼──────┼───────────┤ + * │ Address │ Data │ Status │ + * └─────────┴──────┴───────────┘ + * See wchlink_protocol.h for more information. + * + * Returns true for success, false for failure. + */ +bool wchlink_transfer_dmi(const uint8_t operation, const uint32_t address, const uint32_t data_in, + uint32_t *const data_out, uint8_t *const status) +{ + /* Stack buffer for the transfer */ + uint8_t buffer[9U] = {0}; + + /* Prepare the command packet */ + buffer[WCH_CMD_PACKET_HEADER_OFFSET] = WCH_CMD_PACKET_HEADER_OUT; /* Command packet header */ + buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] = WCH_CMD_DMI; /* Command */ + buffer[WCH_CMD_PACKET_SIZE_OFFSET] = 6U; /* Payload size */ + + /* Payload */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_ADDR_OFFSET] = address & 0xffU; /* Address */ + write_be4(buffer, WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_DATA_OFFSET, data_in); /* Data */ + buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_OP_STATUS_OFFSET] = operation; /* Operation */ + + /* Send the command and receive the response */ + if (bmda_usb_transfer(bmda_probe_info.usb_link, buffer, sizeof(buffer), buffer, sizeof(buffer), WCH_USB_TIMEOUT) < + 0) + return false; + + /* Check the response */ + if (buffer[WCH_CMD_PACKET_HEADER_OFFSET] != WCH_CMD_PACKET_HEADER_IN) { + DEBUG_ERROR("wchlink protocol error: malformed response\n"); + return false; + } + if (buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET] != WCH_CMD_DMI) { + DEBUG_ERROR("wchlink protocol error: 0x%02x - %s\n", buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET], + wchlink_command_error(WCH_CMD_DMI, 0, buffer[WCH_CMD_PACKET_CMD_ERROR_OFFSET])); + return false; + } + if (buffer[WCH_CMD_PACKET_SIZE_OFFSET] != 6U) { + DEBUG_ERROR("wchlink protocol error: response payload size mismatch\n"); + return false; + } + + /* Copy over the result */ + if (data_out) + *data_out = read_be4(buffer, WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_DATA_OFFSET); + if (status) + *status = buffer[WCH_CMD_PACKET_PAYLOAD_OFFSET + WCH_DMI_OP_STATUS_OFFSET]; + + return true; +} + /* * Try to claim the debugging interface of a WCH-Link. * On success this copies the command endpoint addresses identified into the diff --git a/src/platforms/hosted/wchlink_protocol.h b/src/platforms/hosted/wchlink_protocol.h index 0fa46e27736..7c106dcc166 100644 --- a/src/platforms/hosted/wchlink_protocol.h +++ b/src/platforms/hosted/wchlink_protocol.h @@ -317,5 +317,6 @@ bool wchlink_command_send_recv(uint8_t command, uint8_t subcommand, const void *payload, size_t payload_length, void *response, size_t response_length); +bool wchlink_transfer_dmi(uint8_t operation, uint32_t address, uint32_t data_in, uint32_t *data_out, uint8_t *status); #endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ From e5aa9208239c3478086fa6fa3d29b3499f7fe7d2 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 16:49:08 +0100 Subject: [PATCH 114/130] riscv_debug: expose DMI operation and status defines --- src/target/riscv_debug.h | 7 +++++++ src/target/riscv_jtag_dtm.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index f5a9b1f42d1..1ca145ce71a 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -146,6 +146,13 @@ typedef struct riscv_hart { #define RV_STATUS_VERSION_MASK 0x0000000fU +#define RV_DMI_NOOP 0U +#define RV_DMI_READ 1U +#define RV_DMI_WRITE 2U +#define RV_DMI_SUCCESS 0U +#define RV_DMI_FAILURE 2U +#define RV_DMI_TOO_SOON 3U + #define RV_DM_DATA0 0x04U #define RV_DM_DATA1 0x05U #define RV_DM_DATA2 0x06U diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 144e907bb74..9c588139fce 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -51,13 +51,6 @@ #define RV_DTMCS_ADDRESS_MASK 0x000003f0U #define RV_DTMCS_ADDRESS_SHIFT 4U -#define RV_DMI_NOOP 0U -#define RV_DMI_READ 1U -#define RV_DMI_WRITE 2U -#define RV_DMI_SUCCESS 0U -#define RV_DMI_FAILURE 2U -#define RV_DMI_TOO_SOON 3U - static void riscv_jtag_dtm_init(riscv_dmi_s *dmi); static uint32_t riscv_shift_dtmcs(const riscv_dmi_s *dmi, uint32_t control); static bool riscv_jtag_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); From 5157ac5676e63a3fd696db65f19bd1a38643f024 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 31 Jul 2023 23:21:09 +0100 Subject: [PATCH 115/130] jep106: add BMD internal flag Used to mark codes that are not part of the JEP106 or any other standard but are used internally by BMD to identify targets that don't provide a usable identification codes --- src/target/jep106.h | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/target/jep106.h b/src/target/jep106.h index 8eb24da4b5d..a9907a4a45c 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -44,14 +44,27 @@ * * |15 |11 |7|6 0| * | | | | | | | | |0| | | | | | | | - * |\____/ \______/|\_____________/ - * | V V | V - * | Unused Cont | code - * | Code | - * \_ Legacy flag \_ Parity bit (always 0) + * | |\__/ \______/|\_____________/ + * | | V V | V + * | | Unused Cont | code + * | | Code | + * | | \_ Parity bit (always 0) + * | \_ BMD internal flag + * \_ Legacy flag */ #define ASCII_CODE_FLAG (1U << 15U) /* flag the code as legacy ASCII */ +/* + * BMD internal flag + * + * This is used to mark codes that are not part of the JEP106 or any other standard but are used + * internally by BMD to identify targets that don't provide a usable identification code + * + * These codes may be changed or removed at any time, and should not be relied upon outside of + * the context of BMD + */ +#define BMD_INTERNAL_FLAG (1U << 14U) /* flag the code as internal use in BMD */ + #define JEP106_MANUFACTURER_ARM 0x43bU /* ARM Ltd. */ #define JEP106_MANUFACTURER_FREESCALE 0x00eU /* Freescale */ #define JEP106_MANUFACTURER_NXP 0x015U /* NXP */ @@ -69,6 +82,7 @@ #define JEP106_MANUFACTURER_RENESAS 0x423U /* Renesas */ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx */ #define JEP106_MANUFACTURER_ESPRESSIF 0xc12U /* Espressif */ + /* * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, * so in the unlikely event we need to support chips by them, here be dragons. From 9882d33018a947cb5820f3a6a9366864ae995cc1 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 23 Feb 2023 15:27:55 +0000 Subject: [PATCH 116/130] jep106: add internal WCH non-jep106 code --- src/target/jep106.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/jep106.h b/src/target/jep106.h index a9907a4a45c..8c349308eb5 100644 --- a/src/target/jep106.h +++ b/src/target/jep106.h @@ -83,6 +83,10 @@ #define JEP106_MANUFACTURER_XILINX 0x309U /* Xilinx */ #define JEP106_MANUFACTURER_ESPRESSIF 0xc12U /* Espressif */ +/* BMD internal codes */ +/* WCH AKA Nanjing Qinheng Microelectronics */ +#define NOT_JEP106_MANUFACTURER_WCH (BMD_INTERNAL_FLAG | 0x01U) /* WCH (WinChipHead) */ + /* * This JEP code should belong to "Andes Technology Corporation", but is used on RISC-V by GigaDevice, * so in the unlikely event we need to support chips by them, here be dragons. From 3a4e1c2768d571410c922ad86bb9c74692c2de54 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 21:17:56 +0100 Subject: [PATCH 117/130] riscv_jtag_dtm: move RV_DMI_TOO_SOON handling one level up --- src/target/riscv_debug.c | 18 ++++++++++++++++-- src/target/riscv_jtag_dtm.c | 32 ++++++++++---------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 02376c2d96a..d699d5b0bd9 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -424,16 +424,30 @@ static void riscv_hart_free(void *const priv) static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - const bool result = dmi->read(dmi, address, value); + bool result = false; + do { + result = dmi->read(dmi, address, value); + } while (dmi->fault == RV_DMI_TOO_SOON); + if (result) DEBUG_TARGET("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); + else + DEBUG_WARN("%s: %08" PRIx32 " failed: %u\n", __func__, address, dmi->fault); return result; } static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { DEBUG_TARGET("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value); - return dmi->write(dmi, address, value); + + bool result = false; + do { + result = dmi->write(dmi, address, value); + } while (dmi->fault == RV_DMI_TOO_SOON); + + if (!result) + DEBUG_WARN("%s: %08" PRIx32 " failed: %u\n", __func__, address, dmi->fault); + return result; } bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value) diff --git a/src/target/riscv_jtag_dtm.c b/src/target/riscv_jtag_dtm.c index 9c588139fce..bcaab116886 100644 --- a/src/target/riscv_jtag_dtm.c +++ b/src/target/riscv_jtag_dtm.c @@ -179,33 +179,21 @@ static bool riscv_dmi_transfer(riscv_dmi_s *const dmi, const uint8_t operation, static bool riscv_jtag_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) { - bool result = true; - do { - /* Setup the location to read from */ - result = riscv_dmi_transfer(dmi, RV_DMI_READ, address, 0, NULL); - if (result) - /* If that worked, read back the value and check the operation status */ - result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, value); - } while (dmi->fault == RV_DMI_TOO_SOON); - - if (!result) - DEBUG_WARN("DMI read at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + /* Setup the location to read from */ + bool result = riscv_dmi_transfer(dmi, RV_DMI_READ, address, 0, NULL); + if (result) + /* If that worked, read back the value and check the operation status */ + result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, value); return result; } static bool riscv_jtag_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { - bool result = true; - do { - /* Write a value to the requested register */ - result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); - if (result) - /* If that worked, read back the operation status to ensure the write actually worked */ - result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, NULL); - } while (dmi->fault == RV_DMI_TOO_SOON); - - if (!result) - DEBUG_WARN("DMI write at 0x%08" PRIx32 " failed with status %u\n", address, dmi->fault); + /* Write a value to the requested register */ + bool result = riscv_dmi_transfer(dmi, RV_DMI_WRITE, address, value, NULL); + if (result) + /* If that worked, read back the operation status to ensure the write actually worked */ + result = riscv_dmi_transfer(dmi, RV_DMI_NOOP, 0, 0, NULL); return result; } From c769d531ff9e2e43c43336dff2295df68fcfb550 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 18:31:26 +0100 Subject: [PATCH 118/130] hosted/wchlink: implement RISC-V DTM handler --- src/platforms/hosted/Makefile.inc | 2 +- src/platforms/hosted/wchlink.h | 1 + src/platforms/hosted/wchlink_riscv_dtm.c | 90 ++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/platforms/hosted/wchlink_riscv_dtm.c diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index bd31c1010e0..bd612a9e7c4 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -126,7 +126,7 @@ ifneq ($(HOSTED_BMP_ONLY), 1) SRC += bmp_libusb.c stlinkv2.c stlinkv2_jtag.c stlinkv2_swd.c SRC += ftdi_bmp.c ftdi_jtag.c ftdi_swd.c SRC += jlink.c jlink_jtag.c jlink_swd.c - SRC += wchlink.c + SRC += wchlink.c wchlink_riscv_dtm.c else SRC += bmp_serial.c endif diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h index c653801d2ef..720d7f74760 100644 --- a/src/platforms/hosted/wchlink.h +++ b/src/platforms/hosted/wchlink.h @@ -36,5 +36,6 @@ #include "bmp_hosted.h" bool wchlink_init(void); +void wchlink_riscv_dtm_handler(void); #endif /* PLATFORMS_HOSTED_WCHLINK_H */ diff --git a/src/platforms/hosted/wchlink_riscv_dtm.c b/src/platforms/hosted/wchlink_riscv_dtm.c new file mode 100644 index 00000000000..35dfb30d458 --- /dev/null +++ b/src/platforms/hosted/wchlink_riscv_dtm.c @@ -0,0 +1,90 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jep106.h" +#include "riscv_debug.h" +#include "wchlink_protocol.h" + +static void wchlink_riscv_dtm_init(riscv_dmi_s *dmi); +static bool wchlink_riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value); +static bool wchlink_riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value); + +void wchlink_riscv_dtm_handler(void) +{ + riscv_dmi_s *dmi = calloc(1, sizeof(*dmi)); + if (!dmi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + wchlink_riscv_dtm_init(dmi); + /* If we failed to find any DMs or Harts, free the structure */ + if (!dmi->ref_count) + free(dmi); +} + +static void wchlink_riscv_dtm_init(riscv_dmi_s *const dmi) +{ + /* WCH-Link doesn't have any mechanism to identify the DTM manufacturer, so we'll just assume it's WCH */ + dmi->designer_code = NOT_JEP106_MANUFACTURER_WCH; + + dmi->version = RISCV_DEBUG_0_13; /* Assumption, unverified */ + + /* WCH-Link has a fixed address width of 8 bits, limited by the USB protocol (is RVSWD also fixed?) */ + dmi->address_width = 8U; + + dmi->read = wchlink_riscv_dmi_read; + dmi->write = wchlink_riscv_dmi_write; + + riscv_dmi_init(dmi); +} + +static bool wchlink_riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value) +{ + uint8_t status = 0; + const bool result = wchlink_transfer_dmi(RV_DMI_READ, address, 0, value, &status); + + /* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */ + dmi->fault = !result || status == 1U ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} + +static bool wchlink_riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) +{ + uint8_t status = 0; + const bool result = wchlink_transfer_dmi(RV_DMI_WRITE, address, value, NULL, &status); + + /* Translate error 1 into RV_DMI_FAILURE per the spec, also write RV_DMI_FAILURE if the transfer failed */ + dmi->fault = !result || status == 1U ? RV_DMI_FAILURE : status; + return dmi->fault == RV_DMI_SUCCESS; +} From 46c944d4aee07de04b7c522d9548d60a248fedc4 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 15:43:13 +0100 Subject: [PATCH 119/130] command: add rvswd scan routine command --- src/command.c | 45 +++++++++++++++++++++++++++++++++ src/include/target.h | 1 + src/platforms/hosted/platform.c | 10 ++++++++ 3 files changed, 56 insertions(+) diff --git a/src/command.c b/src/command.c index 7ef7a39cb56..a5242df6601 100644 --- a/src/command.c +++ b/src/command.c @@ -57,6 +57,7 @@ static bool cmd_help(target_s *t, int argc, const char **argv); static bool cmd_jtag_scan(target_s *target, int argc, const char **argv); static bool cmd_swd_scan(target_s *t, int argc, const char **argv); +static bool cmd_rvswd_scan(target_s *target, int argc, const char **argv); static bool cmd_auto_scan(target_s *t, int argc, const char **argv); static bool cmd_frequency(target_s *t, int argc, const char **argv); static bool cmd_targets(target_s *t, int argc, const char **argv); @@ -88,6 +89,7 @@ const command_s cmd_list[] = { {"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"}, {"swd_scan", cmd_swd_scan, "Scan SWD interface for devices: [TARGET_ID]"}, {"swdp_scan", cmd_swd_scan, "Deprecated: use swd_scan instead"}, + {"rvswd_scan", cmd_rvswd_scan, "Scan RVSWD for devices"}, {"auto_scan", cmd_auto_scan, "Automatically scan all chain types for devices"}, {"frequency", cmd_frequency, "set minimum high and low times: [FREQ]"}, {"targets", cmd_targets, "Display list of available targets"}, @@ -288,6 +290,49 @@ bool cmd_swd_scan(target_s *t, int argc, const char **argv) return true; } +bool cmd_rvswd_scan(target_s *target, int argc, const char **argv) +{ + (void)target; + (void)argc; + (void)argv; + + if (platform_target_voltage()) + gdb_outf("Target voltage: %s\n", platform_target_voltage()); + + if (connect_assert_nrst) + platform_nrst_set_val(true); /* will be deasserted after attach */ + + bool scan_result = false; + volatile exception_s e; + TRY_CATCH (e, EXCEPTION_ALL) { +#if PC_HOSTED == 1 + scan_result = bmda_rvswd_scan(); +#else + scan_result = false; +#endif + } + switch (e.type) { + case EXCEPTION_TIMEOUT: + gdb_outf("Timeout during scan. Is target stuck in WFI?\n"); + break; + case EXCEPTION_ERROR: + gdb_outf("Exception: %s\n", e.msg); + break; + } + + if (!scan_result) { + platform_target_clk_output_enable(false); + platform_nrst_set_val(false); + gdb_out("RVSWD scan failed!\n"); + return false; + } + + cmd_targets(NULL, 0, NULL); + platform_target_clk_output_enable(false); + morse(NULL, false); + return true; +} + bool cmd_auto_scan(target_s *t, int argc, const char **argv) { (void)t; diff --git a/src/include/target.h b/src/include/target.h index 33303c399c2..98f076722bf 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -37,6 +37,7 @@ typedef struct target_controller target_controller_s; #if PC_HOSTED == 1 bool bmda_swd_scan(uint32_t targetid); bool bmda_jtag_scan(void); +bool bmda_rvswd_scan(void); #endif bool adiv5_swd_scan(uint32_t targetid); bool jtag_scan(void); diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index c97fcc38ca8..a52feb1e46e 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -275,6 +275,16 @@ bool bmda_jtag_init(void) } } +bool bmda_rvswd_scan() +{ + bmda_probe_info.is_jtag = false; + + switch (bmda_probe_info.type) { + default: + return false; + } +} + void bmda_adiv5_dp_init(adiv5_debug_port_s *const dp) { switch (bmda_probe_info.type) { From baaef7850412e9f5954c70ce95d70b190d0efadd Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 18:32:17 +0100 Subject: [PATCH 120/130] hosted/wchlink: implement RVSWD scan --- src/platforms/hosted/Makefile.inc | 2 +- src/platforms/hosted/platform.c | 5 ++ src/platforms/hosted/wchlink.c | 73 +++++++++++++++++++++++++ src/platforms/hosted/wchlink.h | 1 + src/platforms/hosted/wchlink_protocol.h | 2 + src/platforms/hosted/wchlink_rvswd.c | 53 ++++++++++++++++++ 6 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/platforms/hosted/wchlink_rvswd.c diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index bd612a9e7c4..2e671226eae 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -126,7 +126,7 @@ ifneq ($(HOSTED_BMP_ONLY), 1) SRC += bmp_libusb.c stlinkv2.c stlinkv2_jtag.c stlinkv2_swd.c SRC += ftdi_bmp.c ftdi_jtag.c ftdi_swd.c SRC += jlink.c jlink_jtag.c jlink_swd.c - SRC += wchlink.c wchlink_riscv_dtm.c + SRC += wchlink.c wchlink_rvswd.c wchlink_riscv_dtm.c else SRC += bmp_serial.c endif diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index a52feb1e46e..9c6639992d7 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -280,6 +280,11 @@ bool bmda_rvswd_scan() bmda_probe_info.is_jtag = false; switch (bmda_probe_info.type) { +#if HOSTED_BMP_ONLY == 0 + case PROBE_TYPE_WCHLINK: + return wchlink_rvswd_scan(); +#endif + default: return false; } diff --git a/src/platforms/hosted/wchlink.c b/src/platforms/hosted/wchlink.c index 0cc0871c250..b8dea2b2692 100644 --- a/src/platforms/hosted/wchlink.c +++ b/src/platforms/hosted/wchlink.c @@ -43,6 +43,8 @@ typedef struct wchlink { } fw_version; /* Firmware version */ uint8_t hw_type; /* Hardware type */ + + uint8_t riscvchip; /* The attached RISC-V chip code */ } wchlink_s; static wchlink_s wchlink; @@ -254,6 +256,32 @@ static char *wchlink_hw_type_to_string(const uint8_t hardware_id) } } +static char *wchlink_riscvchip_to_string(const uint8_t hardware_id) +{ + switch (hardware_id) { + case WCH_RISCVCHIP_CH32V103: + return "CH32V103 RISC-V3A series"; + case WCH_RISCVCHIP_CH57X: + return "CH571/CH573 RISC-V3A BLE 4.2 series"; + case WCH_RISCVCHIP_CH56X: + return "CH565/CH569 RISC-V3A series"; + case WCH_RISCVCHIP_CH32V20X: + return "CH32V20X RISC-V4B/V4C series"; + case WCH_RISCVCHIP_CH32V30X: + return "CH32V30X RISC-V4C/V4F series"; + case WCH_RISCVCHIP_CH58X: + return "CH581/CH582/CH583 RISC-V4A BLE 5.3 series"; + case WCH_RISCVCHIP_CH32V003: + return "CH32V003 RISC-V2A series"; + case WCH_RISCVCHIP_CH59X: + return "CH59x RISC-V4C BLE 5.4 series"; + case WCH_RISCVCHIP_CH32X035: + return "CH32X035 RISC-V4C series"; + default: + return "Unknown"; + } +} + static bool wchlink_get_version(void) { uint8_t response[4U]; @@ -275,6 +303,51 @@ static bool wchlink_get_version(void) return true; } +/* + * This function is called when the WCH-Link attaches to certain types of RISC-V chip + * It is unknown what this function does, but the official WCH-Link software calls it + * + * Removing this function still allows the WCH-Link to work and the scan is successful + * but it is unknown if it might required for some chips or states + */ +static bool wchlink_after_attach_unknown() +{ + DEBUG_INFO("Sending unknown WCH-Link command after attach\n"); + + /* Response seems to be WCH_CONTROL_SUBCMD_UNKNOWN, but without knowing what the command does we won't check it blindly */ + uint8_t response = 0; + return wchlink_command_send_recv(WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_UNKNOWN, NULL, 0, &response, sizeof(response)); +} + +/* WCH-Link attach routine, attempts to detect and attach to a connected RISC-V chip */ +bool wchlink_attach() +{ + uint8_t response[5U]; + if (!wchlink_command_send_recv(WCH_CMD_CONTROL, WCH_CONTROL_SUBCMD_ATTACH, NULL, 0, response, sizeof(response))) + return false; + + wchlink.riscvchip = response[WCH_RISCVCHIP_OFFSET]; + const uint32_t idcode = read_be4(response, WCH_IDCODDE_OFFSET); + + DEBUG_INFO("WCH-Link attached to RISC-V chip: %s\n", wchlink_riscvchip_to_string(wchlink.riscvchip)); + DEBUG_INFO("ID code: 0x%08" PRIx32 "\n", idcode); + + /* Some RISC-V chips require* an additional command to be sent after attach */ + switch (wchlink.riscvchip) { + case WCH_RISCVCHIP_CH32V103: + case WCH_RISCVCHIP_CH32V20X: + case WCH_RISCVCHIP_CH32V30X: + case WCH_RISCVCHIP_CH32V003: + if (!wchlink_after_attach_unknown()) + return false; + break; + default: + break; + } + + return true; +} + bool wchlink_init(void) { usb_link_s *link = calloc(1U, sizeof(usb_link_s)); diff --git a/src/platforms/hosted/wchlink.h b/src/platforms/hosted/wchlink.h index 720d7f74760..8e6a283dca2 100644 --- a/src/platforms/hosted/wchlink.h +++ b/src/platforms/hosted/wchlink.h @@ -36,6 +36,7 @@ #include "bmp_hosted.h" bool wchlink_init(void); +bool wchlink_rvswd_scan(void); void wchlink_riscv_dtm_handler(void); #endif /* PLATFORMS_HOSTED_WCHLINK_H */ diff --git a/src/platforms/hosted/wchlink_protocol.h b/src/platforms/hosted/wchlink_protocol.h index 7c106dcc166..4de7b70695d 100644 --- a/src/platforms/hosted/wchlink_protocol.h +++ b/src/platforms/hosted/wchlink_protocol.h @@ -319,4 +319,6 @@ bool wchlink_command_send_recv(uint8_t command, uint8_t subcommand, const void * void *response, size_t response_length); bool wchlink_transfer_dmi(uint8_t operation, uint32_t address, uint32_t data_in, uint32_t *data_out, uint8_t *status); +bool wchlink_attach(void); + #endif /* PLATFORMS_HOSTED_WCHLINK_PROTOCOL_H */ diff --git a/src/platforms/hosted/wchlink_rvswd.c b/src/platforms/hosted/wchlink_rvswd.c new file mode 100644 index 00000000000..249aa7f49f9 --- /dev/null +++ b/src/platforms/hosted/wchlink_rvswd.c @@ -0,0 +1,53 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target_internal.h" +#include "wchlink.h" +#include "wchlink_protocol.h" + +bool wchlink_rvswd_scan(void) +{ + target_list_free(); + + /* + * This is redundant as we will run a generic scan routine after this + * but without it the scan will fail + */ + if (!wchlink_attach()) + return false; + + /* deffer to WCH-Link riscv_dtm_handler */ + wchlink_riscv_dtm_handler(); + + return target_list != NULL; +} From f376556c6d2a7636e6dc204d9dfe1b2f00a643d0 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 19:13:59 +0100 Subject: [PATCH 121/130] riscv_debug: handle CH32V3x quirk The chip replies ok when probing the breakpoints but the reply is empty Co-authored-by: mean --- src/target/riscv_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d699d5b0bd9..c6c0688824c 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -684,7 +684,8 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger); /* Try reading the trigger info */ uint32_t info = 0; - if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info)) { + /* Some chips reply ok but return 0 (WCH)*/ + if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info) || !info) { /* * If that fails, it's probably because the tinfo register isn't implemented, so read * the tdata1 register instead and extract the type from the MSb and build the info bitset from that From 0f4392a52983fb3210f336f4dab6f4468644c391 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 23 Feb 2023 15:30:35 +0000 Subject: [PATCH 122/130] riscv32: add ch32v3 target probe --- src/Makefile | 1 + src/target/ch32vx.c | 79 +++++++++++++++++++++++++++++++++++++++ src/target/riscv32.c | 3 ++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 5 files changed, 85 insertions(+) create mode 100644 src/target/ch32vx.c diff --git a/src/Makefile b/src/Makefile index d04f15282b5..fe462553789 100644 --- a/src/Makefile +++ b/src/Makefile @@ -77,6 +77,7 @@ SRC = \ spi.c \ stm32f1.c \ ch32f1.c \ + ch32vx.c \ stm32f4.c \ stm32h5.c \ stm32h7.c \ diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c new file mode 100644 index 00000000000..c85973427ca --- /dev/null +++ b/src/target/ch32vx.c @@ -0,0 +1,79 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements RISC-V CH32Vx target specific functions */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" + +/* IDCODE register */ +#define CH32VX_IDCODE 0x1ffff704U +#define CH32VX_IDCODE_MASK 0x0ffffff0f +#define CH32VX_IDCODE_FAMILY_OFFSET 20U +#define CH32VX_IDCODE_FAMILY_MASK (0xfffU << CH32VX_IDCODE_FAMILY_OFFSET) + +#define CH32V203_IDCODE_FAMILY 0x203U +#define CH32V208_IDCODE_FAMILY 0x208U +#define CH32V305_IDCODE_FAMILY 0x305U +#define CH32V303_IDCODE_FAMILY 0x303U +#define CH32V307_IDCODE_FAMILY 0x307U + +bool ch32vx_probe(target_s *const target) +{ + const uint32_t idcode = target_mem_read32(target, CH32VX_IDCODE); + + switch (idcode & CH32VX_IDCODE_MASK) { + case 0x30330504U: /* CH32V303CBT6 */ + case 0x30320504U: /* CH32V303RBT6 */ + case 0x30310504U: /* CH32V303RCT6 */ + case 0x30300504U: /* CH32V303VCT6 */ + case 0x30520508U: /* CH32V305FBP6 */ + case 0x30500508U: /* CH32V305RBT6 */ + case 0x30730508U: /* CH32V307WCU6 */ + case 0x30720508U: /* CH32V307FBP6 */ + case 0x30710508U: /* CH32V307RCT6 */ + case 0x30700508U: /* CH32V307VCT6 */ + break; + default: + return false; + break; + } + + const uint16_t family = (idcode & CH32VX_IDCODE_FAMILY_MASK) >> CH32VX_IDCODE_FAMILY_OFFSET; + switch (family) { + case CH32V303_IDCODE_FAMILY: + target->driver = "CH32V303"; + break; + case CH32V305_IDCODE_FAMILY: + target->driver = "CH32V305"; + break; + case CH32V307_IDCODE_FAMILY: + target->driver = "CH32V307"; + break; + default: + return false; + break; + } + + target->part_id = idcode; + + return true; +} diff --git a/src/target/riscv32.c b/src/target/riscv32.c index b8fe5855e2e..23b08ef4992 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -101,6 +101,9 @@ bool riscv32_probe(target_s *const target) case JEP106_MANUFACTURER_ESPRESSIF: PROBE(esp32c3_probe); break; + case NOT_JEP106_MANUFACTURER_WCH: + PROBE(ch32vx_probe); + break; } #if PC_HOSTED == 0 gdb_outf("Please report unknown device with Designer 0x%x\n", target->designer_code); diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 8224d65739b..591865c34b9 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -84,6 +84,7 @@ CORTEXM_PROBE_WEAK_NOP(efm32_aap_probe) CORTEXM_PROBE_WEAK_NOP(rp_rescue_probe) CORTEXM_PROBE_WEAK_NOP(lpc55_dmap_probe) +TARGET_PROBE_WEAK_NOP(ch32vx_probe) TARGET_PROBE_WEAK_NOP(ch32f1_probe) TARGET_PROBE_WEAK_NOP(gd32f1_probe) TARGET_PROBE_WEAK_NOP(gd32vf1_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 0246fd0603b..25620d570c1 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -51,6 +51,7 @@ bool efm32_aap_probe(adiv5_access_port_s *ap); bool rp_rescue_probe(adiv5_access_port_s *ap); bool lpc55_dmap_probe(adiv5_access_port_s *ap); +bool ch32vx_probe(target_s *target); bool ch32f1_probe(target_s *target); // will catch all the clones bool at32fxx_probe(target_s *target); // STM32 clones from Artery bool mm32l0xx_probe(target_s *target); From fbca4baa16f313a786df3111720f6964beab28ce Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 8 Sep 2023 00:16:37 +0100 Subject: [PATCH 123/130] !fixme! mass erase rework, drop after merge and rebase --- src/include/target.h | 1 + src/target/efm32.c | 48 +++++++++---------- src/target/hc32l110.c | 37 +++++++-------- src/target/imxrt.c | 1 - src/target/kinetis.c | 32 ++++++------- src/target/lmi.c | 8 ---- src/target/lpc17xx.c | 31 ++++++------ src/target/lpc40xx.c | 33 +++++++------ src/target/lpc43xx.c | 21 ++++---- src/target/lpc546xx.c | 17 ++----- src/target/lpc55xx.c | 5 +- src/target/msp432e4.c | 8 ++-- src/target/nrf51.c | 92 ++++++++++++++++++------------------ src/target/nxpke04.c | 11 +++-- src/target/rp.c | 1 - src/target/samd.c | 20 ++++---- src/target/samx5x.c | 2 +- src/target/spi.c | 35 +++++++------- src/target/spi.h | 2 +- src/target/stm32f4.c | 18 ++++--- src/target/stm32g0.c | 18 ++++--- src/target/stm32h5.c | 15 ++---- src/target/stm32h7.c | 10 ++-- src/target/stm32l0.c | 23 ++------- src/target/stm32l4.c | 16 +++---- src/target/target.c | 8 ++-- src/target/target_flash.c | 76 +++++++++++++++++++++++++++-- src/target/target_internal.h | 39 ++++++++------- 28 files changed, 318 insertions(+), 310 deletions(-) diff --git a/src/include/target.h b/src/include/target.h index 98f076722bf..f4185f6f6eb 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -64,6 +64,7 @@ bool target_mem_access_needs_halt(target_s *target); bool target_flash_erase(target_s *target, target_addr_t addr, size_t len); bool target_flash_write(target_s *target, target_addr_t dest, const void *src, size_t len); bool target_flash_complete(target_s *target); +bool target_flash_mass_erase(target_s *target); /* Register access functions */ size_t target_regs_size(target_s *target); diff --git a/src/target/efm32.c b/src/target/efm32.c index dbfcaa56c32..9fffb478e70 100644 --- a/src/target/efm32.c +++ b/src/target/efm32.c @@ -47,7 +47,7 @@ static bool efm32_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool efm32_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool efm32_mass_erase(target_s *t); +static bool efm32_mass_erase(target_s *target, platform_timeout_s *print_progess); static const uint16_t efm32_flash_write_stub[] = { #include "flashstub/efm32.stub" @@ -666,55 +666,53 @@ static bool efm32_flash_write(target_flash_s *f, target_addr_t dest, const void } /* Uses the MSC ERASEMAIN0/1 command to erase the entire flash */ -static bool efm32_mass_erase(target_s *t) +static bool efm32_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - efm32_priv_s *priv_storage = (efm32_priv_s *)t->target_storage; + efm32_priv_s *priv_storage = (efm32_priv_s *)target->target_storage; if (!priv_storage || !priv_storage->device) return false; if (priv_storage->device->family_id == 71 || priv_storage->device->family_id == 73) { /* original Gecko and Tiny Gecko families don't support mass erase */ - tc_printf(t, "This device does not support mass erase through MSC.\n"); + tc_printf(target, "This device does not support mass erase through MSC.\n"); return false; } uint32_t msc = priv_storage->device->msc_addr; - uint16_t flash_kib = efm32_read_flash_size(t, priv_storage->di_version); + uint16_t flash_kib = efm32_read_flash_size(target, priv_storage->di_version); /* Set WREN bit to enable MSC write and erase functionality */ - target_mem_write32(t, EFM32_MSC_WRITECTRL(msc), 1); + target_mem_write32(target, EFM32_MSC_WRITECTRL(msc), 1); /* Unlock mass erase */ - target_mem_write32(t, EFM32_MSC_MASSLOCK(msc), EFM32_MSC_MASSLOCK_LOCKKEY); + target_mem_write32(target, EFM32_MSC_MASSLOCK(msc), EFM32_MSC_MASSLOCK_LOCKKEY); /* Erase operation */ - target_mem_write32(t, EFM32_MSC_WRITECMD(msc), EFM32_MSC_WRITECMD_ERASEMAIN0); + target_mem_write32(target, EFM32_MSC_WRITECMD(msc), EFM32_MSC_WRITECMD_ERASEMAIN0); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Poll MSC Busy */ - while ((target_mem_read32(t, EFM32_MSC_STATUS(msc)) & EFM32_MSC_STATUS_BUSY)) { - if (target_check_error(t)) + while ((target_mem_read32(target, EFM32_MSC_STATUS(msc)) & EFM32_MSC_STATUS_BUSY)) { + if (target_check_error(target)) return false; - target_print_progress(&timeout); + target_print_progress(print_progess); } /* Parts with >= 512 kiB flash have 2 mass erase regions */ - if (flash_kib >= 512) { + if (flash_kib >= 512U) { /* Erase operation */ - target_mem_write32(t, EFM32_MSC_WRITECMD(msc), EFM32_MSC_WRITECMD_ERASEMAIN1); + target_mem_write32(target, EFM32_MSC_WRITECMD(msc), EFM32_MSC_WRITECMD_ERASEMAIN1); /* Poll MSC Busy */ - while ((target_mem_read32(t, EFM32_MSC_STATUS(msc)) & EFM32_MSC_STATUS_BUSY)) { - if (target_check_error(t)) + while ((target_mem_read32(target, EFM32_MSC_STATUS(msc)) & EFM32_MSC_STATUS_BUSY)) { + if (target_check_error(target)) return false; - target_print_progress(&timeout); + target_print_progress(print_progess); } } /* Relock mass erase */ - target_mem_write32(t, EFM32_MSC_MASSLOCK(msc), 0); + target_mem_write32(target, EFM32_MSC_MASSLOCK(msc), 0); return true; } @@ -926,7 +924,7 @@ static bool efm32_cmd_bootloader(target_s *t, int argc, const char **argv) #define CMDKEY 0xcfacc118U -static bool efm32_aap_mass_erase(target_s *t); +static bool efm32_aap_mass_erase(target_s *target, platform_timeout_s *print_progess); /* AAP Probe */ typedef struct efm32_aap_priv { @@ -967,9 +965,9 @@ bool efm32_aap_probe(adiv5_access_port_s *ap) return true; } -static bool efm32_aap_mass_erase(target_s *t) +static bool efm32_aap_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - adiv5_access_port_s *ap = t->priv; + adiv5_access_port_s *const ap = cortex_ap(target); uint32_t status; /* Read status */ @@ -984,14 +982,12 @@ static bool efm32_aap_mass_erase(target_s *t) DEBUG_INFO("EFM32: Issuing DEVICEERASE...\n"); adiv5_ap_write(ap, AAP_CMDKEY, CMDKEY); - adiv5_ap_write(ap, AAP_CMD, 1); + adiv5_ap_write(ap, AAP_CMD, 1U); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Read until 0, probably should have a timeout here... */ do { status = adiv5_ap_read(ap, AAP_STATUS); - target_print_progress(&timeout); + target_print_progress(print_progess); } while (status & AAP_STATUS_ERASEBUSY); /* Read status */ diff --git a/src/target/hc32l110.c b/src/target/hc32l110.c index fa3364c0334..490952a3a83 100644 --- a/src/target/hc32l110.c +++ b/src/target/hc32l110.c @@ -78,7 +78,7 @@ static bool hc32l110_flash_prepare(target_flash_s *flash); static bool hc32l110_flash_done(target_flash_s *flash); static bool hc32l110_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool hc32l110_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); -static bool hc32l110_mass_erase(target_s *target); +static bool hc32l110_mass_erase(target_s *target, platform_timeout_s *print_progess); static void hc32l110_add_flash(target_s *target, const uint32_t flash_size) { @@ -130,7 +130,8 @@ static void hc32l110_flash_cr_unlock(target_s *const target) target_mem_write32(target, HC32L110_FLASH_BYPASS, 0xa5a5U); } -static bool hc32l110_check_flash_completion(target_s *const target, const uint32_t timeout_ms) +static bool hc32l110_check_flash_completion( + target_s *const target, const uint32_t timeout_ms, platform_timeout_s *const print_progess) { platform_timeout_s timeout; platform_timeout_set(&timeout, timeout_ms); @@ -139,6 +140,8 @@ static bool hc32l110_check_flash_completion(target_s *const target, const uint32 status = target_mem_read32(target, HC32L110_FLASH_CR); if (target_check_error(target) || platform_timeout_is_expired(&timeout)) return false; + if (print_progess) + target_print_progress(print_progess); } return true; } @@ -181,11 +184,17 @@ static bool hc32l110_flash_prepare(target_flash_s *const flash) case FLASH_OPERATION_ERASE: target_mem_write32(flash->t, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_ERASE_SECTOR); break; + case FLASH_OPERATION_MASS_ERASE: + target_mem_write32(flash->t, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_ERASE_CHIP); + break; default: DEBUG_WARN("unsupported operation %u", flash->operation); return false; } + if (!hc32l110_check_flash_completion(flash->t, 500U, NULL)) + return false; + hc32l110_slock_unlock_all(flash->t); return true; } @@ -200,8 +209,8 @@ static bool hc32l110_flash_erase(target_flash_s *const flash, const target_addr_ { (void)length; /* The Flash controller automatically erases the whole sector after one write operation */ - target_mem_write32(flash->t, addr, 0); - return hc32l110_check_flash_completion(flash->t, 1000); + target_mem_write32(flash->t, addr, 0U); + return hc32l110_check_flash_completion(flash->t, 1000U, NULL); } static bool hc32l110_flash_write( @@ -209,24 +218,12 @@ static bool hc32l110_flash_write( { (void)length; target_mem_write32(flash->t, dest, *(const uint32_t *)src); - return hc32l110_check_flash_completion(flash->t, 1000); + return hc32l110_check_flash_completion(flash->t, 1000U, NULL); } -static bool hc32l110_mass_erase(target_s *target) +static bool hc32l110_mass_erase(target_s *target, platform_timeout_s *const print_progess) { - hc32l110_enter_flash_mode(target); - - hc32l110_flash_cr_unlock(target); - target_mem_write32(target, HC32L110_FLASH_CR, HC32L110_FLASH_CR_OP_ERASE_CHIP); - if (!hc32l110_check_flash_completion(target, 500)) - return false; - - hc32l110_slock_unlock_all(target); - // The Flash controller automatically erases the whole Flash after one write operation - target_mem_write32(target, 0, 0); - const bool result = hc32l110_check_flash_completion(target, 4000); - - hc32l110_slock_lock_all(target); - return result; + target_mem_write32(target, 0x0U, 0U); + return hc32l110_check_flash_completion(target, 4000U, print_progess); } diff --git a/src/target/imxrt.c b/src/target/imxrt.c index eeba517c0fa..5b819655833 100644 --- a/src/target/imxrt.c +++ b/src/target/imxrt.c @@ -253,7 +253,6 @@ bool imxrt_probe(target_s *const target) spi_flash_id_s flash_id; imxrt_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); - target->mass_erase = bmp_spi_mass_erase; target->enter_flash_mode = imxrt_enter_flash_mode; target->exit_flash_mode = imxrt_exit_flash_mode; diff --git a/src/target/kinetis.c b/src/target/kinetis.c index 1e5c5853772..8c6bae94883 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -516,7 +516,7 @@ static bool kinetis_flash_done(target_flash_s *const f) * device. This provides a fake target to allow a monitor command interface */ -static bool kinetis_mdm_mass_erase(target_s *t); +static bool kinetis_mdm_mass_erase(target_s *t, platform_timeout_s *const print_progess); static bool kinetis_mdm_cmd_ke04_mode(target_s *t, int argc, const char **argv); const command_s kinetis_mdm_cmd_list[] = { @@ -570,52 +570,50 @@ static bool kinetis_mdm_cmd_ke04_mode(target_s *t, int argc, const char **argv) return true; } -static bool kinetis_mdm_mass_erase(target_s *t) +static bool kinetis_mdm_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - adiv5_access_port_s *ap = t->priv; + adiv5_access_port_s *ap = target->priv; /* Keep the MCU in reset as stated in KL25PxxM48SF0RM */ - if (t->ke04_mode) - adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_SYS_RESET); + if (target->ke04_mode) + adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_SYS_RESET); /* FIXME: move this to enter_flash_mode? */ uint32_t status = adiv5_ap_read(ap, MDM_STATUS); uint32_t control = adiv5_ap_read(ap, MDM_CONTROL); - tc_printf(t, "Requesting mass erase (status = 0x%" PRIx32 ")\n", status); + tc_printf(target, "Requesting mass erase (status = 0x%" PRIx32 ")\n", status); /* This flag does not exist on KE04 */ - if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !t->ke04_mode) { - tc_printf(t, "ERROR: Mass erase disabled!\n"); + if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !target->ke04_mode) { + tc_printf(target, "ERROR: Mass erase disabled!\n"); return false; } /* Flag is not persistent */ - t->ke04_mode = false; + target->ke04_mode = false; if (!(status & MDM_STATUS_FLASH_READY)) { - tc_printf(t, "ERROR: Flash not ready!\n"); + tc_printf(target, "ERROR: Flash not ready!\n"); return false; } if (status & MDM_STATUS_MASS_ERASE_ACK) { - tc_printf(t, "ERROR: Mass erase already in progress!\n"); + tc_printf(target, "ERROR: Mass erase already in progress!\n"); return false; } adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_MASS_ERASE); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); do { status = adiv5_ap_read(ap, MDM_STATUS); - target_print_progress(&timeout); + target_print_progress(print_progess); } while (!(status & MDM_STATUS_MASS_ERASE_ACK)); - tc_printf(t, "Mass erase acknowledged\n"); + tc_printf(target, "Mass erase acknowledged\n"); do { control = adiv5_ap_read(ap, MDM_CONTROL); - target_print_progress(&timeout); + target_print_progress(print_progess); } while (!(control & MDM_CONTROL_MASS_ERASE)); - tc_printf(t, "Mass erase complete\n"); + tc_printf(target, "Mass erase complete\n"); return true; } diff --git a/src/target/lmi.c b/src/target/lmi.c index d54265be761..ed27284237b 100644 --- a/src/target/lmi.c +++ b/src/target/lmi.c @@ -64,7 +64,6 @@ static bool lmi_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool lmi_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool lmi_mass_erase(target_s *t); static const char lmi_driver_str[] = "TI Stellaris/Tiva"; @@ -107,7 +106,6 @@ bool lm3s_probe(target_s *const t, const uint16_t did1) t->driver = driver; return false; } - t->mass_erase = lmi_mass_erase; return true; } @@ -138,7 +136,6 @@ bool tm4c_probe(target_s *const t, const uint16_t did1) t->driver = driver; return false; } - t->mass_erase = lmi_mass_erase; return true; } @@ -196,8 +193,3 @@ static bool lmi_flash_write(target_flash_s *f, target_addr_t dest, const void *s return cortexm_run_stub(t, SRAM_BASE, dest, STUB_BUFFER_BASE, len, 0) == 0; } - -static bool lmi_mass_erase(target_s *t) -{ - return lmi_flash_erase(t->flash, t->flash->start, t->flash->length); -} diff --git a/src/target/lpc17xx.c b/src/target/lpc17xx.c index 95dd381089c..5dc661bf2a7 100644 --- a/src/target/lpc17xx.c +++ b/src/target/lpc17xx.c @@ -65,8 +65,9 @@ typedef struct lpc17xx_priv { static void lpc17xx_extended_reset(target_s *target); static bool lpc17xx_enter_flash_mode(target_s *target); static bool lpc17xx_exit_flash_mode(target_s *target); -static bool lpc17xx_mass_erase(target_s *target); -iap_status_e lpc17xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e cmd, ...); +static bool lpc17xx_mass_erase(target_s *target, platform_timeout_s *print_progess); +iap_status_e lpc17xx_iap_call( + target_s *target, iap_result_s *result, platform_timeout_s *print_progess, iap_cmd_e cmd, ...); static void lpc17xx_add_flash(target_s *target, uint32_t addr, size_t len, size_t erasesize, uint8_t base_sector) { @@ -103,7 +104,7 @@ bool lpc17xx_probe(target_s *target) lpc17xx_enter_flash_mode(target); /* Read the Part ID */ iap_result_s result; - lpc17xx_iap_call(target, &result, IAP_CMD_PARTID); + lpc17xx_iap_call(target, &result, NULL, IAP_CMD_PARTID); /* Transition back to normal mode and resume the target */ lpc17xx_exit_flash_mode(target); target_halt_resume(target, false); @@ -168,31 +169,28 @@ static bool lpc17xx_exit_flash_mode(target_s *const target) return true; } -static bool lpc17xx_mass_erase(target_s *target) +static bool lpc17xx_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { iap_result_s result; - lpc17xx_enter_flash_mode(target); - if (lpc17xx_iap_call(target, &result, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U)) { + if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U)) { lpc17xx_exit_flash_mode(target); DEBUG_ERROR("lpc17xx_cmd_erase: prepare failed %" PRIu32 "\n", result.return_code); return false; } - if (lpc17xx_iap_call(target, &result, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) { + if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) { lpc17xx_exit_flash_mode(target); DEBUG_ERROR("lpc17xx_cmd_erase: erase failed %" PRIu32 "\n", result.return_code); return false; } - if (lpc17xx_iap_call(target, &result, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR - 1U)) { + if (lpc17xx_iap_call(target, &result, print_progess, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR - 1U)) { lpc17xx_exit_flash_mode(target); DEBUG_ERROR("lpc17xx_cmd_erase: blankcheck failed %" PRIu32 "\n", result.return_code); return false; } - lpc17xx_exit_flash_mode(target); - tc_printf(target, "Erase OK.\n"); return true; } @@ -223,7 +221,8 @@ static size_t lpc17xx_iap_params(const iap_cmd_e cmd) } } -iap_status_e lpc17xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e cmd, ...) +iap_status_e lpc17xx_iap_call(target_s *const target, iap_result_s *const result, + platform_timeout_s *const print_progess, const iap_cmd_e cmd, ...) { /* Set up our IAP frame with the break opcode and command to run */ iap_frame_s frame = { @@ -238,7 +237,7 @@ iap_status_e lpc17xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e for (size_t i = 0; i < params_count; ++i) frame.config.params[i] = va_arg(params, uint32_t); va_end(params); - for (size_t i = params_count; i < 4; ++i) + for (size_t i = params_count; i < 4U; ++i) frame.config.params[i] = 0U; /* Copy the structure to RAM */ @@ -251,7 +250,7 @@ iap_status_e lpc17xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e /* Point r0 to the start of the config block */ regs[0] = iap_params_addr; /* And r1 to the same so we re-use the same memory for the results */ - regs[1] = iap_params_addr; + regs[1U] = iap_params_addr; /* Set the top of stack to the top of the RAM block we're using */ regs[CORTEX_REG_MSP] = IAP_RAM_BASE + MIN_RAM_SIZE; /* Point the return address to our breakpoint opcode (thumb mode) */ @@ -261,12 +260,12 @@ iap_status_e lpc17xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e target_regs_write(target, regs); platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); + platform_timeout_set(&timeout, 500U); /* Start the target and wait for it to halt again */ target_halt_resume(target, false); while (!target_halt_poll(target, NULL)) { - if (cmd == IAP_CMD_ERASE) - target_print_progress(&timeout); + if (print_progess) + target_print_progress(print_progess); else if (cmd == IAP_CMD_PARTID && platform_timeout_is_expired(&timeout)) { target_halt_request(target); return IAP_STATUS_INVALID_COMMAND; diff --git a/src/target/lpc40xx.c b/src/target/lpc40xx.c index 82ffa9c4a99..725e47cdc82 100644 --- a/src/target/lpc40xx.c +++ b/src/target/lpc40xx.c @@ -67,8 +67,9 @@ typedef struct lpc40xx_priv { static void lpc40xx_extended_reset(target_s *target); static bool lpc40xx_enter_flash_mode(target_s *target); static bool lpc40xx_exit_flash_mode(target_s *target); -static bool lpc40xx_mass_erase(target_s *target); -iap_status_e lpc40xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e cmd, ...); +static bool lpc40xx_mass_erase(target_s *target, platform_timeout_s *print_progess); +iap_status_e lpc40xx_iap_call( + target_s *target, iap_result_s *result, platform_timeout_s *print_progess, iap_cmd_e cmd, ...); static void lpc40xx_add_flash(target_s *target, uint32_t addr, size_t len, size_t erasesize, uint8_t base_sector) { @@ -106,7 +107,7 @@ bool lpc40xx_probe(target_s *target) lpc40xx_enter_flash_mode(target); /* Read the Part ID */ iap_result_s result; - lpc40xx_iap_call(target, &result, IAP_CMD_PARTID); + lpc40xx_iap_call(target, &result, NULL, IAP_CMD_PARTID); /* Transition back to normal mode and resume the target */ lpc40xx_exit_flash_mode(target); target_halt_resume(target, false); @@ -162,31 +163,28 @@ static bool lpc40xx_exit_flash_mode(target_s *const target) return true; } -static bool lpc40xx_mass_erase(target_s *target) +static bool lpc40xx_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { iap_result_s result; - lpc40xx_enter_flash_mode(target); - if (lpc40xx_iap_call(target, &result, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U)) { + if (lpc40xx_iap_call(target, &result, print_progess, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U)) { lpc40xx_exit_flash_mode(target); DEBUG_ERROR("lpc40xx_cmd_erase: prepare failed %" PRIu32 "\n", result.return_code); return false; } - if (lpc40xx_iap_call(target, &result, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) { + if (lpc40xx_iap_call(target, &result, print_progess, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ)) { lpc40xx_exit_flash_mode(target); DEBUG_ERROR("lpc40xx_cmd_erase: erase failed %" PRIu32 "\n", result.return_code); return false; } - if (lpc40xx_iap_call(target, &result, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR - 1U)) { + if (lpc40xx_iap_call(target, &result, print_progess, IAP_CMD_BLANKCHECK, 0, FLASH_NUM_SECTOR - 1U)) { lpc40xx_exit_flash_mode(target); DEBUG_ERROR("lpc40xx_cmd_erase: blankcheck failed %" PRIu32 "\n", result.return_code); return false; } - lpc40xx_exit_flash_mode(target); - tc_printf(target, "Erase OK.\n"); return true; } @@ -217,7 +215,8 @@ static size_t lpc40xx_iap_params(const iap_cmd_e cmd) } } -iap_status_e lpc40xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e cmd, ...) +iap_status_e lpc40xx_iap_call(target_s *const target, iap_result_s *const result, + platform_timeout_s *const print_progess, const iap_cmd_e cmd, ...) { /* Set up our IAP frame with the break opcode and command to run */ iap_frame_s frame = { @@ -232,7 +231,7 @@ iap_status_e lpc40xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e for (size_t i = 0; i < params_count; ++i) frame.config.params[i] = va_arg(params, uint32_t); va_end(params); - for (size_t i = params_count; i < 4; ++i) + for (size_t i = params_count; i < 4U; ++i) frame.config.params[i] = 0U; /* Copy the structure to RAM */ @@ -245,22 +244,22 @@ iap_status_e lpc40xx_iap_call(target_s *target, iap_result_s *result, iap_cmd_e /* Point r0 to the start of the config block */ regs[0] = iap_params_addr; /* And r1 to the same so we re-use the same memory for the results */ - regs[1] = iap_params_addr; + regs[1U] = iap_params_addr; /* Set the top of stack to the top of the RAM block we're using */ regs[CORTEX_REG_MSP] = IAP_RAM_BASE + MIN_RAM_SIZE; /* Point the return address to our breakpoint opcode (thumb mode) */ - regs[CORTEX_REG_LR] = IAP_RAM_BASE | 1; + regs[CORTEX_REG_LR] = IAP_RAM_BASE | 1U; /* And set the program counter to the IAP ROM entrypoint */ regs[CORTEX_REG_PC] = IAP_ENTRYPOINT; target_regs_write(target, regs); platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); + platform_timeout_set(&timeout, 500U); /* Start the target and wait for it to halt again */ target_halt_resume(target, false); while (!target_halt_poll(target, NULL)) { - if (cmd == IAP_CMD_ERASE) - target_print_progress(&timeout); + if (print_progess) + target_print_progress(print_progess); else if (cmd == IAP_CMD_PARTID && platform_timeout_is_expired(&timeout)) { target_halt_request(target); return IAP_STATUS_INVALID_COMMAND; diff --git a/src/target/lpc43xx.c b/src/target/lpc43xx.c index fc38f80f551..36472f83d9e 100644 --- a/src/target/lpc43xx.c +++ b/src/target/lpc43xx.c @@ -285,7 +285,7 @@ static bool lpc43xx_iap_init(target_flash_s *flash); static lpc43xx_partid_s lpc43xx_iap_read_partid(target_s *t); static bool lpc43xx_enter_flash_mode(target_s *t); static bool lpc43xx_iap_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); -static bool lpc43xx_iap_mass_erase(target_s *t); +static bool lpc43xx_iap_mass_erase(target_s *target, platform_timeout_s *print_progess); static void lpc43xx_wdt_set_period(target_s *t); static void lpc43xx_wdt_kick(target_s *t); @@ -504,7 +504,6 @@ bool lpc43xx_probe(target_s *const t) if (part_id.part == LPC43xx_PARTID_INVALID) return false; - t->mass_erase = bmp_spi_mass_erase; t->enter_flash_mode = lpc43x0_enter_flash_mode; t->exit_flash_mode = lpc43x0_exit_flash_mode; lpc43x0_detect(t, part_id); @@ -1000,19 +999,19 @@ static bool lpc43xx_iap_flash_erase(target_flash_s *f, const target_addr_t addr, return lpc_flash_erase(f, addr, len); } -static bool lpc43xx_iap_mass_erase(target_s *t) +static bool lpc43xx_iap_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - lpc43xx_priv_s *const priv = (lpc43xx_priv_s *)t->target_storage; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); - lpc43xx_iap_init(t->flash); + lpc43xx_priv_s *const priv = (lpc43xx_priv_s *)target->target_storage; + + lpc43xx_iap_init(target->flash); + /* FIXME: since this is looking like bank mass erases, maybe this should be in flash->mass_erase */ for (size_t bank = 0; bank < priv->flash_banks; ++bank) { - lpc_flash_s *const f = (lpc_flash_s *)t->flash; - if (lpc_iap_call(f, NULL, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U, bank) || - lpc_iap_call(f, NULL, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ, bank)) + lpc_flash_s *const flash = (lpc_flash_s *)target->flash; + if (lpc_iap_call(flash, NULL, IAP_CMD_PREPARE, 0, FLASH_NUM_SECTOR - 1U, bank) || + lpc_iap_call(flash, NULL, IAP_CMD_ERASE, 0, FLASH_NUM_SECTOR - 1U, CPU_CLK_KHZ, bank)) return false; - target_print_progress(&timeout); + target_print_progress(print_progess); } return true; diff --git a/src/target/lpc546xx.c b/src/target/lpc546xx.c index 694aebe9c50..98aa99f79c9 100644 --- a/src/target/lpc546xx.c +++ b/src/target/lpc546xx.c @@ -61,7 +61,6 @@ static bool lpc546xx_cmd_write_sector(target_s *t, int argc, const char **argv); static void lpc546xx_reset_attach(target_s *t); static bool lpc546xx_flash_init(target_s *t); static bool lpc546xx_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); -static bool lpc546xx_mass_erase(target_s *t); static void lpc546xx_wdt_set_period(target_s *t); static void lpc546xx_wdt_kick(target_s *t); @@ -152,7 +151,6 @@ bool lpc546xx_probe(target_s *t) */ sram123_size = device->sram123_kbytes * 1024U; - t->mass_erase = lpc546xx_mass_erase; lpc546xx_add_flash(t, IAP_ENTRYPOINT_LOCATION, 0, 0x0, flash_size, 0x8000); /* @@ -182,20 +180,13 @@ static void lpc546xx_reset_attach(target_s *t) cortexm_attach(t); } -static bool lpc546xx_mass_erase(target_s *t) -{ - const int result = lpc546xx_flash_erase(t->flash, t->flash->start, t->flash->length); - if (result != 0) - tc_printf(t, "Error erasing flash: %d\n", result); - return result == 0; -} - -static bool lpc546xx_cmd_erase_sector(target_s *t, int argc, const char **argv) +static bool lpc546xx_cmd_erase_sector(target_s *const target, int argc, const char **argv) { + /* FIXME: is this command really needed? feels like it should at best be a generic target erase */ if (argc > 1) { uint32_t sector_addr = strtoul(argv[1], NULL, 0); - sector_addr *= t->flash->blocksize; - return lpc546xx_flash_erase(t->flash, sector_addr, 1U); + sector_addr *= target->flash->blocksize; + return target_flash_erase(target, sector_addr, 1U); } return true; } diff --git a/src/target/lpc55xx.c b/src/target/lpc55xx.c index 69f9d8a18ca..19d154c4d75 100644 --- a/src/target/lpc55xx.c +++ b/src/target/lpc55xx.c @@ -519,7 +519,7 @@ static const command_s lpc55xx_cmd_list[] = { }; static bool lpc55_dmap_cmd(adiv5_access_port_s *ap, uint32_t cmd); -static bool lpc55_dmap_mass_erase(target_s *target); +static bool lpc55_dmap_mass_erase(target_s *target, platform_timeout_s *print_progess); static void lpc55_dmap_ap_free(void *priv); void lpc55_dp_prepare(adiv5_debug_port_s *const dp) @@ -656,8 +656,9 @@ static bool lpc55_dmap_cmd(adiv5_access_port_s *const ap, const uint32_t cmd) } } -static bool lpc55_dmap_mass_erase(target_s *target) +static bool lpc55_dmap_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { + (void)print_progess; /* * TODO: This doesn't actually work at least on the LPC550x, there seems to be * a lot more to figure out about the debug mailbox before this code can work. diff --git a/src/target/msp432e4.c b/src/target/msp432e4.c index 619da8dd736..e43053eddd1 100644 --- a/src/target/msp432e4.c +++ b/src/target/msp432e4.c @@ -185,7 +185,7 @@ typedef struct msp432e4_flash { static bool msp432e4_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool msp432e4_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); -static bool msp432e4_mass_erase(target_s *target); +static bool msp432e4_mass_erase(target_s *target, platform_timeout_s *print_progess); static void msp432e4_add_flash( target_s *const target, const uint32_t sector_size, const uint32_t base, const size_t length) @@ -298,15 +298,13 @@ static bool msp432e4_flash_write( } /* Mass erases the Flash */ -static bool msp432e4_mass_erase(target_s *const target) +static bool msp432e4_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { const msp432e4_flash_s *const flash = (msp432e4_flash_s *)target->flash; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Kick off the mass erase */ target_mem_write32(target, MSP432E4_FLASH_CTRL, (flash->flash_key << 16U) | MSP432E4_FLASH_CTRL_MASS_ERASE); /* Wait for the erase to complete, printing a '.' every so often to keep GDB happy */ while (target_mem_read32(target, MSP432E4_FLASH_CTRL) & MSP432E4_FLASH_CTRL_MASS_ERASE) - target_print_progress(&timeout); + target_print_progress(print_progess); return true; } diff --git a/src/target/nrf51.c b/src/target/nrf51.c index c010eeeec2b..fe3eea51929 100644 --- a/src/target/nrf51.c +++ b/src/target/nrf51.c @@ -27,11 +27,11 @@ #include "cortexm.h" #include "adiv5.h" -static bool nrf51_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); -static bool nrf51_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool nrf51_flash_prepare(target_flash_s *f); -static bool nrf51_flash_done(target_flash_s *f); -static bool nrf51_mass_erase(target_s *t); +static bool nrf51_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool nrf51_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); +static bool nrf51_flash_prepare(target_flash_s *flash); +static bool nrf51_flash_done(target_flash_s *flash); +static bool nrf51_mass_erase(target_s *target, platform_timeout_s *print_progess); static bool nrf51_cmd_erase_uicr(target_s *t, int argc, const char **argv); static bool nrf51_cmd_protect_flash(target_s *t, int argc, const char **argv); @@ -164,81 +164,85 @@ bool nrf51_probe(target_s *t) return true; } -static bool nrf51_wait_ready(target_s *const t, platform_timeout_s *const timeout) +static bool nrf51_wait_ready(target_s *const t, platform_timeout_s *const print_progress) { /* Poll for NVMC_READY */ while (target_mem_read32(t, NRF51_NVMC_READY) == 0) { if (target_check_error(t)) return false; - if (timeout) - target_print_progress(timeout); + if (print_progress) + target_print_progress(print_progress); } return true; } -static bool nrf51_flash_prepare(target_flash_s *f) +static bool nrf51_flash_prepare(target_flash_s *const flash) { - target_s *t = f->t; - /* If there is a buffer allocated, we're in the Flash write phase, otherwise it's erase */ - if (f->buf) + target_s *const target = flash->t; + + switch (flash->operation) { + case FLASH_OPERATION_WRITE: /* Enable write */ - target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_WEN); - else + target_mem_write32(target, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_WEN); + break; + case FLASH_OPERATION_ERASE: /* Enable erase */ - target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN); - return nrf51_wait_ready(t, NULL); + target_mem_write32(target, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN); + break; + default: + return false; /* Unsupported operation */ + } + + return nrf51_wait_ready(target, NULL); } -static bool nrf51_flash_done(target_flash_s *f) +static bool nrf51_flash_done(target_flash_s *const flash) { - target_s *t = f->t; + target_s *const target = flash->t; /* Return to read-only */ - target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_REN); - return nrf51_wait_ready(t, NULL); + target_mem_write32(target, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_REN); + return nrf51_wait_ready(target, NULL); } -static bool nrf51_flash_erase(target_flash_s *f, target_addr_t addr, size_t len) +static bool nrf51_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t len) { - target_s *t = f->t; + target_s *const target = flash->t; - for (size_t offset = 0; offset < len; offset += f->blocksize) { + for (size_t offset = 0; offset < len; offset += flash->blocksize) { /* If the address to erase is the UICR, we have to handle that separately */ if (addr + offset == NRF51_UICR) /* Write to the ERASE_UICR register to erase */ - target_mem_write32(t, NRF51_NVMC_ERASEUICR, 0x1U); + target_mem_write32(target, NRF51_NVMC_ERASEUICR, 0x1U); else /* Write address of first word in page to erase it */ - target_mem_write32(t, NRF51_NVMC_ERASEPAGE, addr + offset); + target_mem_write32(target, NRF51_NVMC_ERASEPAGE, addr + offset); - if (!nrf51_wait_ready(t, NULL)) + if (!nrf51_wait_ready(target, NULL)) return false; } return true; } -static bool nrf51_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len) +static bool nrf51_flash_write( + target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t len) { /* nrf51_flash_prepare() and nrf51_flash_done() top-and-tail this, just write the data to the target. */ - target_s *t = f->t; - target_mem_write(t, dest, src, len); - return nrf51_wait_ready(t, NULL); + target_s *const target = flash->t; + target_mem_write(target, dest, src, len); + return nrf51_wait_ready(target, NULL); } -static bool nrf51_mass_erase(target_s *t) +static bool nrf51_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - target_reset(t); - /* Enable erase */ - target_mem_write32(t, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN); - if (!nrf51_wait_ready(t, NULL)) + target_mem_write32(target, NRF51_NVMC_CONFIG, NRF51_NVMC_CONFIG_EEN); + if (!nrf51_wait_ready(target, NULL)) return false; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500U); /* Erase all */ - target_mem_write32(t, NRF51_NVMC_ERASEALL, 1U); - return nrf51_wait_ready(t, &timeout); + target_mem_write32(target, NRF51_NVMC_ERASEALL, 1U); + return nrf51_wait_ready(target, print_progess); } static bool nrf51_cmd_erase_uicr(target_s *t, int argc, const char **argv) @@ -399,7 +403,7 @@ static bool nrf51_cmd_read(target_s *t, int argc, const char **argv) #define NRF52_MDM_IDR 0x02880000U -static bool nrf51_mdm_mass_erase(target_s *t); +static bool nrf51_mdm_mass_erase(target_s *target, platform_timeout_s *print_progess); #define MDM_POWER_EN ADIV5_DP_REG(0x01U) #define MDM_SELECT_AP ADIV5_DP_REG(0x02U) @@ -436,21 +440,19 @@ bool nrf51_mdm_probe(adiv5_access_port_s *ap) return true; } -static bool nrf51_mdm_mass_erase(target_s *t) +static bool nrf51_mdm_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - adiv5_access_port_s *ap = t->priv; + adiv5_access_port_s *const ap = cortex_ap(target); uint32_t status = adiv5_ap_read(ap, MDM_STATUS); adiv5_dp_write(ap->dp, MDM_POWER_EN, 0x50000000U); adiv5_dp_write(ap->dp, MDM_SELECT_AP, 0x01000000U); adiv5_ap_write(ap, MDM_CONTROL, 0x00000001U); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500U); // Read until 0, probably should have a timeout here... do { status = adiv5_ap_read(ap, MDM_STATUS); - target_print_progress(&timeout); + target_print_progress(print_progess); } while (status); // The second read will provide true prot status diff --git a/src/target/nxpke04.c b/src/target/nxpke04.c index f23e5bd2819..a78e1acb30a 100644 --- a/src/target/nxpke04.c +++ b/src/target/nxpke04.c @@ -118,7 +118,7 @@ static bool ke04_command(target_s *t, uint8_t cmd, uint32_t addr, const void *da static bool ke04_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool ke04_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); static bool ke04_flash_done(target_flash_s *f); -static bool ke04_mass_erase(target_s *t); +static bool ke04_mass_erase(target_s *target, platform_timeout_s *print_progess); /* Target specific commands */ static bool kinetis_cmd_unsafe(target_s *t, int argc, const char **argv); @@ -239,12 +239,15 @@ bool ke04_probe(target_s *t) return true; } -static bool ke04_mass_erase(target_s *t) +static bool ke04_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { + (void)print_progess; + /* FIXME: looks like this might belong to flash->mass erase */ + /* Erase and verify the whole flash */ - ke04_command(t, CMD_ERASE_ALL_BLOCKS, 0, NULL); + ke04_command(target, CMD_ERASE_ALL_BLOCKS, 0, NULL); /* Adjust security byte if needed */ - ke04_flash_done(t->flash); + ke04_flash_done(target->flash); return true; } diff --git a/src/target/rp.c b/src/target/rp.c index 744f27b5e49..a38450dc23e 100644 --- a/src/target/rp.c +++ b/src/target/rp.c @@ -235,7 +235,6 @@ bool rp_probe(target_s *target) } target->target_storage = (void *)priv_storage; - target->mass_erase = bmp_spi_mass_erase; target->driver = "Raspberry RP2040"; target->target_options |= CORTEXM_TOPT_INHIBIT_NRST; target->attach = rp_attach; diff --git a/src/target/samd.c b/src/target/samd.c index 6beb4cd73e2..df9e05aa7bd 100644 --- a/src/target/samd.c +++ b/src/target/samd.c @@ -44,7 +44,7 @@ static bool samd_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool samd_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); /* NB: This is not marked static on purpose as it's used by samx5x.c. */ -bool samd_mass_erase(target_s *t); +bool samd_mass_erase(target_s *target, platform_timeout_s *print_progess); static bool samd_cmd_lock_flash(target_s *t, int argc, const char **argv); static bool samd_cmd_unlock_flash(target_s *t, int argc, const char **argv); @@ -594,15 +594,15 @@ static bool samd_wait_nvm_ready(target_s *t) return true; } -static bool samd_wait_dsu_ready(target_s *const t, uint32_t *const result, platform_timeout_s *const timeout) +static bool samd_wait_dsu_ready(target_s *const t, uint32_t *const result, platform_timeout_s *const print_progress) { uint32_t status = 0; while ((status & (SAMD_STATUSA_DONE | SAMD_STATUSA_PERR | SAMD_STATUSA_FAIL)) == 0) { status = target_mem_read32(t, SAMD_DSU_CTRLSTAT); if (target_check_error(t)) return false; - if (timeout) - target_print_progress(timeout); + if (print_progress) + target_print_progress(print_progress); } *result = status; return true; @@ -659,23 +659,21 @@ static bool samd_flash_write(target_flash_s *f, target_addr_t dest, const void * } /* Uses the Device Service Unit to erase the entire flash */ -bool samd_mass_erase(target_s *t) +bool samd_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { /* Clear the DSU status bits */ - target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_STATUSA_DONE | SAMD_STATUSA_PERR | SAMD_STATUSA_FAIL); + target_mem_write32(target, SAMD_DSU_CTRLSTAT, SAMD_STATUSA_DONE | SAMD_STATUSA_PERR | SAMD_STATUSA_FAIL); /* Erase all */ - target_mem_write32(t, SAMD_DSU_CTRLSTAT, SAMD_CTRL_CHIP_ERASE); + target_mem_write32(target, SAMD_DSU_CTRLSTAT, SAMD_CTRL_CHIP_ERASE); uint32_t status = 0; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); - if (!samd_wait_dsu_ready(t, &status, &timeout)) + if (!samd_wait_dsu_ready(target, &status, print_progess)) return false; /* Test the protection error bit in Status A */ if (status & SAMD_STATUSA_PERR) { - tc_printf(t, "Erase failed due to a protection error.\n"); + tc_printf(target, "Erase failed due to a protection error.\n"); return true; } diff --git a/src/target/samx5x.c b/src/target/samx5x.c index bcf45e20b58..b99868c9ac9 100644 --- a/src/target/samx5x.c +++ b/src/target/samx5x.c @@ -52,7 +52,7 @@ static bool samx5x_cmd_ssb(target_s *t, int argc, const char **argv); static bool samx5x_cmd_update_user_word(target_s *t, int argc, const char **argv); /* (The SAM D1x/2x implementation of erase_all is reused as it's identical)*/ -bool samd_mass_erase(target_s *t); +extern bool samd_mass_erase(target_s *target, platform_timeout_s *print_progess); #define samx5x_mass_erase samd_mass_erase #ifdef SAMX5X_EXTRA_CMDS diff --git a/src/target/spi.c b/src/target/spi.c index 63d38dbe8b2..0e968d9edec 100644 --- a/src/target/spi.c +++ b/src/target/spi.c @@ -129,8 +129,10 @@ spi_flash_s *bmp_spi_add_flash(target_s *const target, const target_addr_t begin flash->start = begin; flash->length = spi_parameters.capacity; flash->blocksize = spi_parameters.sector_size; + /* FIXME: this can be optimized with prepare done! */ flash->write = bmp_spi_flash_write; flash->erase = bmp_spi_flash_erase; + flash->mass_erase = bmp_spi_mass_erase; flash->erased = 0xffU; target_add_flash(target, flash); @@ -142,29 +144,24 @@ spi_flash_s *bmp_spi_add_flash(target_s *const target, const target_addr_t begin return spi_flash; } -/* Note: These routines assume that the first Flash registered on the target is a SPI Flash device */ -bool bmp_spi_mass_erase(target_s *const target) +bool bmp_spi_mass_erase(target_flash_s *const flash, platform_timeout_s *const print_progess) { - /* Extract the Flash structure and set up timeouts */ - const spi_flash_s *const flash = (spi_flash_s *)target->flash; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); - DEBUG_TARGET("Running %s\n", __func__); - /* Go into Flash mode and tell the Flash to enable writing */ - target->enter_flash_mode(target); - flash->run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); - if (!(bmp_spi_read_status(target, flash) & SPI_FLASH_STATUS_WRITE_ENABLED)) { - target->exit_flash_mode(target); + target_s *const target = flash->t; + const spi_flash_s *const spi_flash = (spi_flash_s *)flash; + + /* Tell the Flash to enable writing */ + spi_flash->run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); + if (!(bmp_spi_read_status(target, spi_flash) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; - } - /* Execute a full chip erase and wait for the operatoin to complete */ - flash->run_command(target, SPI_FLASH_CMD_CHIP_ERASE, 0U); - while (bmp_spi_read_status(target, flash) & SPI_FLASH_STATUS_BUSY) - target_print_progress(&timeout); + /* Execute a full chip erase and wait for the operation to complete */ + spi_flash->run_command(target, SPI_FLASH_CMD_CHIP_ERASE, 0U); + while (bmp_spi_read_status(target, spi_flash) & SPI_FLASH_STATUS_BUSY) { + if (print_progess) + target_print_progress(print_progess); + } - /* Finally, leave Flash mode to conclude business */ - return target->exit_flash_mode(target); + return true; } static bool bmp_spi_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) diff --git a/src/target/spi.h b/src/target/spi.h index 410841c3403..b4a2e4f73ae 100644 --- a/src/target/spi.h +++ b/src/target/spi.h @@ -92,6 +92,6 @@ void bmp_spi_run_command(spi_bus_e bus, uint8_t device, uint16_t command, target spi_flash_s *bmp_spi_add_flash(target_s *target, target_addr_t begin, size_t length, spi_read_func spi_read, spi_write_func spi_write, spi_run_command_func spi_run_command); -bool bmp_spi_mass_erase(target_s *target); +bool bmp_spi_mass_erase(target_flash_s *flash, platform_timeout_s *print_progess); #endif /* TARGET_SPI_H */ diff --git a/src/target/stm32f4.c b/src/target/stm32f4.c index 54d3c2f4633..f40b4b6afbb 100644 --- a/src/target/stm32f4.c +++ b/src/target/stm32f4.c @@ -52,7 +52,7 @@ static bool stm32f4_attach(target_s *t); static void stm32f4_detach(target_s *t); static bool stm32f4_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool stm32f4_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool stm32f4_mass_erase(target_s *t); +static bool stm32f4_mass_erase(target_s *target, platform_timeout_s *print_progess); /* Flash Program and Erase Controller Register Map */ #define FPEC_BASE 0x40023c00U @@ -532,21 +532,19 @@ static bool stm32f4_flash_write(target_flash_s *f, target_addr_t dest, const voi return stm32f4_flash_busy_wait(t, NULL); } -static bool stm32f4_mass_erase(target_s *t) +static bool stm32f4_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { /* XXX: Is it correct to grab the most recently added Flash region here? What is this really trying to do? */ - stm32f4_flash_s *sf = (stm32f4_flash_s *)t->flash; - stm32f4_flash_unlock(t); + stm32f4_flash_s *stm32f4_flash = (stm32f4_flash_s *)target->flash; + stm32f4_flash_unlock(target); /* Flash mass erase start instruction */ - const uint32_t ctrl = FLASH_CR_MER | (sf->bank_split ? FLASH_CR_MER1 : 0); - target_mem_write32(t, FLASH_CR, ctrl); - target_mem_write32(t, FLASH_CR, ctrl | FLASH_CR_STRT); + const uint32_t ctrl = FLASH_CR_MER | (stm32f4_flash->bank_split ? FLASH_CR_MER1 : 0); + target_mem_write32(target, FLASH_CR, ctrl); + target_mem_write32(target, FLASH_CR, ctrl | FLASH_CR_STRT); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Wait for completion or an error */ - return stm32f4_flash_busy_wait(t, &timeout); + return stm32f4_flash_busy_wait(target, print_progess); } /* diff --git a/src/target/stm32g0.c b/src/target/stm32g0.c index 83451529adb..5ff4093e6d1 100644 --- a/src/target/stm32g0.c +++ b/src/target/stm32g0.c @@ -177,7 +177,7 @@ static bool stm32g0_attach(target_s *t); static void stm32g0_detach(target_s *t); static bool stm32g0_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool stm32g0_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool stm32g0_mass_erase(target_s *t); +static bool stm32g0_mass_erase(target_s *target, platform_timeout_s *print_progess); /* Custom commands */ static bool stm32g0_cmd_erase_bank(target_s *t, int argc, const char **argv); @@ -478,24 +478,22 @@ static bool stm32g0_flash_write(target_flash_s *f, target_addr_t dest, const voi return true; } -static bool stm32g0_mass_erase(target_s *t) +static bool stm32g0_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { const uint32_t ctrl = FLASH_CR_MER1 | FLASH_CR_MER2 | FLASH_CR_START; - stm32g0_flash_unlock(t); - target_mem_write32(t, FLASH_CR, ctrl); + stm32g0_flash_unlock(target); + target_mem_write32(target, FLASH_CR, ctrl); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Wait for completion or an error */ - if (!stm32g0_wait_busy(t, &timeout)) { - stm32g0_flash_op_finish(t); + if (!stm32g0_wait_busy(target, print_progess)) { + stm32g0_flash_op_finish(target); return false; } /* Check for error */ - const uint16_t status = target_mem_read32(t, FLASH_SR); - stm32g0_flash_op_finish(t); + const uint16_t status = target_mem_read32(target, FLASH_SR); + stm32g0_flash_op_finish(target); return !(status & FLASH_SR_ERROR_MASK); } diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index a31bb08a628..31326d814cf 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -128,7 +128,7 @@ static bool stm32h5_enter_flash_mode(target_s *target); static bool stm32h5_exit_flash_mode(target_s *target); static bool stm32h5_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); static bool stm32h5_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); -static bool stm32h5_mass_erase(target_s *target); +static bool stm32h5_mass_erase(target_s *target, platform_timeout_s *print_progess); static void stm32h5_add_flash( target_s *const target, const uint32_t base_addr, const size_t length, const uint32_t bank_and_sector_count) @@ -282,22 +282,13 @@ static bool stm32h5_flash_write( return true; } -static bool stm32h5_mass_erase(target_s *const target) +static bool stm32h5_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - /* To start mass erase, enter into Flash mode */ - if (!stm32h5_enter_flash_mode(target)) - return false; - - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Trigger the mass erase */ target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_MASS_ERASE); target_mem_write32(target, STM32H5_FLASH_CTRL, STM32H5_FLASH_CTRL_MASS_ERASE | STM32H5_FLASH_CTRL_START); /* And wait for it to complete, reporting errors along the way */ - const bool result = stm32h5_flash_wait_complete(target, &timeout); - - /* When done, leave Flash mode */ - return stm32h5_exit_flash_mode(target) && result; + return stm32h5_flash_wait_complete(target, print_progess); } static bool stm32h5_cmd_uid(target_s *target, int argc, const char **argv) diff --git a/src/target/stm32h7.c b/src/target/stm32h7.c index cacbb5eee67..53b8371b361 100644 --- a/src/target/stm32h7.c +++ b/src/target/stm32h7.c @@ -160,7 +160,7 @@ static bool stm32h7_attach(target_s *target); static void stm32h7_detach(target_s *target); static bool stm32h7_flash_erase(target_flash_s *target_flash, target_addr_t addr, size_t len); static bool stm32h7_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len); -static bool stm32h7_mass_erase(target_s *target); +static bool stm32h7_mass_erase(target_s *target, platform_timeout_s *print_progess); static void stm32h7_add_flash(target_s *target, uint32_t addr, size_t length, size_t blocksize) { @@ -398,7 +398,7 @@ static bool stm32h7_check_bank(target_s *const target, const uint32_t reg_base) } /* Both banks are erased in parallel.*/ -static bool stm32h7_mass_erase(target_s *target) +static bool stm32h7_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { align_e psize = ALIGN_64BIT; /* @@ -415,11 +415,9 @@ static bool stm32h7_mass_erase(target_s *target) !stm32h7_erase_bank(target, psize, STM32H7_FLASH_BANK2_BASE, FPEC2_BASE)) return false; - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Wait for the banks to finish erasing */ - if (!stm32h7_wait_erase_bank(target, &timeout, FPEC1_BASE) || - !stm32h7_wait_erase_bank(target, &timeout, FPEC2_BASE)) + if (!stm32h7_wait_erase_bank(target, print_progess, FPEC1_BASE) || + !stm32h7_wait_erase_bank(target, print_progess, FPEC2_BASE)) return false; /* Check the banks for final errors */ diff --git a/src/target/stm32l0.c b/src/target/stm32l0.c index 9bf392c71d7..a8904d4fed6 100644 --- a/src/target/stm32l0.c +++ b/src/target/stm32l0.c @@ -131,8 +131,7 @@ static bool stm32lx_nvm_data_erase(target_flash_s *flash, target_addr_t addr, si static bool stm32lx_nvm_data_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); static bool stm32lx_protected_attach(target_s *target); -static bool stm32lx_protected_mass_erase(target_s *target); -static bool stm32lx_mass_erase(target_s *target); +static bool stm32lx_protected_mass_erase(target_s *target, platform_timeout_s *print_progess); static bool stm32lx_cmd_option(target_s *target, int argc, const char **argv); static bool stm32lx_cmd_eeprom(target_s *target, int argc, const char **argv); @@ -263,8 +262,7 @@ bool stm32l0_probe(target_s *const target) if (protected) { target->attach = stm32lx_protected_attach; target->mass_erase = stm32lx_protected_mass_erase; - } else - target->mass_erase = stm32lx_mass_erase; + } return true; } @@ -457,7 +455,7 @@ static bool stm32lx_protected_attach(target_s *const target) return true; } -static bool stm32lx_protected_mass_erase(target_s *const target) +static bool stm32lx_protected_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { const uint32_t nvm = stm32lx_nvm_phys(target); if (!stm32lx_nvm_opt_unlock(target, nvm)) @@ -468,27 +466,14 @@ static bool stm32lx_protected_mass_erase(target_s *const target) target_mem_write32(target, STM32Lx_NVM_OPT_PHYS, 0xff5500aaU); target_mem_write32(target, STM32Lx_NVM_PECR(nvm), STM32Lx_NVM_PECR_OBL_LAUNCH); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); - while (target_mem_read32(target, STM32Lx_NVM_SR(nvm)) & STM32Lx_NVM_SR_BSY) - target_print_progress(&timeout); + target_print_progress(print_progess); /* Disable further programming by locking PECR */ stm32lx_nvm_lock(target, nvm); return true; } -static bool stm32lx_mass_erase(target_s *const target) -{ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) { - const int result = stm32lx_nvm_prog_erase(flash, flash->start, flash->length); - if (result != 0) - return false; - } - return true; -} - /* * Write one option word. * The address is the physical address of the word and the value is a complete word value. diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 70eae2123d6..a39dbec1028 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -62,7 +62,7 @@ static bool stm32l4_attach(target_s *t); static void stm32l4_detach(target_s *t); static bool stm32l4_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); static bool stm32l4_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); -static bool stm32l4_mass_erase(target_s *t); +static bool stm32l4_mass_erase(target_s *target, platform_timeout_s *print_progess); const command_s stm32l4_cmd_list[] = { {"erase_bank1", stm32l4_cmd_erase_bank1, "Erase entire bank1 flash memory"}, @@ -768,7 +768,7 @@ static bool stm32l4_flash_write(target_flash_s *f, target_addr_t dest, const voi return stm32l4_flash_busy_wait(t, NULL); } -static bool stm32l4_cmd_erase(target_s *const t, const uint32_t action) +static bool stm32l4_cmd_erase(target_s *const t, const uint32_t action, platform_timeout_s *const print_progess) { stm32l4_flash_unlock(t); /* Erase time is 25 ms. Timeout logic shouldn't get fired.*/ @@ -776,15 +776,13 @@ static bool stm32l4_cmd_erase(target_s *const t, const uint32_t action) stm32l4_flash_write32(t, FLASH_CR, action); stm32l4_flash_write32(t, FLASH_CR, action | FLASH_CR_STRT); - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); /* Wait for completion or an error */ - return stm32l4_flash_busy_wait(t, &timeout); + return stm32l4_flash_busy_wait(t, print_progess); } -static bool stm32l4_mass_erase(target_s *const t) +static bool stm32l4_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - return stm32l4_cmd_erase(t, FLASH_CR_MER1 | FLASH_CR_MER2); + return stm32l4_cmd_erase(target, FLASH_CR_MER1 | FLASH_CR_MER2, print_progess); } static bool stm32l4_cmd_erase_bank1(target_s *const t, const int argc, const char **const argv) @@ -792,7 +790,7 @@ static bool stm32l4_cmd_erase_bank1(target_s *const t, const int argc, const cha (void)argc; (void)argv; gdb_outf("Erasing bank %u: ", 1); - const bool result = stm32l4_cmd_erase(t, FLASH_CR_MER1); + const bool result = stm32l4_cmd_erase(t, FLASH_CR_MER1, NULL); gdb_out("done\n"); return result; } @@ -802,7 +800,7 @@ static bool stm32l4_cmd_erase_bank2(target_s *const t, const int argc, const cha (void)argc; (void)argv; gdb_outf("Erasing bank %u: ", 2); - const bool result = stm32l4_cmd_erase(t, FLASH_CR_MER2); + const bool result = stm32l4_cmd_erase(t, FLASH_CR_MER2, NULL); gdb_out("done\n"); return result; } diff --git a/src/target/target.c b/src/target/target.c index 3396d99c469..b2d490732d8 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -436,13 +436,11 @@ static bool target_cmd_mass_erase(target_s *const t, const int argc, const char { (void)argc; (void)argv; - if (!t || !t->mass_erase) { - gdb_out("Mass erase not implemented for target_s"); - return true; - } + gdb_out("Erasing device Flash: "); - const bool result = t->mass_erase(t); + const bool result = target_flash_mass_erase(t); gdb_out("done\n"); + return result; } diff --git a/src/target/target_flash.c b/src/target/target_flash.c index bacd19a1c51..ef33b47df23 100644 --- a/src/target/target_flash.c +++ b/src/target/target_flash.c @@ -154,18 +154,29 @@ bool target_flash_erase(target_s *target, target_addr_t addr, size_t len) active_flash = flash; } + /* Align the start address to the erase block size */ const target_addr_t local_start_addr = addr & ~(flash->blocksize - 1U); - const target_addr_t local_end_addr = local_start_addr + flash->blocksize; - if (!flash_prepare(flash, FLASH_OPERATION_ERASE)) + /* Check if we can use mass erase */ + const bool can_use_mass_erase = flash->mass_erase != NULL && local_start_addr == flash->start && + (addr + len) >= (flash->start + flash->length); + + /* Calculate the address at the end of the erase block */ + const target_addr_t local_end_addr = + can_use_mass_erase ? flash->start + flash->length : local_start_addr + flash->blocksize; + + if (!flash_prepare(flash, can_use_mass_erase ? FLASH_OPERATION_MASS_ERASE : FLASH_OPERATION_ERASE)) return false; - result &= flash->erase(flash, local_start_addr, flash->blocksize); + /* Erase flash, either a single aligned block size or a full mass erase */ + result &= can_use_mass_erase ? flash->mass_erase(flash, NULL) : + flash->erase(flash, local_start_addr, flash->blocksize); if (!result) { DEBUG_ERROR("Erase failed at %" PRIx32 "\n", local_start_addr); break; } + /* Update the remaining length and address, taking into account the alignment */ len -= MIN(local_end_addr - addr, len); addr = local_end_addr; } @@ -174,6 +185,65 @@ bool target_flash_erase(target_s *target, target_addr_t addr, size_t len) return result; } +static inline bool flash_manual_mass_erase(target_flash_s *const flash, platform_timeout_s *const print_progess) +{ + for (target_addr_t addr = flash->start; addr < (flash->start + flash->length); addr += flash->blocksize) { + if (!flash->erase(flash, addr, flash->blocksize)) + return false; + target_print_progress(print_progess); + } + return true; +} + +/* Run specialized target mass erase if available, otherwise erase all flash' */ +bool target_flash_mass_erase(target_s *const target) +{ + if (!target_enter_flash_mode(target)) + return false; + + /* Setup progress printout */ + platform_timeout_s print_progess; + platform_timeout_set(&print_progess, 500U); + + bool result = false; + if (target->mass_erase) { + DEBUG_INFO("Running specialized target mass erase\n"); + + /* Run specialized target mass erase */ + result = target->mass_erase(target, &print_progess); + } else { + DEBUG_WARN("No specialized target mass erase available, erasing all flash\n"); + + /* Erase all target flash */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + /* If the flash has a mass erase function, use it */ + const bool can_use_mass_erase = flash->mass_erase != NULL; + + if (can_use_mass_erase) + DEBUG_INFO("Running specialized flash mass erase for flash 0x%08" PRIx32 "\n", flash->start); + else + DEBUG_WARN("No specialized flash mass erase available for 0x%08" PRIx32 "\n", flash->start); + + result = flash_prepare(flash, can_use_mass_erase ? FLASH_OPERATION_MASS_ERASE : FLASH_OPERATION_ERASE); + if (!result) { + DEBUG_ERROR("Failed to prepare flash 0x%08" PRIx32 " for mass erase\n", flash->start); + break; + } + + result = can_use_mass_erase ? flash->mass_erase(flash, &print_progess) : + flash_manual_mass_erase(flash, &print_progess); + result &= flash_done(flash); /* Don't overwrite previous result, AND with it instead */ + if (!result) { + DEBUG_ERROR("Failed to mass erase flash 0x%08" PRIx32 "\n", flash->start); + break; + } + } + } + + target_exit_flash_mode(target); + return result; +} + bool flash_buffer_alloc(target_flash_s *flash) { /* Allocate buffer */ diff --git a/src/target/target_internal.h b/src/target/target_internal.h index f78cf00d300..4e7e7d85a13 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -33,6 +33,7 @@ target_s *target_new(void); typedef enum flash_operation { FLASH_OPERATION_NONE, FLASH_OPERATION_ERASE, + FLASH_OPERATION_MASS_ERASE, FLASH_OPERATION_WRITE, } flash_operation_e; @@ -48,27 +49,29 @@ typedef struct target_flash target_flash_s; typedef bool (*flash_prepare_func)(target_flash_s *flash); typedef bool (*flash_erase_func)(target_flash_s *flash, target_addr_t addr, size_t len); +typedef bool (*flash_mass_erase_func)(target_flash_s *flash, platform_timeout_s *print_progess); typedef bool (*flash_write_func)(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); typedef bool (*flash_done_func)(target_flash_s *flash); struct target_flash { - target_s *t; /* Target this flash is attached to */ - target_addr_t start; /* Start address of flash */ - size_t length; /* Flash length */ - size_t blocksize; /* Erase block size */ - size_t writesize; /* Write operation size, must be <= blocksize/writebufsize */ - size_t writebufsize; /* Size of write buffer, this is calculated and not set in target code */ - uint8_t erased; /* Byte erased state */ - uint8_t operation; /* Current Flash operation (none means it's idle/unprepared) */ - flash_prepare_func prepare; /* Prepare for flash operations */ - flash_erase_func erase; /* Erase a range of flash */ - flash_write_func write; /* Write to flash */ - flash_done_func done; /* Finish flash operations */ - void *buf; /* Buffer for flash operations */ - target_addr_t buf_addr_base; /* Address of block this buffer is for */ - target_addr_t buf_addr_low; /* Address of lowest byte written */ - target_addr_t buf_addr_high; /* Address of highest byte written */ - target_flash_s *next; /* Next flash in list */ + target_s *t; /* Target this flash is attached to */ + target_addr_t start; /* Start address of flash */ + size_t length; /* Flash length */ + size_t blocksize; /* Erase block size */ + size_t writesize; /* Write operation size, must be <= blocksize/writebufsize */ + size_t writebufsize; /* Size of write buffer, this is calculated and not set in target code */ + uint8_t erased; /* Byte erased state */ + uint8_t operation; /* Current Flash operation (none means it's idle/unprepared) */ + flash_prepare_func prepare; /* Prepare for flash operations */ + flash_erase_func erase; /* Erase a range of flash */ + flash_mass_erase_func mass_erase; /* Mass erase flash (this flash only) */ + flash_write_func write; /* Write to flash */ + flash_done_func done; /* Finish flash operations */ + void *buf; /* Buffer for flash operations */ + target_addr_t buf_addr_base; /* Address of block this buffer is for */ + target_addr_t buf_addr_low; /* Address of lowest byte written */ + target_addr_t buf_addr_high; /* Address of highest byte written */ + target_flash_s *next; /* Next flash in list */ }; typedef bool (*cmd_handler_fn)(target_s *target, int argc, const char **argv); @@ -132,7 +135,7 @@ struct target { breakwatch_s *bw_list; /* Recovery functions */ - bool (*mass_erase)(target_s *target); + bool (*mass_erase)(target_s *target, platform_timeout_s *print_progess); /* Mass erase all target flash */ /* Flash functions */ bool (*enter_flash_mode)(target_s *target); From 531b85b9bb20884ba2da3300fac53a13e5574141 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 8 Sep 2023 00:17:55 +0100 Subject: [PATCH 124/130] esp32c3: refactor with new mass erase target API --- src/target/esp32c3.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c index 1f23e062490..5b7122940b1 100644 --- a/src/target/esp32c3.c +++ b/src/target/esp32c3.c @@ -175,7 +175,7 @@ static void esp32c3_spi_read(target_s *target, uint32_t command, target_addr_t a static void esp32c3_spi_write( target_s *target, uint32_t command, target_addr_t address, const void *buffer, size_t length); -static bool esp32c3_mass_erase(target_s *target); +static bool esp32c3_mass_erase(target_s *target, platform_timeout_s *print_progess); static bool esp32c3_enter_flash_mode(target_s *target); static bool esp32c3_exit_flash_mode(target_s *target); static bool esp32c3_spi_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); @@ -451,17 +451,15 @@ static inline void esp32c3_spi_run_command(target_s *const target, const uint32_ esp32c3_spi_wait_complete(target); } -static bool esp32c3_mass_erase(target_s *const target) +static bool esp32c3_mass_erase(target_s *const target, platform_timeout_s *const print_progess) { - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500U); esp32c3_spi_run_command(target, SPI_FLASH_CMD_WRITE_ENABLE, 0U); if (!(esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_WRITE_ENABLED)) return false; esp32c3_spi_run_command(target, SPI_FLASH_CMD_CHIP_ERASE, 0U); while (esp32c3_spi_read_status(target) & SPI_FLASH_STATUS_BUSY) - target_print_progress(&timeout); + target_print_progress(print_progess); return true; } From 7e548c9f492bfc55781ba512f96d70faecfe245d Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 22:38:00 +0100 Subject: [PATCH 125/130] ch32vx: implement electronic signature (ESIG) register handling --- src/target/ch32vx.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c index c85973427ca..52e20ac926d 100644 --- a/src/target/ch32vx.c +++ b/src/target/ch32vx.c @@ -23,6 +23,7 @@ #include "general.h" #include "target.h" #include "target_internal.h" +#include "buffer_utils.h" /* IDCODE register */ #define CH32VX_IDCODE 0x1ffff704U @@ -36,6 +37,30 @@ #define CH32V303_IDCODE_FAMILY 0x303U #define CH32V307_IDCODE_FAMILY 0x307U +/* Electronic Signature (ESIG) registers */ +#define CH32VX_ESIG_FLASH_CAP 0x1ffff7e0U /* Flash capacity register, 16 bits, KiB units */ +#define CH32VX_ESIG_UID1 0x1ffff7e8U /* Unique ID register, bits 0:31 */ +#define CH32VX_ESIG_UID2 0x1ffff7ecU /* Unique ID register, bits 32:63 */ +#define CH32VX_ESIG_UID3 0x1ffff7f0U /* Unique ID register, bits 64:95 */ + +static bool ch32vx_uid_cmd(target_s *t, int argc, const char **argv); + +const command_s ch32vx_cmd_list[] = { + {"uid", ch32vx_uid_cmd, "Prints 96 bit unique id"}, + {NULL, NULL, NULL}, +}; + +static uint32_t ch32vx_read_flash_size(target_s *const t) +{ + return target_mem_read16(t, CH32VX_ESIG_FLASH_CAP) * 1024U; +} + +static void ch32vx_read_uid(target_s *const t, uint8_t *const uid) +{ + for (size_t uid_reg_offset = 0; uid_reg_offset < 12U; uid_reg_offset += 4U) + write_be4(uid, uid_reg_offset, target_mem_read32(t, CH32VX_ESIG_UID1 + uid_reg_offset)); +} + bool ch32vx_probe(target_s *const target) { const uint32_t idcode = target_mem_read32(target, CH32VX_IDCODE); @@ -73,7 +98,28 @@ bool ch32vx_probe(target_s *const target) break; } + DEBUG_WARN("CH32Vx flash size: %u\n", ch32vx_read_flash_size(target)); + target->part_id = idcode; + target_add_commands(target, ch32vx_cmd_list, "CH32Vx"); + + return true; +} + +/* Reads the 96 bit unique id */ +static bool ch32vx_uid_cmd(target_s *const target, const int argc, const char **const argv) +{ + (void)argc; + (void)argv; + + uint8_t uid[12U]; + ch32vx_read_uid(target, uid); + + tc_printf(target, "Unique id: 0x"); + for (size_t i = 0U; i < sizeof(uid); i++) + tc_printf(target, "%02" PRIx8, uid[i]); + tc_printf(target, "\n"); + return true; } From 147ea819adaf2ce2fb31d674259ae98035b5dedb Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 23:29:55 +0100 Subject: [PATCH 126/130] target/ch32f1: replace macros with functions --- src/target/ch32f1.c | 155 ++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 72 deletions(-) diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c index 98398d1bbd5..514e8707ab7 100644 --- a/src/target/ch32f1.c +++ b/src/target/ch32f1.c @@ -1,8 +1,9 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2023 1BitSquared * Written by mean00 + * Modified by Rafael Silva * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -65,6 +66,11 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void #define FLASH_SR_EOP (1U << 5U) // End of programming #define FLASH_BEGIN_ADDRESS_CH32 0x8000000U +// Which one is the right value? +#define MAGIC_WORD 0x100U + +// #define MAGIC_WORD 0x1000U + /* "fast" Flash driver for CH32F10x chips */ static void ch32f1_add_flash(target_s *t, uint32_t addr, size_t length, size_t erasesize) { @@ -84,46 +90,51 @@ static void ch32f1_add_flash(target_s *t, uint32_t addr, size_t length, size_t e target_add_flash(t, f); } -#define WAIT_BUSY() \ - do { \ - sr = target_mem_read32(t, FLASH_SR); \ - if (target_check_error(t)) { \ - DEBUG_ERROR("ch32f1 flash write: comm error\n"); \ - return -1; \ - } \ - } while (sr & FLASH_SR_BSY); - -#define WAIT_EOP() \ - do { \ - sr = target_mem_read32(t, FLASH_SR); \ - if (target_check_error(t)) { \ - DEBUG_ERROR("ch32f1 flash write: comm error\n"); \ - return -1; \ - } \ - } while (!(sr & FLASH_SR_EOP)); - -#define CLEAR_EOP() target_mem_write32(t, FLASH_SR, FLASH_SR_EOP) - -#define SET_CR(bit) \ - do { \ - const uint32_t cr = target_mem_read32(t, FLASH_CR) | (bit); \ - target_mem_write32(t, FLASH_CR, cr); \ - } while (0) - -#define CLEAR_CR(bit) \ - do { \ - const uint32_t cr = target_mem_read32(t, FLASH_CR) & (~(bit)); \ - target_mem_write32(t, FLASH_CR, cr); \ - } while (0) +static bool ch32f1_flash_busy_wait(target_s *const target) +{ + /* Read FLASH_SR to poll for BSY bit */ + uint32_t status = FLASH_SR_BSY; + while (status & FLASH_SR_BSY) { + status = target_mem_read32(target, FLASH_SR); + if (target_check_error(target)) { + DEBUG_ERROR("Lost communications with target"); + return false; + } + } + return true; +} -// Which one is the right value? -#define MAGIC_WORD 0x100U -// #define MAGIC_WORD 0x1000U -#define MAGIC(addr) \ - do { \ - magic = target_mem_read32(t, (addr) ^ MAGIC_WORD); \ - target_mem_write32(t, FLASH_MAGIC, magic); \ - } while (0) +static bool ch32f1_flash_eop_wait(target_s *const target) +{ + /* Read FLASH_SR to poll for EOP bit */ + uint32_t status = FLASH_SR_EOP; + while (status & FLASH_SR_EOP) { + status = target_mem_read32(target, FLASH_SR); + if (target_check_error(target)) { + DEBUG_ERROR("Lost communications with target"); + return false; + } + } + return true; +} + +static void ch32f1_flash_set_cr(target_s *const target, const uint32_t bits) +{ + const uint32_t cr = target_mem_read32(target, FLASH_CR); + target_mem_write32(target, FLASH_CR, cr | bits); +} + +static void ch32f1_flash_clear_cr(target_s *const target, const uint32_t bits) +{ + const uint32_t cr = target_mem_read32(target, FLASH_CR); + target_mem_write32(target, FLASH_CR, cr & ~bits); +} + +static void ch32f1_flash_magic(target_s *const target, const uint32_t addr) +{ + const uint32_t magic = target_mem_read32(target, addr ^ MAGIC_WORD); + target_mem_write32(target, FLASH_MAGIC, magic); +} /* Attempt unlock ch32f103 in fast mode */ static bool ch32f1_flash_unlock(target_s *t) @@ -151,7 +162,7 @@ static bool ch32f1_flash_lock(target_s *t) * The LOCK (bit 7) and FLOCK (bit 15) must be set (1) in the same write * operation, if not FLOCK will be read back as unset (0). */ - SET_CR(FLASH_CR_LOCK | FLASH_CR_FLOCK_CH32); + ch32f1_flash_set_cr(t, FLASH_CR_LOCK | FLASH_CR_FLOCK_CH32); const uint32_t cr = target_mem_read32(t, FLASH_CR); if (!(cr & FLASH_CR_FLOCK_CH32)) DEBUG_ERROR("Fast lock failed, cr: 0x%08" PRIx32 "\n", cr); @@ -165,7 +176,7 @@ static bool ch32f1_has_fast_unlock(target_s *t) { DEBUG_INFO("CH32: has fast unlock \n"); // reset fast unlock - SET_CR(FLASH_CR_FLOCK_CH32); + ch32f1_flash_set_cr(t, FLASH_CR_FLOCK_CH32); platform_delay(1); // The flash controller is timing sensitive if (!(target_mem_read32(t, FLASH_CR) & FLASH_CR_FLOCK_CH32)) return false; @@ -225,7 +236,6 @@ bool ch32f1_probe(target_s *t) /* Fast erase of CH32 devices */ bool ch32f1_flash_erase(target_flash_s *f, target_addr_t addr, size_t len) { - volatile uint32_t sr, magic; target_s *t = f->t; DEBUG_INFO("CH32: flash erase \n"); @@ -235,23 +245,24 @@ bool ch32f1_flash_erase(target_flash_s *f, target_addr_t addr, size_t len) } // Fast Erase 128 bytes pages (ch32 mode) while (len) { - SET_CR(FLASH_CR_FTER_CH32); // CH32 PAGE_ER + ch32f1_flash_set_cr(t, FLASH_CR_FTER_CH32); // CH32 PAGE_ER /* Write address to FMA */ target_mem_write32(t, FLASH_AR, addr); /* Flash page erase start instruction */ - SET_CR(FLASH_CR_STRT); - WAIT_EOP(); - CLEAR_EOP(); - CLEAR_CR(FLASH_CR_STRT); + ch32f1_flash_set_cr(t, FLASH_CR_STRT); + if (!ch32f1_flash_eop_wait(t)) + return -1; + target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(t, FLASH_CR_STRT); // Magic - MAGIC(addr); + ch32f1_flash_magic(t, addr); if (len > 128U) len -= 128U; else len = 0; addr += 128U; } - sr = target_mem_read32(t, FLASH_SR); + const uint32_t sr = target_mem_read32(t, FLASH_SR); ch32f1_flash_lock(t); if (sr & SR_ERROR_MASK) DEBUG_ERROR("ch32f1 flash erase error 0x%" PRIx32 "\n", sr); @@ -281,31 +292,31 @@ static bool ch32f1_wait_flash_ready(target_s *t, uint32_t addr) /* Fast flash for ch32. Load 128 bytes chunk and then write them */ static int ch32f1_upload(target_s *t, uint32_t dest, const void *src, uint32_t offset) { - volatile uint32_t sr, magic; const uint32_t *ss = (const uint32_t *)(src + offset); uint32_t dd = dest + offset; - SET_CR(FLASH_CR_FTPG_CH32); + ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); target_mem_write32(t, dd + 0, ss[0]); target_mem_write32(t, dd + 4U, ss[1]); target_mem_write32(t, dd + 8U, ss[2]); target_mem_write32(t, dd + 12U, ss[3]); - SET_CR(FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ - WAIT_EOP(); - CLEAR_EOP(); - CLEAR_CR(FLASH_CR_FTPG_CH32); - MAGIC(dest + offset); + ch32f1_flash_set_cr(t, FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ + if (!ch32f1_flash_eop_wait(t)) + return -1; + target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); + ch32f1_flash_magic(t, dest + offset); return 0; } /* Clear the write buffer */ static int ch32f1_buffer_clear(target_s *t) { - volatile uint32_t sr; - SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4- - SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- - WAIT_BUSY(); // 6- - CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4- + ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); // Fast page program 4- + ch32f1_flash_set_cr(t, FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- + if (!ch32f1_flash_busy_wait(t)) + return -1; + ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); // Fast page program 4- return 0; } @@ -314,7 +325,6 @@ static int ch32f1_buffer_clear(target_s *t) */ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len) { - volatile uint32_t sr; target_s *t = f->t; size_t length = len; #ifdef CH32_VERIFY @@ -328,7 +338,8 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void DEBUG_ERROR("ch32f1 cannot fast unlock\n"); return false; } - WAIT_BUSY(); + if (!ch32f1_flash_busy_wait(t)) + return -1; // Buffer reset... ch32f1_buffer_clear(t); @@ -343,15 +354,15 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void } } // write buffer - SET_CR(FLASH_CR_FTPG_CH32); + ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); target_mem_write32(t, FLASH_AR, dest); // 10 - SET_CR(FLASH_CR_STRT); // 11 Start - WAIT_EOP(); // 12 - CLEAR_EOP(); - CLEAR_CR(FLASH_CR_FTPG_CH32); + ch32f1_flash_set_cr(t, FLASH_CR_STRT); // 11 Start + if (!ch32f1_flash_eop_wait(t)) // 12 + return -1; + target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); - uint32_t magic; - MAGIC(dest); + ch32f1_flash_magic(t, dest); // next if (length > 128U) @@ -361,7 +372,7 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void dest += 128U; src += 128U; - sr = target_mem_read32(t, FLASH_SR); // 13 + const uint32_t sr = target_mem_read32(t, FLASH_SR); // 13 ch32f1_flash_lock(t); if (sr & SR_ERROR_MASK) { DEBUG_ERROR("ch32f1 flash write error 0x%" PRIx32 "\n", sr); From 23e48eb5de2ee5ae0bf8876cbec060c7f9fceb74 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 23:38:16 +0100 Subject: [PATCH 127/130] target/ch32f1: longer variable naming --- src/target/ch32f1.c | 172 ++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c index 514e8707ab7..060332ecd94 100644 --- a/src/target/ch32f1.c +++ b/src/target/ch32f1.c @@ -35,8 +35,8 @@ extern const command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff -static bool ch32f1_flash_erase(target_flash_s *f, target_addr_t addr, size_t len); -static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len); +static bool ch32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); // These are common with stm32f1/gd32f1/... #define FPEC_BASE 0x40022000U @@ -72,22 +72,22 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void // #define MAGIC_WORD 0x1000U /* "fast" Flash driver for CH32F10x chips */ -static void ch32f1_add_flash(target_s *t, uint32_t addr, size_t length, size_t erasesize) +static void ch32f1_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize) { - target_flash_s *f = calloc(1, sizeof(*f)); - if (!f) { /* calloc failed: heap exhaustion */ + target_flash_s *const flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ DEBUG_ERROR("calloc: failed in %s\n", __func__); return; } - f->start = addr; - f->length = length; - f->blocksize = erasesize; - f->erase = ch32f1_flash_erase; - f->write = ch32f1_flash_write; - f->writesize = erasesize; - f->erased = 0xffU; - target_add_flash(t, f); + flash->start = addr; + flash->length = length; + flash->blocksize = erasesize; + flash->erase = ch32f1_flash_erase; + flash->write = ch32f1_flash_write; + flash->writesize = erasesize; + flash->erased = 0xffU; + target_add_flash(target, flash); } static bool ch32f1_flash_busy_wait(target_s *const target) @@ -137,16 +137,16 @@ static void ch32f1_flash_magic(target_s *const target, const uint32_t addr) } /* Attempt unlock ch32f103 in fast mode */ -static bool ch32f1_flash_unlock(target_s *t) +static bool ch32f1_flash_unlock(target_s *target) { DEBUG_INFO("CH32: flash unlock \n"); - target_mem_write32(t, FLASH_KEYR, KEY1); - target_mem_write32(t, FLASH_KEYR, KEY2); + target_mem_write32(target, FLASH_KEYR, KEY1); + target_mem_write32(target, FLASH_KEYR, KEY2); // fast mode - target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY1); - target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY2); - uint32_t cr = target_mem_read32(t, FLASH_CR); + target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY1); + target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY2); + uint32_t cr = target_mem_read32(target, FLASH_CR); if (cr & FLASH_CR_FLOCK_CH32) DEBUG_ERROR("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr); return !(cr & FLASH_CR_FLOCK_CH32); @@ -155,15 +155,15 @@ static bool ch32f1_flash_unlock(target_s *t) /* * lock ch32f103 in fast mode */ -static bool ch32f1_flash_lock(target_s *t) +static bool ch32f1_flash_lock(target_s *target) { DEBUG_INFO("CH32: flash lock \n"); /* * The LOCK (bit 7) and FLOCK (bit 15) must be set (1) in the same write * operation, if not FLOCK will be read back as unset (0). */ - ch32f1_flash_set_cr(t, FLASH_CR_LOCK | FLASH_CR_FLOCK_CH32); - const uint32_t cr = target_mem_read32(t, FLASH_CR); + ch32f1_flash_set_cr(target, FLASH_CR_LOCK | FLASH_CR_FLOCK_CH32); + const uint32_t cr = target_mem_read32(target, FLASH_CR); if (!(cr & FLASH_CR_FLOCK_CH32)) DEBUG_ERROR("Fast lock failed, cr: 0x%08" PRIx32 "\n", cr); return cr & FLASH_CR_FLOCK_CH32; @@ -172,35 +172,35 @@ static bool ch32f1_flash_lock(target_s *t) /* *check fast_unlock is there, if so it is a CH32fx */ -static bool ch32f1_has_fast_unlock(target_s *t) +static bool ch32f1_has_fast_unlock(target_s *target) { DEBUG_INFO("CH32: has fast unlock \n"); // reset fast unlock - ch32f1_flash_set_cr(t, FLASH_CR_FLOCK_CH32); + ch32f1_flash_set_cr(target, FLASH_CR_FLOCK_CH32); platform_delay(1); // The flash controller is timing sensitive - if (!(target_mem_read32(t, FLASH_CR) & FLASH_CR_FLOCK_CH32)) + if (!(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32)) return false; // send unlock sequence - target_mem_write32(t, FLASH_KEYR, KEY1); - target_mem_write32(t, FLASH_KEYR, KEY2); + target_mem_write32(target, FLASH_KEYR, KEY1); + target_mem_write32(target, FLASH_KEYR, KEY2); platform_delay(1); // The flash controller is timing sensitive // send fast unlock sequence - target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY1); - target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY2); + target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY1); + target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY2); platform_delay(1); // The flash controller is timing sensitive - return !(target_mem_read32(t, FLASH_CR) & FLASH_CR_FLOCK_CH32); + return !(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32); } /* * Try to identify the ch32f1 chip family * (Actually grab all Cortex-M3 with designer == ARM not caught earlier...) */ -bool ch32f1_probe(target_s *t) +bool ch32f1_probe(target_s *target) { - if ((t->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3) + if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3) return false; - const uint32_t dbgmcu_idcode = target_mem_read32(t, DBGMCU_IDCODE); + const uint32_t dbgmcu_idcode = target_mem_read32(target, DBGMCU_IDCODE); const uint32_t device_id = dbgmcu_idcode & 0x00000fffU; const uint32_t revision_id = (dbgmcu_idcode & 0xffff0000U) >> 16U; @@ -214,11 +214,11 @@ bool ch32f1_probe(target_s *t) return false; // Try to flock (if this fails it is not a CH32 chip) - if (!ch32f1_has_fast_unlock(t)) + if (!ch32f1_has_fast_unlock(target)) return false; - t->part_id = device_id; - uint32_t signature = target_mem_read32(t, FLASHSIZE); + target->part_id = device_id; + uint32_t signature = target_mem_read32(target, FLASHSIZE); /* Some ch32f103c8t6 MCU's found on Blue Pill boards have a zero (0) in the flash memory capacity register */ if (signature == 0) { signature = 64; @@ -226,44 +226,44 @@ bool ch32f1_probe(target_s *t) } uint32_t flashSize = signature & 0xffffU; - target_add_ram(t, 0x20000000, 0x5000); - ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024U, 128); - target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); - t->driver = "CH32F1 medium density (stm32f1 clone)"; + target_add_ram(target, 0x20000000, 0x5000); + ch32f1_add_flash(target, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024U, 128); + target_add_commands(target, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); + target->driver = "CH32F1 medium density (stm32f1 clone)"; return true; } /* Fast erase of CH32 devices */ -bool ch32f1_flash_erase(target_flash_s *f, target_addr_t addr, size_t len) +bool ch32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len) { - target_s *t = f->t; + target_s *target = flash->t; DEBUG_INFO("CH32: flash erase \n"); - if (!ch32f1_flash_unlock(t)) { + if (!ch32f1_flash_unlock(target)) { DEBUG_ERROR("CH32: Unlock failed\n"); return false; } // Fast Erase 128 bytes pages (ch32 mode) while (len) { - ch32f1_flash_set_cr(t, FLASH_CR_FTER_CH32); // CH32 PAGE_ER + ch32f1_flash_set_cr(target, FLASH_CR_FTER_CH32); // CH32 PAGE_ER /* Write address to FMA */ - target_mem_write32(t, FLASH_AR, addr); + target_mem_write32(target, FLASH_AR, addr); /* Flash page erase start instruction */ - ch32f1_flash_set_cr(t, FLASH_CR_STRT); - if (!ch32f1_flash_eop_wait(t)) + ch32f1_flash_set_cr(target, FLASH_CR_STRT); + if (!ch32f1_flash_eop_wait(target)) return -1; - target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(t, FLASH_CR_STRT); + target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(target, FLASH_CR_STRT); // Magic - ch32f1_flash_magic(t, addr); + ch32f1_flash_magic(target, addr); if (len > 128U) len -= 128U; else len = 0; addr += 128U; } - const uint32_t sr = target_mem_read32(t, FLASH_SR); - ch32f1_flash_lock(t); + const uint32_t sr = target_mem_read32(target, FLASH_SR); + ch32f1_flash_lock(target); if (sr & SR_ERROR_MASK) DEBUG_ERROR("ch32f1 flash erase error 0x%" PRIx32 "\n", sr); return !(sr & SR_ERROR_MASK); @@ -275,13 +275,13 @@ bool ch32f1_flash_erase(target_flash_s *f, target_addr_t addr, size_t len) * We do 32 to have a bit of headroom, then we check we read ffff (erased flash) * NB: Just reading fff is not enough as it could be a transient previous operation value */ -static bool ch32f1_wait_flash_ready(target_s *t, uint32_t addr) +static bool ch32f1_wait_flash_ready(target_s *target, uint32_t addr) { uint32_t flash_val = 0; /* Certain ch32f103c8t6 MCU's found on Blue Pill boards need some uninterrupted time (no SWD link activity) */ platform_delay(2); for (size_t cnt = 0; cnt < 32U && flash_val != 0xffffffffU; ++cnt) - flash_val = target_mem_read32(t, addr); + flash_val = target_mem_read32(target, addr); if (flash_val != 0xffffffffU) { DEBUG_ERROR("ch32f1 Not erased properly at %" PRIx32 " or flash access issue\n", addr); return false; @@ -290,42 +290,42 @@ static bool ch32f1_wait_flash_ready(target_s *t, uint32_t addr) } /* Fast flash for ch32. Load 128 bytes chunk and then write them */ -static int ch32f1_upload(target_s *t, uint32_t dest, const void *src, uint32_t offset) +static int ch32f1_upload(target_s *target, uint32_t dest, const void *src, uint32_t offset) { const uint32_t *ss = (const uint32_t *)(src + offset); uint32_t dd = dest + offset; - ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); - target_mem_write32(t, dd + 0, ss[0]); - target_mem_write32(t, dd + 4U, ss[1]); - target_mem_write32(t, dd + 8U, ss[2]); - target_mem_write32(t, dd + 12U, ss[3]); - ch32f1_flash_set_cr(t, FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ - if (!ch32f1_flash_eop_wait(t)) + ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); + target_mem_write32(target, dd + 0, ss[0]); + target_mem_write32(target, dd + 4U, ss[1]); + target_mem_write32(target, dd + 8U, ss[2]); + target_mem_write32(target, dd + 12U, ss[3]); + ch32f1_flash_set_cr(target, FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ + if (!ch32f1_flash_eop_wait(target)) return -1; - target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); - ch32f1_flash_magic(t, dest + offset); + target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); + ch32f1_flash_magic(target, dest + offset); return 0; } /* Clear the write buffer */ -static int ch32f1_buffer_clear(target_s *t) +static int ch32f1_buffer_clear(target_s *target) { - ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); // Fast page program 4- - ch32f1_flash_set_cr(t, FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- - if (!ch32f1_flash_busy_wait(t)) + ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); // Fast page program 4- + ch32f1_flash_set_cr(target, FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- + if (!ch32f1_flash_busy_wait(target)) return -1; - ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); // Fast page program 4- + ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); // Fast page program 4- return 0; } /* * CH32 implementation of Flash write using the CH32-specific fast write */ -static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void *src, size_t len) +static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) { - target_s *t = f->t; + target_s *target = flash->t; size_t length = len; #ifdef CH32_VERIFY target_addr_t org_dest = dest; @@ -334,35 +334,35 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%" PRIu32 "\n", dest, (uint32_t)len); while (length > 0) { - if (!ch32f1_flash_unlock(t)) { + if (!ch32f1_flash_unlock(target)) { DEBUG_ERROR("ch32f1 cannot fast unlock\n"); return false; } - if (!ch32f1_flash_busy_wait(t)) + if (!ch32f1_flash_busy_wait(target)) return -1; // Buffer reset... - ch32f1_buffer_clear(t); + ch32f1_buffer_clear(target); // Load 128 bytes to buffer - if (!ch32f1_wait_flash_ready(t, dest)) + if (!ch32f1_wait_flash_ready(target, dest)) return false; for (size_t i = 0; i < 8U; i++) { - if (ch32f1_upload(t, dest, src, i * 16U)) { + if (ch32f1_upload(target, dest, src, i * 16U)) { DEBUG_ERROR("Cannot upload to buffer\n"); return false; } } // write buffer - ch32f1_flash_set_cr(t, FLASH_CR_FTPG_CH32); - target_mem_write32(t, FLASH_AR, dest); // 10 - ch32f1_flash_set_cr(t, FLASH_CR_STRT); // 11 Start - if (!ch32f1_flash_eop_wait(t)) // 12 + ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); + target_mem_write32(target, FLASH_AR, dest); // 10 + ch32f1_flash_set_cr(target, FLASH_CR_STRT); // 11 Start + if (!ch32f1_flash_eop_wait(target)) // 12 return -1; - target_mem_write32(t, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(t, FLASH_CR_FTPG_CH32); + target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ + ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); - ch32f1_flash_magic(t, dest); + ch32f1_flash_magic(target, dest); // next if (length > 128U) @@ -372,8 +372,8 @@ static bool ch32f1_flash_write(target_flash_s *f, target_addr_t dest, const void dest += 128U; src += 128U; - const uint32_t sr = target_mem_read32(t, FLASH_SR); // 13 - ch32f1_flash_lock(t); + const uint32_t sr = target_mem_read32(target, FLASH_SR); // 13 + ch32f1_flash_lock(target); if (sr & SR_ERROR_MASK) { DEBUG_ERROR("ch32f1 flash write error 0x%" PRIx32 "\n", sr); return false; From 4d13cc3983850d386c7642c37823609d5041f404 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 23:40:15 +0100 Subject: [PATCH 128/130] target/ch32f1: remove debug verify code --- src/target/ch32f1.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c index 060332ecd94..f6ffa5cc19e 100644 --- a/src/target/ch32f1.c +++ b/src/target/ch32f1.c @@ -327,10 +327,6 @@ static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const { target_s *target = flash->t; size_t length = len; -#ifdef CH32_VERIFY - target_addr_t org_dest = dest; - const void *org_src = src; -#endif DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%" PRIu32 "\n", dest, (uint32_t)len); while (length > 0) { @@ -380,19 +376,5 @@ static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const } } -#ifdef CH32_VERIFY - DEBUG_INFO("Verifying\n"); - for (size_t i = 0; i < len; i += 4U) { - const uint32_t expected = *(uint32_t *)(org_src + i); - const uint32_t actual = target_mem_read32(t, org_dest + i); - if (expected != actual) { - DEBUG_ERROR(">>>>write mismatch at address 0x%" PRIx32 "\n", org_dest + i); - DEBUG_ERROR(">>>>expected: 0x%" PRIx32 "\n", expected); - DEBUG_ERROR(">>>> actual: 0x%" PRIx32 "\n", actual); - return false; - } - } -#endif - return true; } From d357726fcb53fc111adb965ea674ecf8644182e5 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 11 Aug 2023 23:42:24 +0100 Subject: [PATCH 129/130] target/ch32f1: const correctness --- src/target/ch32f1.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c index f6ffa5cc19e..5e300370f28 100644 --- a/src/target/ch32f1.c +++ b/src/target/ch32f1.c @@ -72,9 +72,9 @@ static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const // #define MAGIC_WORD 0x1000U /* "fast" Flash driver for CH32F10x chips */ -static void ch32f1_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize) +static void ch32f1_add_flash(target_s *const target, const uint32_t addr, const size_t length, const size_t erasesize) { - target_flash_s *const flash = calloc(1, sizeof(*flash)); + target_flash_s *const flash = calloc(1U, sizeof(*flash)); if (!flash) { /* calloc failed: heap exhaustion */ DEBUG_ERROR("calloc: failed in %s\n", __func__); return; @@ -137,7 +137,7 @@ static void ch32f1_flash_magic(target_s *const target, const uint32_t addr) } /* Attempt unlock ch32f103 in fast mode */ -static bool ch32f1_flash_unlock(target_s *target) +static bool ch32f1_flash_unlock(target_s *const target) { DEBUG_INFO("CH32: flash unlock \n"); @@ -155,7 +155,7 @@ static bool ch32f1_flash_unlock(target_s *target) /* * lock ch32f103 in fast mode */ -static bool ch32f1_flash_lock(target_s *target) +static bool ch32f1_flash_lock(target_s *const target) { DEBUG_INFO("CH32: flash lock \n"); /* @@ -172,22 +172,22 @@ static bool ch32f1_flash_lock(target_s *target) /* *check fast_unlock is there, if so it is a CH32fx */ -static bool ch32f1_has_fast_unlock(target_s *target) +static bool ch32f1_has_fast_unlock(target_s *const target) { DEBUG_INFO("CH32: has fast unlock \n"); // reset fast unlock ch32f1_flash_set_cr(target, FLASH_CR_FLOCK_CH32); - platform_delay(1); // The flash controller is timing sensitive + platform_delay(1U); // The flash controller is timing sensitive if (!(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32)) return false; // send unlock sequence target_mem_write32(target, FLASH_KEYR, KEY1); target_mem_write32(target, FLASH_KEYR, KEY2); - platform_delay(1); // The flash controller is timing sensitive + platform_delay(1U); // The flash controller is timing sensitive // send fast unlock sequence target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY1); target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY2); - platform_delay(1); // The flash controller is timing sensitive + platform_delay(1U); // The flash controller is timing sensitive return !(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32); } @@ -195,7 +195,7 @@ static bool ch32f1_has_fast_unlock(target_s *target) * Try to identify the ch32f1 chip family * (Actually grab all Cortex-M3 with designer == ARM not caught earlier...) */ -bool ch32f1_probe(target_s *target) +bool ch32f1_probe(target_s *const target) { if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3) return false; @@ -234,7 +234,7 @@ bool ch32f1_probe(target_s *target) } /* Fast erase of CH32 devices */ -bool ch32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len) +bool ch32f1_flash_erase(target_flash_s *const flash, target_addr_t addr, size_t len) { target_s *target = flash->t; DEBUG_INFO("CH32: flash erase \n"); @@ -275,11 +275,11 @@ bool ch32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len) * We do 32 to have a bit of headroom, then we check we read ffff (erased flash) * NB: Just reading fff is not enough as it could be a transient previous operation value */ -static bool ch32f1_wait_flash_ready(target_s *target, uint32_t addr) +static bool ch32f1_wait_flash_ready(target_s *const target, const uint32_t addr) { uint32_t flash_val = 0; /* Certain ch32f103c8t6 MCU's found on Blue Pill boards need some uninterrupted time (no SWD link activity) */ - platform_delay(2); + platform_delay(2U); for (size_t cnt = 0; cnt < 32U && flash_val != 0xffffffffU; ++cnt) flash_val = target_mem_read32(target, addr); if (flash_val != 0xffffffffU) { @@ -290,16 +290,16 @@ static bool ch32f1_wait_flash_ready(target_s *target, uint32_t addr) } /* Fast flash for ch32. Load 128 bytes chunk and then write them */ -static int ch32f1_upload(target_s *target, uint32_t dest, const void *src, uint32_t offset) +static int ch32f1_upload(target_s *const target, const uint32_t dest, const void *const src, const uint32_t offset) { const uint32_t *ss = (const uint32_t *)(src + offset); uint32_t dd = dest + offset; ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); target_mem_write32(target, dd + 0, ss[0]); - target_mem_write32(target, dd + 4U, ss[1]); - target_mem_write32(target, dd + 8U, ss[2]); - target_mem_write32(target, dd + 12U, ss[3]); + target_mem_write32(target, dd + 4U, ss[1U]); + target_mem_write32(target, dd + 8U, ss[2U]); + target_mem_write32(target, dd + 12U, ss[3U]); ch32f1_flash_set_cr(target, FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ if (!ch32f1_flash_eop_wait(target)) return -1; @@ -310,7 +310,7 @@ static int ch32f1_upload(target_s *target, uint32_t dest, const void *src, uint3 } /* Clear the write buffer */ -static int ch32f1_buffer_clear(target_s *target) +static int ch32f1_buffer_clear(target_s *const target) { ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); // Fast page program 4- ch32f1_flash_set_cr(target, FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- @@ -323,7 +323,7 @@ static int ch32f1_buffer_clear(target_s *target) /* * CH32 implementation of Flash write using the CH32-specific fast write */ -static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +static bool ch32f1_flash_write(target_flash_s *const flash, target_addr_t dest, const void *src, const size_t len) { target_s *target = flash->t; size_t length = len; From 18e9f012cdb45858aa12fd76d12e31c45ce74a7c Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Thu, 5 Oct 2023 12:49:24 +0100 Subject: [PATCH 130/130] !fixme! stm32 flash --- src/Makefile | 6 +- src/target/ch32_flash.c | 372 ++++++++++++++++ src/target/ch32_flash.h | 100 +++++ src/target/ch32f1.c | 380 ----------------- src/target/ch32f1x.c | 153 +++++++ src/target/ch32v3x.c | 205 +++++++++ src/target/ch32vx.c | 125 ------ src/target/samx5x.c | 9 +- src/target/stm32_flash.c | 443 +++++++++++++++++++ src/target/stm32_flash.h | 270 ++++++++++++ src/target/stm32f1.c | 900 ++++++++++++--------------------------- 11 files changed, 1833 insertions(+), 1130 deletions(-) create mode 100644 src/target/ch32_flash.c create mode 100644 src/target/ch32_flash.h delete mode 100644 src/target/ch32f1.c create mode 100644 src/target/ch32f1x.c create mode 100644 src/target/ch32v3x.c delete mode 100644 src/target/ch32vx.c create mode 100644 src/target/stm32_flash.c create mode 100644 src/target/stm32_flash.h diff --git a/src/Makefile b/src/Makefile index fe462553789..8666e89f2b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -76,8 +76,10 @@ SRC = \ sfdp.c \ spi.c \ stm32f1.c \ - ch32f1.c \ - ch32vx.c \ + ch32_flash.c \ + stm32_flash.c \ + ch32f1x.c \ + ch32v3x.c \ stm32f4.c \ stm32h5.c \ stm32h7.c \ diff --git a/src/target/ch32_flash.c b/src/target/ch32_flash.c new file mode 100644 index 00000000000..0b178a06a39 --- /dev/null +++ b/src/target/ch32_flash.c @@ -0,0 +1,372 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ch32_flash.h" + +/* + * FIXME: these defines are here to determine if these are actually required + * if not remove them, else remove the defines + */ +#define ENABLE_CH32_FLASH_DELAYS 1U +#define ENABLE_CH32F1X_FLASH_MAGIC 1U + +/* Generic CH32 flash routines */ + +bool ch32_flash_fast_mode_locked(target_s *const target, const uint32_t fpec_base) +{ + const uint32_t ctrl = target_mem_read32(target, STM32_FLASH_CR(fpec_base)); + return ctrl & CH32_FLASH_CR_FLOCK; +} + +bool ch32_flash_fast_mode_unlock(target_s *const target, const uint32_t fpec_base) +{ + target_mem_write32(target, CH32_FLASH_MODEKEYR(fpec_base), STM32_FLASH_KEY1); + target_mem_write32(target, CH32_FLASH_MODEKEYR(fpec_base), STM32_FLASH_KEY2); + +#if ENABLE_CH32_FLASH_DELAYS + platform_delay(1U); /* FIXME: The flash controller is timing sensitive? */ +#endif + + /* Check that the fast mode is unlocked */ + if (ch32_flash_fast_mode_locked(target, fpec_base)) { + DEBUG_ERROR("ch32 flash fast unlock failed\n"); + return false; + } + return true; +} + +void ch32_flash_lock(target_s *const target, const uint32_t fpec_base) +{ + const uint32_t ctrl = target_mem_read32(target, STM32_FLASH_CR(fpec_base)); + /* Retain the EOPIE, ERRIE and OPTWRE bits, and set the LOCK and FLOCK bits */ + target_mem_write32(target, STM32_FLASH_CR(fpec_base), + (ctrl & (STM32_FLASH_CR_EOPIE | STM32_FLASH_CR_ERRIE | STM32_FLASH_CR_OPTWRE)) | STM32_FLASH_CR_LOCK | + CH32_FLASH_CR_FLOCK); + +#if ENABLE_CH32_FLASH_DELAYS + platform_delay(1U); /* FIXME: The flash controller is timing sensitive? */ +#endif +} + +static bool ch32_flash_prepare(target_flash_s *const flash) +{ + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* Unlock the flash if required */ + if (stm32_flash_locked(target, stm32_flash->fpec_base, 0U) && + !stm32_flash_unlock(target, stm32_flash->fpec_base, 0U)) + return false; + + /* Ensure no operation is ongoing */ + if (target_mem_read32(target, STM32_FLASH_SR_BANK(stm32_flash->fpec_base, 0U)) & STM32_FLASH_SR_BSY) { + DEBUG_ERROR("ch32 flash unexpectedly busy\n"); + return false; /* FIXME: should we re-lock here? */ + } + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, 0U); + + /* Set the instruction to the control register */ + uint32_t ctrl_instruction = 0U; + switch (flash->operation) { + case FLASH_OPERATION_WRITE: + /* Set flash fast mode programming instruction */ + ctrl_instruction = CH32_FLASH_CR_FTPG; + break; + case FLASH_OPERATION_ERASE: + /* Set flash fast mode page erase instruction */ + ctrl_instruction = CH32_FLASH_CR_FTER; + break; + case FLASH_OPERATION_MASS_ERASE: + /* Set flash mass erase instruction */ + ctrl_instruction = STM32_FLASH_CR_MER; + break; + default: + return false; /* Unsupported operation */ + } + + /* Unlock the fast mode extension if required by the requested instruction */ + if (flash->operation != FLASH_OPERATION_MASS_ERASE) { + if (ch32_flash_fast_mode_locked(target, stm32_flash->fpec_base) && + !ch32_flash_fast_mode_unlock(target, stm32_flash->fpec_base)) + return false; + } + + /* + * This will clear EOPIE, ERRIE and OPTWRE, but we don't care about them and expect them cleared + * after reset anyway, on CH32FV2x/V3x this also clears the EHMOD and SCKMOD, which follow the same logic + * as the former. + * + * note that we don't read-modify-write the control register after this, meaning we set the instruction + * allways, this is to avoid the extra overhead of reading the register since we know what bits should be set. + */ + /* FIXME: on CH32FV2x/V3x we might want to check the default SYSCLK and if setting SCKMOD makes sense */ + target_mem_write32(target, STM32_FLASH_CR(stm32_flash->fpec_base), ctrl_instruction); + + return true; +} + +static bool ch32_flash_done(target_flash_s *const flash) +{ + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* Lock the flash */ + ch32_flash_lock(target, stm32_flash->fpec_base); + + return true; +} + +/* CH32F1x flash routines */ + +#if ENABLE_CH32F1X_FLASH_MAGIC +static inline void ch32f1x_flash_magic(target_s *const target, const uint32_t fpec_base, const uint32_t page_addr) +{ + /* We don't know what this does or if we actually need it, but it is done on the standard peripheral lib */ + const uint32_t magic = target_mem_read32(target, page_addr ^ CH32F1X_FLASH_MAGIC_XOR); + target_mem_write32(target, CH32F1X_FLASH_MAGIC(fpec_base), magic); +} +#endif + +static bool ch32f1x_flash_fast_mode_buffer_clear(target_s *const target, const uint32_t fpec_base) +{ + /* Clear any previous operation status */ + stm32_flash_clear_status(target, fpec_base, 0U); + + /* Clear the internal buffer */ + target_mem_write32(target, STM32_FLASH_CR(fpec_base), CH32_FLASH_CR_FTPG | CH32F1X_FLASH_CR_BUFRST); + const bool result = stm32_flash_busy_wait(target, fpec_base, 0U, NULL); + +#if ENABLE_CH32_FLASH_DELAYS + platform_delay(2U); /* FIXME: The flash controller is timing sensitive? */ +#endif + + return result; +} + +static bool ch32f1x_flash_fast_mode_buffer_load( + target_s *const target, const uint32_t fpec_base, const uint32_t dest, const void *const src) +{ + /* + * The fast mode buffer is 128 bytes long, it is loaded in 8x16 byte chunks. + * The 16 byte chunks should be written continuously in 32 bit words to the destination address. + * The 8 chunks should be written consecutively. + */ + for (size_t offset = 0; offset < 128U; offset += 16U) { + /* Clear any previous operation status */ + stm32_flash_clear_status(target, fpec_base, 0U); + + /* Continuously write 16 bytes of data to the specified address in 32 bit writes */ + target_mem_write(target, dest + offset, (const uint8_t *)src + offset, 16U); + + /* Start buffer load instruction */ + target_mem_write32(target, STM32_FLASH_CR(fpec_base), CH32_FLASH_CR_FTPG | CH32F1X_FLASH_CR_BUFRST); + if (!stm32_flash_busy_wait(target, fpec_base, 0U, NULL)) + return false; + +#if ENABLE_CH32F1X_FLASH_MAGIC + /* Unknown magic sequence */ + ch32f1x_flash_magic(target, fpec_base, dest + offset); +#endif + } + return true; +} + +static bool ch32f1x_flash_fast_mode_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + (void)length; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §24.4.7 Main Memory Fast Erasure in CH32xRM */ + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, 0U); + + /* Write page address to address register */ + target_mem_write32(target, STM32_FLASH_AR(stm32_flash->fpec_base), addr); + + /* Start fast flash page erase instruction */ + target_mem_write32(target, STM32_FLASH_CR(stm32_flash->fpec_base), STM32_FLASH_CR_STRT | CH32_FLASH_CR_FTER); + + /* Wait for completion or an error, return false on error */ + const bool result = stm32_flash_busy_wait(target, stm32_flash->fpec_base, 0U, NULL); + +#if ENABLE_CH32F1X_FLASH_MAGIC + /* Unknown magic sequence */ + ch32f1x_flash_magic(target, stm32_flash->fpec_base, addr); +#endif + + return result; +} + +/* CH32F1x use a "buffer" for fast programming */ +static bool ch32f1x_flash_fast_mode_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +{ + (void)len; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §24.4.6 Main Memory Fast Programming in CH32xRM */ + + /* Clear the internal buffer */ + ch32f1x_flash_fast_mode_buffer_clear(target, stm32_flash->fpec_base); + + /* Load the internal buffer with the 128 byte page */ + ch32f1x_flash_fast_mode_buffer_load(target, stm32_flash->fpec_base, dest, src); + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, 0U); + + /* Write page address to address register */ + target_mem_write32(target, STM32_FLASH_AR(stm32_flash->fpec_base), dest); + + /* Start fast mode flash programming instruction */ + target_mem_write32(target, STM32_FLASH_CR(stm32_flash->fpec_base), STM32_FLASH_CR_STRT | CH32_FLASH_CR_FTPG); + + /* Wait for completion or an error, return false on error */ + const bool result = stm32_flash_busy_wait(target, stm32_flash->fpec_base, 0U, NULL); + +#if ENABLE_CH32F1X_FLASH_MAGIC + /* Unknown magic sequence */ + ch32f1x_flash_magic(target, stm32_flash->fpec_base, dest); +#endif + + return result; +} + +void ch32f1x_add_flash(target_s *const target, const target_addr_t addr, const size_t length) +{ + stm32_flash_s *const stm32_flash = calloc(1U, sizeof(*stm32_flash)); + if (!stm32_flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + target_flash_s *const flash = &stm32_flash->flash; + + stm32_flash->fpec_base = CH32_FPEC_BASE; + + flash->start = addr; + flash->length = length; + flash->blocksize = CH32F1X_FAST_MODE_PAGE_SIZE; + flash->writesize = CH32F1X_FAST_MODE_PAGE_SIZE; + flash->erased = 0xffU; + flash->erase = ch32f1x_flash_fast_mode_erase; + flash->mass_erase = stm32_flash_mass_erase; + flash->write = ch32f1x_flash_fast_mode_write; + flash->prepare = ch32_flash_prepare; + flash->done = ch32_flash_done; + + target_add_flash(target, flash); +} + +/* CH32FV2x/V3x flash routines */ + +/* FIXME: this function is very close to ch32f1x_flash_fast_mode_erase, the only exception is the magic sequence, optimize */ +static bool ch32fv2x_v3x_flash_fast_mode_erase( + target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + (void)length; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §32.5.7 Main Memory Fast Erasure in CH32FV2x_V3xRM */ + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, 0U); + + /* Write page address to address register */ + target_mem_write32(target, STM32_FLASH_AR(stm32_flash->fpec_base), addr); + + /* Start fast flash page erase instruction */ + target_mem_write32(target, STM32_FLASH_CR(stm32_flash->fpec_base), STM32_FLASH_CR_STRT | CH32_FLASH_CR_FTER); + + /* Wait for completion or an error, return false on error */ + return stm32_flash_busy_wait(target, stm32_flash->fpec_base, 0U, NULL); +} + +/* CH32FV2x/V3x don't use a "buffer" for fast programming */ +static bool ch32fv2x_v3x_flash_fast_mode_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +{ + (void)len; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §32.5.6 Main Memory Fast Programming in CH32FV2x_V3xRM */ + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, 0U); + + /* Write data to the FLASH address in 32 bit writes, 64 times */ + for (size_t offset = 0; offset < 64U; offset++) { + target_mem_write32(target, dest + (offset << 2U), *((const uint32_t *)src + offset)); + if (!stm32_flash_busy_wait(target, stm32_flash->fpec_base, 0U, NULL)) + return false; + } + + /* Start fast mode flash programming instruction */ + target_mem_write32( + target, STM32_FLASH_CR(stm32_flash->fpec_base), CH32FV2X_V3X_FLASH_CR_PGSTRT | CH32_FLASH_CR_FTPG); + + /* Wait for completion or an error, return false on error */ + return stm32_flash_busy_wait(target, stm32_flash->fpec_base, 0U, NULL); +} + +void ch32fv2x_v3x_add_flash(target_s *const target, const target_addr_t addr, const size_t length) +{ + stm32_flash_s *const stm32_flash = calloc(1U, sizeof(*stm32_flash)); + if (!stm32_flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + target_flash_s *const flash = &stm32_flash->flash; + + stm32_flash->fpec_base = CH32_FPEC_BASE; + + flash->start = addr; + flash->length = length; + flash->blocksize = CH32FV2X_V3X_FAST_MODE_PAGE_SIZE; + flash->writesize = CH32FV2X_V3X_FAST_MODE_PAGE_SIZE; + flash->erased = 0xffU; + flash->erase = ch32fv2x_v3x_flash_fast_mode_erase; + flash->mass_erase = stm32_flash_mass_erase; + flash->write = ch32fv2x_v3x_flash_fast_mode_write; + flash->prepare = ch32_flash_prepare; + flash->done = ch32_flash_done; + + target_add_flash(target, flash); +} diff --git a/src/target/ch32_flash.h b/src/target/ch32_flash.h new file mode 100644 index 00000000000..84ea7a9f4f2 --- /dev/null +++ b/src/target/ch32_flash.h @@ -0,0 +1,100 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_CH32_FLASH_H +#define TARGET_CH32_FLASH_H + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "stm32_flash.h" + +/* + * STM32 Flash program and erase controller (FPEC) extension for CH32 + * contains the CH32 specific extension registers and bits. + * Macros named CH32FV2X_V3X are shared between CH32F2x, CH32V2x and CH32V3x + * + * This is based on CH32xRM Chapter §24 Flash Memory and CH32FV2x_V3xRM Chapter §32 lash Memory and User Option Bytes + * https://www.wch-ic.com/downloads/file/306.html + * https://www.wch-ic.com/downloads/file/324.html + */ +#define CH32_FPEC_BASE 0x40022000U /* Shared between CH32F1x, CH32F2x, CH32V2x and CH32V3x, may not apply to others */ + +#define CH32F1X_FAST_MODE_PAGE_SIZE 128U /* CH32F1x Fast erase/programming works on 128 byte pages */ +#define CH32FV2X_V3X_FAST_MODE_PAGE_SIZE 256U /* CH32FV2x/V3x Fast erase/programming works on 256 byte pages */ + +/* Flash control register (FLASH_CR) */ +#define CH32_FLASH_CR_FTER (1U << 17U) /* Fast page (128Byte) erase operation */ +#define CH32_FLASH_CR_FTPG (1U << 16U) /* Fast programming operation */ +#define CH32_FLASH_CR_FLOCK (1U << 15U) /* Fast programming lock (locked: fast programming/erase mode not available) */ +/* CH32F1x Only */ +#define CH32F1X_FLASH_CR_BUFRST (1U << 19U) /* Clear the internal buffer data */ +#define CH32F1X_FLASH_CR_BUFLOAD (1U << 18U) /* Load data into the internal buffer */ +/* CH32FV2x/V3x Only */ +#define CH32FV2X_V3X_FLASH_CR_SCKMOD (1U << 25U) /* Flash access clock (1: SYSCLK, 0: ½SYSCLK) (must be < 60 MHz) */ +#define CH32FV2X_V3X_FLASH_CR_EHMOD (1U << 24U) /* Flash enhanced read mode */ +#define CH32FV2X_V3X_FLASH_CR_RSENACT (1U << 22U) /* Exit the enhanced read mode, first clear the ENHANCE_MOD */ +#define CH32FV2X_V3X_FLASH_CR_PGSTRT (1U << 21U) /* Start a page programming */ +#define CH32FV2X_V3X_FLASH_CR_BER64 (1U << 19U) /* Perform a 64KB erase */ +#define CH32FV2X_V3X_FLASH_CR_BER32 (1U << 18U) /* Perform a 32KB erase */ + +/* Flash status register (FLASH_SR), CH32FV2x/V3x Only */ +#define CH32FV2X_V3X_FLASH_SR_EHMODS (1U << 7U) /* Flash enhanced read mode (1: enabled, 0: disabled) */ +#define CH32FV2X_V3X_FLASH_SR_WRBSY (1U << 0U) /* Fast page programming busy */ + +/* + * Extension key register (FLASH_MODEKEYR) + * Shares the keys with the ST FPEC key register (FLASH_KEYR) + */ +#define CH32_FLASH_MODEKEYR_OFFSET 0x24U +#define CH32_FLASH_MODEKEYR(fpec_base) ((fpec_base) + CH32_FLASH_MODEKEYR_OFFSET) + +/* + * FIXME: What is this?, CH32F1x Only + * It's unclear what the purpose of this register is, it's not referenced in CH32xRM documentation + * but it is used on the standard peripherals library for CH32F103x + */ +#define CH32F1X_FLASH_MAGIC_OFFSET 0x34U +#define CH32F1X_FLASH_MAGIC(fpec_base) ((fpec_base) + CH32F1X_FLASH_MAGIC_OFFSET) + +#define CH32F1X_FLASH_MAGIC_XOR 0x00000100U + +/* Insternal API */ +bool ch32_flash_fast_mode_locked(target_s *target, uint32_t fpec_base); +bool ch32_flash_fast_mode_unlock(target_s *target, uint32_t fpec_base); +void ch32_flash_lock(target_s *target, uint32_t fpec_base); + +/* Generic flash routines */ +void ch32f1x_add_flash(target_s *target, target_addr_t addr, size_t length); +void ch32fv2x_v3x_add_flash(target_s *target, target_addr_t addr, size_t length); + +#endif /* TARGET_CH32_FLASH_H */ diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c deleted file mode 100644 index 5e300370f28..00000000000 --- a/src/target/ch32f1.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2023 1BitSquared - * Written by mean00 - * Modified by Rafael Silva - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* - * This file implements CH32F1xx target specific functions. - * The ch32 flash is rather slow so this code is using the so called fast mode (ch32 specific). - * 128 bytes are copied to a write buffer, then the write buffer is committed to flash - * /!\ There is some sort of bus stall/bus arbitration going on that does NOT work when - * programmed through SWD/jtag - * The workaround is to wait a few cycles before filling the write buffer. This is performed by reading the flash a few times - */ - -#include "general.h" -#include "target.h" -#include "target_internal.h" -#include "cortex.h" - -extern const command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff - -static bool ch32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); -static bool ch32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); - -// These are common with stm32f1/gd32f1/... -#define FPEC_BASE 0x40022000U -#define FLASH_ACR (FPEC_BASE + 0x00U) -#define FLASH_KEYR (FPEC_BASE + 0x04U) -#define FLASH_SR (FPEC_BASE + 0x0cU) -#define FLASH_CR (FPEC_BASE + 0x10U) -#define FLASH_AR (FPEC_BASE + 0x14U) -#define FLASH_CR_LOCK (1U << 7U) -#define FLASH_CR_STRT (1U << 6U) -#define FLASH_SR_BSY (1U << 0U) -#define KEY1 0x45670123U -#define KEY2 0xcdef89abU -#define SR_ERROR_MASK 0x14U -#define SR_EOP 0x20U -#define DBGMCU_IDCODE 0xe0042000U -#define FLASHSIZE 0x1ffff7e0U - -// These are specific to ch32f1 -#define FLASH_MAGIC (FPEC_BASE + 0x34U) -#define FLASH_MODEKEYR_CH32 (FPEC_BASE + 0x24U) // Fast mode for CH32F10x -#define FLASH_CR_FLOCK_CH32 (1U << 15U) // Fast unlock -#define FLASH_CR_FTPG_CH32 (1U << 16U) // Fast page program -#define FLASH_CR_FTER_CH32 (1U << 17U) // Fast page erase -#define FLASH_CR_BUF_LOAD_CH32 (1U << 18U) // Buffer load -#define FLASH_CR_BUF_RESET_CH32 (1U << 19U) // Buffer reset -#define FLASH_SR_EOP (1U << 5U) // End of programming -#define FLASH_BEGIN_ADDRESS_CH32 0x8000000U - -// Which one is the right value? -#define MAGIC_WORD 0x100U - -// #define MAGIC_WORD 0x1000U - -/* "fast" Flash driver for CH32F10x chips */ -static void ch32f1_add_flash(target_s *const target, const uint32_t addr, const size_t length, const size_t erasesize) -{ - target_flash_s *const flash = calloc(1U, sizeof(*flash)); - if (!flash) { /* calloc failed: heap exhaustion */ - DEBUG_ERROR("calloc: failed in %s\n", __func__); - return; - } - - flash->start = addr; - flash->length = length; - flash->blocksize = erasesize; - flash->erase = ch32f1_flash_erase; - flash->write = ch32f1_flash_write; - flash->writesize = erasesize; - flash->erased = 0xffU; - target_add_flash(target, flash); -} - -static bool ch32f1_flash_busy_wait(target_s *const target) -{ - /* Read FLASH_SR to poll for BSY bit */ - uint32_t status = FLASH_SR_BSY; - while (status & FLASH_SR_BSY) { - status = target_mem_read32(target, FLASH_SR); - if (target_check_error(target)) { - DEBUG_ERROR("Lost communications with target"); - return false; - } - } - return true; -} - -static bool ch32f1_flash_eop_wait(target_s *const target) -{ - /* Read FLASH_SR to poll for EOP bit */ - uint32_t status = FLASH_SR_EOP; - while (status & FLASH_SR_EOP) { - status = target_mem_read32(target, FLASH_SR); - if (target_check_error(target)) { - DEBUG_ERROR("Lost communications with target"); - return false; - } - } - return true; -} - -static void ch32f1_flash_set_cr(target_s *const target, const uint32_t bits) -{ - const uint32_t cr = target_mem_read32(target, FLASH_CR); - target_mem_write32(target, FLASH_CR, cr | bits); -} - -static void ch32f1_flash_clear_cr(target_s *const target, const uint32_t bits) -{ - const uint32_t cr = target_mem_read32(target, FLASH_CR); - target_mem_write32(target, FLASH_CR, cr & ~bits); -} - -static void ch32f1_flash_magic(target_s *const target, const uint32_t addr) -{ - const uint32_t magic = target_mem_read32(target, addr ^ MAGIC_WORD); - target_mem_write32(target, FLASH_MAGIC, magic); -} - -/* Attempt unlock ch32f103 in fast mode */ -static bool ch32f1_flash_unlock(target_s *const target) -{ - DEBUG_INFO("CH32: flash unlock \n"); - - target_mem_write32(target, FLASH_KEYR, KEY1); - target_mem_write32(target, FLASH_KEYR, KEY2); - // fast mode - target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY1); - target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY2); - uint32_t cr = target_mem_read32(target, FLASH_CR); - if (cr & FLASH_CR_FLOCK_CH32) - DEBUG_ERROR("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr); - return !(cr & FLASH_CR_FLOCK_CH32); -} - -/* - * lock ch32f103 in fast mode - */ -static bool ch32f1_flash_lock(target_s *const target) -{ - DEBUG_INFO("CH32: flash lock \n"); - /* - * The LOCK (bit 7) and FLOCK (bit 15) must be set (1) in the same write - * operation, if not FLOCK will be read back as unset (0). - */ - ch32f1_flash_set_cr(target, FLASH_CR_LOCK | FLASH_CR_FLOCK_CH32); - const uint32_t cr = target_mem_read32(target, FLASH_CR); - if (!(cr & FLASH_CR_FLOCK_CH32)) - DEBUG_ERROR("Fast lock failed, cr: 0x%08" PRIx32 "\n", cr); - return cr & FLASH_CR_FLOCK_CH32; -} - -/* - *check fast_unlock is there, if so it is a CH32fx - */ -static bool ch32f1_has_fast_unlock(target_s *const target) -{ - DEBUG_INFO("CH32: has fast unlock \n"); - // reset fast unlock - ch32f1_flash_set_cr(target, FLASH_CR_FLOCK_CH32); - platform_delay(1U); // The flash controller is timing sensitive - if (!(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32)) - return false; - // send unlock sequence - target_mem_write32(target, FLASH_KEYR, KEY1); - target_mem_write32(target, FLASH_KEYR, KEY2); - platform_delay(1U); // The flash controller is timing sensitive - // send fast unlock sequence - target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY1); - target_mem_write32(target, FLASH_MODEKEYR_CH32, KEY2); - platform_delay(1U); // The flash controller is timing sensitive - return !(target_mem_read32(target, FLASH_CR) & FLASH_CR_FLOCK_CH32); -} - -/* - * Try to identify the ch32f1 chip family - * (Actually grab all Cortex-M3 with designer == ARM not caught earlier...) - */ -bool ch32f1_probe(target_s *const target) -{ - if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3) - return false; - - const uint32_t dbgmcu_idcode = target_mem_read32(target, DBGMCU_IDCODE); - const uint32_t device_id = dbgmcu_idcode & 0x00000fffU; - const uint32_t revision_id = (dbgmcu_idcode & 0xffff0000U) >> 16U; - - DEBUG_WARN("DBGMCU_IDCODE 0x%" PRIx32 ", DEVID 0x%" PRIx32 ", REVID 0x%" PRIx32 " \n", dbgmcu_idcode, device_id, - revision_id); - - if (device_id != 0x410U) // ch32f103, cks32f103, apm32f103 - return false; - - if (revision_id != 0x2000U) // (Hopefully!) only ch32f103 - return false; - - // Try to flock (if this fails it is not a CH32 chip) - if (!ch32f1_has_fast_unlock(target)) - return false; - - target->part_id = device_id; - uint32_t signature = target_mem_read32(target, FLASHSIZE); - /* Some ch32f103c8t6 MCU's found on Blue Pill boards have a zero (0) in the flash memory capacity register */ - if (signature == 0) { - signature = 64; - DEBUG_WARN("CH32: FLASHSIZE = 0, assuming CH32F103C8T6 MCU, seting FLASHSIZE = 64\n"); - } - uint32_t flashSize = signature & 0xffffU; - - target_add_ram(target, 0x20000000, 0x5000); - ch32f1_add_flash(target, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024U, 128); - target_add_commands(target, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); - target->driver = "CH32F1 medium density (stm32f1 clone)"; - return true; -} - -/* Fast erase of CH32 devices */ -bool ch32f1_flash_erase(target_flash_s *const flash, target_addr_t addr, size_t len) -{ - target_s *target = flash->t; - DEBUG_INFO("CH32: flash erase \n"); - - if (!ch32f1_flash_unlock(target)) { - DEBUG_ERROR("CH32: Unlock failed\n"); - return false; - } - // Fast Erase 128 bytes pages (ch32 mode) - while (len) { - ch32f1_flash_set_cr(target, FLASH_CR_FTER_CH32); // CH32 PAGE_ER - /* Write address to FMA */ - target_mem_write32(target, FLASH_AR, addr); - /* Flash page erase start instruction */ - ch32f1_flash_set_cr(target, FLASH_CR_STRT); - if (!ch32f1_flash_eop_wait(target)) - return -1; - target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(target, FLASH_CR_STRT); - // Magic - ch32f1_flash_magic(target, addr); - if (len > 128U) - len -= 128U; - else - len = 0; - addr += 128U; - } - const uint32_t sr = target_mem_read32(target, FLASH_SR); - ch32f1_flash_lock(target); - if (sr & SR_ERROR_MASK) - DEBUG_ERROR("ch32f1 flash erase error 0x%" PRIx32 "\n", sr); - return !(sr & SR_ERROR_MASK); -} - -/* - * Wait a bit for the previous operation to finish. - * As per test result we need a time similar to 10 read operation over SWD - * We do 32 to have a bit of headroom, then we check we read ffff (erased flash) - * NB: Just reading fff is not enough as it could be a transient previous operation value - */ -static bool ch32f1_wait_flash_ready(target_s *const target, const uint32_t addr) -{ - uint32_t flash_val = 0; - /* Certain ch32f103c8t6 MCU's found on Blue Pill boards need some uninterrupted time (no SWD link activity) */ - platform_delay(2U); - for (size_t cnt = 0; cnt < 32U && flash_val != 0xffffffffU; ++cnt) - flash_val = target_mem_read32(target, addr); - if (flash_val != 0xffffffffU) { - DEBUG_ERROR("ch32f1 Not erased properly at %" PRIx32 " or flash access issue\n", addr); - return false; - } - return true; -} - -/* Fast flash for ch32. Load 128 bytes chunk and then write them */ -static int ch32f1_upload(target_s *const target, const uint32_t dest, const void *const src, const uint32_t offset) -{ - const uint32_t *ss = (const uint32_t *)(src + offset); - uint32_t dd = dest + offset; - - ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); - target_mem_write32(target, dd + 0, ss[0]); - target_mem_write32(target, dd + 4U, ss[1U]); - target_mem_write32(target, dd + 8U, ss[2U]); - target_mem_write32(target, dd + 12U, ss[3U]); - ch32f1_flash_set_cr(target, FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ - if (!ch32f1_flash_eop_wait(target)) - return -1; - target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); - ch32f1_flash_magic(target, dest + offset); - return 0; -} - -/* Clear the write buffer */ -static int ch32f1_buffer_clear(target_s *const target) -{ - ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); // Fast page program 4- - ch32f1_flash_set_cr(target, FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- - if (!ch32f1_flash_busy_wait(target)) - return -1; - ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); // Fast page program 4- - return 0; -} - -/* - * CH32 implementation of Flash write using the CH32-specific fast write - */ -static bool ch32f1_flash_write(target_flash_s *const flash, target_addr_t dest, const void *src, const size_t len) -{ - target_s *target = flash->t; - size_t length = len; - DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%" PRIu32 "\n", dest, (uint32_t)len); - - while (length > 0) { - if (!ch32f1_flash_unlock(target)) { - DEBUG_ERROR("ch32f1 cannot fast unlock\n"); - return false; - } - if (!ch32f1_flash_busy_wait(target)) - return -1; - - // Buffer reset... - ch32f1_buffer_clear(target); - // Load 128 bytes to buffer - if (!ch32f1_wait_flash_ready(target, dest)) - return false; - - for (size_t i = 0; i < 8U; i++) { - if (ch32f1_upload(target, dest, src, i * 16U)) { - DEBUG_ERROR("Cannot upload to buffer\n"); - return false; - } - } - // write buffer - ch32f1_flash_set_cr(target, FLASH_CR_FTPG_CH32); - target_mem_write32(target, FLASH_AR, dest); // 10 - ch32f1_flash_set_cr(target, FLASH_CR_STRT); // 11 Start - if (!ch32f1_flash_eop_wait(target)) // 12 - return -1; - target_mem_write32(target, FLASH_SR, FLASH_SR_EOP); /* Clear EOP flag */ - ch32f1_flash_clear_cr(target, FLASH_CR_FTPG_CH32); - - ch32f1_flash_magic(target, dest); - - // next - if (length > 128U) - length -= 128U; - else - length = 0; - dest += 128U; - src += 128U; - - const uint32_t sr = target_mem_read32(target, FLASH_SR); // 13 - ch32f1_flash_lock(target); - if (sr & SR_ERROR_MASK) { - DEBUG_ERROR("ch32f1 flash write error 0x%" PRIx32 "\n", sr); - return false; - } - } - - return true; -} diff --git a/src/target/ch32f1x.c b/src/target/ch32f1x.c new file mode 100644 index 00000000000..1431256ff7e --- /dev/null +++ b/src/target/ch32f1x.c @@ -0,0 +1,153 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by mean00 + * Modified by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements CH32F1x target specific functions. */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "buffer_utils.h" +#include "ch32_flash.h" + +/* IDCODE register */ +#define CH32F1X_IDCODE 0xe0042000U +#define CH32F1X_IDCODE_REVISION_ID_OFFSET 16U +#define CH32F1X_IDCODE_REVIDION_ID_MASK (0xffffU << CH32F1X_IDCODE_REVISION_ID_OFFSET) +#define CH32F1X_IDCODE_DEVICE_ID_MASK 0xfffU + +#define CH32F103X_DEVICE_ID 0x410U /* Matches CH32F103, CKS32F103, APM32F103 */ +#define CH32F103X_REVISION_ID 0x2000U /* Only matches CH32F103 (hopefully!) */ + +/* Electronic Signature (ESIG) registers */ +#define CH32F1X_ESIG_BASE 0x1ffff7e0U /* Electronic signature base address */ +#define CH32F1X_ESIG_FLASH_CAP (CH32F1X_ESIG_BASE + 0x00U) /* Flash capacity register, 16 bits, KiB units */ +#define CH32F1X_ESIG_UID1 (CH32F1X_ESIG_BASE + 0x08U) /* Unique ID register, bits 0:31 */ +#define CH32F1X_ESIG_UID2 (CH32F1X_ESIG_BASE + 0x0cU) /* Unique ID register, bits 32:63 */ +#define CH32F1X_ESIG_UID3 (CH32F1X_ESIG_BASE + 0x10U) /* Unique ID register, bits 64:95 */ + +/* Memory mapping */ +#define CH32F103X_FLASH_MEMORY_ADDR 0x08000000U +#define CH32F103X_SRAM_ADDR 0x20000000U + +static bool ch32f1x_uid_cmd(target_s *target, int argc, const char **argv); + +const command_s ch32f1x_cmd_list[] = { + {"uid", ch32f1x_uid_cmd, "Prints 96 bit unique id"}, + {"option", stm32_option_bytes_cmd, "Manipulate option bytes"}, + {NULL, NULL, NULL}, +}; + +/* Check if the FPEC has the CH32 fast mode extension */ +static bool ch32f1x_has_fast_mode_extension(target_s *const target) +{ + const uint32_t fpec_base = CH32_FPEC_BASE; + + /* Start with reset state flash and fast mode locked */ + ch32_flash_lock(target, fpec_base); + + /* Check if the flash and fast mode are locked */ + if (!stm32_flash_locked(target, fpec_base, 0U) || ch32_flash_fast_mode_locked(target, fpec_base)) + return false; + + /* Try to unlock the flash and fast mode, if this fails the fast mode extension is not available */ + const bool result = stm32_flash_unlock(target, fpec_base, 0U) && ch32_flash_fast_mode_unlock(target, fpec_base); + + /* Lock the flash again */ + ch32_flash_lock(target, fpec_base); + + return result; +} + +/* Reads the flash capacity in KiB */ +static size_t ch32f1x_read_flash_capacity(target_s *const target) +{ + /* Get flash capacity from ESIG register */ + const size_t flash_capacity = target_mem_read16(target, CH32F1X_ESIG_FLASH_CAP); + if (flash_capacity == 0U) { + /* + * Some CH32F103C8T6 MCUs seem to have an errata, having zero (0) in the flash capacity ESIG register + * If CH32F103C6xx can be affected this fixup is wrong, as they only have 32KiB of flash + */ + DEBUG_WARN("CH32F1x errata? ESIG_FLASH_CAP = 0, assuming CH32F103C8T6 with 64 KiB flash!\n"); + return 64U; /* 64KiB */ + } + return flash_capacity; +} + +/* Reads the 96 bit unique id */ +static void ch32f1x_read_uid(target_s *const target, uint8_t *const uid) +{ + for (size_t uid_reg_offset = 0; uid_reg_offset < 3U; uid_reg_offset++) + write_be4(uid, uid_reg_offset, target_mem_read32(target, CH32F1X_ESIG_UID1 + (uid_reg_offset << 2U))); +} + +/* Try to identify CH32F1x chip family */ +bool ch32f1x_probe(target_s *const target) +{ + if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M3) + return false; + + const uint32_t idcode = target_mem_read32(target, CH32F1X_IDCODE); + const uint16_t device_id = idcode & CH32F1X_IDCODE_DEVICE_ID_MASK; + const uint16_t revision_id = (idcode & CH32F1X_IDCODE_REVIDION_ID_MASK) >> CH32F1X_IDCODE_REVISION_ID_OFFSET; + + DEBUG_INFO("%s IDCODE 0x%" PRIx32 ", Device ID 0x%03" PRIx16 ", Revision ID 0x%04" PRIx16 " \n", __func__, idcode, + device_id, revision_id); + + if (device_id != CH32F103X_DEVICE_ID || revision_id != CH32F103X_REVISION_ID) + return false; + + /* Check if the FPEC has the CH32 fast mode extension, if not this isn't a CH32F1 */ + if (!ch32f1x_has_fast_mode_extension(target)) + return false; + + target->part_id = device_id; + target->driver = "CH32F103x"; + + const size_t flash_capacity = ch32f1x_read_flash_capacity(target); + const size_t ram_capacity = flash_capacity == 32U ? 10U : 20U; /* 10KiB or 20KiB */ + + /* KiB to bytes */ + target_add_ram(target, CH32F103X_SRAM_ADDR, ram_capacity << 10U); + ch32f1x_add_flash(target, CH32F103X_FLASH_MEMORY_ADDR, flash_capacity << 10U); + + target_add_commands(target, ch32f1x_cmd_list, target->driver); + + return true; +} + +/* Reads the 96 bit unique id */ +static bool ch32f1x_uid_cmd(target_s *const target, const int argc, const char **const argv) +{ + (void)argc; + (void)argv; + + uint8_t uid[12U]; + ch32f1x_read_uid(target, uid); + + tc_printf(target, "Unique id: 0x"); + for (size_t i = 0U; i < sizeof(uid); i++) + tc_printf(target, "%02" PRIx8, uid[i]); + tc_printf(target, "\n"); + + return true; +} diff --git a/src/target/ch32v3x.c b/src/target/ch32v3x.c new file mode 100644 index 00000000000..cfd6aae98ac --- /dev/null +++ b/src/target/ch32v3x.c @@ -0,0 +1,205 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rafael Silva + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file implements RISC-V CH32V3x target specific functions + * Macros named CH32FV2X_V3X are shared between CH32F2x, CH32V2x and CH32V3x + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "buffer_utils.h" +#include "ch32_flash.h" + +/* IDCODE register */ +#define CH32FV2X_V3X_IDCODE 0x1ffff704U +#define CH32FV2X_V3X_IDCODE_REVISION_ID_OFFSET 16U +#define CH32FV2X_V3X_IDCODE_REVIDION_ID_MASK (0xffffU << CH32FV2X_V3X_IDCODE_REVISION_ID_OFFSET) +#define CH32FV2X_V3X_IDCODE_DEVICE_ID_MASK 0xfffU + +/* + * Known IDCODE values: + * CH32V303CBT6: 0x303 3 0 5x4 + * CH32V303RBT6: 0x303 2 0 5x4 + * CH32V303RCT6: 0x303 1 0 5x4 + * CH32V303VCT6: 0x303 0 0 5x4 + * CH32V305FBP6: 0x305 2 0 5x8 + * CH32V305RBT6: 0x305 0 0 5x8 + * CH32V307WCU6: 0x307 3 0 5x8 + * CH32V307FBP6: 0x307 2 0 5x8 + * CH32V307RCT6: 0x307 1 0 5x8 + * CH32V307VCT6: 0x307 0 0 5x8 + */ +#define CH32FV2X_V3X_DEVICE_ID_MASK 0xf0fU /* Helper to glob families (FIXME: verify) */ +#define CH32V303X_DEVICE_ID 0x504U /* Matches CH32V303x */ +#define CH32V305X_7X_DEVICE_ID 0x508U /* Matches CH32V305x and CH32V307x */ + +#define CH32V2X_3X_REVISION_ID_FAMILY_OFFSET 4U /* Helper to extract family code */ +#define CH32V2X_3X_REVISION_ID_FAMILY_MASK (0xfffU << CH32V2X_3X_REVISION_ID_FAMILY_OFFSET) +#define CH32V203X_REVISION_ID_FAMILY 0x203U +#define CH32V208X_REVISION_ID_FAMILY 0x208U +#define CH32V305X_REVISION_ID_FAMILY 0x305U +#define CH32V303X_REVISION_ID_FAMILY 0x303U +#define CH32V307X_REVISION_ID_FAMILY 0x307U + +/* Electronic Signature (ESIG) registers */ +#define CH32FV2X_V3X_ESIG_BASE 0x1ffff7e0U /* Electronic signature base address */ +#define CH32FV2X_V3X_ESIG_FLASH_CAP (CH32FV2X_V3X_ESIG_BASE + 0x00U) /* Flash capacity register, 16 bits, KiB units */ +#define CH32FV2X_V3X_ESIG_UID1 (CH32FV2X_V3X_ESIG_BASE + 0x08U) /* Unique ID register, bits 0:31 */ +#define CH32FV2X_V3X_ESIG_UID2 (CH32FV2X_V3X_ESIG_BASE + 0x0cU) /* Unique ID register, bits 32:63 */ +#define CH32FV2X_V3X_ESIG_UID3 (CH32FV2X_V3X_ESIG_BASE + 0x10U) /* Unique ID register, bits 64:95 */ + +/* Memory mapping */ +#define CH32FV2X_V3X_FLASH_MEMORY_ADDR 0x08000000U +#define CH32FV2X_V3X_SRAM_ADDR 0x20000000U + +static bool ch32fv2x_v3x_uid_cmd(target_s *target, int argc, const char **argv); + +const command_s ch32fv2x_v3x_cmd_list[] = { + {"uid", ch32fv2x_v3x_uid_cmd, "Prints 96 bit unique id"}, + {"option", stm32_option_bytes_cmd, "Manipulate option bytes"}, + {NULL, NULL, NULL}, +}; + +/* Reads the 96 bit unique id */ +static void ch32fv2x_v3x_read_uid(target_s *const target, uint8_t *const uid) +{ + for (size_t uid_reg_offset = 0; uid_reg_offset < 3U; uid_reg_offset++) + write_be4(uid, uid_reg_offset, target_mem_read32(target, CH32FV2X_V3X_ESIG_UID1 + (uid_reg_offset << 2U))); +} + +/* Reads the flash capacity in KiB */ +static inline size_t ch32fv2x_v3x_read_flash_capacity(target_s *const target) +{ + return target_mem_read16(target, CH32FV2X_V3X_ESIG_FLASH_CAP); +} + +/* Returns RAM capacity based on the flash capacity */ +static inline size_t ch32fv2x_v3x_get_ram_capacity(const uint16_t family, const size_t flash_capacity) +{ + /* + * With exception of CH32FV208x all lines follow the same pattern: + * ┌────────────┬─────────┬────────┐ + * │ Family │ Flash │ RAM │ + * ├────────────┴─────────┴────────┤ + * │ Low/medium-density line │ + * ├────────────┬─────────┬────────┤ + * │ CH32FV203x │ 32 KiB │ 10KiB │ + * │ CH32FV203x │ 64 KiB │ 20KiB │ + * ├────────────┴─────────┴────────┤ + * │ High-density general line │ + * ├────────────┬─────────┬────────┤ + * │ CH32F203x │ 128 KiB │ 32 KiB │ + * │ CH32F203x │ 256 KiB │ 64 KiB │ + * │ CH32V303x │ 128 KiB │ 32 KiB │ + * │ CH32V303x │ 256 KiB │ 64 KiB │ + * ├────────────┴─────────┴────────┤ + * │ Connectivity line │ + * ├────────────┬─────────┬────────┤ + * │ CH32F205x │ 128 KiB │ 32 KiB │ + * │ CH32V305x │ 128 KiB │ 32 KiB │ + * ├────────────┴─────────┴────────┤ + * │ Interconnectivity line │ + * ├────────────┬─────────┬────────┤ + * │ CH32F207x │ 256 KiB │ 64 KiB │ + * │ CH32V307x │ 256 KiB │ 64 KiB │ + * ├────────────┴─────────┴────────┤ + * │ Wireless line │ + * ├────────────┬─────────┬────────┤ + * │ CH32FV208x │ 128 KiB │ 64 KiB │ + * └────────────┴─────────┴────────┘ + */ + + /* FIXME: CH32*F*208x may not share this family code */ + if (family == CH32V208X_REVISION_ID_FAMILY) + return 64U; /* 64 KiB */ + else if (flash_capacity <= 32U) + return 10U; /* 10 KiB */ + else if (flash_capacity <= 64U) + return 20U; /* 20 KiB */ + else if (flash_capacity <= 128U) + return 32U; /* 32 KiB */ + else + return 64U; /* 64 KiB */ +} + +/* Probe for Risc-V CH32V3x family */ +bool ch32v3x_probe(target_s *const target) +{ + const uint32_t idcode = target_mem_read32(target, CH32FV2X_V3X_IDCODE); + const uint16_t device_id = idcode & CH32FV2X_V3X_IDCODE_DEVICE_ID_MASK; + const uint16_t revision_id = + (idcode & CH32FV2X_V3X_IDCODE_REVIDION_ID_MASK) >> CH32FV2X_V3X_IDCODE_REVISION_ID_OFFSET; + const uint16_t family = (revision_id & CH32V2X_3X_REVISION_ID_FAMILY_MASK) >> CH32V2X_3X_REVISION_ID_FAMILY_OFFSET; + + DEBUG_INFO("%s IDCODE 0x%" PRIx32 ", Device ID 0x%03" PRIx16 ", Revision ID 0x%04" PRIx16 ", Family 0x%03" PRIx16 + " \n", + __func__, idcode, device_id, revision_id, family); + + if (device_id != CH32V303X_DEVICE_ID && device_id != CH32V305X_7X_DEVICE_ID) + return false; + + switch (family) { + case CH32V305X_REVISION_ID_FAMILY: + target->driver = "CH32V305x"; + break; + case CH32V303X_REVISION_ID_FAMILY: + target->driver = "CH32V303x"; + break; + case CH32V307X_REVISION_ID_FAMILY: + target->driver = "CH32V307x"; + break; + default: + return false; + } + + target->part_id = idcode; + + const size_t flash_capacity = ch32fv2x_v3x_read_flash_capacity(target); + const size_t ram_capacity = ch32fv2x_v3x_get_ram_capacity(family, flash_capacity); + + DEBUG_INFO("%s Flash size: %zu KiB, RAM size: %zu KiB\n", __func__, flash_capacity, ram_capacity); + + /* KiB to bytes */ + target_add_ram(target, CH32FV2X_V3X_SRAM_ADDR, ram_capacity << 10U); + ch32fv2x_v3x_add_flash(target, CH32FV2X_V3X_FLASH_MEMORY_ADDR, flash_capacity << 10U); + + target_add_commands(target, ch32fv2x_v3x_cmd_list, target->driver); + + return true; +} + +/* Reads the 96 bit unique id */ +static bool ch32fv2x_v3x_uid_cmd(target_s *const target, const int argc, const char **const argv) +{ + (void)argc; + (void)argv; + + uint8_t uid[12U]; + ch32fv2x_v3x_read_uid(target, uid); + + tc_printf(target, "Unique id: 0x"); + for (size_t i = 0U; i < sizeof(uid); i++) + tc_printf(target, "%02" PRIx8, uid[i]); + tc_printf(target, "\n"); + + return true; +} diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c deleted file mode 100644 index 52e20ac926d..00000000000 --- a/src/target/ch32vx.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the Black Magic Debug project. - * - * Copyright (C) 2022 1BitSquared - * Written by Rafael Silva - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* This file implements RISC-V CH32Vx target specific functions */ - -#include "general.h" -#include "target.h" -#include "target_internal.h" -#include "buffer_utils.h" - -/* IDCODE register */ -#define CH32VX_IDCODE 0x1ffff704U -#define CH32VX_IDCODE_MASK 0x0ffffff0f -#define CH32VX_IDCODE_FAMILY_OFFSET 20U -#define CH32VX_IDCODE_FAMILY_MASK (0xfffU << CH32VX_IDCODE_FAMILY_OFFSET) - -#define CH32V203_IDCODE_FAMILY 0x203U -#define CH32V208_IDCODE_FAMILY 0x208U -#define CH32V305_IDCODE_FAMILY 0x305U -#define CH32V303_IDCODE_FAMILY 0x303U -#define CH32V307_IDCODE_FAMILY 0x307U - -/* Electronic Signature (ESIG) registers */ -#define CH32VX_ESIG_FLASH_CAP 0x1ffff7e0U /* Flash capacity register, 16 bits, KiB units */ -#define CH32VX_ESIG_UID1 0x1ffff7e8U /* Unique ID register, bits 0:31 */ -#define CH32VX_ESIG_UID2 0x1ffff7ecU /* Unique ID register, bits 32:63 */ -#define CH32VX_ESIG_UID3 0x1ffff7f0U /* Unique ID register, bits 64:95 */ - -static bool ch32vx_uid_cmd(target_s *t, int argc, const char **argv); - -const command_s ch32vx_cmd_list[] = { - {"uid", ch32vx_uid_cmd, "Prints 96 bit unique id"}, - {NULL, NULL, NULL}, -}; - -static uint32_t ch32vx_read_flash_size(target_s *const t) -{ - return target_mem_read16(t, CH32VX_ESIG_FLASH_CAP) * 1024U; -} - -static void ch32vx_read_uid(target_s *const t, uint8_t *const uid) -{ - for (size_t uid_reg_offset = 0; uid_reg_offset < 12U; uid_reg_offset += 4U) - write_be4(uid, uid_reg_offset, target_mem_read32(t, CH32VX_ESIG_UID1 + uid_reg_offset)); -} - -bool ch32vx_probe(target_s *const target) -{ - const uint32_t idcode = target_mem_read32(target, CH32VX_IDCODE); - - switch (idcode & CH32VX_IDCODE_MASK) { - case 0x30330504U: /* CH32V303CBT6 */ - case 0x30320504U: /* CH32V303RBT6 */ - case 0x30310504U: /* CH32V303RCT6 */ - case 0x30300504U: /* CH32V303VCT6 */ - case 0x30520508U: /* CH32V305FBP6 */ - case 0x30500508U: /* CH32V305RBT6 */ - case 0x30730508U: /* CH32V307WCU6 */ - case 0x30720508U: /* CH32V307FBP6 */ - case 0x30710508U: /* CH32V307RCT6 */ - case 0x30700508U: /* CH32V307VCT6 */ - break; - default: - return false; - break; - } - - const uint16_t family = (idcode & CH32VX_IDCODE_FAMILY_MASK) >> CH32VX_IDCODE_FAMILY_OFFSET; - switch (family) { - case CH32V303_IDCODE_FAMILY: - target->driver = "CH32V303"; - break; - case CH32V305_IDCODE_FAMILY: - target->driver = "CH32V305"; - break; - case CH32V307_IDCODE_FAMILY: - target->driver = "CH32V307"; - break; - default: - return false; - break; - } - - DEBUG_WARN("CH32Vx flash size: %u\n", ch32vx_read_flash_size(target)); - - target->part_id = idcode; - - target_add_commands(target, ch32vx_cmd_list, "CH32Vx"); - - return true; -} - -/* Reads the 96 bit unique id */ -static bool ch32vx_uid_cmd(target_s *const target, const int argc, const char **const argv) -{ - (void)argc; - (void)argv; - - uint8_t uid[12U]; - ch32vx_read_uid(target, uid); - - tc_printf(target, "Unique id: 0x"); - for (size_t i = 0U; i < sizeof(uid); i++) - tc_printf(target, "%02" PRIx8, uid[i]); - tc_printf(target, "\n"); - - return true; -} diff --git a/src/target/samx5x.c b/src/target/samx5x.c index b99868c9ac9..eff313bac5b 100644 --- a/src/target/samx5x.c +++ b/src/target/samx5x.c @@ -53,7 +53,6 @@ static bool samx5x_cmd_update_user_word(target_s *t, int argc, const char **argv /* (The SAM D1x/2x implementation of erase_all is reused as it's identical)*/ extern bool samd_mass_erase(target_s *target, platform_timeout_s *print_progess); -#define samx5x_mass_erase samd_mass_erase #ifdef SAMX5X_EXTRA_CMDS static bool samx5x_cmd_mbist(target_s *t, int argc, const char **argv); @@ -194,7 +193,6 @@ const command_s samx5x_cmd_list[] = { * (Reuses the SAM D1x/2x implementation as it is identical) */ extern void samd_reset(target_s *t); -#define samx5x_reset samd_reset /* * Overload the default cortexm attach for when the samd is protected. @@ -207,7 +205,6 @@ extern void samd_reset(target_s *t); * (Reuses the SAM D1x/2x implementation as it is identical) */ extern bool samd_protected_attach(target_s *t); -#define samx5x_protected_attach samd_protected_attach /* * Use the DSU Device Identification Register to populate a struct @@ -348,8 +345,8 @@ bool samx5x_probe(target_s *t) /* Setup Target */ t->driver = priv_storage->samx5x_variant_string; - t->reset = samx5x_reset; - t->mass_erase = samx5x_mass_erase; + t->reset = samd_reset; + t->mass_erase = samd_mass_erase; /* * Overload the default cortexm attach when the samx5x is protected. @@ -357,7 +354,7 @@ bool samx5x_probe(target_s *t) * can rescue the device. */ if (protected) - t->attach = samx5x_protected_attach; + t->attach = samd_protected_attach; switch (samx5x.mem) { default: diff --git a/src/target/stm32_flash.c b/src/target/stm32_flash.c new file mode 100644 index 00000000000..5a22b3bf2c3 --- /dev/null +++ b/src/target/stm32_flash.c @@ -0,0 +1,443 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stm32_flash.h" +#include "cortexm.h" + +/* Code flash routines */ + +bool stm32_flash_locked(target_s *const target, const uint32_t fpec_base, const uint8_t bank) +{ + const uint32_t ctrl = target_mem_read32(target, STM32_FLASH_CR_BANK(fpec_base, bank)); + return ctrl & STM32_FLASH_CR_LOCK; +} + +bool stm32_flash_unlock(target_s *const target, const uint32_t fpec_base, const uint8_t bank) +{ + target_mem_write32(target, STM32_FLASH_KEYR_BANK(fpec_base, bank), STM32_FLASH_KEY1); + target_mem_write32(target, STM32_FLASH_KEYR_BANK(fpec_base, bank), STM32_FLASH_KEY2); + + /* Check that the bank is unlocked */ + if (stm32_flash_locked(target, fpec_base, bank)) { + DEBUG_ERROR("stm32 flash unlock failed\n"); + return false; + } + return true; +} + +void stm32_flash_lock(target_s *const target, const uint32_t fpec_base, const uint8_t bank) +{ + const uint32_t ctrl = target_mem_read32(target, STM32_FLASH_CR_BANK(fpec_base, bank)); + /* Retain the EOPIE, ERRIE and OPTWRE bits, and set the LOCK bit */ + target_mem_write32(target, STM32_FLASH_CR_BANK(fpec_base, bank), + (ctrl & (STM32_FLASH_CR_EOPIE | STM32_FLASH_CR_ERRIE | STM32_FLASH_CR_OPTWRE)) | STM32_FLASH_CR_LOCK); +} + +void stm32_flash_clear_status(target_s *const target, const uint32_t fpec_base, const uint8_t bank) +{ + /* EOP, WRPRTERR and PGERR are reset by writing 1 */ + target_mem_write32(target, STM32_FLASH_SR_BANK(fpec_base, bank), + STM32_FLASH_SR_EOP | STM32_FLASH_SR_PGERR | STM32_FLASH_SR_WRPRTERR); +} + +bool stm32_flash_busy_wait( + target_s *const target, const uint32_t fpec_base, const uint8_t bank, platform_timeout_s *const print_progess) +{ + /* Read FLASH_SR to poll for BSY bit */ + uint32_t status = STM32_FLASH_SR_BSY; + /* + * Note that checking EOP here is only legal ifevery operation is preceded by a call to + * stm32_flash_clear_status, without it the flag could be set from a previous operation. + * + * See §3.4 Flash status register (FLASH_SR) in PM0068/PM0075 Programming manual + */ + while (!(status & STM32_FLASH_SR_EOP) && (status & STM32_FLASH_SR_BSY)) { + status = target_mem_read32(target, STM32_FLASH_SR_BANK(fpec_base, bank)); + if (target_check_error(target)) { + DEBUG_ERROR("Lost communications with target"); + return false; + } + if (print_progess) + target_print_progress(print_progess); + }; + + /* Check for errors */ + const uint32_t error = status & (STM32_FLASH_SR_PGERR | STM32_FLASH_SR_WRPRTERR); + if (error) + DEBUG_ERROR("stm32 flash error 0x%" PRIx32 "\n", error); + return error == 0U; +} + +static bool stm32_flash_prepare(target_flash_s *const flash) +{ + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* Unlock the flash bank if required */ + if (stm32_flash_locked(target, stm32_flash->fpec_base, stm32_flash->bank) && + !stm32_flash_unlock(target, stm32_flash->fpec_base, stm32_flash->bank)) + return false; + + /* Ensure no operation is ongoing */ + if (target_mem_read32(target, STM32_FLASH_SR_BANK(stm32_flash->fpec_base, stm32_flash->bank)) & + STM32_FLASH_SR_BSY) { + DEBUG_ERROR("stm32 flash unexpectedly busy\n"); + return false; /* FIXME: should we re-lock here? */ + } + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, stm32_flash->bank); + + /* Set the instruction to the control register */ + uint32_t ctrl_instruction = 0U; + switch (flash->operation) { + case FLASH_OPERATION_WRITE: + /* Set flash programming instruction */ + ctrl_instruction = STM32_FLASH_CR_PG; + break; + case FLASH_OPERATION_ERASE: + /* Set flash page erase instruction */ + ctrl_instruction = STM32_FLASH_CR_PER_SER; + break; + case FLASH_OPERATION_MASS_ERASE: + /* Set flash bank mass erase instruction */ + ctrl_instruction = STM32_FLASH_CR_MER; + break; + default: + return false; /* Unsupported operation */ + } + /* + * This will clear EOPIE, ERRIE and OPTWRE, but we don't care about them and expect them cleared anyway + * note that we don't read-modify-write the control register after this, meaning we need to set the instruction + * allways, this is to avoid the extra overhead of reading the register since we know what bits should be set. + */ + target_mem_write32(target, STM32_FLASH_CR_BANK(stm32_flash->fpec_base, stm32_flash->bank), ctrl_instruction); + + return true; +} + +static bool stm32_flash_done(target_flash_s *const flash) +{ + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* Lock the flash bank */ + stm32_flash_lock(target, stm32_flash->fpec_base, stm32_flash->bank); + + return true; +} + +static bool stm32_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + (void)length; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §2.3.4 Flash memory erase in PM0068/PM0075 Programming manual */ + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, stm32_flash->bank); + + /* Write page address to address register */ + target_mem_write32(target, STM32_FLASH_AR_BANK(stm32_flash->fpec_base, stm32_flash->bank), addr); + + /* Start flash page erase instruction */ + target_mem_write32(target, STM32_FLASH_CR_BANK(stm32_flash->fpec_base, stm32_flash->bank), + STM32_FLASH_CR_STRT | STM32_FLASH_CR_PER_SER); + + /* Wait for completion or an error, return false on error */ + return stm32_flash_busy_wait(target, stm32_flash->fpec_base, stm32_flash->bank, NULL); +} + +static bool stm32_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) +{ + (void)len; + + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* See §2.3.3 Main Flash memory programming in PM0068/PM0075 Programming manual */ + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, stm32_flash->fpec_base, stm32_flash->bank); + + /* + * The operatio is started on a half-word write into a main Flash memory address. + * Any attempt to write data that are not half-word long will result in a bus error response from the FPEC. + */ + target_mem_write16( + target, dest, *(const uint16_t *)src); // target_mem_write_aligned(target, dest, src, len, ALIGN_HALFWORD); + + return stm32_flash_busy_wait(target, stm32_flash->fpec_base, stm32_flash->bank, NULL); +} + +bool stm32_flash_mass_erase(target_flash_s *const flash, platform_timeout_s *const print_progess) +{ + stm32_flash_s *const stm32_flash = (stm32_flash_s *)flash; + target_s *const target = flash->t; + + /* Start flash bank mass erase instruction */ + target_mem_write32(target, STM32_FLASH_CR_BANK(stm32_flash->fpec_base, stm32_flash->bank), + STM32_FLASH_CR_STRT | STM32_FLASH_CR_MER); + + /* Wait for completion or an error, return false on error */ + return stm32_flash_busy_wait(target, stm32_flash->fpec_base, stm32_flash->bank, print_progess); +} + +static void stm32_add_flash_bank(target_s *const target, target_flash_s *const flash, const target_addr_t addr, + const size_t length, const size_t block_size) +{ + flash->start = addr; + flash->length = length; + flash->blocksize = block_size; + flash->writesize = 2U; /* The smallest write size is 16 bits, in the interest of speed we might want to bump this */ + flash->erased = 0xffU; + flash->erase = stm32_flash_erase; + flash->mass_erase = stm32_flash_mass_erase; + flash->write = stm32_flash_write; + flash->prepare = stm32_flash_prepare; + flash->done = stm32_flash_done; + + target_add_flash(target, flash); +} + +void stm32_add_flash(target_s *target, target_addr_t addr, size_t length, uint32_t fpec_base, size_t block_size) +{ + stm32_flash_s *const stm32_flash = calloc(1U, sizeof(*stm32_flash)); + if (!stm32_flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + stm32_flash->fpec_base = fpec_base; + + stm32_add_flash_bank(target, &stm32_flash->flash, addr, length, block_size); +} + +void stm32_add_banked_flash(target_s *target, target_addr_t addr, size_t length, target_addr_t bank_split_addr, + uint32_t fpec_base, size_t block_size) +{ + /* Allocate two sequential flash structures, one for each bank */ + stm32_flash_s *const stm32_flash = calloc(2U, sizeof(*stm32_flash)); + if (!stm32_flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + /* Add the two flash banks */ + for (size_t bank = 0; bank < 2U; bank++) { + stm32_flash[bank].fpec_base = fpec_base; + stm32_flash[bank].bank = bank; + + const target_addr_t bank_addr = bank == 0U ? addr : bank_split_addr; + const size_t bank_length = bank == 0U ? bank_split_addr - addr : length - (bank_split_addr - addr); + + stm32_add_flash_bank(target, &stm32_flash[bank].flash, bank_addr, bank_length, block_size); + } +} + +/* Option byte flash routines */ + +bool stm32_option_bytes_locked(target_s *const target, const uint32_t fpec_base) +{ + const uint32_t ctrl = target_mem_read32(target, STM32_FLASH_CR(fpec_base)); + return !(ctrl & STM32_FLASH_CR_OPTWRE); +} + +bool stm32_option_bytes_unlock(target_s *const target, const uint32_t fpec_base) +{ + target_mem_write32(target, STM32_FLASH_OPTKEYR(fpec_base), STM32_FLASH_KEY1); + target_mem_write32(target, STM32_FLASH_OPTKEYR(fpec_base), STM32_FLASH_KEY2); + + /* Check that the bank is unlocked */ + if (stm32_option_bytes_locked(target, fpec_base)) { + DEBUG_ERROR("stm32 option bytes unlock failed\n"); + return false; + } + return true; +} + +static bool stm32_option_bytes_erase(target_s *const target, const uint32_t fpec_base) +{ + /* Clear any previous operation status */ + stm32_flash_clear_status(target, fpec_base, 0U); + + /* Set option byte erase instruction */ + target_mem_write32(target, STM32_FLASH_CR(fpec_base), STM32_FLASH_CR_OPTWRE | STM32_FLASH_CR_OPTER); + + /* Start option byte erase instruction */ + target_mem_write32( + target, STM32_FLASH_CR(fpec_base), STM32_FLASH_CR_OPTWRE | STM32_FLASH_CR_OPTER | STM32_FLASH_CR_STRT); + + /* Wait for completion or an error, return false on error */ + return stm32_flash_busy_wait(target, fpec_base, 0U, NULL); +} + +static bool stm32_option_bytes_write( + target_s *const target, const uint32_t fpec_base, const size_t offset, const uint16_t value) +{ + if (value == 0xffffU) + return true; + + /* Clear any previous operation status */ + stm32_flash_clear_status(target, fpec_base, 0U); + + /* Set option byte programming instruction */ + target_mem_write32(target, STM32_FLASH_CR(fpec_base), STM32_FLASH_CR_OPTWRE | STM32_FLASH_CR_OPTPG); + + const uint32_t addr = STM32_FLASH_OPT_ADDR + (offset * 2U); + + /* + * Write changed values, taking into account if we can use 32- or have to use 16-bit writes. + * GD32E230 is a special case as target_mem_write16 does not work + */ + const bool write16_broken = target->part_id == 0x410U && (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23; + if (write16_broken) + target_mem_write32(target, addr, 0xffff0000U | value); + else + target_mem_write16(target, addr, value); + + /* Wait for completion or an error, return false on error */ + const bool result = stm32_flash_busy_wait(target, fpec_base, 0U, NULL); + if (offset != 0U || result) + return result; + /* + * FIXME: I don't follow the explanation here, clarify + * In the case that the write failed and we're handling option byte 0 (RDP), + * check if we got a status of "Program Error" in FLASH_SR, indicating the target + * refused to erase the read protection option bytes (and turn it into a truthy return). + */ + const bool programming_error = !!(target_mem_read32(target, STM32_FLASH_SR(fpec_base)) & STM32_FLASH_SR_PGERR); + return programming_error; +} + +static bool stm32_option_bytes_read_modify_write( + target_s *const target, const uint32_t fpec_base, const uint32_t addr, const uint16_t value) +{ + const uint32_t index = (addr - STM32_FLASH_OPT_ADDR) >> 1U; + /* If index would be negative, the high most bit is set, so we get a giant positive number. */ + if (index > 7U) + return false; + + uint16_t option_bytes[8U]; + + /* Retrieve old values */ + target_mem_read(target, option_bytes, STM32_FLASH_OPT_ADDR, sizeof(option_bytes)); + + if (option_bytes[index] == value) + return true; + + /* Check for erased value */ + if (option_bytes[index] != 0xffffU && !stm32_option_bytes_erase(target, fpec_base)) + return false; + + option_bytes[index] = value; + + /* Write the modified option bytes */ + for (size_t byte_index = 0U; byte_index < 8U; ++byte_index) { + if (!stm32_option_bytes_write(target, fpec_base, byte_index, option_bytes[byte_index])) + return false; + } + + return true; +} + +static inline uint16_t stm32_option_bytes_read_protect_key(const target_s *const target) +{ + /* FIXME: there must be a better way */ + switch (target->part_id) { + case 0x422U: /* STM32F30x */ + case 0x432U: /* STM32F37x */ + case 0x438U: /* STM32F303x6/8 and STM32F328 */ + case 0x440U: /* STM32F0 */ + case 0x446U: /* STM32F303xD/E and STM32F398xE */ + case 0x445U: /* STM32F04 RM0091 Rev.7, STM32F070x6 RM0360 Rev. 4*/ + case 0x448U: /* STM32F07 RM0091 Rev.7, STM32F070xb RM0360 Rev. 4*/ + case 0x442U: /* STM32F09 RM0091 Rev.7, STM32F030xc RM0360 Rev. 4*/ + return STM32F3X_FLASH_RDPRT; + } + return STM32F10X_FLASH_RDPRT; +} + +bool stm32_option_bytes_cmd(target_s *const target, int argc, const char **argv) +{ + const uint32_t fpec_base = ((stm32_flash_s *)target->flash)->fpec_base; /* Get the FPEC base from a target flash */ + + /* Fast-exit if the Flash is not readable and the user didn't ask us to erase the option bytes */ + const bool erase_requested = argc == 2 && strcmp(argv[1], "erase") == 0; + if (!erase_requested) { + const bool read_protected = + !!(target_mem_read32(target, STM32_FLASH_OBR(fpec_base)) & STM32F10X_FLASH_OBR_RDPRT); + + if (read_protected) { + tc_printf(target, "Device is Read Protected\nUse `monitor option erase` to unprotect and erase device\n"); + return true; + } + } + + /* Unlock the flash if required */ + if (stm32_flash_locked(target, fpec_base, 0U) && !stm32_flash_unlock(target, fpec_base, 0U)) + return false; + + /* Unlock the option bytes if required */ + if (stm32_option_bytes_locked(target, fpec_base) && !stm32_option_bytes_unlock(target, fpec_base)) + return false; + + if (erase_requested) { + /* When the user asks us to erase the option bytes, kick of an erase */ + if (!stm32_option_bytes_erase(target, fpec_base)) + return false; + + /* Write the option bytes Flash readable key */ + if (!stm32_option_bytes_write(target, fpec_base, 0U, stm32_option_bytes_read_protect_key(target))) + return false; + } else if (argc == 3) { + /* If 3 arguments are given, assume the second is an address, and the third a value */ + const uint32_t addr = strtoul(argv[1], NULL, 0); + const uint32_t value = strtoul(argv[2], NULL, 0); + + /* Try and program the new option value to the requested option byte */ + if (!stm32_option_bytes_read_modify_write(target, fpec_base, addr, value)) + return false; + } else + tc_printf(target, "usage: monitor option erase\nusage: monitor option \n"); + + /* When all gets said and done, display the current option bytes values */ + for (size_t offset = 0U; offset < 16U; offset += 4U) { + const uint32_t addr = STM32_FLASH_OPT_ADDR + offset; + const uint32_t val = target_mem_read32(target, addr); + tc_printf(target, "0x%08X: 0x%04X\n", addr, val & 0xffffU); + tc_printf(target, "0x%08X: 0x%04X\n", addr + 2U, val >> 16U); + } + + return true; +} diff --git a/src/target/stm32_flash.h b/src/target/stm32_flash.h new file mode 100644 index 00000000000..88b3c6e97ca --- /dev/null +++ b/src/target/stm32_flash.h @@ -0,0 +1,270 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2023 1BitSquared + * Written by Rafael Silva + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_STM32_FLASH_H +#define TARGET_STM32_FLASH_H + +#include "general.h" +#include "target.h" +#include "target_internal.h" + +/* + * Flash program and erase controller (FPEC) + * + * This accounts for STM32F1x, STM32F3x and STM32F4x series + * + * This is based on the PM0068 and PM0075 ST programing manuals + * https://www.st.com/resource/en/programming_manual/pm0068-stm32f10xxx-xldensity-flash-programming-stmicroelectronics.pdf + * https://www.st.com/resource/en/programming_manual/pm0075-stm32f10xxx-flash-memory-microcontrollers-stmicroelectronics.pdf + */ +#define STM32F10X_FPEC_BASE 0x40022000U +#define STM32F4X_FPEC_BASE 0x40023c00U +#define STM32_FLASH_BANK_OFFSET 0x40U + +/* Flash access control register (FLASH_ACR) */ +#define STM32_FLASH_ACR_OFFSET 0x00U +#define STM32_FLASH_ACR(fpec_base) ((fpec_base) + STM32_FLASH_ACR_OFFSET) + +/* STM32F4x Only */ +#define STM32F4X_FLASH_ACR_DCRST (1U << 12U) /* Data cache reset (1: reset, 0: not reset) */ +#define STM32F4X_FLASH_ACR_ICRST (1U << 11U) /* Instruction cache reset (1: reset, 0: not reset) */ +#define STM32F4X_FLASH_ACR_DCEN (1U << 10U) /* Data cache enable (1: enabled, 0: disabled) */ +#define STM32F4X_FLASH_ACR_ICEN (1U << 9U) /* Instruction cache enable (1: enabled, 0: disabled) */ +#define STM32F4X_FLASH_ACR_PRFTEN (1U << 8U) /* Prefetch enable (1: enabled, 0: disabled) */ +/* STM32F1x/F3x Only */ +#define STM32F10X_FLASH_ACR_PRFTBS (1U << 5U) /* Prefetch buffer status (1: enabled, 0: disabled) */ +#define STM32F10X_FLASH_ACR_PRFTBE (1U << 4U) /* Prefetch buffer enable (1: enable, 0: disable) */ +#define STM32F10X_FLASH_ACR_HLFCYA (1U << 3U) /* Flash half cycle access enable (1: enabled, 0: disabled) */ + +#define STM32_FLASH_ACR_LATENCY_MASK 0x7U /* Latency */ +#define STM32_FLASH_ACR_LATENCY_0WS 0U /* 0b000 - Zero wait state (STM32F10X: 0 < SYSCLK≤ 24 MHz) */ +#define STM32_FLASH_ACR_LATENCY_1WS 1U /* 0b001 - One wait state (STM32F10X: 24 MHz < SYSCLK ≤ 48 MHz) */ +#define STM32_FLASH_ACR_LATENCY_2WS 2U /* 0b010 - Two wait states (STM32F10X: 48 MHz < SYSCLK ≤ 72 MHz) */ +#define STM32_FLASH_ACR_LATENCY_3WS 3U /* 0b011 - Three wait states (STM32F4x Only) */ +#define STM32_FLASH_ACR_LATENCY_4WS 4U /* 0b100 - Four wait states (STM32F4x Only) */ +#define STM32_FLASH_ACR_LATENCY_5WS 5U /* 0b101 - Five wait states (STM32F4x Only) */ +#define STM32_FLASH_ACR_LATENCY_6WS 6U /* 0b110 - Six wait states (STM32F4x Only) */ +#define STM32_FLASH_ACR_LATENCY_7WS 7U /* 0b111 - Seven wait states (STM32F4x Only) */ + +/* FPEC key register (FLASH_KEYR) */ +#define STM32_FLASH_KEYR_OFFSET 0x04U +#define STM32_FLASH_KEYR(fpec_base) ((fpec_base) + STM32_FLASH_KEYR_OFFSET) +#define STM32_FLASH_KEYR_BANK(fpec_base, bank) (STM32_FLASH_KEYR(fpec_base) + (bank)*STM32_FLASH_BANK_OFFSET) + +#define STM32_FLASH_KEY1 0x45670123U +#define STM32_FLASH_KEY2 0xcdef89abU + +/* Flash OPTKEY register (FLASH_OPTKEYR) */ +#define STM32_FLASH_OPTKEYR_OFFSET 0x08U +#define STM32_FLASH_OPTKEYR(fpec_base) ((fpec_base) + STM32_FLASH_OPTKEYR_OFFSET) + +#define STM32F4X_FLASH_OPTKEY1 0x08192a3bU +#define STM32F4X_FLASH_OPTKEY2 0x4c5d6e7fU + +/* Flash status register (FLASH_SR) */ +#define STM32_FLASH_SR_OFFSET 0x0cU +#define STM32_FLASH_SR(fpec_base) ((fpec_base) + STM32_FLASH_SR_OFFSET) +#define STM32_FLASH_SR_BANK(fpec_base, bank) (STM32_FLASH_SR(fpec_base) + (bank)*STM32_FLASH_BANK_OFFSET) + +#define STM32_FLASH_SR_WRPRTERR (1U << 4U) /* Write protection error */ +// /* STM32F4x Only */ +// #define STM32F4X_FLASH_SR_BSY (1U << 16U) /* Busy */ +// #define STM32F4X_FLASH_SR_RDERR (1U << 8U) /* Proprietary readout protection error (STM32F42/3x Only) */ +// #define STM32F4X_FLASH_SR_PGSERR (1U << 7U) /* Programming sequence error */ +// #define STM32F4X_FLASH_SR_PGPERR (1U << 6U) /* Programming parallelism error */ +// #define STM32F4X_FLASH_SR_PGAERR (1U << 5U) /* Programming alignment error */ +// #define STM32F4X_FLASH_SR_OPERR (1U << 1U) /* Operation error */ +// #define STM32F4X_FLASH_SR_EOP (1U << 0U) /* End of operation */ +/* STM32F1x/F3x Only */ +// #define STM32F10X_FLASH_SR_EOP (1U << 5U) /* End of operation */ +// #define STM32F10X_FLASH_SR_PGERR (1U << 2U) /* Programming error */ +// #define STM32F10X_FLASH_SR_BSY (1U << 0U) /* Busy operation */ +#define STM32_FLASH_SR_EOP (1U << 5U) /* End of operation */ +#define STM32_FLASH_SR_PGERR (1U << 2U) /* Programming error */ +#define STM32_FLASH_SR_BSY (1U << 0U) /* Busy operation */ + +/* Flash control register (FLASH_CR) */ +#define STM32_FLASH_CR_OFFSET 0x10U +#define STM32_FLASH_CR(fpec_base) ((fpec_base) + STM32_FLASH_CR_OFFSET) +#define STM32_FLASH_CR_BANK(fpec_base, bank) (STM32_FLASH_CR(fpec_base) + (bank)*STM32_FLASH_BANK_OFFSET) + +#define STM32_FLASH_CR_MER (1U << 2U) /* Mass erase bank */ +#define STM32_FLASH_CR_PER_SER (1U << 1U) /* Page / Sector erase */ +#define STM32_FLASH_CR_PG (1U << 0U) /* Programming */ +// /* STM32F4x Only */ +// #define STM32F4X_FLASH_CR_LOCK (1U << 31) /* Lock */ +// #define STM32F4X_FLASH_CR_ERRIE (1U << 25U) /* Error interrupt enable */ +// #define STM32F4X_FLASH_CR_EOPIE (1U << 24U) /* End of operation interrupt enable */ +// #define STM32F4X_FLASH_CR_STRT (1U << 16U) /* Start */ +// #define STM32F4X_FLASH_CR_MER2 (1U << 15U) /* Mass Erase of bank 2 (STM32F42/3x Only) */ +// #define STM32F4X_FLASH_CR_PSIZE_OFFSET 8U /* Program parallelism */ +// #define STM32F4X_FLASH_CR_PSIZE_MASK (0x3U << STM32F4X_FLASH_CR_PSIZE_OFFSET) /* Program parallelism */ +// #define STM32F4X_FLASH_CR_PSIZE_8BIT (0x0U << STM32F4X_FLASH_CR_PSIZE_OFFSET) /* 0b00 program x8 */ +// #define STM32F4X_FLASH_CR_PSIZE_16BIT (0x1U << STM32F4X_FLASH_CR_PSIZE_OFFSET) /* 0b01 program x16 */ +// #define STM32F4X_FLASH_CR_PSIZE_32BIT (0x2U << STM32F4X_FLASH_CR_PSIZE_OFFSET) /* 0b10 program x32 */ +// #define STM32F4X_FLASH_CR_PSIZE_64BIT (0x3U << STM32F4X_FLASH_CR_PSIZE_OFFSET) /* 0b11 program x64 */ +// #define STM32F4X_FLASH_CR_SNB_OFFSET 3U /* Sector number */ +// #define STM32F4X_FLASH_CR_SNB_MASK (0x1fU << STM32F4X_FLASH_CR_SNB_OFFSET) /* Sector number */ +// /* STM32F1x/F3x Only */ +// #define STM32F10X_FLASH_CR_EOPIE (1U << 12U) /* End of operation interrupt enable */ +// #define STM32F10X_FLASH_CR_ERRIE (1U << 10U) /* Error interrupt enable */ +// #define STM32F10X_FLASH_CR_OPTWRE (1U << 9U) /* Option bytes write enable */ +// #define STM32F10X_FLASH_CR_LOCK (1U << 7U) /* Lock */ +// #define STM32F10X_FLASH_CR_STRT (1U << 6U) /* Start */ +// #define STM32F10X_FLASH_CR_OPTER (1U << 5U) /* Option byte erase */ +// #define STM32F10X_FLASH_CR_OPTPG (1U << 4U) /* Option byte programming */ +#define STM32_FLASH_CR_EOPIE (1U << 12U) /* End of operation interrupt enable */ +#define STM32_FLASH_CR_ERRIE (1U << 10U) /* Error interrupt enable */ +#define STM32_FLASH_CR_OPTWRE (1U << 9U) /* Option bytes write enable */ +#define STM32_FLASH_CR_LOCK (1U << 7U) /* Lock */ +#define STM32_FLASH_CR_STRT (1U << 6U) /* Start */ +#define STM32_FLASH_CR_OPTER (1U << 5U) /* Option byte erase */ +#define STM32_FLASH_CR_OPTPG (1U << 4U) /* Option byte programming */ + +/* OBL_LAUNCH is not available on all families */ +#define STM32_FLASH_CR_OBL_LAUNCH (1U << 13U) /* Force the option byte loading (generates system reset) */ + +/* Flash address register (FLASH_AR) (STM32F1x/F3x Only) */ +// #define STM32F10X_FLASH_AR_OFFSET 0x14U +// #define STM32F10X_FLASH_AR(fpec_base) ((fpec_base) + STM32_FLASH_AR_OFFSET) +// #define STM32F10X_FLASH_AR_BANK(fpec_base, bank) (STM32_FLASH_AR(fpec_base) + (bank) * STM32_FLASH_BANK_OFFSET) +#define STM32_FLASH_AR_OFFSET 0x14U +#define STM32_FLASH_AR(fpec_base) ((fpec_base) + STM32_FLASH_AR_OFFSET) +#define STM32_FLASH_AR_BANK(fpec_base, bank) (STM32_FLASH_AR(fpec_base) + (bank)*STM32_FLASH_BANK_OFFSET) + +/* Flash option control register (FLASH_OPTCR) (STM32F4x Only) */ +#define STM32F4X_FLASH_OPTCR_OFFSET 0x14U +#define STM32F4X_FLASH_OPTCR(fpec_base) ((fpec_base) + STM32F4X_FLASH_OPTCR_OFFSET) + +/* TODO */ + +/* Flash option control register (FLASH_OPTCR1) (STM32F4x Only) */ +#define STM32F4X_FLASH_OPTCR1_OFFSET 0x18U +#define STM32F4X_FLASH_OPTCR1(fpec_base) ((fpec_base) + STM32F4X_FLASH_OPTCR1_OFFSET) + +/* TODO */ + +/* Option byte register (FLASH_OBR) (STM32F1x/F3x Only) */ +#define STM32_FLASH_OBR_OFFSET 0x1cU +#define STM32_FLASH_OBR(fpec_base) ((fpec_base) + STM32_FLASH_OBR_OFFSET) + +#define STM32_FLASH_OBR_OPTERR (1U << 0U) /* OPTERR Option byte error */ + +/* Write protection register (FLASH_WRPR) (STM32F1x/F3x Only) */ +#define STM32_FLASH_WRPR_OFFSET 0x20U +#define STM32_FLASH_WRPR(fpec_base) ((fpec_base) + STM32_FLASH_WRPR_OFFSET) + +/* + * §2.5 - Option byte description + * + * ┌───────────────────────────────────────────────────────────────────────────────────────┐ + * │ Table 3. Option byte format │ + * ├─────────────────────────────┬───────────────┬─────────────────────────┬───────────────┤ + * │ 31:24 │ 23:16 │ 15:8 │ 7:0 │ + * ├─────────────────────────────┼───────────────┼─────────────────────────┼───────────────┤ + * │ Complement option byte1 │ Option byte 1 │ Complement option byte0 │ Option byte 0 │ + * └─────────────────────────────┴───────────────┴─────────────────────────┴───────────────┘ + * + * Table 4. Option byte organization + * ┌────────────┬────────┬───────┬────────┬───────┐ + * │ Address │ 31:24 │ 23:16 │ 15:8 │ 7:0 │ + * ├────────────┼────────┼───────┼────────┼───────┤ + * │ 0x1ffff800 │ nUSER │ USER │ nRDP │ RDP │ + * │ 0x1ffff804 │ nData1 │ Data1 │ nData0 │ Data0 │ + * │ 0x1ffff808 │ nWRP1 │ WRP1 │ nWRP0 │ WRP0 │ + * │ 0x1ffff80c │ nWRP3 │ WRP3 │ nWRP2 │ WRP2 │ + * └────────────┴────────┴───────┴────────┴───────┘ + */ + +#define STM32_FLASH_OPT_ADDR 0x1ffff800U + +/* Option byte register STM32F10x */ +#define STM32F10X_FLASH_OBR_DATA1_OFFSET 18U /* 8 bits */ +#define STM32F10X_FLASH_OBR_DATA0_OFFSET 10U /* 8 bits */ +#define STM32F10X_FLASH_OBR_USER_9 (1U << 9U) /* Not used */ +#define STM32F10X_FLASH_OBR_USER_8 (1U << 8U) /* Not used */ +#define STM32F10X_FLASH_OBR_USER_7 (1U << 7U) /* Not used */ +#define STM32F10X_FLASH_OBR_USER_6 (1U << 6U) /* Not used */ +#define STM32F10X_FLASH_OBR_USER_BFB2 (1U << 5U) +#define STM32F10X_FLASH_OBR_USER_NRST_STDBY (1U << 4U) +#define STM32F10X_FLASH_OBR_USER_NRST_STOP (1U << 3U) +#define STM32F10X_FLASH_OBR_USER_WDG_SW (1U << 2U) +#define STM32F10X_FLASH_OBR_RDPRT (1U << 1U) /* Read protection */ + +#define STM32F10X_FLASH_RDPRT 0xa5U + +/* Option byte register STM32F3xx */ +//#define STM32F3XX_FLASH_RDPRT 0x55U +// Bits 31:24 Data1 +// Bits 23:16 Data0 +// Bits 15:8 OBR: User Option Byte +// Bit 15: Reserved, must be kept at reset value. +// Bit 14: Reserved, must be kept at reset value. +// Bit 13: VDDA_MONITOR +// Bit 12: nBOOT1 +// Bit 11: Reserved, must be kept at reset value. +// Bit 10: nRST_STDBY +// Bit 9: nRST_STOP +// Bit 8: WDG_SW +// Bits 7:3 Reserved, must be kept at reset value. +// Bit 2:1 RDPRT[1:0]: Read protection Level status +// 00: Read protection Level 0 is enabled (ST production set up) +// 01: Read protection Level 1 is enabled +// 10: Reserved +// 11: Read protection Level 2 is enabled +// Note: These bits are read-only. + +#define STM32F3X_FLASH_RDPRT 0xaaU + +/* Insternal API */ +typedef struct stm32_flash { + target_flash_s flash; + uint32_t fpec_base; + uint8_t bank; +} stm32_flash_s; + +bool stm32_flash_locked(target_s *target, uint32_t fpec_base, uint8_t bank); +bool stm32_flash_unlock(target_s *target, uint32_t fpec_base, uint8_t bank); +void stm32_flash_lock(target_s *target, uint32_t fpec_base, uint8_t bank); +void stm32_flash_clear_status(target_s *target, uint32_t fpec_base, uint8_t bank); +bool stm32_flash_busy_wait(target_s *target, uint32_t fpec_base, uint8_t bank, platform_timeout_s *print_progess); +bool stm32_flash_mass_erase(target_flash_s *flash, platform_timeout_s *print_progess); + +/* Generic flash routines */ +void stm32_add_flash(target_s *target, target_addr_t addr, size_t length, uint32_t fpec_base, size_t block_size); +void stm32_add_banked_flash(target_s *target, target_addr_t addr, size_t length, target_addr_t bank_split_addr, + uint32_t fpec_base, size_t block_size); + +/* Option bytes command */ +bool stm32_option_bytes_cmd(target_s *target, int argc, const char **argv); + +#endif /* TARGET_STM32_FLASH_H */ diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 4f634e303ac..79e8ad9e2d0 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -42,111 +42,71 @@ #include "target_internal.h" #include "cortexm.h" #include "jep106.h" - -static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv); - -const command_s stm32f1_cmd_list[] = { - {"option", stm32f1_cmd_option, "Manipulate option bytes"}, +#include "stm32_flash.h" + +/* IDCODE register */ +#define STM32_IDCODE_REVISION_ID_OFFSET 16U +#define STM32_IDCODE_REVISION_ID_MASK (0xffffU << STM32_IDCODE_REVISION_ID_OFFSET) +#define STM32_IDCODE_DEVICE_ID_MASK 0xfffU + +/* Noted as reserved by ST, but contains useful information for AT32F43x */ +#define STM32_IDCODE_RESERVED_OFFSET 12U +#define STM32_IDCODE_RESERVED_MASK (0xfU << STM32_IDCODE_RESERVED_OFFSET) + +#define STM32F10X_IDCODE 0xe0042000U +#define STM32F0X_IDCODE 0x40015800U +#define GD32E5X_IDCODE 0xe0044000U +#define MM32L0X_IDCODE 0x40013400U +#define MM32FX_IDCODE 0x40007080U + +#define AT32F40X_REVISION_ID 0x7005U +#define AT32F41X_REVISION_ID 0x7003U +#define AT32F43X_REVISION_ID 0x7008U + +#define AT32F43X_RESERVED_B_2K 3U +#define AT32F43X_RESERVED_B_4K 4U + +/* Electronic Signature (ESIG) registers */ +#define GD32_ESIG_MEM_DENSITY(esig_base) ((esig_base) + 0x00U) /* Memory density register */ +#define GD32_ESIG_MEM_DENSITY_RAM_OFFSET 16U +#define GD32_ESIG_MEM_DENSITY_RAM_MASK (0xffffU << GD32_ESIG_MEM_DENSITY_RAM_OFFSET) /* KiB units */ +#define GD32_ESIG_MEM_DENSITY_FLASH_MASK 0xffffU /* KiB units */ +#define GD32_ESIG_UID1(esig_base) ((esig_base) + 0x08U) /* Unique ID register, bits 0:31 */ +#define GD32_ESIG_UID2(esig_base) ((esig_base) + 0x0cU) /* Unique ID register, bits 32:63 */ +#define GD32_ESIG_UID3(esig_base) ((esig_base) + 0x10U) /* Unique ID register, bits 64:95 */ + +#define GD32FX_ESIG_BASE 0x1ffff7e0U /* GD32Fx Electronic signature base address */ +#define GD32F0X_ESIG_BASE 0x1ffff7ccU /* GD32F0x Electronic signature base address */ + +/* STM32F10X memory mapping */ +#define STM32F10X_FLASH_MEMORY_ADDR 0x08000000U +#define STM32F10X_SRAM_ADDR 0x20000000U +#define STM32F10X_FLASH_BANK_SIZE 512U /* KiB */ +#define STM32F10X_FLASH_BANK_SPLIT_ADDR (STM32F10X_FLASH_MEMORY_ADDR + (STM32F10X_FLASH_BANK_SIZE << 10U)) + +const command_s stm32_cmd_list[] = { + {"option", stm32_option_bytes_cmd, "Manipulate option bytes"}, {NULL, NULL, NULL}, }; -static bool stm32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); -static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); -static bool stm32f1_mass_erase(target_s *target); - -/* Flash Program ad Erase Controller Register Map */ -#define FPEC_BASE 0x40022000U -#define FLASH_ACR (FPEC_BASE + 0x00U) -#define FLASH_KEYR (FPEC_BASE + 0x04U) -#define FLASH_OPTKEYR (FPEC_BASE + 0x08U) -#define FLASH_SR (FPEC_BASE + 0x0cU) -#define FLASH_CR (FPEC_BASE + 0x10U) -#define FLASH_AR (FPEC_BASE + 0x14U) -#define FLASH_OBR (FPEC_BASE + 0x1cU) -#define FLASH_WRPR (FPEC_BASE + 0x20U) - -#define FLASH_BANK1_OFFSET 0x00U -#define FLASH_BANK2_OFFSET 0x40U -#define FLASH_BANK_SPLIT 0x08080000U - -#define FLASH_CR_OBL_LAUNCH (1U << 13U) -#define FLASH_CR_OPTWRE (1U << 9U) -#define FLASH_CR_LOCK (1U << 7U) -#define FLASH_CR_STRT (1U << 6U) -#define FLASH_CR_OPTER (1U << 5U) -#define FLASH_CR_OPTPG (1U << 4U) -#define FLASH_CR_MER (1U << 2U) -#define FLASH_CR_PER (1U << 1U) -#define FLASH_CR_PG (1U << 0U) - -#define FLASH_OBR_RDPRT (1U << 1U) - -#define FLASH_SR_BSY (1U << 0U) - -#define FLASH_OBP_RDP 0x1ffff800U -#define FLASH_OBP_RDP_KEY 0x5aa5U -#define FLASH_OBP_RDP_KEY_F3 0x55aaU - -#define KEY1 0x45670123U -#define KEY2 0xcdef89abU - -#define SR_ERROR_MASK 0x14U -#define SR_PROG_ERROR 0x04U -#define SR_EOP 0x20U - -#define DBGMCU_IDCODE 0xe0042000U -#define DBGMCU_IDCODE_F0 0x40015800U -#define DBGMCU_IDCODE_GD32E5 0xe0044000U - -#define GD32Fx_FLASHSIZE 0x1ffff7e0U -#define GD32F0_FLASHSIZE 0x1ffff7ccU - -#define AT32F4x_IDCODE_SERIES_MASK 0xfffff000U -#define AT32F4x_IDCODE_PART_MASK 0x00000fffU -#define AT32F41_SERIES 0x70030000U -#define AT32F40_SERIES 0x70050000U -#define AT32F43_SERIES_4K 0x70084000U -#define AT32F43_SERIES_2K 0x70083000U - -#define DBGMCU_IDCODE_MM32L0 0x40013400U -#define DBGMCU_IDCODE_MM32F3 0x40007080U - -static void stm32f1_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize) -{ - target_flash_s *flash = calloc(1, sizeof(*flash)); - if (!flash) { /* calloc failed: heap exhaustion */ - DEBUG_ERROR("calloc: failed in %s\n", __func__); - return; - } - - flash->start = addr; - flash->length = length; - flash->blocksize = erasesize; - flash->writesize = 1024U; - flash->erase = stm32f1_flash_erase; - flash->write = stm32f1_flash_write; - flash->erased = 0xff; - target_add_flash(target, flash); -} - -static uint16_t stm32f1_read_idcode(target_s *const target) +static uint16_t stm32_read_idcode(target_s *const target) { if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M0 || (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23) - return target_mem_read32(target, DBGMCU_IDCODE_F0) & 0xfffU; + return target_mem_read32(target, STM32F0X_IDCODE) & 0xfffU; /* Is this a Cortex-M33 core with STM32F1-style peripherals? (GD32E50x) */ if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M33) - return target_mem_read32(target, DBGMCU_IDCODE_GD32E5) & 0xfffU; + return target_mem_read32(target, GD32E5X_IDCODE) & 0xfffU; - return target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU; + return target_mem_read32(target, STM32F0X_IDCODE) & 0xfffU; } /* Identify GD32F1, GD32F2 and GD32F3 chips */ -bool gd32f1_probe(target_s *target) +bool gd32f1_probe(target_s *const target) { - const uint16_t device_id = stm32f1_read_idcode(target); - size_t block_size = 0x400; + const uint16_t device_id = stm32_read_idcode(target); + uint16_t block_size = 1U; /* KiB */ switch (device_id) { case 0x414U: /* Gigadevice gd32f303 */ case 0x430U: @@ -165,21 +125,23 @@ bool gd32f1_probe(target_s *target) break; case 0x444U: /* Gigadevice gd32e50x */ target->driver = "GD32E5"; - block_size = 0x2000; + block_size = 8U; break; default: return false; } - const uint32_t signature = target_mem_read32(target, GD32Fx_FLASHSIZE); - const uint16_t flash_size = signature & 0xffffU; - const uint16_t ram_size = signature >> 16U; - target->part_id = device_id; - target->mass_erase = stm32f1_mass_erase; - target_add_ram(target, 0x20000000, ram_size * 1024U); - stm32f1_add_flash(target, 0x8000000, (size_t)flash_size * 1024U, block_size); - target_add_commands(target, stm32f1_cmd_list, target->driver); + + /* Get flash capacity from ESIG register */ + const uint32_t memory_density = target_mem_read32(target, GD32FX_ESIG_BASE); + const uint16_t flash_size = memory_density & GD32_ESIG_MEM_DENSITY_FLASH_MASK; + const uint16_t ram_size = (memory_density & GD32_ESIG_MEM_DENSITY_RAM_MASK) >> GD32_ESIG_MEM_DENSITY_RAM_OFFSET; + + target_add_ram(target, 0x20000000, ram_size << 10U); + stm32_add_flash(target, 0x8000000, flash_size << 10U, STM32F10X_FPEC_BASE, block_size << 10U); + + target_add_commands(target, stm32_cmd_list, target->driver); return true; } @@ -190,8 +152,9 @@ bool gd32vf1_probe(target_s *const target) /* Make sure the architecture ID matches */ if (target->cpuid != 0x80000022U) return false; - /* Then read out the device ID */ - const uint16_t device_id = target_mem_read32(target, DBGMCU_IDCODE) & 0xfffU; + + /* Then read out the device IDCODE */ + const uint16_t device_id = target_mem_read32(target, STM32F10X_IDCODE) & STM32_IDCODE_DEVICE_ID_MASK; switch (device_id) { case 0x410U: /* GD32VF103 */ target->driver = "GD32VF1"; @@ -200,104 +163,115 @@ bool gd32vf1_probe(target_s *const target) return false; } - const uint32_t signature = target_mem_read32(target, GD32Fx_FLASHSIZE); - const uint16_t flash_size = signature & 0xffffU; - const uint16_t ram_size = signature >> 16U; + /* Get flash capacity from ESIG register */ + const uint32_t memory_density = target_mem_read32(target, GD32FX_ESIG_BASE); + const uint16_t flash_size = memory_density & GD32_ESIG_MEM_DENSITY_FLASH_MASK; + const uint16_t ram_size = (memory_density & GD32_ESIG_MEM_DENSITY_RAM_MASK) >> GD32_ESIG_MEM_DENSITY_RAM_OFFSET; target->part_id = device_id; - target->mass_erase = stm32f1_mass_erase; - target_add_ram(target, 0x20000000, ram_size * 1024U); - stm32f1_add_flash(target, 0x8000000, (size_t)flash_size * 1024U, 0x400U); - target_add_commands(target, stm32f1_cmd_list, target->driver); + + target_add_ram(target, 0x20000000, ram_size << 10U); /* KiB to bytes */ + stm32_add_flash(target, 0x8000000, flash_size << 10U, STM32F10X_FPEC_BASE, 1U << 10U); /* KiB to bytes */ + + target_add_commands(target, stm32_cmd_list, target->driver); return true; } -static bool at32f40_detect(target_s *target, const uint16_t part_id) +static bool at32f40x_probe(target_s *const target, const uint16_t device_id) { - // Current driver supports only *default* memory layout (256 KB Flash / 96 KB SRAM) - // XXX: Support for external Flash for 512KB and 1024KB parts requires specific flash code (not implemented) - switch (part_id) { - case 0x0240U: // AT32F403AVCT7 256KB / LQFP100 - case 0x0241U: // AT32F403ARCT7 256KB / LQFP64 - case 0x0242U: // AT32F403ACCT7 256KB / LQFP48 - case 0x0243U: // AT32F403ACCU7 256KB / QFN48 - case 0x0249U: // AT32F407VCT7 256KB / LQFP100 - case 0x024aU: // AT32F407RCT7 256KB / LQFP64 - case 0x0254U: // AT32F407AVCT7 256KB / LQFP100 - case 0x02cdU: // AT32F403AVET7 512KB / LQFP100 (*) - case 0x02ceU: // AT32F403ARET7 512KB / LQFP64 (*) - case 0x02cfU: // AT32F403ACET7 512KB / LQFP48 (*) - case 0x02d0U: // AT32F403ACEU7 512KB / QFN48 (*) - case 0x02d1U: // AT32F407VET7 512KB / LQFP100 (*) - case 0x02d2U: // AT32F407RET7 512KB / LQFP64 (*) - case 0x0344U: // AT32F403AVGT7 1024KB / LQFP100 (*) - case 0x0345U: // AT32F403ARGT7 1024KB / LQFP64 (*) - case 0x0346U: // AT32F403ACGT7 1024KB / LQFP48 (*) - case 0x0347U: // AT32F403ACGU7 1024KB / QFN48 (found on BlackPill+ WeAct Studio) (*) - case 0x034bU: // AT32F407VGT7 1024KB / LQFP100 (*) - case 0x034cU: // AT32F407VGT7 1024KB / LQFP64 (*) - case 0x0353U: // AT32F407AVGT7 1024KB / LQFP100 (*) - // Flash: 256 KB / 2KB per block - stm32f1_add_flash(target, 0x08000000, 256U * 1024U, 2U * 1024U); + /* + * Current driver supports only *default* memory layout (256 KB Flash / 96 KB SRAM) + * XXX: Support for external Flash for 512KB and 1024KB parts requires specific flash code (not implemented) + */ + switch (device_id) { + case 0x0240U: /* AT32F403AVCT7 256KB / LQFP100 */ + case 0x0241U: /* AT32F403ARCT7 256KB / LQFP64 */ + case 0x0242U: /* AT32F403ACCT7 256KB / LQFP48 */ + case 0x0243U: /* AT32F403ACCU7 256KB / QFN48 */ + target->driver = "AT32F403x"; + break; + case 0x0249U: /* AT32F407VCT7 256KB / LQFP100 */ + case 0x024aU: /* AT32F407RCT7 256KB / LQFP64 */ + case 0x0254U: /* AT32F407AVCT7 256KB / LQFP100 */ + case 0x02cdU: /* AT32F403AVET7 512KB / LQFP100 (*) */ + case 0x02ceU: /* AT32F403ARET7 512KB / LQFP64 (*) */ + case 0x02cfU: /* AT32F403ACET7 512KB / LQFP48 (*) */ + case 0x02d0U: /* AT32F403ACEU7 512KB / QFN48 (*) */ + case 0x02d1U: /* AT32F407VET7 512KB / LQFP100 (*) */ + case 0x02d2U: /* AT32F407RET7 512KB / LQFP64 (*) */ + case 0x0344U: /* AT32F403AVGT7 1024KB / LQFP100 (*) */ + case 0x0345U: /* AT32F403ARGT7 1024KB / LQFP64 (*) */ + case 0x0346U: /* AT32F403ACGT7 1024KB / LQFP48 (*) */ + case 0x0347U: /* AT32F403ACGU7 1024KB / QFN48 (found on BlackPill+ WeAct Studio) */ + case 0x034bU: /* AT32F407VGT7 1024KB / LQFP100 (*) */ + case 0x034cU: /* AT32F407VGT7 1024KB / LQFP64 (*) */ + case 0x0353U: /* AT32F407AVGT7 1024KB / LQFP100 (*) */ + target->driver = "AT32F407x"; break; - // Unknown/undocumented default: - return false; + return false; /* Unknown/undocumented */ } - // All parts have 96KB SRAM - target_add_ram(target, 0x20000000, 96U * 1024U); - target->driver = "AT32F403A/407"; - target->mass_erase = stm32f1_mass_erase; + + /* 256 KiB Flash with 2 KiB blocks */ + stm32_add_flash(target, 0x08000000, 256U << 10U, STM32F10X_FPEC_BASE, 2U << 10U); /* KiB to bytes */ + + /* All parts have 96KB SRAM */ + target_add_ram(target, 0x20000000, 96U << 10U); /* KiB to bytes */ + return true; } -static bool at32f41_detect(target_s *target, const uint16_t part_id) +static bool at32f41x_probe(target_s *const target, const uint16_t device_id) { - switch (part_id) { - case 0x0240U: // LQFP64_10x10 - case 0x0241U: // LQFP48_7x7 - case 0x0242U: // QFN32_4x4 - case 0x0243U: // LQFP64_7x7 - case 0x024cU: // QFN48_6x6 - // Flash: 256 KB / 2KB per block - stm32f1_add_flash(target, 0x08000000, 256U * 1024U, 2U * 1024U); + size_t flash_size = 0U; /* KiB */ + switch (device_id) { + case 0x0240U: /* LQFP64_10x10 */ + case 0x0241U: /* LQFP48_7x7 */ + case 0x0242U: /* QFN32_4x4 */ + case 0x0243U: /* LQFP64_7x7 */ + case 0x024cU: /* QFN48_6x6 */ + flash_size = 256U; /* KiB */ break; - case 0x01c4U: // LQFP64_10x10 - case 0x01c5U: // LQFP48_7x7 - case 0x01c6U: // QFN32_4x4 - case 0x01c7U: // LQFP64_7x7 - case 0x01cdU: // QFN48_6x6 - // Flash: 128 KB / 2KB per block - stm32f1_add_flash(target, 0x08000000, 128U * 1024U, 2U * 1024U); + case 0x01c4U: /* LQFP64_10x10 */ + case 0x01c5U: /* LQFP48_7x7 */ + case 0x01c6U: /* QFN32_4x4 */ + case 0x01c7U: /* LQFP64_7x7 */ + case 0x01cdU: /* QFN48_6x6 */ + flash_size = 128U; /* KiB */ break; - case 0x0108U: // LQFP64_10x10 - case 0x0109U: // LQFP48_7x7 - case 0x010aU: // QFN32_4x4 - // Flash: 64 KB / 2KB per block - stm32f1_add_flash(target, 0x08000000, 64U * 1024U, 2U * 1024U); + case 0x0108U: /* LQFP64_10x10 */ + case 0x0109U: /* LQFP48_7x7 */ + case 0x010aU: /* QFN32_4x4 */ + flash_size = 64U; /* KiB */ break; - // Unknown/undocumented default: - return false; + return false; /* Unknown/undocumented */ } - // All parts have 32KB SRAM - target_add_ram(target, 0x20000000, 32U * 1024U); + target->driver = "AT32F415"; - target->mass_erase = stm32f1_mass_erase; + + /* Flash with 2 KiB blocks */ + stm32_add_flash(target, 0x08000000, flash_size << 10U, STM32F10X_FPEC_BASE, 2U << 10U); /* KiB to bytes */ + + /* All parts have 32KB SRAM */ + target_add_ram(target, 0x20000000, 32U << 10U); + return true; } -static bool at32f43_detect(target_s *target, const uint16_t part_id) +static bool at32f43x_probe(target_s *const target, const uint16_t device_id) { - /* AT32F435 EOPB0 ZW/NZW split reconfiguration unsupported, + /* + * AT32F435 EOPB0 ZW/NZW split reconfiguration unsupported, * assuming default split ZW=256 SRAM=384. * AT32F437 also have a working "EMAC" (Ethernet MAC) */ - uint32_t flash_size_kb = 0; - uint32_t sector_size = 0; - switch (part_id) { - // 0x70084000U parts with 4KB sectors: + size_t flash_size = 0; /* KiB */ + target_addr_t bank_split = 0; + size_t block_size = 0; /* KiB */ + switch (device_id) { + /* Parts with 4 KiB sectors */ case 0x0540U: // LQFP144 case 0x0543U: // LQFP100 case 0x0546U: // LQFP64 @@ -306,9 +280,10 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x054fU: // LQFP144 w/Eth case 0x0552U: // LQFP100 w/Eth case 0x0555U: // LQFP64 w/Eth - // Flash (G): 4032 KB in 2 banks (2048+1984), 4KB per sector. - flash_size_kb = 4032; - sector_size = 4096; + /* Flash (G): 4032 KB in 2 banks (2048 + 1984) */ + flash_size = 4032U; /* KiB */ + bank_split = 0x08000000 + (2048U << 10U); + block_size = 4U; /* KiB */ break; case 0x0598U: // LQFP144 case 0x0599U: // LQFP100 @@ -318,11 +293,11 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x059dU: // LQFP144 w/Eth case 0x059eU: // LQFP100 w/Eth case 0x059fU: // LQFP64 w/Eth - // Flash (D): 448 KB, only bank 1, 4KB per sector. - flash_size_kb = 448; - sector_size = 4096; + /* Flash (D): 448 KiB, only 1 bank */ + flash_size = 448U; /* KiB */ + block_size = 4U; /* KiB */ break; - // 0x70083000U parts with 2KB sectors: + /* Parts with 2 KiB sectors */ case 0x0341U: // LQFP144 case 0x0344U: // LQFP100 case 0x0347U: // LQFP64 @@ -331,9 +306,10 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x0350U: // LQFP144 w/Eth case 0x0353U: // LQFP100 w/Eth case 0x0356U: // LQFP64 w/Eth - // Flash (M): 1024 KB in 2 banks (equal), 2KB per sector. - flash_size_kb = 1024; - sector_size = 2048; + /* Flash (M): 1024 KB in 2 banks (equal sized) */ + flash_size = 1024U; /* KiB */ + bank_split = 0x08000000 + (512U << 10U); + block_size = 2U; /* KiB */ break; case 0x0242U: // LQFP144 case 0x0245U: // LQFP100 @@ -343,63 +319,74 @@ static bool at32f43_detect(target_s *target, const uint16_t part_id) case 0x0251U: // LQFP144 w/Eth case 0x0254U: // LQFP100 w/Eth case 0x0257U: // LQFP64 w/Eth - // Flash (C): 256 KB, only bank 1, 2KB per sector. - flash_size_kb = 256; - sector_size = 2048; + /* Flash (C): 256 KB, only 1 bank */ + flash_size = 256U; /* KiB */ + block_size = 2U; /* KiB */ break; default: return false; } + + target->driver = "AT32F435"; + /* - * Arterytek F43x Flash controller has BLKERS (1<<3U). + * FIXME: This sounds like it can be handled + * Arterytek F43x Flash controller has BLKERS (1U << 3U). * Block erase operates on 64 KB at once for all parts. * Using here only sector erase (page erase) for compatibility. */ - stm32f1_add_flash(target, 0x08000000, flash_size_kb * 1024U, sector_size); - // SRAM1 (64KB) can be remapped to 0x10000000. - target_add_ram(target, 0x20000000, 64U * 1024U); - // SRAM2 (384-64=320 KB default). - target_add_ram(target, 0x20010000, 320U * 1024U); + if (bank_split) + stm32_add_banked_flash(target, 0x08000000, flash_size << 10U, bank_split, STM32F10X_FPEC_BASE, + block_size << 10U); /* KiB to bytes */ + else + stm32_add_flash(target, 0x08000000, flash_size, STM32F10X_FPEC_BASE, block_size << 10U); /* KiB to bytes */ + /* + * FIXME: handle dynamic SRAM mapping (see SAMx7x) * SRAM total is adjustable between 128 KB and 512 KB (max). * Out of 640 KB SRAM present on silicon, at least 128 KB are always * dedicated to "zero-wait-state Flash". ZW region is limited by - * specific part flash capacity (for 256, 448 KB) or at 512 KB. + * specific part flash capacity (for 256, 448 KiB) or at 512 KiB. * AT32F435ZMT default EOPB0=0xffff05fa, - * EOPB[0:2]=0b010 for 384 KB SRAM + 256 KB zero-wait-state flash. + * EOPB[0:2]=0b010 for 384 KiB SRAM + 256 KiB zero-wait-state flash. */ - target->driver = "AT32F435"; - target->mass_erase = stm32f1_mass_erase; + + /* SRAM1 (64 KiB) can be remapped to 0x10000000 */ + target_add_ram(target, 0x20000000 + 7U, 64U << 10U); + /* SRAM2 (384 - 64 = 320 KiB default) */ + target_add_ram(target, 0x20010000, 320U << 10U); + return true; } /* Identify AT32F4x devices (Cortex-M4) */ -bool at32fxx_probe(target_s *target) +bool at32fxx_probe(target_s *const target) { // Artery clones use Cortex M4 cores if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) != CORTEX_M4) return false; // Artery chips use the complete idcode word for identification - const uint32_t idcode = target_mem_read32(target, DBGMCU_IDCODE); - const uint32_t series = idcode & AT32F4x_IDCODE_SERIES_MASK; - const uint16_t part_id = idcode & AT32F4x_IDCODE_PART_MASK; - - if (series == AT32F40_SERIES) - return at32f40_detect(target, part_id); - if (series == AT32F41_SERIES) - return at32f41_detect(target, part_id); - if (series == AT32F43_SERIES_4K || series == AT32F43_SERIES_2K) - return at32f43_detect(target, part_id); + const uint32_t idcode = target_mem_read32(target, STM32F10X_IDCODE); + const uint16_t revision_id = (idcode & STM32_IDCODE_REVISION_ID_MASK) >> STM32_IDCODE_REVISION_ID_OFFSET; + const uint16_t device_id = idcode & STM32_IDCODE_DEVICE_ID_MASK; + + if (revision_id == AT32F40X_REVISION_ID) + return at32f40x_probe(target, device_id); + if (revision_id == AT32F41X_REVISION_ID) + return at32f41x_probe(target, device_id); + if (revision_id == AT32F43X_REVISION_ID) + return at32f43x_probe(target, device_id); return false; } /* * On STM32, 16-bit writes use bits 0:15 for even halfwords; bits 16:31 for odd halfwords. * On MM32 cortex-m0, 16-bit writes always use bits 0:15. - * Set both halfwords to the same value, works on both STM32 and MM32. + * Set both halfwords to the same value, works on both STM32 and NN32. */ -void mm32l0_mem_write_sized(adiv5_access_port_s *ap, uint32_t dest, const void *src, size_t len, align_e align) +void mm32l0_mem_write_sized( + adiv5_access_port_s *const ap, uint32_t dest, const void *src, size_t len, const align_e align) { uint32_t odest = dest; @@ -446,495 +433,174 @@ void mm32l0_mem_write_sized(adiv5_access_port_s *ap, uint32_t dest, const void * } /* Identify MM32 devices (Cortex-M0) */ - -bool mm32l0xx_probe(target_s *target) +bool mm32l0xx_probe(target_s *const target) { - const char *name; - size_t flash_kbyte = 0; - size_t ram_kbyte = 0; - size_t block_size = 0x400U; - - const uint32_t mm32_id = target_mem_read32(target, DBGMCU_IDCODE_MM32L0); - if (target_check_error(target)) { - DEBUG_ERROR("%s: read error at 0x%" PRIx32 "\n", __func__, (uint32_t)DBGMCU_IDCODE_MM32L0); - return false; - } - switch (mm32_id) { + const uint32_t idcode = target_mem_read32(target, MM32L0X_IDCODE); + + size_t flash_size = 0; + size_t ram_size = 0; + switch (idcode) { case 0xcc568091U: - name = "MM32L07x"; - flash_kbyte = 128; - ram_kbyte = 8; - break; - case 0xcc4460b1: - name = "MM32SPIN05"; - flash_kbyte = 32; - ram_kbyte = 4; + target->driver = "MM32L07x"; + flash_size = 128U; /* KiB */ + ram_size = 8U; /* KiB */ break; case 0xcc56a097U: - name = "MM32SPIN27"; - flash_kbyte = 128; - ram_kbyte = 12; + target->driver = "MM32SPIN27"; + flash_size = 128U; /* KiB */ + ram_size = 12U; /* KiB */ break; - case 0x00000000U: - case 0xffffffffU: - return false; default: - DEBUG_WARN("%s: unknown mm32 dev_id 0x%" PRIx32 "\n", __func__, mm32_id); + DEBUG_WARN("%s: unknown MM32 IDCODE 0x%" PRIx32 "\n", __func__, idcode); return false; } - target->part_id = mm32_id & 0xfffU; - target->driver = name; - target->mass_erase = stm32f1_mass_erase; - target_add_ram(target, 0x20000000U, ram_kbyte * 1024U); - stm32f1_add_flash(target, 0x08000000U, flash_kbyte * 1024U, block_size); - target_add_commands(target, stm32f1_cmd_list, name); + + target->part_id = idcode & STM32_IDCODE_DEVICE_ID_MASK; + cortex_ap(target)->dp->mem_write = mm32l0_mem_write_sized; + + target_add_ram(target, 0x20000000U, ram_size << 10U); /* KiB to bytes */ + stm32_add_flash(target, 0x08000000U, flash_size << 10U, STM32F10X_FPEC_BASE, 1U << 10U); /* KiB to bytes */ + + target_add_commands(target, stm32_cmd_list, target->driver); + return true; } /* Identify MM32 devices (Cortex-M3, Star-MC1) */ bool mm32f3xx_probe(target_s *target) { - const char *name; - size_t flash_kbyte = 0; - size_t ram1_kbyte = 0; /* ram at 0x20000000 */ - size_t ram2_kbyte = 0; /* ram at 0x30000000 */ - size_t block_size = 0x400U; - - const uint32_t mm32_id = target_mem_read32(target, DBGMCU_IDCODE_MM32F3); - if (target_check_error(target)) { - DEBUG_ERROR("%s: read error at 0x%" PRIx32 "\n", __func__, (uint32_t)DBGMCU_IDCODE_MM32F3); - return false; - } - switch (mm32_id) { + const uint32_t idcode = target_mem_read32(target, MM32FX_IDCODE); + + size_t flash_size = 0; /* KiB */ + size_t ram1_size = 0; /* RAM at 0x20000000, KiB */ + size_t ram2_size = 0; /* RAM at 0x30000000, KiB */ + switch (idcode) { case 0xcc9aa0e7U: - name = "MM32F3273"; - flash_kbyte = 512; - ram1_kbyte = 128; + target->driver = "MM32F3273"; + flash_size = 512U; /* KiB */ + ram1_size = 128U; /* KiB */ break; case 0x4d4d0800U: - name = "MM32F5277"; - flash_kbyte = 256; - ram1_kbyte = 32; - ram2_kbyte = 128; + target->driver = "MM32F5277"; + flash_size = 256U; /* KiB */ + ram1_size = 32U; /* KiB */ + ram2_size = 128U; /* KiB */ break; - case 0x00000000U: - case 0xffffffffU: - return false; default: - DEBUG_WARN("%s: unknown mm32 ID code 0x%" PRIx32 "\n", __func__, mm32_id); + DEBUG_WARN("%s: unknown MM32 IDCODE 0x%" PRIx32 "\n", __func__, idcode); return false; } - target->part_id = mm32_id & 0xfffU; - target->driver = name; - target->mass_erase = stm32f1_mass_erase; - if (ram1_kbyte != 0) - target_add_ram(target, 0x20000000U, ram1_kbyte * 1024U); - if (ram2_kbyte != 0) - target_add_ram(target, 0x30000000U, ram2_kbyte * 1024U); - stm32f1_add_flash(target, 0x08000000U, flash_kbyte * 1024U, block_size); - target_add_commands(target, stm32f1_cmd_list, name); + + target->part_id = idcode & STM32_IDCODE_DEVICE_ID_MASK; + + stm32_add_flash(target, 0x08000000U, flash_size << 10U, STM32F10X_FPEC_BASE, 1U << 10U); /* KiB to bytes */ + if (ram1_size) + target_add_ram(target, 0x20000000U, ram1_size << 10U); /* KiB to bytes */ + if (ram2_size) + target_add_ram(target, 0x30000000U, ram2_size << 10U); /* KiB to bytes */ + + target_add_commands(target, stm32_cmd_list, target->driver); + return true; } /* Identify real STM32F0/F1/F3 devices */ bool stm32f1_probe(target_s *target) { - const uint16_t device_id = stm32f1_read_idcode(target); - - target->mass_erase = stm32f1_mass_erase; - size_t flash_size = 0; - size_t block_size = 0x400; + const uint16_t device_id = stm32_read_idcode(target); + size_t ram_size = 20U; /* KiB */ + size_t flash_size = 0; /* KiB */ + size_t block_size = 1U; /* KiB */ switch (device_id) { case 0x29bU: /* CS clone */ case 0x410U: /* Medium density */ case 0x412U: /* Low density */ case 0x420U: /* Value Line, Low-/Medium density */ - target_add_ram(target, 0x20000000, 0x5000); - stm32f1_add_flash(target, 0x8000000, 0x20000, 0x400); - target_add_commands(target, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); /* Test for clone parts with Core rev 2*/ - adiv5_access_port_s *ap = cortex_ap(target); - if ((ap->idr >> 28U) > 1U) { + if ((cortex_ap(target)->idr >> 28U) > 1U) { target->driver = "STM32F1 (clone) medium density"; DEBUG_WARN("Detected clone STM32F1\n"); } else target->driver = "STM32F1 medium density"; - target->part_id = device_id; - return true; + flash_size = 128U; /* KiB */ + break; case 0x414U: /* High density */ case 0x418U: /* Connectivity Line */ case 0x428U: /* Value Line, High Density */ - target->driver = "STM32F1 VL density"; - target->part_id = device_id; - target_add_ram(target, 0x20000000, 0x10000); - stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800); - target_add_commands(target, stm32f1_cmd_list, "STM32 HF/CL/VL-HD"); - return true; + target->driver = "STM32F1 VL density"; + ram_size = 64U; /* KiB */ + flash_size = 512U; /* KiB */ + block_size = 2U; /* KiB */ + break; case 0x430U: /* XL-density */ - target->driver = "STM32F1 XL density"; - target->part_id = device_id; - target_add_ram(target, 0x20000000, 0x18000); - stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800); - stm32f1_add_flash(target, 0x8080000, 0x80000, 0x800); - target_add_commands(target, stm32f1_cmd_list, "STM32 XL/VL-XL"); - return true; + target->driver = "STM32F1 XL density"; + ram_size = 96U; /* KiB */ + flash_size = 1024U; /* KiB */ + block_size = 2U; /* KiB */ + break; case 0x438U: /* STM32F303x6/8 and STM32F328 */ case 0x422U: /* STM32F30x */ case 0x446U: /* STM32F303xD/E and STM32F398xE */ - target_add_ram(target, 0x10000000, 0x4000); - /* fall through */ + /* Additional RAM */ + target_add_ram(target, 0x10000000, 16U << 10U); /* KiB to bytes */ + /* fall through */ case 0x432U: /* STM32F37x */ case 0x439U: /* STM32F302C8 */ target->driver = "STM32F3"; - target->part_id = device_id; - target_add_ram(target, 0x20000000, 0x10000); - stm32f1_add_flash(target, 0x8000000, 0x80000, 0x800); - target_add_commands(target, stm32f1_cmd_list, "STM32F3"); - return true; + ram_size = 64U; /* KiB */ + flash_size = 512U; /* KiB */ + block_size = 2U; /* KiB */ + break; case 0x444U: /* STM32F03 RM0091 Rev. 7, STM32F030x[4|6] RM0360 Rev. 4 */ target->driver = "STM32F03"; - flash_size = 0x8000; + flash_size = 32U; /* KiB */ break; case 0x445U: /* STM32F04 RM0091 Rev. 7, STM32F070x6 RM0360 Rev. 4 */ target->driver = "STM32F04/F070x6"; - flash_size = 0x8000; + flash_size = 32U; /* KiB */ break; case 0x440U: /* STM32F05 RM0091 Rev. 7, STM32F030x8 RM0360 Rev. 4 */ target->driver = "STM32F05/F030x8"; - flash_size = 0x10000; + flash_size = 64U; /* KiB */ break; case 0x448U: /* STM32F07 RM0091 Rev. 7, STM32F070xb RM0360 Rev. 4 */ target->driver = "STM32F07"; - flash_size = 0x20000; - block_size = 0x800; + flash_size = 128U; /* KiB */ + block_size = 2U; /* KiB */ break; case 0x442U: /* STM32F09 RM0091 Rev. 7, STM32F030xc RM0360 Rev. 4 */ target->driver = "STM32F09/F030xc"; - flash_size = 0x40000; - block_size = 0x800; + flash_size = 256U; /* KiB */ + block_size = 2U; /* KiB */ break; - default: /* NONE */ - return false; + default: + return false; /* Unknown/undocumented */ } target->part_id = device_id; - target_add_ram(target, 0x20000000, 0x5000); - stm32f1_add_flash(target, 0x8000000, flash_size, block_size); - target_add_commands(target, stm32f1_cmd_list, "STM32F0"); - return true; -} - -static bool stm32f1_flash_unlock(target_s *target, uint32_t bank_offset) -{ - target_mem_write32(target, FLASH_KEYR + bank_offset, KEY1); - target_mem_write32(target, FLASH_KEYR + bank_offset, KEY2); - uint32_t ctrl = target_mem_read32(target, FLASH_CR); - if (ctrl & FLASH_CR_LOCK) - DEBUG_ERROR("unlock failed, cr: 0x%08" PRIx32 "\n", ctrl); - return !(ctrl & FLASH_CR_LOCK); -} -static inline void stm32f1_flash_clear_eop(target_s *const target, const uint32_t bank_offset) -{ - const uint32_t status = target_mem_read32(target, FLASH_SR + bank_offset); - target_mem_write32(target, FLASH_SR + bank_offset, status | SR_EOP); /* EOP is W1C */ -} - -static bool stm32f1_flash_busy_wait( - target_s *const target, const uint32_t bank_offset, platform_timeout_s *const timeout) -{ - /* Read FLASH_SR to poll for BSY bit */ - uint32_t status = FLASH_SR_BSY; - /* - * Please note that checking EOP here is only legal because every operation is preceded by - * a call to stm32f1_flash_clear_eop. Without this the flag could be stale from a previous - * operation and is always set at the end of every program/erase operation. - * For more information, see FLASH_SR register description §3.4 pg 25. - * https://www.st.com/resource/en/programming_manual/pm0075-stm32f10xxx-flash-memory-microcontrollers-stmicroelectronics.pdf - */ - while (!(status & SR_EOP) && (status & FLASH_SR_BSY)) { - status = target_mem_read32(target, FLASH_SR + bank_offset); - if (target_check_error(target)) { - DEBUG_ERROR("Lost communications with target"); - return false; - } - if (timeout) - target_print_progress(timeout); - }; - if (status & SR_ERROR_MASK) - DEBUG_ERROR("stm32f1 flash error 0x%" PRIx32 "\n", status); - return !(status & SR_ERROR_MASK); -} + target_add_ram(target, STM32F10X_SRAM_ADDR, ram_size << 10U); /* KiB to bytes */ -static uint32_t stm32f1_bank_offset_for(target_addr_t addr) -{ - if (addr >= FLASH_BANK_SPLIT) - return FLASH_BANK2_OFFSET; - return FLASH_BANK1_OFFSET; -} - -static bool stm32f1_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length) -{ - target_s *target = flash->t; - target_addr_t end = addr + length - 1U; - DEBUG_TARGET("%s: at %08" PRIx32 "\n", __func__, addr); - - /* Unlocked an appropriate flash bank */ - if ((target->part_id == 0x430U && end >= FLASH_BANK_SPLIT && !stm32f1_flash_unlock(target, FLASH_BANK2_OFFSET)) || - (addr < FLASH_BANK_SPLIT && !stm32f1_flash_unlock(target, 0))) - return false; - - const uint32_t bank_offset = stm32f1_bank_offset_for(addr); - stm32f1_flash_clear_eop(target, bank_offset); - - /* Flash page erase instruction */ - target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_PER); - /* write address to FMA */ - target_mem_write32(target, FLASH_AR + bank_offset, addr); - /* Flash page erase start instruction */ - target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_STRT | FLASH_CR_PER); - - /* Wait for completion or an error */ - return stm32f1_flash_busy_wait(target, bank_offset, NULL); -} - -static size_t stm32f1_bank1_length(target_addr_t addr, size_t len) -{ - if (addr >= FLASH_BANK_SPLIT) - return 0; - if (addr + len > FLASH_BANK_SPLIT) - return FLASH_BANK_SPLIT - addr; - return len; -} - -static bool stm32f1_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len) -{ - target_s *target = flash->t; - const size_t offset = stm32f1_bank1_length(dest, len); - DEBUG_TARGET("%s: at %08" PRIx32 " for %zu bytes\n", __func__, dest, len); - - /* Start by writing any bank 1 data */ - if (offset) { - stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET); - - target_mem_write32(target, FLASH_CR, FLASH_CR_PG); - /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ - if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) - target_mem_write(target, dest, src, offset); - else - cortexm_mem_write_sized(target, dest, src, offset, ALIGN_16BIT); - - /* Wait for completion or an error */ - if (!stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL)) - return false; - } - - /* If there's anything to write left over and we're on a part with a second bank, write to bank 2 */ - const size_t remainder = len - offset; - if (target->part_id == 0x430U && remainder) { - const uint8_t *data = src; - stm32f1_flash_clear_eop(target, FLASH_BANK2_OFFSET); - - target_mem_write32(target, FLASH_CR + FLASH_BANK2_OFFSET, FLASH_CR_PG); - /* Use the target API instead of a direct Cortex-M call for GD32VF103 parts */ - if (target->designer_code == JEP106_MANUFACTURER_RV_GIGADEVICE && target->cpuid == 0x80000022U) - target_mem_write(target, dest + offset, data + offset, remainder); - else - cortexm_mem_write_sized(target, dest + offset, data + offset, remainder, ALIGN_16BIT); - - /* Wait for completion or an error */ - if (!stm32f1_flash_busy_wait(target, FLASH_BANK2_OFFSET, NULL)) - return false; - } - - return true; -} - -static bool stm32f1_mass_erase_bank( - target_s *const target, const uint32_t bank_offset, platform_timeout_s *const timeout) -{ - /* Unlock the bank */ - if (!stm32f1_flash_unlock(target, bank_offset)) - return false; - stm32f1_flash_clear_eop(target, bank_offset); - - /* Flash mass erase start instruction */ - target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_MER); - target_mem_write32(target, FLASH_CR + bank_offset, FLASH_CR_STRT | FLASH_CR_MER); - - /* Wait for completion or an error */ - return stm32f1_flash_busy_wait(target, bank_offset, timeout); -} - -static bool stm32f1_mass_erase(target_s *target) -{ - if (!stm32f1_flash_unlock(target, 0)) - return false; - - platform_timeout_s timeout; - platform_timeout_set(&timeout, 500); - if (!stm32f1_mass_erase_bank(target, FLASH_BANK1_OFFSET, &timeout)) - return false; - - /* If we're on a part that has a second bank, mass erase that bank too */ - if (target->part_id == 0x430U) - return stm32f1_mass_erase_bank(target, FLASH_BANK2_OFFSET, &timeout); - return true; -} - -static uint16_t stm32f1_flash_readable_key(const target_s *const target) -{ - switch (target->part_id) { - case 0x422U: /* STM32F30x */ - case 0x432U: /* STM32F37x */ - case 0x438U: /* STM32F303x6/8 and STM32F328 */ - case 0x440U: /* STM32F0 */ - case 0x446U: /* STM32F303xD/E and STM32F398xE */ - case 0x445U: /* STM32F04 RM0091 Rev.7, STM32F070x6 RM0360 Rev. 4*/ - case 0x448U: /* STM32F07 RM0091 Rev.7, STM32F070xb RM0360 Rev. 4*/ - case 0x442U: /* STM32F09 RM0091 Rev.7, STM32F030xc RM0360 Rev. 4*/ - return FLASH_OBP_RDP_KEY_F3; - } - return FLASH_OBP_RDP_KEY; -} - -static bool stm32f1_option_erase(target_s *target) -{ - stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET); - - /* Erase option bytes instruction */ - target_mem_write32(target, FLASH_CR, FLASH_CR_OPTER | FLASH_CR_OPTWRE); - target_mem_write32(target, FLASH_CR, FLASH_CR_STRT | FLASH_CR_OPTER | FLASH_CR_OPTWRE); - - /* Wait for completion or an error */ - return stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL); -} - -static bool stm32f1_option_write_erased( - target_s *const target, const size_t offset, const uint16_t value, const bool write16_broken) -{ - if (value == 0xffffU) - return true; - - stm32f1_flash_clear_eop(target, FLASH_BANK1_OFFSET); - - /* Erase option bytes instruction */ - target_mem_write32(target, FLASH_CR, FLASH_CR_OPTPG | FLASH_CR_OPTWRE); - - const uint32_t addr = FLASH_OBP_RDP + (offset * 2U); - if (write16_broken) - target_mem_write32(target, addr, 0xffff0000U | value); + if (flash_size > STM32F10X_FLASH_BANK_SIZE) + stm32_add_banked_flash(target, STM32F10X_FLASH_MEMORY_ADDR, flash_size << 10U, STM32F10X_FLASH_BANK_SPLIT_ADDR, + STM32F10X_FPEC_BASE, block_size << 10U); else - target_mem_write16(target, addr, value); - - /* Wait for completion or an error */ - const bool result = stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL); - if (result || offset != 0U) - return result; - /* - * In the case that the write failed and we're handling option byte 0 (RDP), - * check if we got a status of "Program Error" in FLASH_SR, indicating the target - * refused to erase the read protection option bytes (and turn it into a truthy return). - */ - const uint8_t status = target_mem_read32(target, FLASH_SR) & SR_ERROR_MASK; - return status == SR_PROG_ERROR; -} + stm32_add_flash(target, STM32F10X_FLASH_MEMORY_ADDR, flash_size << 10U, STM32F10X_FPEC_BASE, + block_size << 10U); /* KiB to bytes */ -static bool stm32f1_option_write(target_s *const target, const uint32_t addr, const uint16_t value) -{ - const uint32_t index = (addr - FLASH_OBP_RDP) >> 1U; - /* If index would be negative, the high most bit is set, so we get a giant positive number. */ - if (index > 7U) - return false; - - uint16_t opt_val[8]; - /* Retrieve old values */ - for (size_t i = 0U; i < 16U; i += 4U) { - const size_t offset = i >> 1U; - uint32_t val = target_mem_read32(target, FLASH_OBP_RDP + i); - opt_val[offset] = val & 0xffffU; - opt_val[offset + 1U] = val >> 16U; - } - - if (opt_val[index] == value) - return true; - - /* Check for erased value */ - if (opt_val[index] != 0xffffU && !stm32f1_option_erase(target)) - return false; - opt_val[index] = value; - - /* - * Write changed values, taking into account if we can use 32- or have to use 16-bit writes. - * GD32E230 is a special case as target_mem_write16 does not work - */ - const bool write16_broken = target->part_id == 0x410U && (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23; - for (size_t i = 0U; i < 8U; ++i) { - if (!stm32f1_option_write_erased(target, i, opt_val[i], write16_broken)) - return false; - } - - return true; -} - -static bool stm32f1_cmd_option(target_s *target, int argc, const char **argv) -{ - const uint32_t read_protected = target_mem_read32(target, FLASH_OBR) & FLASH_OBR_RDPRT; - const bool erase_requested = argc == 2 && strcmp(argv[1], "erase") == 0; - /* Fast-exit if the Flash is not readable and the user didn't ask us to erase the option bytes */ - if (read_protected && !erase_requested) { - tc_printf(target, "Device is Read Protected\nUse `monitor option erase` to unprotect and erase device\n"); - return true; - } - - /* Unprotect the option bytes so we can modify them */ - if (!stm32f1_flash_unlock(target, FLASH_BANK1_OFFSET)) - return false; - target_mem_write32(target, FLASH_OPTKEYR, KEY1); - target_mem_write32(target, FLASH_OPTKEYR, KEY2); - - if (erase_requested) { - /* When the user asks us to erase the option bytes, kick of an erase */ - if (!stm32f1_option_erase(target)) - return false; - /* - * Write the option bytes Flash readable key, taking into account if we can - * use 32- or have to use 16-bit writes. - * GD32E230 is a special case as target_mem_write16 does not work - */ - const bool write16_broken = - target->part_id == 0x410U && (target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23; - if (!stm32f1_option_write_erased(target, 0U, stm32f1_flash_readable_key(target), write16_broken)) - return false; - } else if (argc == 3) { - /* If 3 arguments are given, assume the second is an address, and the third a value */ - const uint32_t addr = strtoul(argv[1], NULL, 0); - const uint32_t val = strtoul(argv[2], NULL, 0); - /* Try and program the new option value to the requested option byte */ - if (!stm32f1_option_write(target, addr, val)) - return false; - } else - tc_printf(target, "usage: monitor option erase\nusage: monitor option \n"); - - /* When all gets said and done, display the current option bytes values */ - for (size_t i = 0U; i < 16U; i += 4U) { - const uint32_t addr = FLASH_OBP_RDP + i; - const uint32_t val = target_mem_read32(target, addr); - tc_printf(target, "0x%08X: 0x%04X\n", addr, val & 0xffffU); - tc_printf(target, "0x%08X: 0x%04X\n", addr + 2U, val >> 16U); - } + target_add_commands(target, stm32_cmd_list, target->driver); return true; }