From cab0ce76298891331f82c8331e35dc14620dc0c7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:20:52 +0000 Subject: [PATCH 01/65] 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 55e0107533f..148591328d9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,6 +61,7 @@ SRC = \ nrf51.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 7ea769faa59..4c4be3e512f 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[] = { @@ -112,9 +113,10 @@ const jtag_dev_descr_s dev_descr[] = { }, }, { - .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 e971f4ebcff3f973a40000d2c2f64f46dd274d2c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:27:57 +0000 Subject: [PATCH 02/65] 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 8b8a9cd499adfa08ade23eb1ac1484a6c34f0076 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:39:46 +0000 Subject: [PATCH 03/65] riscv_debug: Implemented a way to read/write the DTM control/status register --- src/Makefile | 115 ++++++++++++++++++------------------ src/target/riscv_debug.h | 2 + src/target/riscv_jtag_dtm.c | 47 +++++++++++++++ 3 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 src/target/riscv_jtag_dtm.c diff --git a/src/Makefile b/src/Makefile index 148591328d9..971bd0262e2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,63 +23,64 @@ ifeq ($(ENABLE_DEBUG), 1) CFLAGS += -DENABLE_DEBUG endif -SRC = \ - adiv5.c \ - adiv5_jtag.c \ - adiv5_swd.c \ - command.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 \ - 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 \ - stm32l0.c \ - stm32l4.c \ - stm32g0.c \ - renesas.c \ - target.c \ - target_flash.c \ +SRC = \ + adiv5.c \ + adiv5_jtag.c \ + adiv5_swd.c \ + command.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 \ + 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 \ + 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 8591f9da9a9dc92008756a321c2bcf0b3c852474 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 21 Mar 2023 22:05:30 +0000 Subject: [PATCH 04/65] 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 4c4be3e512f..e1c092a3098 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -116,7 +116,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 c6116c62f92afd3ec5f29f00b1059336cef3e5c2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 03:55:07 +0000 Subject: [PATCH 05/65] 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 ff935997fd5ec10dd136f41e64aa20ac434510b4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:14:17 +0000 Subject: [PATCH 06/65] 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 9d6d31a876d5462c0930465ac36536d404238b38 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:30:56 +0000 Subject: [PATCH 07/65] 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 bccb9d83cadc5192ae6a8457bdc763fa92ade368 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 05:49:58 +0000 Subject: [PATCH 08/65] riscv_debug: Handle the idle cycles component of the DTMCS register --- 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..949befb6f22 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 machinary 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 a21e4daa542a20adeea631ccf5d0af73678c7fd7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 07:02:33 +0000 Subject: [PATCH 09/65] 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 949befb6f22..af89997e731 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 f969d2083ae9eabe4d3816cf2f4f73ec14740cee Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 07:05:21 +0000 Subject: [PATCH 10/65] 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 af89997e731..0c80fc8cb47 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 dac65a933246c2361719cb1637af720f64c51e37 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 08:37:34 +0000 Subject: [PATCH 11/65] 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 0c80fc8cb47..688b89aa7cd 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 5ff9be9bd81a982baa34e1fa589d4a16a8fb985e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 11:04:37 +0000 Subject: [PATCH 12/65] 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 be170e376801cf6ec2b8870b04e8ee750d18a6a7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 13:39:47 +0000 Subject: [PATCH 13/65] 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 688b89aa7cd..6dbabf08e65 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 8873ffc7a0118db0b737b3d092a4322cb3e9d5d7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 13:59:19 +0000 Subject: [PATCH 14/65] 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 886f9521e242e6b1bf1d022bd4561c2cd172a1c3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 14:23:44 +0000 Subject: [PATCH 15/65] 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 869b3a6cc33355283c1fd43490a83b4546c1b866 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 8 Feb 2023 14:44:25 +0000 Subject: [PATCH 16/65] 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 e32e9e9c64528124b31b6b1f6d62b01d194830c4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 01:48:03 +0000 Subject: [PATCH 17/65] 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 1af6766a4cf4c64a4387d2643eaea6b1468dca66 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 01:57:44 +0000 Subject: [PATCH 18/65] 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 a9aed1d57f131dc6ec203495fe581bf78c8d2418 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 03:42:05 +0000 Subject: [PATCH 19/65] 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 160c39fda4810449b4e6c77e568fd4a535c6dc2b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 04:11:38 +0000 Subject: [PATCH 20/65] 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 971bd0262e2..71ae28f5c2d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,6 +61,8 @@ SRC = \ nrf51.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 a183fa28c3c..39c7a3c2eb3 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 eadfea264c3..455d61e6d59 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -32,6 +32,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 0cf9ddb2b1f113f6d2dd50f0d73f5893456779d4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 04:43:49 +0000 Subject: [PATCH 21/65] 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 5d34e84cc215d97f042fb16190bf8d603d15d089 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:02:20 +0000 Subject: [PATCH 22/65] 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 d569c25b8778ffbe0b8905ea986e936dcde9bdf2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:03:05 +0000 Subject: [PATCH 23/65] 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 44ff42fcdf47ca52f9be09d82085405ad6bf3642 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 05:03:38 +0000 Subject: [PATCH 24/65] 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 b62ee67564fdf5bb2cf0f481c9d7c5f68ccb39c0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 06:03:04 +0000 Subject: [PATCH 25/65] 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 ccb018050b434b0f61790517c8f6c122e64cf0d0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:18:57 +0000 Subject: [PATCH 26/65] 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 375c534e59b5cee2f0cb1963d698e78f1294db85 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:19:44 +0000 Subject: [PATCH 27/65] 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 2a8d6ecb900c327ef492c5cd53e7872be702a79b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:20:27 +0000 Subject: [PATCH 28/65] 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 0e42830626098b06127ee44ca784f94ca14272ff Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 07:34:50 +0000 Subject: [PATCH 29/65] 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 894e67eb7ea4dc3881be6737a323d8b2e37f676a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:35:02 +0000 Subject: [PATCH 30/65] 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 303dc9bd8107f6f5d85785212ff4ab1c310962e4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:37:00 +0000 Subject: [PATCH 31/65] 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 7a23e9b74ccb0391e74e1ede3e119dd8afeffdfa Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 10:37:27 +0000 Subject: [PATCH 32/65] 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 b785227f9d2127bdcc56fbf1cfecb8b701aee420 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:09:06 +0000 Subject: [PATCH 33/65] 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 893b1ea9aad..e737ccbb9fa 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -181,6 +181,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 39c7a3c2eb3..0cc5418a647 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 455d61e6d59..54a3ad9bca4 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -46,6 +46,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 0222b0aa4a724a62b7dd06d519630b08b26c5b6e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:09:47 +0000 Subject: [PATCH 34/65] riscv32: Implemented probing for the GD32VF1 --- src/target/jep106.h | 5 +++++ src/target/riscv32.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) 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..6e1db8c016e 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -35,15 +35,36 @@ #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); +#define STRINGIFY(x) #x +#define PROBE(x) \ + do { \ + DEBUG_INFO("Calling " STRINGIFY(x) "\n"); \ + if ((x)(target)) \ + return true; \ + } while (0) + 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; } From b97b1f3efc1b858063e47c948a056ac09eaa2324 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:16:29 +0000 Subject: [PATCH 35/65] 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 924a68b061fc61852cac041d21a400cc4b57f018 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:22:37 +0000 Subject: [PATCH 36/65] 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 6dbabf08e65..bcf53f3ef92 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 21c47e0c2f059168df25e53cc38896f68b86b201 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 9 Feb 2023 11:38:44 +0000 Subject: [PATCH 37/65] riscv_debug: Populated target->cpuid and made use of it in gd32vf1_probe() for an even more positive part identification --- 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 e737ccbb9fa..a4e849136e1 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -184,6 +184,10 @@ bool gd32f1_probe(target_s *target) /* Identify RISC-V GD32VF1 chips */ bool gd32vf1_probe(target_s *const target) { + /* Make sure the architechture 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 1fdffd0f1b1d0b24bc4149e99e4d60e6716409c7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 08:13:33 +0000 Subject: [PATCH 38/65] 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 1f3b55c0e560ba2ba1b795d4a94fc6de8ae9c6ea Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 09:51:34 +0000 Subject: [PATCH 39/65] 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 6e1db8c016e..9b2936c796c 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -39,6 +39,12 @@ #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); #define STRINGIFY(x) #x @@ -52,6 +58,9 @@ static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, si 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) { @@ -68,6 +77,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 100390cedf1d4401d8d759be96075a79c566a2d7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 09:52:29 +0000 Subject: [PATCH 40/65] 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 aac72c0108061e6a79d6e9baec21fffeedc992b3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:05:42 +0000 Subject: [PATCH 41/65] 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 9b2936c796c..1f11cb9115f 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -57,6 +57,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); @@ -79,16 +80,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 4b460518ff8ae70a446f0fd19a5cf90f7eefa945 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:07:45 +0000 Subject: [PATCH 42/65] 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 93ec38dfaeae659370d8fd5ab00bdb7f30aa68cc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:17:09 +0000 Subject: [PATCH 43/65] 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 ef9da51ae2615d8c46b348320081a816f4c160ee Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 10:43:41 +0000 Subject: [PATCH 44/65] 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 2d57b3e4d421bd4ee593d7fa51e690a2ba66b9d2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 17:18:11 +0000 Subject: [PATCH 45/65] 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 b68ac4e8f4fa396285c2d1d399d8e84bf3621183 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:15:54 +0000 Subject: [PATCH 46/65] 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 c5954427fe680885da8c690a4ff4404b735e5ae7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:16:27 +0000 Subject: [PATCH 47/65] 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 c510620193f5b76d606650139010ca3b3dad2002 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:46:57 +0000 Subject: [PATCH 48/65] 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 024ea09c6ec782d5b70242eb2a5d86c0cd38580d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 18:58:45 +0000 Subject: [PATCH 49/65] 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 1f11cb9115f..3acfaa42412 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); + #define STRINGIFY(x) #x #define PROBE(x) \ do { \ @@ -64,6 +89,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); @@ -141,3 +168,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 f2be6c9cfb0bca136d22184c8a63cbde4a9b2da8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:48:28 +0000 Subject: [PATCH 50/65] 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 3acfaa42412..600671120ee 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); #define STRINGIFY(x) #x #define PROBE(x) \ @@ -90,6 +91,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: @@ -221,3 +223,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 a52a7af5e9f6fb97ea95662e753eb553312a5a2e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 10 Feb 2023 20:48:54 +0000 Subject: [PATCH 51/65] 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 ddc00b0ccb2..fb392cb9928 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -682,10 +682,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 12ce818dd4ff7fe3ee42a09e80c29f8b85db42ee Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 05:52:53 +0000 Subject: [PATCH 52/65] 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 a4e849136e1..643283cb6b3 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -40,6 +40,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); @@ -727,7 +728,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_HALFWORD); + /* 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_HALFWORD); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK1_OFFSET, NULL)) @@ -741,7 +746,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_HALFWORD); + /* 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_HALFWORD); /* Wait for completion or an error */ if (!stm32f1_flash_busy_wait(target, FLASH_BANK2_OFFSET, NULL)) From 47887cd31a4e6feda58c7f50b8d47341ab18595e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 05:57:00 +0000 Subject: [PATCH 53/65] 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 334e5bd93241c66f548130647d08f85c4bdd5414 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 06:38:03 +0000 Subject: [PATCH 54/65] 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 806ba5977673dd51654a16c55e95e36ce0458f29 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:02:07 +0000 Subject: [PATCH 55/65] 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 b4e5bbdf7bd71110c8bbed57bf6441945a785d68 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:02:40 +0000 Subject: [PATCH 56/65] 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 27ee42bfcabeb714addb4f436a8e7c2ffcacf641 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:22:39 +0000 Subject: [PATCH 57/65] 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 600671120ee..fc00ecc58df 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -184,7 +184,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 5984ecee2ab816376ef0664523cb68954c653465 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 07:23:09 +0000 Subject: [PATCH 58/65] 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 cbfb20dcbe965c9d3dfda62113c2171965c0cd4e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 09:02:25 +0000 Subject: [PATCH 59/65] 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 fc00ecc58df..c77979bd3bf 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); @@ -88,6 +89,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; @@ -122,6 +124,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 7869da3b276a857060ec19b71188a414910c8d6f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 11 Feb 2023 10:00:43 +0000 Subject: [PATCH 60/65] 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 c77979bd3bf..dc37d7e67a0 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); @@ -91,6 +92,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; @@ -159,6 +161,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); @@ -188,6 +213,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 d9692a322c6f2513e2272a53f41fd0a7eaacc4dd Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Sun, 19 Feb 2023 03:58:47 +0000 Subject: [PATCH 61/65] 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 9928a80dfec..7f87e77c012 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -185,6 +185,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_ARM_DPv0 UINT32_C(0x4ba00477) 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 bcf53f3ef92..ea36d6298ad 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 machinary back to bypass to scan the next device in the chain */ jtag_dev_write_ir(dev_index, IR_BYPASS); } From a3b1afc907945e03893a08ddb18a38db3c5e713f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 28 Mar 2023 02:55:13 +0100 Subject: [PATCH 62/65] 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 4d881c017fb14865561ce7feac769cedbd613398 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Apr 2023 16:45:17 +0100 Subject: [PATCH 63/65] 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 9829a38f8e3f379554b1b40a3a1995379196f4b6 Mon Sep 17 00:00:00 2001 From: mean Date: Sat, 29 Jul 2023 18:26:29 +0200 Subject: [PATCH 64/65] RISCV: Add some quirks to be compatible with CH32V3x chips. The error code wrong bus address is different, the chip replies ok when probing the breakpoints but the reply is empty --- src/target/riscv_debug.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 1d5747a7d17..dff772c4cce 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -504,8 +504,15 @@ 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) + switch (hart->status) { + case RISCV_HART_BUS_ERROR: + case RISCV_HART_EXCEPTION: + case RISCV_HART_NOT_SUPP: // WCH CH32Vx chips reply that + break; + default: return 0; + break; + } if (hart->access_width == 32U) { hart->access_width = 0U; return 0; /* We are unable to read the misa register */ @@ -646,7 +653,15 @@ 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)) { + bool alternate = false; + /* Some chips reply ok but returns 0 in the following call (WCH)*/ + if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info)) + alternate = true; + else { + if (!info) + alternate = true; + } + if (alternate) { /* * 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 86a86ab9c56e61b8a063581b8f003bd9fe929396 Mon Sep 17 00:00:00 2001 From: mean Date: Sun, 30 Jul 2023 10:43:28 +0200 Subject: [PATCH 65/65] RISCV: WCH, take style remark into account --- src/target/riscv_debug.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index dff772c4cce..7995fcfcce3 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -653,15 +653,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; - bool alternate = false; /* Some chips reply ok but returns 0 in the following call (WCH)*/ - if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info)) - alternate = true; - else { - if (!info) - alternate = true; - } - if (alternate) { + 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