From da9e44a5e3e430aeaa5cadfe03336c41087a4016 Mon Sep 17 00:00:00 2001 From: gojimmypi Date: Sun, 8 Mar 2026 09:20:52 -0700 Subject: [PATCH 001/247] misc: update links github to codeberg --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- CONTRIBUTING.md | 6 +++--- README.md | 4 ++-- UsingRTT.md | 2 +- deps/hidapi.wrap | 2 +- deps/libftdi.wrap | 2 +- deps/libopencm3.wrap | 2 +- deps/libusb.wrap | 2 +- src/platforms/common/blackpill-f4/README.md | 6 +++--- src/platforms/ctxlink/README.md | 2 +- src/platforms/stlink/README.md | 2 +- src/platforms/stlinkv3/README.md | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 633996ff88f..7180a49cbbb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,10 +13,10 @@ Information embedded in the description part of the commits doesn't count. ## Your checklist for this pull request -* [ ] I've read the [Code of Conduct](https://github.com/blackmagic-debug/blackmagic/blob/main/CODE_OF_CONDUCT.md) -* [ ] I've read the [guidelines for contributing](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md) to this repository -* [ ] It builds for hardware native (see [Building the firmware](https://github.com/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-firmware)) -* [ ] It builds as BMDA (see [Building the BMDA](https://github.com/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-app)) +* [ ] I've read the [Code of Conduct](https://codeberg.org/blackmagic-debug/blackmagic/src/CODE_OF_CONDUCT.md) +* [ ] I've read the [guidelines for contributing](https://codeberg.org/blackmagic-debug/blackmagic/src/CONTRIBUTING.md) to this repository +* [ ] It builds for hardware native (see [Building the firmware](https://codeberg.org/blackmagic-debug/blackmagic?tab=readme-ov-file#building-the-firmware)) +* [ ] It builds as BMDA (see [Building the BMDA](https://codeberg.org/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-app)) * [ ] I've tested it to the best of my ability * [ ] My commit messages provide a useful short description of what the commits do diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7661661067a..29a1d253fc5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,17 +36,17 @@ When reporting issues, be as specific as possible! ### If contributing for the first time - 1. [Fork](https://github.com/blackmagic-debug/blackmagic/fork) and clone the repository + 1. [Fork](https://codeberg.org/blackmagic-debug/blackmagic/fork) and clone the repository 2. Create a new branch: `git switch -c type/branch-name` (`git checkout -b type/branch-name` in the old syntax) 3. Make your change - 4. Push to your fork and submit a [pull request](https://github.com/blackmagic-debug/blackmagic/compare) + 4. Push to your fork and submit a [pull request](https://codeberg.org/blackmagic-debug/blackmagic/compare) If you wish to fix a bug, `type` in the new branch name should be `fix`, otherwise if you wish to implement a new feature, `type` should be `feature`. ### If you are working from an existing clone of the repository -1. Ensure you have our repo as a remote (`git remote add upstream https://github.com/blackmagic-debug/blackmagic`) +1. Ensure you have our repo as a remote (`git remote add upstream https://codeberg.org/blackmagic-debug/blackmagic`) 2. Switch back to `main` (`git switch main`/`git checkout main`) 3. Pull to ensure you're up to date (`git pull upstream`) 4. Push to your fork (`git push`) diff --git a/README.md b/README.md index 6151cca001d..4422306385f 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ a debugger-in-a-dongle that provides multi-voltage debug with no other external or as Black Magic Debug App (BMDA) which is the project built for the host machine, more details below. The project allows debugging of devices connected over JTAG or SWD, and via the companion tool -[bmpflash](https://github.com/blackmagic-debug/bmpflash) the programming of SPI Flash devices. +[bmpflash](https://codeberg.org/blackmagic-debug/bmpflash) the programming of SPI Flash devices. This includes support for ARM and RISC-V devices, the complete list can be found on the website. [![Discord](https://img.shields.io/discord/613131135903596547?logo=discord)](https://discord.gg/P7FYThy) -[![Current release](https://img.shields.io/github/v/release/blackmagic-debug/blackmagic.svg?logo=github)](https://github.com/blackmagic-debug/blackmagic/releases) +[![Current release](https://codeberg.org/blackmagic-debug/blackmagic/badges/release.svg)](https://codeberg.org/blackmagic-debug/blackmagic/releases) [![CI flow status](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml/badge.svg)](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml) [![AI free project](https://badges.ws/badge/NO-AI-ff0000)](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md#contributing) diff --git a/UsingRTT.md b/UsingRTT.md index 0e59323b813..387a4992a26 100644 --- a/UsingRTT.md +++ b/UsingRTT.md @@ -206,7 +206,7 @@ to the RTT input of the target. ### Linux On Linux, install udev rules as described in the [driver -documentation](https://github.com/blackmagic-debug/blackmagic/blob/main/driver/README.md). +documentation](https://codeberg.org/blackmagic-debug/blackmagic/src/driver/README.md). Disconnect and re-connect the BMP. Check the device shows up in `/dev/`: ```sh diff --git a/deps/hidapi.wrap b/deps/hidapi.wrap index eb8cc2e83b3..f7c57bcc276 100644 --- a/deps/hidapi.wrap +++ b/deps/hidapi.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/hidapi +url = https://codeberg.org/blackmagic-debug/hidapi revision = hidapi-0.14.0-meson clone-recursive = false diff --git a/deps/libftdi.wrap b/deps/libftdi.wrap index 88cd4319a9c..cfbbe3a7e8d 100644 --- a/deps/libftdi.wrap +++ b/deps/libftdi.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libftdi +url = https://codeberg.org/blackmagic-debug/libftdi revision = v1.5-meson clone-recursive = false diff --git a/deps/libopencm3.wrap b/deps/libopencm3.wrap index 1abb643ddc1..42573bbdb78 100644 --- a/deps/libopencm3.wrap +++ b/deps/libopencm3.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libopencm3 +url = https://codeberg.org/blackmagic-debug/libopencm3 revision = head depth = 1 diff --git a/deps/libusb.wrap b/deps/libusb.wrap index 8d691f0dce4..6850c43bb4c 100644 --- a/deps/libusb.wrap +++ b/deps/libusb.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libusb +url = https://codeberg.org/blackmagic-debug/libusb revision = v1.0.27-meson clone-recursive = false diff --git a/src/platforms/common/blackpill-f4/README.md b/src/platforms/common/blackpill-f4/README.md index fdddb63303c..d6c09300b8b 100644 --- a/src/platforms/common/blackpill-f4/README.md +++ b/src/platforms/common/blackpill-f4/README.md @@ -38,7 +38,7 @@ In the example command lines for building and flashing the firmware to the Black 0. Clone the repo and libopencm3 submodule, install toolchains, meson, etc. ```sh -git clone https://github.com/blackmagic-debug/blackmagic.git +git clone https://codeberg.org/blackmagic-debug/blackmagic.git cd blackmagic ``` @@ -53,7 +53,7 @@ meson setup build --cross-file=cross-file/blackpill-xxxxxx.ini -Dbmd_bootloader= Also Note: If the bootloader and firmware are going to be built for a Blackpill connected to a "Blackpill Carrier", the above setup MUST have "-Don_carrier_board=true" added to it. This is required to ensure the LEDs are correctly mapped to the Blackpill Carrier Board. - + 2. Compile the firmware and bootloader ```sh @@ -99,7 +99,7 @@ If you flashed the bootloader using the above instructions, it may be invoked us - Wait a moment - Release KEY -Once activated the BMD bootloader may be used to flash the device using 'bmputil,' available [here](https://github.com/blackmagic-debug/bmputil). +Once activated the BMD bootloader may be used to flash the device using 'bmputil,' available [here](https://codeberg.org/blackmagic-debug/bmputil). ## SWD/JTAG frequency setting diff --git a/src/platforms/ctxlink/README.md b/src/platforms/ctxlink/README.md index 6d5a215365f..86ea3966523 100644 --- a/src/platforms/ctxlink/README.md +++ b/src/platforms/ctxlink/README.md @@ -36,7 +36,7 @@ ctxLink uses the common 2 x 5 0.05" pin-header. 1. Clone the Blackmagic Debug Repository ```sh -git clone https://github.com/blackmagic-debug/blackmagic.git +git clone https://codeberg.org/blackmagic-debug/blackmagic.git cd blackmagic ``` diff --git a/src/platforms/stlink/README.md b/src/platforms/stlink/README.md index 436576b52f2..85004c4251a 100644 --- a/src/platforms/stlink/README.md +++ b/src/platforms/stlink/README.md @@ -52,7 +52,7 @@ NB: SWDIO/TMS is on P**B**14, not P**A**14. * Keep the original ST Bootloader. * Compile firmware with the option `-Dbmd_bootloader=false` -* Upload firmware with stlink-tool from [stlink-tool](https://github.com/blackmagic-debug/stlink-tool)(*3). +* Upload firmware with stlink-tool from [stlink-tool](https://codeberg.org/blackmagic-debug/stlink-tool)(*3). * For ST-Link v2, as on older disco boards, un- and replug USB to enter the bootloader. * Upload BMP firmware with `stlink-tool blackmagic_stlink_firmware.bin` * For ST-Link v2, after each stlink replug, call either `blackmacic -t` or `stlink-tool` without arguments or on Linux use some udev rule like the one shown below to enter the BMP firmware diff --git a/src/platforms/stlinkv3/README.md b/src/platforms/stlinkv3/README.md index a43077dcb84..d9fbb3b2f65 100644 --- a/src/platforms/stlinkv3/README.md +++ b/src/platforms/stlinkv3/README.md @@ -77,7 +77,7 @@ use soldered connections to CN3. For [STLINK-V3MINI](https://www.st.com/resource It is a good idea to keep a full image of the original flash content as backup! If you want to keep the original bootloader or access via SWD is disabled, clone -https://github.com/blackmagic-debug/stlink-tool +https://codeberg.org/blackmagic-debug/stlink-tool make and use like `stlink-tool blackmagic_stlinkv3_firmware.bin` Revert to original ST firmware with From 884b1fbc9ef74a02d4877437b5be759f05515cf5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Oct 2025 09:53:01 +0000 Subject: [PATCH 002/247] bmp-v3: Begun fleshing out the platform definitions for the pinout --- src/platforms/bmp-v3/platform.h | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/platforms/bmp-v3/platform.h diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h new file mode 100644 index 00000000000..65669671b51 --- /dev/null +++ b/src/platforms/bmp-v3/platform.h @@ -0,0 +1,97 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 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. + */ + +/* This file provides the platform specific declarations for the BMPv3 implementation. */ + +#ifndef PLATFORMS_BMP_V3_PLATFORM_H +#define PLATFORMS_BMP_V3_PLATFORM_H + +#include "gpio.h" + +#define PLATFORM_IDENT "v3 " + +/* + * Important pin mappings for BMPv3: + * + * State Indication LEDs: + * LED0 = PB5 (Yellow LED: Running) + * LED1 = PB4 (Orange LED: Idle) + * LED2 = PA10 (Red LED : Error) + * LED3 = PA8 (Green LED : Power/Connection state) + * + * Host Interface & Misc: + * USB_VBUS = PA9 + * USB_D+ = PA12 + * USB_D- = PA11 + * BTN1 = PA15 + * + * Target Debug Interface: + * TPWR_SNS = PB2 + * TPWR_EN = PA5 + * nRST = PH1 + * nRST_SNS = PH0 + * TCK = PB13 + * TMS = PB12 + * TDI = PB15 + * TDO = PB14 + * SWCLK = PB13 + * SWDIO = PB12 + * SWO = PA1 + * TCKTDI_EN = PC15 + * TMS_DIR = PC14 + * SWCLK_DIR = PC15 + * SWDIO_DIR = PC14 + * + * Target Comms Interface: + * TXD1 = PA2 + * RXD1 = PA3 + * TXD2 = PB6 + * RXD2 = PB7 + * UART2_DIR = PC13 + * + * On-Board Flash: + * FLASH_nCS = PA4 + * FLASH_CLK = PB10 + * FLASH_IO0 = PB1 + * FLASH_IO1 = PB0 + * FLASH_IO2 = PA7 + * FLASH_IO3 = PA6 + * + * AUX Interface: + * AUX_SCL = PB8 + * AUX_SDA = PB9 + */ + +/* Hardware definitions... */ + +#endif /* PLATFORMS_BMP_V3_PLATFORM_H */ From 3d97712e358f92af685604e3b917b9b225f7cb25 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Oct 2025 12:24:17 +0000 Subject: [PATCH 003/247] bmp-v3: Added pin definitions for all the most major moving pieces --- src/platforms/bmp-v3/platform.h | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 65669671b51..238af874369 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -37,6 +37,8 @@ #define PLATFORMS_BMP_V3_PLATFORM_H #include "gpio.h" +#include "timing.h" +#include "timing_stm32.h" #define PLATFORM_IDENT "v3 " @@ -93,5 +95,71 @@ */ /* Hardware definitions... */ +#define TCK_PORT GPIOB +#define TCK_PIN GPIO13 +#define TMS_PORT GPIOB +#define TMS_PIN GPIO12 +#define TDI_PORT GPIOB +#define TDI_PIN GPIO15 +#define TDO_PORT GPIOB +#define TDO_PIN GPIO14 +#define TCK_DIR_PORT GPIOC +#define TCK_DIR_PIN GPIO15 +#define TMS_DIR_PORT GPIOC +#define TMS_DIR_PIN GPIO14 + +#define SWCLK_PORT GPIOB +#define SWDIO_PORT GPIOB +#define SWCLK_PIN GPIO13 +#define SWDIO_PIN GPIO12 +#define SWCLK_DIR_PORT GPIOC +#define SWCLK_DIR_PIN GPIO15 +#define SWDIO_DIR_PORT GPIOC +#define SWDIO_DIR_PIN GPIO14 + +#define EXT_SPI SPI2 +#define EXT_SPI_SCLK_PORT GPIOB +#define EXT_SPI_SCLK_PIN GPIO13 +#define EXT_SPI_CS_PORT GPIOB +#define EXT_SPI_CS_PIN GPIO12 +#define EXT_SPI_POCI_PORT GPIOB +#define EXT_SPI_POCI_PIN GPIO14 +#define EXT_SPI_PICO_PORT GPIOB +#define EXT_SPI_PICO_PIN GPIO15 + +#define NRST_PORT GPIOH +#define NRST_PIN GPIO1 +#define NRST_SENSE_PORT GPIOH +#define NRST_SENSE_PIN GPIO0 + +#define SWO_PORT GPIOA +#define SWO_PIN GPIO1 + +#define TPWR_EN_PORT GPIOA +#define TPWR_EN_PIN GPIO5 +#define TPWR_SENSE_PORT GPIOB +#define TPWR_SENSE_PIN GPIO2 + +#define USB_PORT GPIOA +#define USB_DP_PIN GPIO12 +#define USB_DM_PIN GPIO11 + +#define USB_VBUS_PORT GPIOA +#define USB_VBUS_PIN GPIO9 + +#define LED0_PORT GPIOB +#define LED0_PIN GPIO5 +#define LED1_PORT GPIOB +#define LED1_PIN GPIO4 +#define LED2_PORT GPIOA +#define LED2_PIN GPIO10 +#define LED3_PORT GPIOA +#define LED3_PIN GPIO8 +#define LED_UART_PORT LED0_PORT +#define LED_UART_PIN LED0_PIN +#define LED_IDLE_RUN_PORT LED1_PORT +#define LED_IDLE_RUN_PIN LED1_PIN +#define LED_ERROR_PORT LED2_PORT +#define LED_ERROR_PIN LED2_PIN #endif /* PLATFORMS_BMP_V3_PLATFORM_H */ From bc9594934d9a74ac3959196e5134209ca391b6b5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Oct 2025 12:24:34 +0000 Subject: [PATCH 004/247] adiv5_swd: Fixed some issues in the copyright header --- src/target/adiv5_swd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/adiv5_swd.c b/src/target/adiv5_swd.c index 06e41e5efd8..fa08c2c2c7a 100644 --- a/src/target/adiv5_swd.c +++ b/src/target/adiv5_swd.c @@ -1,9 +1,9 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin - * Copyright (C) 2020- 2021 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) + * Copyright (C) 2020-2021 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 4d904babffc83831f9045e67fff1f5002d0aa2f9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Oct 2025 12:45:19 +0000 Subject: [PATCH 005/247] bmp-v3: Started filling in interrupt setup and control macros --- src/platforms/bmp-v3/platform.h | 24 +++++++++++++++++++++++ src/platforms/common/aux_serial.c | 8 ++++++++ src/platforms/common/aux_serial.h | 4 ++-- src/platforms/common/stm32/timing_stm32.c | 4 ++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 238af874369..ff931cab672 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -162,4 +162,28 @@ #define LED_ERROR_PORT LED2_PORT #define LED_ERROR_PIN LED2_PIN +#define TMS_SET_MODE() \ + do { \ + gpio_set(TMS_DIR_PORT, TMS_DIR_PIN); \ + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); \ + } while (0) + +#define USB_DRIVER otgfs_usb_driver +#define USB_IRQ NVIC_USB_IRQ +#define USB_ISR(x) usb_isr(x) +/* + * Interrupt priorities. Low numbers are high priority. + * TIM5 is used for SWO capture and must be highest priority. + */ +#define IRQ_PRI_USB (1U << 4U) +#define IRQ_PRI_USBUSART (2U << 4U) +#define IRQ_PRI_USBUSART_DMA (2U << 4U) +#define IRQ_PRI_USB_VBUS (14U << 4U) +#define IRQ_PRI_SWO_TIM (0U << 4U) +#define IRQ_PRI_SWO_DMA (0U << 4U) + +#define SET_RUN_STATE(state) running_status = (state) +#define SET_IDLE_STATE(state) gpio_set_val(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN, state) +#define SET_ERROR_STATE(state) gpio_set_val(LED_ERROR_PORT, LED_ERROR_PIN, state) + #endif /* PLATFORMS_BMP_V3_PLATFORM_H */ diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 5d44dfb37d1..64711966daa 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -338,14 +338,22 @@ void aux_serial_get_encoding(usb_cdc_line_coding_s *const coding) void aux_serial_set_led(const aux_serial_led_e led) { aux_serial_led_state |= led; +#ifdef LED_UART_PORT + gpio_set(LED_UART_PORT, LED_UART_PIN); +#else gpio_set(LED_PORT_UART, LED_UART); +#endif } void aux_serial_clear_led(const aux_serial_led_e led) { aux_serial_led_state &= ~led; if (!aux_serial_led_state) +#ifdef LED_UART_PORT + gpio_clear(LED_UART_PORT, LED_UART_PIN); +#else gpio_clear(LED_PORT_UART, LED_UART); +#endif } char *aux_serial_current_transmit_buffer(void) diff --git a/src/platforms/common/aux_serial.h b/src/platforms/common/aux_serial.h index a8aa2912a41..ff2859fcdd6 100644 --- a/src/platforms/common/aux_serial.h +++ b/src/platforms/common/aux_serial.h @@ -25,9 +25,9 @@ #include #include "usb_types.h" -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) /* XXX: Does the st_usbfs_v2_usb_driver work on F3 with 128 byte buffers? */ -#if defined(STM32F1) || defined(STM32F3) || defined(STM32F4) +#if defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32U5) #define USART_DMA_BUF_SHIFT 7U #elif defined(STM32F0) /* The st_usbfs_v2_usb_driver only works with up to 64-byte buffers on the F0 parts */ diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 6f291380486..81708a0a5ce 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -84,7 +84,11 @@ void sys_tick_handler(void) if (morse_tick >= MORSECNT) { if (running_status) +#ifdef LED_IDLE_RUN_PORT + gpio_toggle(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN); +#else gpio_toggle(LED_PORT, LED_IDLE_RUN); +#endif usb_config_morse_msg_update(); SET_ERROR_STATE(morse_update()); morse_tick = 0; From 4102404464f25616ebac7a4e216764686d79fbcc Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 31 Oct 2025 12:46:21 +0000 Subject: [PATCH 006/247] common/stm32: Defined the STM32U5 as a viable BMP platform target --- src/platforms/common/stm32/meson.build | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/platforms/common/stm32/meson.build b/src/platforms/common/stm32/meson.build index cb31141ce27..40b4168f2ec 100644 --- a/src/platforms/common/stm32/meson.build +++ b/src/platforms/common/stm32/meson.build @@ -1,7 +1,8 @@ # This file is part of the Black Magic Debug project. # -# Copyright (C) 2023 1BitSquared +# Copyright (C) 2023-2025 1BitSquared # Written by Rafael Silva +# Modified by Rachel Mant # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -211,3 +212,32 @@ platform_stm32f7_dfu = declare_dependency( link_args: platform_stm32f7_link_args, dependencies: [platform_stm32_dfu_common, dependency('opencm3_stm32f7')], ) + +## STM32U5 Platform +## ________________ + +platform_stm32u5_args = [ + '-mcpu=cortex-m33', + '-mfloat-abi=hard', + '-mfpu=fpv5-sp-d16', + '-DSTM32U5', +] + +platform_stm32u5_link_args = [ + '-mcpu=cortex-m33', + '-mfloat-abi=hard', + '-mfpu=fpv5-sp-d16', +] + +platform_stm32u5 = declare_dependency( + compile_args: platform_stm32u5_args, + link_args: platform_stm32u5_link_args, + dependencies: [platform_stm32_common, dependency('opencm3_stm32u5')], +) + +platform_stm32u5_dfu = declare_dependency( + sources: platform_stm32f4_dfu_sources, + compile_args: platform_stm32u5_args, + link_args: platform_stm32u5_link_args, + dependencies: [platform_stm32_dfu_common, dependency('opencm3_stm32u5')], +) From abe324f2b6736be9cd1229dd5953cb4529ab89e3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 1 Nov 2025 16:08:05 +0000 Subject: [PATCH 007/247] native: Fixed the copyright headers --- src/platforms/native/platform.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index 45ada7551a9..579bc149b90 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/native/platform.h @@ -1,8 +1,11 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2021-2025 1BitSquared * Written by Gareth McMullin + * Modified by Piotr Esden-Tempski + * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 5fc8e01bb076f5438d1aefc693959842af1d663d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 3 Nov 2025 14:20:10 +0000 Subject: [PATCH 008/247] common: Enable various facilities for the STM32U5 series --- src/platforms/common/aux_serial.c | 12 ++++++------ src/platforms/common/aux_serial.h | 4 ++-- src/platforms/common/stm32/gdb_if.c | 8 ++++---- src/platforms/common/stm32/swo_manchester.c | 4 ++-- src/platforms/common/stm32/swo_uart.c | 4 ++-- src/platforms/common/stm32/timing_stm32.c | 17 +++++++++++++++++ src/platforms/common/usb_serial.c | 14 +++++++------- src/platforms/common/usb_serial.h | 4 ++-- 8 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 64711966daa..eba0bca5b6c 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2022-2025 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) #include #include #include @@ -42,7 +42,7 @@ static uint16_t aux_serial_receive_write_index = 0; /* FIFO out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */ static uint16_t aux_serial_receive_read_index = 0; -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) static char aux_serial_transmit_buffer[2U][AUX_UART_BUFFER_SIZE]; static uint16_t aux_serial_transmit_buffer_index = 0; static uint16_t aux_serial_transmit_buffer_consumed = 0; @@ -120,7 +120,7 @@ void bmd_usart_set_baudrate(uint32_t usart, uint32_t baud_rate) usart_set_baudrate(usart, baud_rate); } -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) void aux_serial_init(void) { /* Enable clocks */ @@ -253,7 +253,7 @@ void aux_serial_set_encoding(const usb_cdc_line_coding_s *const coding) usart_disable(USBUSART); bmd_usart_set_baudrate(USBUSART, coding->dwDTERate); -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) if (coding->bParityType != USB_CDC_NO_PARITY) usart_set_databits(USBUSART, coding->bDataBits + 1U <= 8U ? 8 : 9); else @@ -334,7 +334,7 @@ void aux_serial_get_encoding(usb_cdc_line_coding_s *const coding) coding->bDataBits = data_bits - 1; } -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) void aux_serial_set_led(const aux_serial_led_e led) { aux_serial_led_state |= led; diff --git a/src/platforms/common/aux_serial.h b/src/platforms/common/aux_serial.h index ff2859fcdd6..3fe2cdefe89 100644 --- a/src/platforms/common/aux_serial.h +++ b/src/platforms/common/aux_serial.h @@ -49,7 +49,7 @@ void aux_serial_init(void); void aux_serial_set_encoding(const usb_cdc_line_coding_s *coding); void aux_serial_get_encoding(usb_cdc_line_coding_s *coding); -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) typedef enum aux_serial_led { AUX_SERIAL_LED_TX = (1U << 0U), AUX_SERIAL_LED_RX = (1U << 1U) @@ -68,7 +68,7 @@ size_t aux_serial_transmit_buffer_fullness(void); /* Send a number of bytes staged into the current transmit buffer */ void aux_serial_send(size_t len); -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) void aux_serial_update_receive_buffer_fullness(void); bool aux_serial_receive_buffer_empty(void); void aux_serial_drain_receive_buffer(void); diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 516ffda36fa..16e4851d11d 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -36,7 +36,7 @@ static uint32_t count_in; static uint32_t out_ptr; static char buffer_out[CDCACM_PACKET_SIZE]; static char buffer_in[CDCACM_PACKET_SIZE]; -#if defined(STM32F4) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) static volatile uint32_t count_new; static char double_buffer_out[CDCACM_PACKET_SIZE]; #endif @@ -64,7 +64,7 @@ void gdb_if_flush(const bool force) /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ if (force && count_in == CDCACM_PACKET_SIZE) { - /* + /* * libopencm3 needs a change for us to confirm when that transfer is complete, * so we just send a packet containing a null character for now. */ @@ -76,7 +76,7 @@ void gdb_if_flush(const bool force) count_in = 0U; } -#if defined(STM32F4) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) { (void)ep; @@ -91,7 +91,7 @@ static void gdb_if_update_buf(void) { while (usb_get_config() != 1) continue; -#if !defined(STM32F4) && !defined(STM32F7) +#if !defined(STM32F4) && !defined(STM32F7) && !defined(STM32U5) count_out = usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_out, CDCACM_PACKET_SIZE); out_ptr = 0; #else diff --git a/src/platforms/common/stm32/swo_manchester.c b/src/platforms/common/stm32/swo_manchester.c index 61c71f8a1f8..ac780d41e54 100644 --- a/src/platforms/common/stm32/swo_manchester.c +++ b/src/platforms/common/stm32/swo_manchester.c @@ -68,7 +68,7 @@ void swo_manchester_init(void) /* Make sure the timer block is clocked on platforms that don't do this in their `platform_init()` */ SWO_TIM_CLK_EN(); -#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) || defined(STM32U5) /* Set any required pin alt-function configuration - TIM3/TIM4/TIM5 are AF2 */ gpio_mode_setup(SWO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SWO_PIN); gpio_set_af(SWO_PORT, SWO_TIM_PIN_AF, SWO_PIN); @@ -122,7 +122,7 @@ void swo_manchester_deinit(void) swo_data_bit_index = 0U; swo_half_bit_period = 0U; -#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) || defined(STM32U5) gpio_mode_setup(SWO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWO_PIN); #else /* Put the GPIO back into normal service as a GPIO */ diff --git a/src/platforms/common/stm32/swo_uart.c b/src/platforms/common/stm32/swo_uart.c index 44c64743267..735a2a4ee5f 100644 --- a/src/platforms/common/stm32/swo_uart.c +++ b/src/platforms/common/stm32/swo_uart.c @@ -67,7 +67,7 @@ void swo_uart_init(const uint32_t baudrate) rcc_periph_clock_enable(SWO_DMA_CLK); /* Reconfigure the GPIO over to UART mode */ -#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) || defined(STM32U5) gpio_mode_setup(SWO_UART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, SWO_UART_RX_PIN); gpio_set_output_options(SWO_UART_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_100MHZ, SWO_UART_RX_PIN); gpio_set_af(SWO_UART_PORT, SWO_UART_PIN_AF, SWO_UART_RX_PIN); @@ -139,7 +139,7 @@ void swo_uart_deinit(void) swo_buffer_bytes_available += amount; /* Put the GPIO back into normal service as a GPIO */ -#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) +#if defined(STM32F4) || defined(STM32F0) || defined(STM32F3) || defined(STM32F7) || defined(STM32U5) gpio_mode_setup(SWO_UART_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWO_UART_RX_PIN); #else gpio_set_mode(SWO_UART_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SWO_UART_RX_PIN); diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 81708a0a5ce..a3b3cc7f8d3 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -61,9 +61,18 @@ static void usb_config_morse_msg_update(void) void platform_timing_init(void) { /* Setup heartbeat timer */ +#ifndef STM32U5 systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); +#else + rcc_set_peripheral_clk_sel(SYS_TICK_BASE, RCC_CCIPR1_SYSTICKSEL_HCLK_DIV8); + systick_set_clocksource(STK_CSR_CLKSOURCE_EXT); +#endif /* Interrupt us at 1kHz */ +#ifndef STM32U5 systick_set_reload((rcc_ahb_frequency / (8U * SYSTICKHZ)) - 1U); +#else + systick_set_reload((rcc_get_bus_clk_freq(RCC_SYSTICKCLK) / SYSTICKHZ) - 1U); +#endif /* SYSTICK_IRQ with low priority */ nvic_set_priority(NVIC_SYSTICK_IRQ, 14U << 4U); systick_interrupt_enable(); @@ -182,7 +191,11 @@ void platform_max_frequency_set(const uint32_t frequency) target_clk_divider = (ratio - BITBANG_DIVIDER_OFFSET) / BITBANG_DIVIDER_FACTOR; } #else +#ifndef STM32U5 uint32_t divisor = rcc_ahb_frequency - USED_SWD_CYCLES * frequency; +#else + uint32_t divisor = rcc_get_bus_clk_freq(RCC_AHBCLK) - USED_SWD_CYCLES * frequency; +#endif /* If we now have an insanely big divisor, the above operation wrapped to a negative signed number. */ if (divisor >= 0x80000000U) { target_clk_divider = UINT32_MAX; @@ -210,7 +223,11 @@ uint32_t platform_max_frequency_get(void) const uint32_t ratio = (target_clk_divider * BITBANG_DIVIDER_FACTOR) + BITBANG_DIVIDER_OFFSET; return rcc_ahb_frequency / ratio; #else +#ifndef STM32U5 uint32_t result = rcc_ahb_frequency; +#else + uint32_t result = rcc_get_bus_clk_freq(RCC_AHBCLK); +#endif result /= USED_SWD_CYCLES + CYCLES_PER_CNT * target_clk_divider; return result; #endif diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index bd634f7f3e3..18b9dbfe31a 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -3,7 +3,7 @@ * * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin - * Copyright (C) 2022-2024 1BitSquared + * Copyright (C) 2022-2025 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -56,7 +56,7 @@ #include #include #include -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) #include #include #endif @@ -68,7 +68,7 @@ static void usb_serial_set_state(usbd_device *dev, uint16_t iface, uint8_t ep); static void debug_serial_send_callback(usbd_device *dev, uint8_t ep); static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep); -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) static bool debug_serial_send_complete = true; #endif @@ -174,7 +174,7 @@ void usb_serial_set_config(usbd_device *dev, uint16_t value) usb_config = value; /* GDB interface */ -#if defined(STM32F4) || defined(LM4F) || defined(STM32F7) +#if defined(STM32F4) || defined(LM4F) || defined(STM32F7) || defined(STM32U5) usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, gdb_usb_out_cb); #else usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); @@ -256,7 +256,7 @@ static bool debug_serial_fifo_buffer_empty(void) } #endif -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) /* * Runs deferred processing for AUX serial RX, draining RX FIFO by sending * characters to host PC via the debug serial interface. @@ -304,7 +304,7 @@ static void debug_serial_send_callback(usbd_device *dev, uint8_t ep) { (void)ep; (void)dev; -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) debug_serial_send_data(); #endif } @@ -331,7 +331,7 @@ static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep) aux_serial_send(len); -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) /* Disable USBUART TX packet reception if buffer does not have enough space */ if (AUX_UART_BUFFER_SIZE - aux_serial_transmit_buffer_fullness() < CDCACM_PACKET_SIZE) usbd_ep_nak_set(dev, ep, 1); diff --git a/src/platforms/common/usb_serial.h b/src/platforms/common/usb_serial.h index 1c10d9d36eb..8707069a658 100644 --- a/src/platforms/common/usb_serial.h +++ b/src/platforms/common/usb_serial.h @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2022-2025 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -33,7 +33,7 @@ void debug_serial_run(void); uint32_t debug_serial_fifo_send(const char *fifo, uint32_t fifo_begin, uint32_t fifo_end); #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) -size_t debug_serial_debug_write(const char *buf, const size_t len); +size_t debug_serial_debug_write(const char *buf, size_t len); #endif #endif /* PLATFORMS_COMMON_USB_SERIAL_H */ From 2e9efe720fc643a1194ca05b99876944b7e294a3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 3 Nov 2025 14:21:48 +0000 Subject: [PATCH 009/247] bmp-v3: Roughly implemented the SWDIO drive mode switch macros --- src/platforms/bmp-v3/platform.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index ff931cab672..d92828947f3 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -168,6 +168,18 @@ gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); \ } while (0) +#define SWDIO_MODE_FLOAT() \ + do { \ + gpio_clear(SWDIO_DIR_PORT, SWDIO_DIR_PIN); \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWDIO_PIN); \ + } while (0) + +#define SWDIO_MODE_DRIVE() \ + do { \ + gpio_set(SWDIO_DIR_PORT, SWDIO_DIR_PIN); \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); \ + } while (0) + #define USB_DRIVER otgfs_usb_driver #define USB_IRQ NVIC_USB_IRQ #define USB_ISR(x) usb_isr(x) From 7909e62f1e29cef689dc82ceda7991719029bfca Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 3 Nov 2025 14:22:21 +0000 Subject: [PATCH 010/247] bmp-v3: Added configuration for the SWO pin --- src/platforms/bmp-v3/platform.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index d92828947f3..15216a7baf1 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -40,6 +40,8 @@ #include "timing.h" #include "timing_stm32.h" +#define PLATFORM_HAS_TRACESWO + #define PLATFORM_IDENT "v3 " /* @@ -194,6 +196,31 @@ #define IRQ_PRI_SWO_TIM (0U << 4U) #define IRQ_PRI_SWO_DMA (0U << 4U) +/* Use TIM5 Input 2 (from PA1/SWO) for Manchester data recovery */ +#define SWO_TIM TIM5 +#define SWO_TIM_CLK_EN() +#define SWO_TIM_IRQ NVIC_TIM5_IRQ +#define SWO_TIM_ISR(x) tim5_isr(x) +#define SWO_IC_IN TIM_IC_IN_TI2 +#define SWO_IC_RISING TIM_IC2 +#define SWO_CC_RISING TIM5_CCR2 +#define SWO_ITR_RISING TIM_DIER_CC2IE +#define SWO_STATUS_RISING TIM_SR_CC2IF +#define SWO_IC_FALLING TIM_IC1 +#define SWO_CC_FALLING TIM5_CCR1 +#define SWO_STATUS_FALLING TIM_SR_CC1IF +#define SWO_STATUS_OVERFLOW (TIM_SR_CC1OF | TIM_SR_CC2OF) +#define SWO_TRIG_IN TIM_SMCR_TS_TI2FP2 +#define SWO_TIM_PIN_AF GPIO_AF2 + +/* Use PA1 (UART4) for UART/NRZ/Async data recovery */ +#define SWO_UART USART4 +#define SWO_UART_CLK RCC_UART4 +#define SWO_UART_DR USART4_RDR +#define SWO_UART_PORT SWO_PORT +#define SWO_UART_RX_PIN SWO_PIN +#define SWO_UART_PIN_AF GPIO_AF8 + #define SET_RUN_STATE(state) running_status = (state) #define SET_IDLE_STATE(state) gpio_set_val(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN, state) #define SET_ERROR_STATE(state) gpio_set_val(LED_ERROR_PORT, LED_ERROR_PIN, state) From ed6236d0e838748f092f8adbdb29a4bb73c2162a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 3 Nov 2025 18:13:25 +0000 Subject: [PATCH 011/247] bmp-v3: Defined the DMA setup for the SWO and AUX UART subsystems --- src/platforms/bmp-v3/platform.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 15216a7baf1..af8ab3f34de 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -196,6 +196,15 @@ #define IRQ_PRI_SWO_TIM (0U << 4U) #define IRQ_PRI_SWO_DMA (0U << 4U) +#define USBUSART_DMA_BUS GPDMA1 +#define USBUSART_DMA_CLK RCC_GPDMA1 +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL0 +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL1 +#define USBUSART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ +#define USBUSART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) +#define USBUSART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ +#define USBUSART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) + /* Use TIM5 Input 2 (from PA1/SWO) for Manchester data recovery */ #define SWO_TIM TIM5 #define SWO_TIM_CLK_EN() @@ -221,6 +230,12 @@ #define SWO_UART_RX_PIN SWO_PIN #define SWO_UART_PIN_AF GPIO_AF8 +#define SWO_DMA_BUS GPDMA1 +#define SWO_DMA_CLK RCC_GPDMA1 +#define SWO_DMA_CHAN DMA_CHANNEL2 +#define SWO_DMA_IRQ NVIC_GPDMA1_CH2_IRQ +#define SWO_DMA_ISR(x) gpdma1_ch2_isr(x) + #define SET_RUN_STATE(state) running_status = (state) #define SET_IDLE_STATE(state) gpio_set_val(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN, state) #define SET_ERROR_STATE(state) gpio_set_val(LED_ERROR_PORT, LED_ERROR_PIN, state) From 419998cbab61ad10c08c1b4bf376986ab454e3a6 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 4 Nov 2025 14:15:08 +0000 Subject: [PATCH 012/247] common/aux_serial: Implemented support for sourcing data from two UARTs and switching between them automatically --- src/platforms/bmp-v3/platform.h | 37 ++++++++- src/platforms/common/aux_serial.c | 126 +++++++++++++++++++++++------- src/platforms/common/usb_serial.c | 9 ++- 3 files changed, 137 insertions(+), 35 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index af8ab3f34de..92f0fb6fbbd 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -41,6 +41,7 @@ #include "timing_stm32.h" #define PLATFORM_HAS_TRACESWO +#define PLATFORM_MULTI_UART #define PLATFORM_IDENT "v3 " @@ -182,6 +183,16 @@ gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); \ } while (0) +#define UART_PIN_SETUP() \ + do { \ + gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); \ + gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); \ + gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART1_RX_PIN); \ + gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); \ + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); \ + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); \ + } while (0) + #define USB_DRIVER otgfs_usb_driver #define USB_IRQ NVIC_USB_IRQ #define USB_ISR(x) usb_isr(x) @@ -190,19 +201,37 @@ * TIM5 is used for SWO capture and must be highest priority. */ #define IRQ_PRI_USB (1U << 4U) -#define IRQ_PRI_USBUSART (2U << 4U) -#define IRQ_PRI_USBUSART_DMA (2U << 4U) +#define IRQ_PRI_AUX_UART (2U << 4U) +#define IRQ_PRI_AUX_UART_DMA (2U << 4U) #define IRQ_PRI_USB_VBUS (14U << 4U) #define IRQ_PRI_SWO_TIM (0U << 4U) #define IRQ_PRI_SWO_DMA (0U << 4U) +/* PA2/3 as USART2 TX/RX */ +#define AUX_UART1 USART2 +#define AUX_UART1_CLK RCC_USART2 +#define AUX_UART1_IRQ NVIC_USART2_IRQ +#define AUX_UART1_ISR(x) usart2_isr(x) +#define AUX_UART1_PORT GPIOA +#define AUX_UART1_TX_PIN GPIO2 +#define AUX_UART1_RX_PIN GPIO3 + +/* PB6/7 as USART1 TX/RX */ +#define AUX_UART2 USART1 +#define AUX_UART2_CLK RCC_USART1 +#define AUX_UART2_IRQ NVIC_USART1_IRQ +#define AUX_UART2_ISR(x) usart1_isr(x) +#define AUX_UART2_PORT GPIOB +#define AUX_UART2_TX_PIN GPIO6 +#define AUX_UART2_RX_PIN GPIO7 + #define USBUSART_DMA_BUS GPDMA1 #define USBUSART_DMA_CLK RCC_GPDMA1 #define USBUSART_DMA_TX_CHAN DMA_CHANNEL0 #define USBUSART_DMA_RX_CHAN DMA_CHANNEL1 -#define USBUSART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ +#define AUX_UART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ #define USBUSART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) -#define USBUSART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ +#define AUX_UART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ #define USBUSART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) /* Use TIM5 Input 2 (from PA1/SWO) for Manchester data recovery */ diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index eba0bca5b6c..cb1a6ffd23d 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -87,10 +87,17 @@ static char aux_serial_transmit_buffer[AUX_UART_BUFFER_SIZE]; #define usart_set_parity(uart, parity) uart_set_parity(uart, parity) #endif -void bmd_usart_set_baudrate(uint32_t usart, uint32_t baud_rate) +#ifndef PLATFORM_MULTI_UART +#define AUX_UART USBUSART +#else +static uintptr_t active_uart = 0U; +#define AUX_UART active_uart +#endif + +void bmd_usart_set_baudrate(const uintptr_t usart, const uint32_t baud_rate) { /* If the new baud rate is out of supported range for a given USART, then keep previous */ -#if defined(LM4F) +#ifdef LM4F /* Are we running off the internal clock or system clock? */ const uint32_t clock = UART_CC(usart) == UART_CC_CS_PIOSC ? 16000000U : rcc_get_system_clock_frequency(); #else @@ -98,7 +105,7 @@ void bmd_usart_set_baudrate(uint32_t usart, uint32_t baud_rate) #endif const uint32_t baud_lowest = clock / 65535U; const uint32_t baud_highest_16x = clock / 16U; -#if defined(USART_CR1_OVER8) +#ifdef USART_CR1_OVER8 const uint32_t baud_highest_8x = clock / 8U; /* Four-way range match */ @@ -121,23 +128,43 @@ void bmd_usart_set_baudrate(uint32_t usart, uint32_t baud_rate) } #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) +void aux_serial_uart_init(const uintptr_t uart_base) +{ +#ifndef STM32U5 + bmd_usart_set_baudrate(uart_base, 38400U); +#else + bmd_usart_set_baudrate(uart_base, 115200U); +#endif + usart_set_databits(uart_base, 8); + usart_set_stopbits(uart_base, USART_STOPBITS_1); + usart_set_mode(uart_base, USART_MODE_TX_RX); + usart_set_parity(uart_base, USART_PARITY_NONE); + usart_set_flow_control(uart_base, USART_FLOWCONTROL_NONE); + USART_CR1(uart_base) |= USART_CR1_IDLEIE; +} + void aux_serial_init(void) { - /* Enable clocks */ +/* Enable clocks */ +#ifndef PLATFORM_MULTI_UART rcc_periph_clock_enable(USBUSART_CLK); +#else + rcc_periph_clock_enable(AUX_UART1_CLK); + rcc_periph_clock_enable(AUX_UART2_CLK); +#endif rcc_periph_clock_enable(USBUSART_DMA_CLK); /* Setup UART parameters */ UART_PIN_SETUP(); - bmd_usart_set_baudrate(USBUSART, 38400); - usart_set_databits(USBUSART, 8); - usart_set_stopbits(USBUSART, USART_STOPBITS_1); - usart_set_mode(USBUSART, USART_MODE_TX_RX); - usart_set_parity(USBUSART, USART_PARITY_NONE); - usart_set_flow_control(USBUSART, USART_FLOWCONTROL_NONE); - USART_CR1(USBUSART) |= USART_CR1_IDLEIE; +#ifndef PLATFORM_MULTI_UART + aux_serial_uart_init(USBUSART); +#else + aux_serial_uart_init(AUX_UART1); + aux_serial_uart_init(AUX_UART2); +#endif - /* Setup USART TX DMA */ + /* Set up data register defines if we're not in multi-UART mode */ +#ifndef PLATFORM_MULTI_UART #if !defined(USBUSART_TDR) && defined(USBUSART_DR) #define USBUSART_TDR USBUSART_DR #elif !defined(USBUSART_TDR) @@ -148,8 +175,13 @@ void aux_serial_init(void) #elif !defined(USBUSART_RDR) #define USBUSART_RDR USART_DR(USBUSART) #endif +#endif + + /* Setup USART TX DMA */ dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); +#ifndef PLATFORM_MULTI_UART dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)&USBUSART_TDR); +#endif dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PSIZE_8BIT); dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_MSIZE_8BIT); @@ -166,7 +198,9 @@ void aux_serial_init(void) /* Setup USART RX DMA */ dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#ifndef PLATFORM_MULTI_UART dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uintptr_t)&USBUSART_RDR); +#endif dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uintptr_t)aux_serial_receive_buffer); dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, AUX_UART_BUFFER_SIZE); dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); @@ -187,6 +221,7 @@ void aux_serial_init(void) dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); /* Enable interrupts */ +#ifndef PLATFORM_MULTI_UART nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); #if defined(USBUSART_DMA_RXTX_IRQ) nvic_set_priority(USBUSART_DMA_RXTX_IRQ, IRQ_PRI_USBUSART_DMA); @@ -201,6 +236,16 @@ void aux_serial_init(void) nvic_enable_irq(USBUSART_DMA_TX_IRQ); nvic_enable_irq(USBUSART_DMA_RX_IRQ); #endif +#else + nvic_set_priority(AUX_UART1_IRQ, IRQ_PRI_AUX_UART); + nvic_set_priority(AUX_UART2_IRQ, IRQ_PRI_AUX_UART); + nvic_set_priority(AUX_UART_DMA_TX_IRQ, IRQ_PRI_AUX_UART_DMA); + nvic_set_priority(AUX_UART_DMA_RX_IRQ, IRQ_PRI_AUX_UART_DMA); + nvic_enable_irq(AUX_UART1_IRQ); + nvic_enable_irq(AUX_UART2_IRQ); + nvic_enable_irq(AUX_UART_DMA_TX_IRQ); + nvic_enable_irq(AUX_UART_DMA_RX_IRQ); +#endif /* Finally enable the USART */ usart_enable(USBUSART); @@ -238,7 +283,7 @@ void aux_serial_init(void) /* Enable interrupts */ uart_enable_interrupts(UART0, UART_INT_RX | UART_INT_RT); - /* Finally enable the USART. */ + /* Finally enable the USART */ uart_enable(USBUART); //nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); @@ -246,18 +291,17 @@ void aux_serial_init(void) } #endif -void aux_serial_set_encoding(const usb_cdc_line_coding_s *const coding) +static void aux_serial_setup_uart(const uintptr_t uart, const usb_cdc_line_coding_s *const coding) { - /* Some devices require that the usart is disabled before - * changing the usart registers. */ - usart_disable(USBUSART); - bmd_usart_set_baudrate(USBUSART, coding->dwDTERate); + /* Some devices require that the usart is disabled before changing the usart registers */ + usart_disable(uart); + bmd_usart_set_baudrate(uart, coding->dwDTERate); #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) if (coding->bParityType != USB_CDC_NO_PARITY) - usart_set_databits(USBUSART, coding->bDataBits + 1U <= 8U ? 8 : 9); + usart_set_databits(uart, coding->bDataBits + 1U <= 8U ? 8 : 9); else - usart_set_databits(USBUSART, coding->bDataBits <= 8U ? 8 : 9); + usart_set_databits(uart, coding->bDataBits <= 8U ? 8 : 9); #elif defined(LM4F) uart_set_databits(USBUART, coding->bDataBits); #endif @@ -274,32 +318,42 @@ void aux_serial_set_encoding(const usb_cdc_line_coding_s *const coding) default: break; } - usart_set_stopbits(USBUSART, stop_bits); + usart_set_stopbits(uart, stop_bits); switch (coding->bParityType) { case USB_CDC_NO_PARITY: default: - usart_set_parity(USBUSART, USART_PARITY_NONE); + usart_set_parity(uart, USART_PARITY_NONE); break; case USB_CDC_ODD_PARITY: - usart_set_parity(USBUSART, USART_PARITY_ODD); + usart_set_parity(uart, USART_PARITY_ODD); break; case USB_CDC_EVEN_PARITY: - usart_set_parity(USBUSART, USART_PARITY_EVEN); + usart_set_parity(uart, USART_PARITY_EVEN); break; } - usart_enable(USBUSART); + usart_enable(uart); +} + +void aux_serial_set_encoding(const usb_cdc_line_coding_s *const coding) +{ +#ifndef PLATFORM_MULTI_UART + aux_serial_setup_uart(AUX_UART, coding); +#else + aux_serial_setup_uart(AUX_UART1, coding); + aux_serial_setup_uart(AUX_UART2, coding); +#endif } void aux_serial_get_encoding(usb_cdc_line_coding_s *const coding) { - coding->dwDTERate = usart_get_baudrate(USBUSART); + coding->dwDTERate = usart_get_baudrate(AUX_UART); - switch (usart_get_stopbits(USBUSART)) { + switch (usart_get_stopbits(AUX_UART)) { case USART_STOPBITS_1: coding->bCharFormat = USB_CDC_1_STOP_BITS; break; -#if !defined(LM4F) +#ifndef LM4F /* * Only include this back mapping on non-Tiva-C platforms as USART_STOPBITS_1 and * USART_STOPBITS_1_5 are the same thing on LM4F @@ -314,7 +368,7 @@ void aux_serial_get_encoding(usb_cdc_line_coding_s *const coding) break; } - switch (usart_get_parity(USBUSART)) { + switch (usart_get_parity(AUX_UART)) { case USART_PARITY_NONE: default: coding->bParityType = USB_CDC_NO_PARITY; @@ -327,7 +381,7 @@ void aux_serial_get_encoding(usb_cdc_line_coding_s *const coding) break; } - const uint32_t data_bits = usart_get_databits(USBUSART); + const uint32_t data_bits = usart_get_databits(AUX_UART); if (coding->bParityType == USB_CDC_NO_PARITY) coding->bDataBits = data_bits; else @@ -472,6 +526,7 @@ static void aux_serial_dma_receive_isr(const uint8_t usart_irq, const uint8_t dm nvic_enable_irq(usart_irq); } +#ifndef PLATFORM_MULTI_UART #if defined(USBUSART_ISR) void USBUSART_ISR(void) { @@ -504,6 +559,17 @@ void USBUSART2_ISR(void) #endif } #endif +#else +void AUX_UART1_ISR(void) +{ + aux_serial_receive_isr(AUX_UART1, AUX_UART_DMA_RX_IRQ); +} + +void AUX_UART2_ISR(void) +{ + aux_serial_receive_isr(AUX_UART2, AUX_UART_DMA_RX_IRQ); +} +#endif #if defined(USBUSART_DMA_TX_ISR) void USBUSART_DMA_TX_ISR(void) diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index 18b9dbfe31a..ac9a9b76e48 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -348,7 +348,14 @@ static void debug_serial_append_char(const char c) size_t debug_serial_debug_write(const char *buf, const size_t len) { - if (nvic_get_active_irq(USB_IRQ) || nvic_get_active_irq(USBUSART_IRQ) || nvic_get_active_irq(USBUSART_DMA_RX_IRQ)) + if (nvic_get_active_irq(USB_IRQ) || +#ifndef PLATFORM_MULTI_UART + nvic_get_active_irq(USBUSART_IRQ) || nvic_get_active_irq(USBUSART_DMA_RX_IRQ) +#else + nvic_get_active_irq(AUX_UART1_IRQ) || nvic_get_active_irq(AUX_UART2_IRQ) || + nvic_get_active_irq(AUX_UART_DMA_RX_IRQ) +#endif + ) return 0; CM_ATOMIC_CONTEXT(); From 168a2f29d00a9105117eda3c4c23dee465785349 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 4 Nov 2025 14:16:17 +0000 Subject: [PATCH 013/247] bmp-v3: Added a linker script for the platform --- src/platforms/bmp-v3/bmp-v3.ld | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/platforms/bmp-v3/bmp-v3.ld diff --git a/src/platforms/bmp-v3/bmp-v3.ld b/src/platforms/bmp-v3/bmp-v3.ld new file mode 100644 index 00000000000..56782df14b6 --- /dev/null +++ b/src/platforms/bmp-v3/bmp-v3.ld @@ -0,0 +1,42 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * Copyright (C) 2024-2025 1BitSquared + * Modified by Rachel Mant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 2M + /* + * We actually have 786KiB available, but we want to be in the main 512KiB area + * that optionally runs ECC. We're not using ECC, however this is one large 8x64KiB + * block that is contiguous, marked NS, and behaves the way we expect it to. + * + * NB: SRAM on this part is mapped as: + * SRAM1: 0x20000000 - 0x20030000 + * SRAM2: 0x20030000 - 0x20080000 + * SRAM3: 0x20080000 - 0x200c0000 + * + * SRAM2 is erased when the system is reset, all other SRAMs retain their contents. + */ + ram (rwx) : ORIGIN = 0x20080000, LENGTH = 512K +} + +/* Include the platform common linker script. */ +INCLUDE ../common/blackmagic.ld From 707835f5e4aa605e2bda9fb0d0915f2672b91e7a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 10 Nov 2025 20:56:24 +0000 Subject: [PATCH 014/247] common/aux_serial: Built out DMA configuration for the STM32U5 as it's quite different to the other platforms --- src/platforms/bmp-v3/platform.h | 12 ++--- src/platforms/common/aux_serial.c | 75 ++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 92f0fb6fbbd..9663e89b424 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -225,14 +225,14 @@ #define AUX_UART2_TX_PIN GPIO6 #define AUX_UART2_RX_PIN GPIO7 -#define USBUSART_DMA_BUS GPDMA1 -#define USBUSART_DMA_CLK RCC_GPDMA1 -#define USBUSART_DMA_TX_CHAN DMA_CHANNEL0 -#define USBUSART_DMA_RX_CHAN DMA_CHANNEL1 +#define AUX_UART_DMA_BUS GPDMA1 +#define AUX_UART_DMA_CLK RCC_GPDMA1 +#define AUX_UART_DMA_TX_CHAN DMA_CHANNEL0 +#define AUX_UART_DMA_RX_CHAN DMA_CHANNEL1 #define AUX_UART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ -#define USBUSART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) +#define AUX_UART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) #define AUX_UART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ -#define USBUSART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) +#define AUX_UART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) /* Use TIM5 Input 2 (from PA1/SWO) for Manchester data recovery */ #define SWO_TIM TIM5 diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index cb1a6ffd23d..bea5aaa600a 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -59,6 +59,12 @@ static volatile uint8_t aux_serial_led_state = 0; #define DMA_MSIZE_8BIT DMA_SxCR_MSIZE_8BIT #define DMA_PL_HIGH DMA_SxCR_PL_HIGH #define DMA_CGIF DMA_ISR_FLAGS +#elif defined(STM32U5) +#define DMA_PL_HIGH DMA_CxCR_PRIO_HIGH +#define DMA_CGIF DMA_ISR_FLAGS +#define USBUSART_DMA_BUS AUX_UART_DMA_BUS +#define USBUSART_DMA_TX_CHAN AUX_UART_DMA_TX_CHAN +#define USBUSART_DMA_RX_CHAN AUX_UART_DMA_RX_CHAN #else #define DMA_PSIZE_8BIT DMA_CCR_PSIZE_8BIT #define DMA_MSIZE_8BIT DMA_CCR_MSIZE_8BIT @@ -94,6 +100,16 @@ static uintptr_t active_uart = 0U; #define AUX_UART active_uart #endif +#ifdef STM32U5 +/* NOLINTBEGIN(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ +/* Defines a linked list of things to be done at the completion of RX DMA */ +static const uintptr_t aux_serial_dma_receive_ll[] = { + /* This controls the next RX destination address to use */ + (uintptr_t)aux_serial_receive_buffer, +}; +/* NOLINTEND(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ +#endif + void bmd_usart_set_baudrate(const uintptr_t usart, const uint32_t baud_rate) { /* If the new baud rate is out of supported range for a given USART, then keep previous */ @@ -152,7 +168,11 @@ void aux_serial_init(void) rcc_periph_clock_enable(AUX_UART1_CLK); rcc_periph_clock_enable(AUX_UART2_CLK); #endif +#ifndef PLATFORM_MULTI_UART rcc_periph_clock_enable(USBUSART_DMA_CLK); +#else + rcc_periph_clock_enable(AUX_UART_DMA_CLK); +#endif /* Setup UART parameters */ UART_PIN_SETUP(); @@ -182,6 +202,7 @@ void aux_serial_init(void) #ifndef PLATFORM_MULTI_UART dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)&USBUSART_TDR); #endif +#ifndef STM32U5 dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PSIZE_8BIT); dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_MSIZE_8BIT); @@ -195,9 +216,26 @@ void aux_serial_init(void) #else dma_set_read_from_memory(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); #endif +#else +#ifndef PLATFORM_MULTI_UART + dma_set_destination_address(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, (uintptr_t)&USBUSART_TDR); +#endif + dma_enable_source_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN); + dma_disable_destination_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN); + dma_set_source_width(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, DMA_CxTR1_DW_BYTE); + dma_set_destination_width(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, DMA_CxTR1_DW_BYTE); + + dma_set_priority(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, DMA_PL_HIGH); + dma_enable_interrupts(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, DMA_TCIF); + dma_set_transfer_complete_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_TRANSFER_COMPLETE_MODE_BLOCK); + dma_set_hardware_request(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN); + dma_set_destination_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN); + dma_set_burst_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN); +#endif /* Setup USART RX DMA */ dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#ifndef STM32U5 #ifndef PLATFORM_MULTI_UART dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uintptr_t)&USBUSART_RDR); #endif @@ -217,6 +255,26 @@ void aux_serial_init(void) dma_enable_direct_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #else dma_set_read_from_peripheral(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#endif +#else +#ifndef PLATFORM_MULTI_UART + dma_set_source_address(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, (uintptr_t)&USBUSART_RDR); +#endif + dma_set_destination_address(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, (uintptr_t)aux_serial_receive_buffer); + dma_set_number_of_data(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, AUX_UART_BUFFER_SIZE); + dma_disable_source_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); + dma_enable_destination_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); + dma_setup_linked_list(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, aux_serial_dma_receive_ll, DMA_CxLLR_UDA); + dma_set_source_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); + dma_set_destination_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); + + dma_set_priority(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_PL_HIGH); + dma_enable_interrupts(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_HTIF | DMA_TCIF); + dma_set_transfer_complete_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_TRANSFER_COMPLETE_MODE_BLOCK); + // dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, ); + dma_set_hardware_request(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); + dma_set_source_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); + dma_set_burst_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); #endif dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); @@ -247,10 +305,19 @@ void aux_serial_init(void) nvic_enable_irq(AUX_UART_DMA_RX_IRQ); #endif - /* Finally enable the USART */ + /* Finally enable the USART(s) */ +#ifndef PLATFORM_MULTI_UART usart_enable(USBUSART); usart_enable_tx_dma(USBUSART); usart_enable_rx_dma(USBUSART); +#else + usart_enable(AUX_UART1); + /* Don't enable UART2 though because it has switchable TX/RX and must be handled differently */ + usart_enable_tx_dma(AUX_UART1); + usart_enable_rx_dma(AUX_UART1); + usart_enable_tx_dma(AUX_UART2); + usart_enable_rx_dma(AUX_UART2); +#endif } #elif defined(LM4F) void aux_serial_init(void) @@ -428,7 +495,11 @@ void aux_serial_switch_transmit_buffers(void) { /* Make the buffer we've been filling the active DMA buffer, and swap to the other */ char *const current_buffer = aux_serial_current_transmit_buffer(); +#ifndef STM32U5 dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)current_buffer); +#else + dma_set_source_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)current_buffer); +#endif dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, aux_serial_transmit_buffer_consumed); dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); @@ -519,7 +590,7 @@ static void aux_serial_dma_receive_isr(const uint8_t usart_irq, const uint8_t dm { nvic_disable_irq(usart_irq); - /* Clear flags and transmit a packet*/ + /* Clear flags and transmit a packet */ dma_clear_interrupt_flags(USBUSART_DMA_BUS, dma_rx_channel, DMA_CGIF); debug_serial_run(); From b554f1d07828fd7c1e6321657487e5f9db0d0627 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 11 Nov 2025 13:47:34 +0000 Subject: [PATCH 015/247] ch32vx: Gate the read Flash size function properly on whether DEBUG_INFO does anything to avoid unused warnings --- src/target/ch32vx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/ch32vx.c b/src/target/ch32vx.c index 288820fe070..f16a9bb2694 100644 --- a/src/target/ch32vx.c +++ b/src/target/ch32vx.c @@ -58,10 +58,12 @@ const command_s ch32vx_cmd_list[] = { {NULL, NULL, NULL}, }; +#ifndef DEBUG_INFO_IS_NOOP static size_t ch32vx_read_flash_size(target_s *const target) { return target_mem32_read16(target, CH32VX_ESIG_FLASH_CAP) * 1024U; } +#endif static void ch32vx_read_uid(target_s *const target, uint8_t *const uid) { From c829e3fe0ba8a255fe5639aa916c4fd171ea84e8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 12 Nov 2025 02:04:19 +0000 Subject: [PATCH 016/247] common/swo_uart: Built out DMA configuration for the STM32U5 as it's quite different to the other platforms --- src/platforms/bmp-v3/platform.h | 11 +++---- src/platforms/common/stm32/swo_uart.c | 43 +++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 9663e89b424..afb3ccf8d9f 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -259,11 +259,12 @@ #define SWO_UART_RX_PIN SWO_PIN #define SWO_UART_PIN_AF GPIO_AF8 -#define SWO_DMA_BUS GPDMA1 -#define SWO_DMA_CLK RCC_GPDMA1 -#define SWO_DMA_CHAN DMA_CHANNEL2 -#define SWO_DMA_IRQ NVIC_GPDMA1_CH2_IRQ -#define SWO_DMA_ISR(x) gpdma1_ch2_isr(x) +#define SWO_DMA_BUS GPDMA1 +#define SWO_DMA_CLK RCC_GPDMA1 +#define SWO_DMA_CHAN DMA_CHANNEL2 +#define SWO_DMA_IRQ NVIC_GPDMA1_CH2_IRQ +#define SWO_DMA_ISR(x) gpdma1_ch2_isr(x) +#define SWO_DMA_REQ_SRC GPDMA1_CxTR2_REQSEL_UART4_RX #define SET_RUN_STATE(state) running_status = (state) #define SET_IDLE_STATE(state) gpio_set_val(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN, state) diff --git a/src/platforms/common/stm32/swo_uart.c b/src/platforms/common/stm32/swo_uart.c index 735a2a4ee5f..0344cf6e1d0 100644 --- a/src/platforms/common/stm32/swo_uart.c +++ b/src/platforms/common/stm32/swo_uart.c @@ -45,7 +45,7 @@ #include #include -#if defined(DMA_STREAM0) +#ifdef DMA_STREAM0 #define dma_channel_reset(dma, channel) dma_stream_reset(dma, channel) #define dma_enable_channel(dma, channel) dma_enable_stream(dma, channel) #define dma_disable_channel(dma, channel) dma_disable_stream(dma, channel) @@ -53,12 +53,24 @@ #define DMA_PSIZE_8BIT DMA_SxCR_PSIZE_8BIT #define DMA_MSIZE_8BIT DMA_SxCR_MSIZE_8BIT #define DMA_PL_HIGH DMA_SxCR_PL_HIGH +#elif defined(STM32U5) +#define DMA_PL_HIGH DMA_CxCR_PRIO_HIGH #else #define DMA_PSIZE_8BIT DMA_CCR_PSIZE_8BIT #define DMA_MSIZE_8BIT DMA_CCR_MSIZE_8BIT #define DMA_PL_HIGH DMA_CCR_PL_HIGH #endif +#ifdef STM32U5 +/* NOLINTBEGIN(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ +/* Defines a linked list of things to be done at the completion of DMA */ +static uintptr_t swo_uart_dma_ll[] = { + /* This controls the next RX destination address to use, however is only known at runtime */ + 0U, +}; +/* NOLINTEND(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ +#endif + void swo_uart_init(const uint32_t baudrate) { /* Ensure required peripherals are spun up */ @@ -87,20 +99,33 @@ void swo_uart_init(const uint32_t baudrate) /* Set up DMA channel and tell the DMA subsystem where to put the data received from the UART */ dma_channel_reset(SWO_DMA_BUS, SWO_DMA_CHAN); +#ifndef STM32U5 // NOLINTNEXTLINE(clang-diagnostic-pointer-to-int-cast,performance-no-int-to-ptr) dma_set_peripheral_address(SWO_DMA_BUS, SWO_DMA_CHAN, (uintptr_t)&SWO_UART_DR); // NOLINTNEXTLINE(clang-diagnostic-pointer-to-int-cast) dma_set_memory_address(SWO_DMA_BUS, SWO_DMA_CHAN, (uintptr_t)swo_buffer); +#else + // NOLINTNEXTLINE(clang-diagnostic-pointer-to-int-cast,performance-no-int-to-ptr) + dma_set_source_address(SWO_DMA_BUS, SWO_DMA_CHAN, (uintptr_t)&SWO_UART_DR); + // NOLINTNEXTLINE(clang-diagnostic-pointer-to-int-cast) + dma_set_destination_address(SWO_DMA_BUS, SWO_DMA_CHAN, (uintptr_t)swo_buffer); +#endif /* Define the buffer length and configure this as a peripheral -> memory transfer */ dma_set_number_of_data(SWO_DMA_BUS, SWO_DMA_CHAN, SWO_BUFFER_SIZE); -#if defined(DMA_STREAM0) +#ifdef DMA_STREAM0 dma_set_transfer_mode(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_SxCR_DIR_PERIPHERAL_TO_MEM); dma_channel_select(SWO_DMA_BUS, SWO_DMA_CHAN, SWO_DMA_TRG); dma_set_dma_flow_control(SWO_DMA_BUS, SWO_DMA_CHAN); dma_enable_direct_mode(SWO_DMA_BUS, SWO_DMA_CHAN); +#elif defined(STM32U5) + dma_request_select(SWO_DMA_BUS, SWO_DMA_CHAN, SWO_DMA_REQ_SRC); + dma_set_hardware_request(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_set_source_flow_control(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_set_burst_flow_control(SWO_DMA_BUS, SWO_DMA_CHAN); #else dma_set_read_from_peripheral(SWO_DMA_BUS, SWO_DMA_CHAN); #endif +#ifndef STM32U5 dma_enable_memory_increment_mode(SWO_DMA_BUS, SWO_DMA_CHAN); /* Define it as being bytewise into a circular buffer with high priority */ dma_set_peripheral_size(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_PSIZE_8BIT); @@ -110,6 +135,20 @@ void swo_uart_init(const uint32_t baudrate) /* Enable the 50% and 100% interrupts so we can update the buffer counters to initiate the USB half of the picture */ dma_enable_transfer_complete_interrupt(SWO_DMA_BUS, SWO_DMA_CHAN); dma_enable_half_transfer_interrupt(SWO_DMA_BUS, SWO_DMA_CHAN); +#else + dma_disable_source_increment_mode(SWO_DMA_BUS, SWO_DMA_CHAN); + dma_enable_destination_increment_mode(SWO_DMA_BUS, SWO_DMA_CHAN); + /* Define it as being bytewise into a circular buffer with high priority */ + dma_set_source_width(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_CxTR1_DW_BYTE); + dma_set_destination_width(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_CxTR1_DW_BYTE); + dma_set_priority(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_PL_HIGH); + /* Set up the address of the buffer to loop back to each time DMA completes */ + swo_uart_dma_ll[0] = (uintptr_t)swo_buffer; + dma_setup_linked_list(SWO_DMA_BUS, SWO_DMA_CHAN, swo_uart_dma_ll, DMA_CxLLR_UDA); + /* Enable the 50% and 100% interrupts so we can update the buffer counters to initiate the USB half of the picture */ + dma_enable_interrupts(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_TCIF | DMA_HTIF); + dma_set_transfer_complete_mode(SWO_DMA_BUS, SWO_DMA_CHAN, DMA_TRANSFER_COMPLETE_MODE_BLOCK); +#endif /* Enable DMA trigger on receive for the UART */ usart_enable_rx_dma(SWO_UART); From 1ffd45e0c2d3abc5faee70092fd8ebb180f2fe67 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 17 Nov 2025 03:42:01 +0000 Subject: [PATCH 017/247] common/swo_manchester: Handle the STM32U5 timer input multiplexing properly --- src/platforms/bmp-v3/platform.h | 2 ++ src/platforms/common/stm32/swo_manchester.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index afb3ccf8d9f..d0d7e4038c4 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -239,6 +239,8 @@ #define SWO_TIM_CLK_EN() #define SWO_TIM_IRQ NVIC_TIM5_IRQ #define SWO_TIM_ISR(x) tim5_isr(x) +#define SWO_IC_IN_CH TIM_IC2 /* Input channel 2 */ +#define SWO_IC_IN_CH_SEL TIM_IC_SEL_IN0 /* TIM5_CH2 from the input mux */ #define SWO_IC_IN TIM_IC_IN_TI2 #define SWO_IC_RISING TIM_IC2 #define SWO_CC_RISING TIM5_CCR2 diff --git a/src/platforms/common/stm32/swo_manchester.c b/src/platforms/common/stm32/swo_manchester.c index ac780d41e54..55136c6bb66 100644 --- a/src/platforms/common/stm32/swo_manchester.c +++ b/src/platforms/common/stm32/swo_manchester.c @@ -4,7 +4,7 @@ * Copyright (C) 2012 Black Sphere Technologies Ltd. * Written by Gareth McMullin * Modified by Uwe Bonnes - * Copyright (C) 2024 1BitSquared + * Copyright (C) 2024-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -77,6 +77,11 @@ void swo_manchester_init(void) gpio_set_mode(SWO_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SWO_PIN); #endif +#ifdef STM32U5 + /* Route the correct input signal to the input channel in the input multiplexer */ + timer_ic_input_selection(SWO_TIM, SWO_IC_IN_CH, SWO_IC_IN_CH_SEL); +#endif + /* * Start setting the timer block up by picking a pair of cross-linked capture channels suitable for the input, * and configure them to consume the input channel for the SWO pin. We use one in rising edge mode and the From 967623bef812b3616b7de1ff07dd7cc9bec4ff34 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 17 Nov 2025 04:06:02 +0000 Subject: [PATCH 018/247] meson: Added the new BMPv3 platform to the build system, integrating a suitable cross file too --- cross-file/bmp-v3.ini | 25 +++++++++++ meson_options.txt | 1 + src/platforms/bmp-v3/meson.build | 75 ++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 cross-file/bmp-v3.ini create mode 100644 src/platforms/bmp-v3/meson.build diff --git a/cross-file/bmp-v3.ini b/cross-file/bmp-v3.ini new file mode 100644 index 00000000000..c6cc2c319eb --- /dev/null +++ b/cross-file/bmp-v3.ini @@ -0,0 +1,25 @@ +# This a cross-file for the Black Magic Probe v3, providing sane default options for it. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v3' +targets = 'cortexar,cortexm,riscv32,riscv64,apollo3,at32f4,ch32,ch32v,ch579,efm,gd32,hc32,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' +rtt_support = true +bmd_bootloader = true +enable_riscv_accel = true diff --git a/meson_options.txt b/meson_options.txt index 9129524f380..5f4e5253a03 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,6 +8,7 @@ option( 'blackpill-f401ce', 'blackpill-f411ce', 'bluepill', + 'bmp-v3', 'ctxlink', 'f072', 'f3', diff --git a/src/platforms/bmp-v3/meson.build b/src/platforms/bmp-v3/meson.build new file mode 100644 index 00000000000..0f6fdebe3f6 --- /dev/null +++ b/src/platforms/bmp-v3/meson.build @@ -0,0 +1,75 @@ +# This file is part of the Black Magic Debug project. +# +# Copyright (C) 2025 1BitSquared +# Written by Rachel Mant +# +# 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. + +probe_bmp_includes = include_directories('.') + +probe_bmp_sources = files() + +probe_bmp_args = [ + '-DDFU_SERIAL_LENGTH=9', + '-DBLACKMAGICPROBE_V3', +] + +trace_protocol = get_option('trace_protocol') +probe_bmp_args += [f'-DSWO_ENCODING=@trace_protocol@'] +probe_bmp_dependencies = [platform_stm32_swo] +if trace_protocol in ['1', '3'] + probe_bmp_dependencies += platform_stm32_swo_manchester +endif +if trace_protocol in ['2', '3'] + probe_bmp_dependencies += platform_stm32_swo_uart +endif + +probe_bmp_common_link_args = [ + '-L@0@'.format(meson.current_source_dir()), + '-T@0@'.format('bmp-v3.ld'), +] + +probe_bmp_link_args = [ + #'-Wl,-Ttext=0x8002000', +] + +probe_host = declare_dependency( + include_directories: probe_bmp_includes, + sources: probe_bmp_sources, + compile_args: probe_bmp_args, + link_args: probe_bmp_common_link_args + probe_bmp_link_args, + dependencies: [platform_common, platform_stm32u5, probe_bmp_dependencies], +) + +summary( + { + 'Name': 'Black Magic Probe v3', + 'Platform': 'STM32U5', + 'Bootloader': 'Black Magic Debug Bootloader', + 'Load Address': '0x8002000', + }, + section: 'Probe', +) From 83ed674fc631099cb1fe60a330781447b8727cb9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 17 Nov 2025 04:29:06 +0000 Subject: [PATCH 019/247] bmp-v3: Begun implementing the platform-specific functions --- src/platforms/bmp-v3/meson.build | 2 +- src/platforms/bmp-v3/platform.c | 143 +++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/platforms/bmp-v3/platform.c diff --git a/src/platforms/bmp-v3/meson.build b/src/platforms/bmp-v3/meson.build index 0f6fdebe3f6..a0befd65ef3 100644 --- a/src/platforms/bmp-v3/meson.build +++ b/src/platforms/bmp-v3/meson.build @@ -30,7 +30,7 @@ probe_bmp_includes = include_directories('.') -probe_bmp_sources = files() +probe_bmp_sources = files('platform.c') probe_bmp_args = [ '-DDFU_SERIAL_LENGTH=9', diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c new file mode 100644 index 00000000000..1122c660a13 --- /dev/null +++ b/src/platforms/bmp-v3/platform.c @@ -0,0 +1,143 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 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 "platform.h" + +#include +#include + +void platform_nrst_set_val(bool assert) +{ + gpio_set(TMS_PORT, TMS_PIN); + gpio_set_val(NRST_PORT, NRST_PIN, assert); + + if (assert) { + for (volatile size_t i = 0; i < 10000U; ++i) + continue; + } +} + +bool platform_nrst_get_val(void) +{ + return gpio_get(NRST_SENSE_PORT, NRST_SENSE_PIN) != 0; +} + +bool platform_target_get_power(void) +{ + return !gpio_get(TPWR_EN_PORT, TPWR_EN_PIN); +} + +void platform_target_clk_output_enable(bool enable) +{ + /* If we're switching to tristate mode, first convert the processor pin to an input */ + if (!enable) + gpio_mode_setup(TCK_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TCK_PIN); + /* Reconfigure the logic levelt translator */ + gpio_set_val(TCK_DIR_PORT, TCK_DIR_PIN, enable); + /* If we're switching back out of tristate mode, we're now safe to make the processor pin an output again */ + if (enable) + gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); +} + +bool platform_spi_init(const spi_bus_e bus) +{ + /* Test to see which bus we're supposed to be initialising */ + if (bus == SPI_BUS_EXTERNAL) { + rcc_set_peripheral_clk_sel(EXT_SPI, RCC_CCIPR_SPIxSEL_PCLKx); + rcc_periph_clock_enable(RCC_SPI2); + gpio_set_af(EXT_SPI_SCLK_PORT, GPIO_AF5, EXT_SPI_SCLK_PIN); + gpio_mode_setup(EXT_SPI_SCLK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, EXT_SPI_SCLK_PIN); + gpio_set_af(EXT_SPI_POCI_PORT, GPIO_AF5, EXT_SPI_POCI_PIN); + gpio_mode_setup(EXT_SPI_POCI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, EXT_SPI_POCI_PIN); + gpio_set_af(EXT_SPI_PICO_PORT, GPIO_AF5, EXT_SPI_PICO_PIN); + gpio_mode_setup(EXT_SPI_PICO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, EXT_SPI_PICO_PIN); + gpio_set(TCK_DIR_PORT, TCK_DIR_PIN); + gpio_set(TMS_DIR_PORT, TMS_DIR_PIN); + } else + /* For now, we only support the external SPI bus */ + return false; + + const uintptr_t controller = EXT_SPI; + spi_init_master(controller, SPI_CFG1_MBR_DIV16, SPI_CFG2_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CFG2_CPHA_CLK_TRANSITION_1, + SPI_CFG1_DSIZE_8BIT, SPI_CFG2_MSBFIRST, SPI_CFG2_SP_MOTOROLA); + spi_enable(controller); + return true; +} + +bool platform_spi_deinit(spi_bus_e bus) +{ + if (bus != SPI_BUS_EXTERNAL) + return false; + + spi_disable(EXT_SPI); + + if (bus == SPI_BUS_EXTERNAL) { + rcc_periph_clock_disable(RCC_SPI2); + gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); + gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); + platform_target_clk_output_enable(false); + } + return true; +} + +bool platform_spi_chip_select(const uint8_t device_select) +{ + const uint8_t device = device_select & 0x7fU; + const bool select = !(device_select & 0x80U); + uintptr_t port; + uint16_t pin; + switch (device) { + /* + case SPI_DEVICE_INT_FLASH: + port = AUX_PORT; + pin = AUX_FCS; + break; + */ + case SPI_DEVICE_EXT_FLASH: + port = EXT_SPI_CS_PORT; + pin = EXT_SPI_CS_PIN; + break; + default: + return false; + } + gpio_set_val(port, pin, select); + return true; +} + +uint8_t platform_spi_xfer(const spi_bus_e bus, const uint8_t value) +{ + if (bus != SPI_BUS_EXTERNAL) + return 0xffU; + return spi_xfer8(EXT_SPI, value); +} From ca1da303f4b07b22849bfa4a58ff5c8929dabb2a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 18 Nov 2025 04:33:08 +0000 Subject: [PATCH 020/247] native: Removed a duplicated include line --- src/platforms/native/platform.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index bd8250c4ef0..6fc698fe711 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -27,7 +27,6 @@ #include "morse.h" #include -#include #include #include #include From 634f916ac1b60ea45d18475abbea30249dfa1a3d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 18 Nov 2025 04:45:49 +0000 Subject: [PATCH 021/247] native: Fixed up the copyright header --- src/platforms/native/platform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index 6fc698fe711..e95cdc5dfae 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -1,8 +1,11 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2022-2025 1BitSquared * Written by Gareth McMullin + * Modified by Piotr Esden-Tempski + * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 09f7e65b916d95cc97147a4ef23d618fb40d55b9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 18 Nov 2025 16:00:24 -0800 Subject: [PATCH 022/247] bmp-v3: Defined the first part of platform initialisation, bringing up USB, timing, and setting up the correct vector table address --- src/platforms/bmp-v3/platform.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 1122c660a13..fbe65f042d4 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -33,10 +33,26 @@ #include "general.h" #include "platform.h" +#include "usb.h" +#include +#include #include #include +void platform_init(void) +{ + /* Enable the FPU as we're in hard float mode and defined it to the compiler */ + SCB_CPACR |= SCB_CPACR_CP10_FULL | SCB_CPACR_CP11_FULL; + + /* Set up the NVIC vector table for the firmware */ + SCB_VTOR = (uintptr_t)&vector_table; // NOLINT(clang-diagnostic-pointer-to-int-cast, performance-no-int-to-ptr) + + /* Bring up timing and USB */ + platform_timing_init(); + blackmagic_usb_init(); +} + void platform_nrst_set_val(bool assert) { gpio_set(TMS_PORT, TMS_PIN); From ec70fa81f0ef7f8792a930c8ad76186bf9de392b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 20 Nov 2025 09:30:56 +0000 Subject: [PATCH 023/247] stlinkv3: Removed a duplicated include line --- src/platforms/stlinkv3/platform.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/stlinkv3/platform.c b/src/platforms/stlinkv3/platform.c index 4c1befefc10..18403bda17c 100644 --- a/src/platforms/stlinkv3/platform.c +++ b/src/platforms/stlinkv3/platform.c @@ -38,7 +38,6 @@ #include #include #include -#include #include uint16_t srst_pin; From c0c6cd37bf37de78a6d03d97869d307028b89489 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 20 Nov 2025 09:31:21 +0000 Subject: [PATCH 024/247] native: Added some `const` to the ADC code the updated locm3 allows --- src/platforms/native/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index e95cdc5dfae..cb505a649cf 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -393,7 +393,7 @@ uint32_t platform_target_voltage_sense(void) if (hwversion == 0) return 0; - uint8_t channel = 8; + const uint8_t channel = 8U; adc_set_regular_sequence(ADC1, 1, &channel); adc_start_conversion_direct(ADC1); From 0bd5e0b171e06247e07b6381c656de83d050a1fa Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 20 Nov 2025 09:46:28 +0000 Subject: [PATCH 025/247] bmp-v3: Implemented target voltage readout using the ADCs and tpwr sense pin --- src/platforms/bmp-v3/platform.c | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index fbe65f042d4..21910e4bb63 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -38,8 +38,11 @@ #include #include #include +#include #include +static void adc_init(void); + void platform_init(void) { /* Enable the FPU as we're in hard float mode and defined it to the compiler */ @@ -48,11 +51,41 @@ void platform_init(void) /* Set up the NVIC vector table for the firmware */ SCB_VTOR = (uintptr_t)&vector_table; // NOLINT(clang-diagnostic-pointer-to-int-cast, performance-no-int-to-ptr) + /* Power up the analog domain */ + pwr_enable_vdda(); + + /* Bring up the ADC */ + adc_init(); + /* Bring up timing and USB */ platform_timing_init(); blackmagic_usb_init(); } +static void adc_init(void) +{ + /* + * Configure the ADC/DAC mux and bring the peripheral clock up, knocking it down from 160MHz to 40MHz + * to bring it into range for the peripheral per the f(ADC) characteristic of 5MHz <= f(ADC) <= 55MHz + */ + rcc_set_peripheral_clk_sel(ADC1, RCC_CCIPR3_ADCDACSEL_SYSCLK); + rcc_periph_clock_enable(RCC_ADC1_2); + adc_ungate_power(ADC1); + adc_set_common_prescaler(ADC12_CCR_PRESC_DIV4); + + gpio_mode_setup(TPWR_SENSE_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, TPWR_SENSE_PIN); + + adc_power_off(ADC1); + adc_set_single_conversion_mode(ADC1); + adc_disable_external_trigger_regular(ADC1); + adc_set_sample_time(ADC1, 17U, ADC12_SMPR_SMP_68CYC); + adc_channel_preselect(ADC1, 17U); + adc_enable_temperature_sensor(); + adc_calibrate_linearity(ADC1); + adc_calibrate(ADC1); + adc_power_on(ADC1); +} + void platform_nrst_set_val(bool assert) { gpio_set(TMS_PORT, TMS_PIN); @@ -74,6 +107,36 @@ bool platform_target_get_power(void) return !gpio_get(TPWR_EN_PORT, TPWR_EN_PIN); } +uint32_t platform_target_voltage_sense(void) +{ + /* + * Returns the voltage in tenths of a volt (so 33 means 3.3V). + * BMPv3 uses ADC1_IN17 for target power sense + */ + const uint8_t channel = 17U; + adc_set_regular_sequence(ADC1, 1U, &channel); + adc_clear_eoc(ADC1); + + adc_start_conversion_regular(ADC1); + + /* Wait for end of conversion */ + while (!adc_eoc(ADC1)) + continue; + + const uint32_t voltage = adc_read_regular(ADC1); /* 0-16383 */ + return (voltage * 99U) / 32767U; +} + +const char *platform_target_voltage(void) +{ + static char result[] = "0.0V"; + uint32_t voltage = platform_target_voltage_sense(); + result[0] = (char)('0' + (voltage / 10U)); + result[2] = (char)('0' + (voltage % 10U)); + + return result; +} + void platform_target_clk_output_enable(bool enable) { /* If we're switching to tristate mode, first convert the processor pin to an input */ From d6f573f807f10bb2ee6cbd90d3c5f7b43ce34b00 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 20 Nov 2025 09:55:59 +0000 Subject: [PATCH 026/247] bmp-v3: Implemented bringup of the various core peripherals and clocking --- src/platforms/bmp-v3/platform.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 21910e4bb63..4727721ca34 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -54,6 +54,17 @@ void platform_init(void) /* Power up the analog domain */ pwr_enable_vdda(); + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_OTGFS); + rcc_periph_clock_enable(RCC_CRS); + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_GPIOH); + /* Make sure to power up the timer used for trace */ + rcc_periph_clock_enable(RCC_TIM5); + rcc_periph_clock_enable(RCC_CRC); + /* Bring up the ADC */ adc_init(); From c2e966286ac0081410a8fccfdbd37c1c937d610b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 21 Nov 2025 02:51:34 +0000 Subject: [PATCH 027/247] bmp-v3: Implemented the boot request logic --- src/platforms/bmp-v3/platform.c | 20 ++++++++++++++++++++ src/platforms/bmp-v3/platform.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 4727721ca34..f591b7c7796 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -37,9 +37,11 @@ #include #include +#include #include #include #include +#include static void adc_init(void); @@ -148,6 +150,24 @@ const char *platform_target_voltage(void) return result; } +void platform_request_boot(void) +{ + /* Disconnect USB cable */ + usbd_disconnect(usbdev, true); + gpio_mode_setup(USB_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, USB_DP_PIN | USB_DM_PIN); + /* Make sure we drive the USB reset condition for at least 10ms */ + while (!(STK_CSR & STK_CSR_COUNTFLAG)) + continue; + for (size_t count = 0U; count < 10U * SYSTICKMS; ++count) { + while (!(STK_CSR & STK_CSR_COUNTFLAG)) + continue; + } + + /* Drive boot request pin */ + gpio_mode_setup(BNT_BOOT_REQ_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BTN_BOOT_REQ_PIN); + gpio_clear(BNT_BOOT_REQ_PORT, BTN_BOOT_REQ_PIN); +} + void platform_target_clk_output_enable(bool enable) { /* If we're switching to tristate mode, first convert the processor pin to an input */ diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index d0d7e4038c4..928f263b4d7 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -150,6 +150,9 @@ #define USB_VBUS_PORT GPIOA #define USB_VBUS_PIN GPIO9 +#define BNT_BOOT_REQ_PORT GPIOA +#define BTN_BOOT_REQ_PIN GPIO15 + #define LED0_PORT GPIOB #define LED0_PIN GPIO5 #define LED1_PORT GPIOB From 2f6d25b9536047c891c1191aae129f5854361e2b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 21 Nov 2025 03:06:08 +0000 Subject: [PATCH 028/247] bmp-v3: Implemented handling for the hardware version info --- src/platforms/bmp-v3/platform.c | 9 +++++++++ src/platforms/bmp-v3/platform.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index f591b7c7796..041c53df391 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -45,8 +45,12 @@ static void adc_init(void); +int hwversion = -1; + void platform_init(void) { + hwversion = 0; + /* Enable the FPU as we're in hard float mode and defined it to the compiler */ SCB_CPACR |= SCB_CPACR_CP10_FULL | SCB_CPACR_CP11_FULL; @@ -99,6 +103,11 @@ static void adc_init(void) adc_power_on(ADC1); } +int platform_hwversion(void) +{ + return hwversion; +} + void platform_nrst_set_val(bool assert) { gpio_set(TMS_PORT, TMS_PIN); diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 928f263b4d7..f366d287a63 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -45,6 +45,8 @@ #define PLATFORM_IDENT "v3 " +extern int hwversion; + /* * Important pin mappings for BMPv3: * From 010c15a57175a37d2509d6166eda90063752caf1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 21 Nov 2025 03:19:55 +0000 Subject: [PATCH 029/247] bmp-v3: Fleshed out GPIO configuration and bringup --- src/platforms/bmp-v3/platform.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 041c53df391..db2b7f0cca1 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -71,6 +71,12 @@ void platform_init(void) rcc_periph_clock_enable(RCC_TIM5); rcc_periph_clock_enable(RCC_CRC); + /* Setup GPIO ports */ + gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); + gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); + gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + /* Bring up the ADC */ adc_init(); From 8c0d5703033b59cf8a4ba16a5cdcfa793d4be45b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 21 Nov 2025 03:53:45 +0000 Subject: [PATCH 030/247] bmp-v3: Defined clocking for the platform --- src/platforms/bmp-v3/platform.c | 12 ++++ src/platforms/bmp-v3/rcc_clocking.h | 87 +++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/platforms/bmp-v3/rcc_clocking.h diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index db2b7f0cca1..7940dd4f90e 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -34,11 +34,14 @@ #include "general.h" #include "platform.h" #include "usb.h" +#include "rcc_clocking.h" #include #include +#include #include #include +#include #include #include #include @@ -50,6 +53,7 @@ int hwversion = -1; void platform_init(void) { hwversion = 0; + SCS_DEMCR |= SCS_DEMCR_VC_MON_EN; /* Enable the FPU as we're in hard float mode and defined it to the compiler */ SCB_CPACR |= SCB_CPACR_CP10_FULL | SCB_CPACR_CP11_FULL; @@ -57,8 +61,16 @@ void platform_init(void) /* Set up the NVIC vector table for the firmware */ SCB_VTOR = (uintptr_t)&vector_table; // NOLINT(clang-diagnostic-pointer-to-int-cast, performance-no-int-to-ptr) + /* Bring up the PLLs, set up HSI48 for USB, and set up the clock recovery system for that */ + rcc_clock_setup_pll(&rcc_hsi_config); + rcc_clock_setup_hsi48(); + crs_autotrim_usb_enable(); + /* Power up USB controller */ + pwr_enable_vddusb(); /* Power up the analog domain */ pwr_enable_vdda(); + /* Route HSI48 to the USB controller */ + rcc_set_iclk_clksel(RCC_CCIPR1_ICLKSEL_HSI48); /* Enable peripherals */ rcc_periph_clock_enable(RCC_OTGFS); diff --git a/src/platforms/bmp-v3/rcc_clocking.h b/src/platforms/bmp-v3/rcc_clocking.h new file mode 100644 index 00000000000..4af4cf75e0b --- /dev/null +++ b/src/platforms/bmp-v3/rcc_clocking.h @@ -0,0 +1,87 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 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. + */ + +/* This file provides the RCC clocking configuration for the BMPv3 platform. */ + +#ifndef PLATFORMS_BMP_V3_RCC_CLOCKING_H +#define PLATFORMS_BMP_V3_RCC_CLOCKING_H + +#include +#include +#include + +static struct rcc_pll_config rcc_hsi_config = { + /* Use PLL1 as our clock source, HSE unused */ + .sysclock_source = RCC_PLL1, + .hse_frequency = 0U, + /* Set the MSIS up to output 48MHz, which is the 3x the max in for the PLLs */ + .msis_range = RCC_MSI_RANGE_48MHZ, + .pll1 = + { + /* PLL1 is then set up to consume MSIS as input */ + .pll_source = RCC_PLLCFGR_PLLSRC_MSIS, + /* Divide 48MHz down to 16MHz as input to get the clock in range */ + .divm = 3U, + /* Multiply up to 320 MHz */ + .divn = 20U, + /* Make use of output R for the main system clock at 160MHz */ + .divr = 2U, + }, + .pll2 = + { + .pll_source = RCC_PLLCFGR_PLLSRC_NONE, + .divm = 0U, + }, + .pll3 = + { + .pll_source = RCC_PLLCFGR_PLLSRC_NONE, + .divm = 0U, + }, + /* SYSCLK is 160MHz, so no need to divide it down for AHB */ + .hpre = RCC_CFGR2_HPRE_NODIV, + /* Or for APB1 */ + .ppre1 = RCC_PPRE_NODIV, + /* Or for APB2 */ + .ppre2 = RCC_PPRE_NODIV, + /* APB3 is fed by SYSCLK too and may also run at 160MHz */ + .ppre3 = RCC_PPRE_NODIV, + /* We aren't using DSI, so let that be at defaults */ + .dpre = RCC_CFGR2_DPRE_DEFAULT, + /* Flash requires 4 wait states to access at 160MHz per RM0456 §7.3.3 Read access latency */ + .flash_waitstates = FLASH_ACR_LATENCY_4WS, + /* 1.2V -> 160MHz f(max), user the LDO to power everything as we don't have a SMPS in this package */ + .voltage_scale = PWR_VOS_SCALE_1, + .power_mode = PWR_SYS_LDO, +}; + +#endif /* PLATFORMS_BMP_V3_RCC_CLOCKING_H */ From 8768d5a1fbf3961aacd89fe6583577a725dfa1af Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 21 Nov 2025 03:54:13 +0000 Subject: [PATCH 031/247] bmp-v3: Built out the bootloader for the platform --- src/platforms/bmp-v3/bootloader.c | 154 ++++++++++++++++++++++++++++++ src/platforms/bmp-v3/meson.build | 13 ++- 2 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/platforms/bmp-v3/bootloader.c diff --git a/src/platforms/bmp-v3/bootloader.c b/src/platforms/bmp-v3/bootloader.c new file mode 100644 index 00000000000..209df83dc61 --- /dev/null +++ b/src/platforms/bmp-v3/bootloader.c @@ -0,0 +1,154 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 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 +#include +#include +#include +#include +#include +#include + +#include "usbdfu.h" +#include "platform.h" +#include "rcc_clocking.h" + +uintptr_t app_address = 0x08004000U; +uint8_t dfu_activity_counter = 0U; + +void dfu_detach(void) +{ + /* USB device must detach, we just reset... */ + scb_reset_system(); +} + +int main(void) +{ + /* Check the force bootloader pin */ + rcc_periph_clock_enable(RCC_GPIOA); + if (gpio_get(BNT_BOOT_REQ_PORT, BTN_BOOT_REQ_PIN)) + dfu_jump_app_if_valid(); + + dfu_protect(false); + + /* Bring up the clocks for operation, setting SysTick to 160MHz / 8 (20MHz) */ + rcc_clock_setup_pll(&rcc_hsi_config); + rcc_clock_setup_hsi48(); + crs_autotrim_usb_enable(); + rcc_set_iclk_clksel(RCC_CCIPR1_ICLKSEL_HSI48); + rcc_set_peripheral_clk_sel(SYS_TICK_BASE, RCC_CCIPR1_SYSTICKSEL_HCLK_DIV8); + systick_set_clocksource(STK_CSR_CLKSOURCE_EXT); + /* Reload every 100ms */ + systick_set_reload(2000000U); + /* Power up USB controller */ + pwr_enable_vddusb(); + + /* Configure USB related clocks and pins. */ + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_OTGFS); + + /* Finish setting up and enabling SysTick */ + systick_interrupt_enable(); + systick_counter_enable(); + + /* Configure the LED pins. */ + gpio_set_output_options(LED0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, LED0_PIN); + gpio_mode_setup(LED0_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED0_PIN); + gpio_set_output_options(LED1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, LED1_PIN); + gpio_mode_setup(LED1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED1_PIN); + gpio_set_output_options(LED2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, LED2_PIN); + gpio_mode_setup(LED2_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED2_PIN); + gpio_set_output_options(LED3_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, LED3_PIN); + gpio_mode_setup(LED3_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED3_PIN); + + dfu_init(&otgfs_usb_driver); + + dfu_main(); +} + +void dfu_event(void) +{ + /* If the counter was at 0 before we should reset LED status. */ + if (dfu_activity_counter == 0) { + gpio_clear(LED0_PORT, LED0_PIN); + gpio_clear(LED1_PORT, LED1_PIN); + gpio_clear(LED2_PORT, LED2_PIN); + gpio_clear(LED3_PORT, LED3_PIN); + } + + /* Prevent the sys_tick_handler from blinking leds for a bit. */ + dfu_activity_counter = 10; + + /* Toggle the DFU activity LED. */ + gpio_toggle(LED1_PORT, LED1_PIN); +} + +void sys_tick_handler(void) +{ + static int count = 0; + static bool reset = true; + + /* Run the LED show only if there is no DFU activity. */ + if (dfu_activity_counter != 0) { + --dfu_activity_counter; + reset = true; + } else { + if (reset) { + gpio_clear(LED0_PORT, LED0_PIN); + gpio_clear(LED1_PORT, LED1_PIN); + gpio_clear(LED2_PORT, LED2_PIN); + gpio_clear(LED3_PORT, LED3_PIN); + count = 0; + reset = false; + } + + switch (count) { + case 0: + gpio_toggle(LED3_PORT, LED3_PIN); /* LED3 on/off */ + break; + case 1: + gpio_toggle(LED2_PORT, LED2_PIN); /* LED2 on/off */ + break; + case 2: + gpio_toggle(LED1_PORT, LED1_PIN); /* LED1 on/off */ + break; + case 3: + gpio_toggle(LED0_PORT, LED0_PIN); /* LED0 on/off */ + break; + default: + break; + } + ++count; + count &= 3U; + } +} diff --git a/src/platforms/bmp-v3/meson.build b/src/platforms/bmp-v3/meson.build index a0befd65ef3..9e144b2e727 100644 --- a/src/platforms/bmp-v3/meson.build +++ b/src/platforms/bmp-v3/meson.build @@ -32,6 +32,8 @@ probe_bmp_includes = include_directories('.') probe_bmp_sources = files('platform.c') +probe_bmp_dfu_sources = files('bootloader.c') + probe_bmp_args = [ '-DDFU_SERIAL_LENGTH=9', '-DBLACKMAGICPROBE_V3', @@ -53,7 +55,8 @@ probe_bmp_common_link_args = [ ] probe_bmp_link_args = [ - #'-Wl,-Ttext=0x8002000', + # Reserve two pages for the bootloader + '-Wl,-Ttext=0x8004000', ] probe_host = declare_dependency( @@ -64,6 +67,14 @@ probe_host = declare_dependency( dependencies: [platform_common, platform_stm32u5, probe_bmp_dependencies], ) +probe_bootloader = declare_dependency( + include_directories: [platform_common_includes, probe_bmp_includes], + sources: probe_bmp_dfu_sources, + compile_args: probe_bmp_args, + link_args: probe_bmp_common_link_args, + dependencies: platform_stm32u5_dfu, +) + summary( { 'Name': 'Black Magic Probe v3', From f199fdd2ea5f200e2917635f4c5eca4f3e6ed1e1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 24 Nov 2025 18:06:49 +0000 Subject: [PATCH 032/247] common: Built a new, more suitable bootloader backend for the STM32U5 --- src/platforms/common/stm32/dfu_u5.c | 121 +++++++++++++++++++++++++ src/platforms/common/stm32/meson.build | 4 +- src/platforms/common/usb.c | 2 +- 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 src/platforms/common/stm32/dfu_u5.c diff --git a/src/platforms/common/stm32/dfu_u5.c b/src/platforms/common/stm32/dfu_u5.c new file mode 100644 index 00000000000..025669a902c --- /dev/null +++ b/src/platforms/common/stm32/dfu_u5.c @@ -0,0 +1,121 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025 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 "usbdfu.h" + +#include +#include + +#define FLASH_BLOCK_SIZE 8192U +#define FLASH_PAGE_SHIFT 13U +#define FLASH_PAGE_MASK 0x7fU +#define FLASH_BANK_MASK 0x80U + +#define SCB_VTOR_MASK 0xffffff80U +/* + * Ignore both the bottom bit of the top most nibble, and all bits below the bottom of the 3rd - + * this carves out both the NS/S bit (0x30000000 is the secure mirror of 0x20000000), and + * any possible location of the stack pointer within the first 3 SRAMs in the system + */ +#define SRAM_MASK 0xeff00000U + +static uint32_t last_erased_page = 0xffffffffU; + +void dfu_check_and_do_sector_erase(uint32_t sector) +{ + sector &= ~(FLASH_BLOCK_SIZE - 1U); + if (sector != last_erased_page) { + const uint16_t page = (sector >> FLASH_PAGE_SHIFT); + flash_erase_page((page & FLASH_BANK_MASK) ? FLASH_BANK_2 : FLASH_BANK_1, page & FLASH_PAGE_MASK); + flash_wait_for_last_operation(); + last_erased_page = sector; + } +} + +void dfu_flash_program_buffer(const uint32_t address, const void *const buf, const size_t len) +{ + const uint8_t *const buffer = (const uint8_t *)buf; + flash_program(address, buffer, len); + + /* Call the platform specific dfu event callback. */ + dfu_event(); +} + +/* A polling timeout, in miliseconds, for the ongoing programming/erase operation */ +uint32_t dfu_poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum) +{ + /* We don't care about the address as that's not used here */ + (void)addr; + /* DfuSe uses this as a special indicator to perform erases */ + if (blocknum == 0U && cmd == CMD_ERASE) { + /* + * If we're doing an erase of a block, it'll take up to 3.4ms to erase 8KiB. + * Round up to the nearest milisecond. + */ + return 4U; + } + /* + * From dfucore.c, we receive up to 1KiB at a time to program, which is is 64 u128 blocks. + * DS13086 (STM32U585x) specifies the programming time for the Flash at 118µs a block + * (§5.3.11 Flash memory characteristics, Table 88. pg228). + * This works out to 7552µs, so round that up to the nearest whole milisecond. + */ + return 8U; +} + +void dfu_protect(bool enable) +{ + /* For now, this function is a no-op and the bootloader is fully unprotected */ + (void)enable; +} + +void dfu_jump_app_if_valid(void) +{ + const uint32_t stack_pointer = *((uint32_t *)app_address); + /* Boot the application if it's valid */ + if ((stack_pointer & SRAM_MASK) == 0x20000000U) { + /* Set vector table base address which must be aligned to the nearest 128 bytes */ + SCB_VTOR = app_address & SCB_VTOR_MASK; + /* clang-format off */ + __asm__( + "msr msp, %1\n" /* Load the system stack register with the new stack pointer */ + "ldr pc, [%0, 4]\n" /* Jump to application */ + : : "l"(app_address), "l"(stack_pointer) : "r0" + ); + /* clang-format on */ + + while (true) + continue; + } +} diff --git a/src/platforms/common/stm32/meson.build b/src/platforms/common/stm32/meson.build index 40b4168f2ec..4e5a386ab63 100644 --- a/src/platforms/common/stm32/meson.build +++ b/src/platforms/common/stm32/meson.build @@ -216,6 +216,8 @@ platform_stm32f7_dfu = declare_dependency( ## STM32U5 Platform ## ________________ +platform_stm32u5_dfu_sources = files('dfu_u5.c') + platform_stm32u5_args = [ '-mcpu=cortex-m33', '-mfloat-abi=hard', @@ -236,7 +238,7 @@ platform_stm32u5 = declare_dependency( ) platform_stm32u5_dfu = declare_dependency( - sources: platform_stm32f4_dfu_sources, + sources: platform_stm32u5_dfu_sources, compile_args: platform_stm32u5_args, link_args: platform_stm32u5_link_args, dependencies: [platform_stm32_dfu_common, dependency('opencm3_stm32u5')], diff --git a/src/platforms/common/usb.c b/src/platforms/common/usb.c index 7baa04d8cf4..99c57276a66 100644 --- a/src/platforms/common/usb.c +++ b/src/platforms/common/usb.c @@ -32,7 +32,7 @@ usbd_device *usbdev = NULL; uint16_t usb_config; /* We need a special large control buffer for this device: */ -static uint8_t usbd_control_buffer[512]; +static uint8_t usbd_control_buffer[512U]; /* * Please note, if you change the descriptors and any result exceeds this buffer size From 825a922dd8bab30ca65805341541fb9dcbf5041a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 24 Nov 2025 18:07:27 +0000 Subject: [PATCH 033/247] common: Enabled the STM32U5 in the core DFU bootloader --- src/platforms/common/stm32/dfucore.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/platforms/common/stm32/dfucore.c b/src/platforms/common/stm32/dfucore.c index 1a697d499e7..c2b78e70285 100644 --- a/src/platforms/common/stm32/dfucore.c +++ b/src/platforms/common/stm32/dfucore.c @@ -2,6 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2013 Gareth McMullin + * Copyright (C) 2025 1BitSquared * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,6 +43,9 @@ #define DFU_IFACE_STRING_OFFSET 54 #define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,000*128Kg" #endif +#elif defined(STM32U5) +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/2*8Ka,000*8Kg" +#define DFU_IFACE_STRING_OFFSET 33 #endif #include @@ -302,7 +306,8 @@ void dfu_init(const usbd_driver *driver) { get_dev_unique_id(); - usbdev = usbd_init(driver, &dev_desc, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer)); + usbdev = usbd_init(driver, &dev_desc, &config, usb_strings, ARRAY_LENGTH(usb_strings), usbd_control_buffer, + sizeof(usbd_control_buffer)); usbd_register_control_callback(usbdev, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usbdfu_control_request); @@ -317,7 +322,7 @@ void dfu_main(void) #if defined(DFU_IFACE_STRING_OFFSET) static void set_dfu_iface_string(uint32_t size) { - char *const p = if_string + DFU_IFACE_STRING_OFFSET; + char *const sectors = if_string + DFU_IFACE_STRING_OFFSET; #if DFU_IFACE_PAGESIZE > 1 size /= DFU_IFACE_PAGESIZE; #endif @@ -326,16 +331,16 @@ static void set_dfu_iface_string(uint32_t size) * Fill the size digits by hand. */ if (size >= 999) { - p[0] = '9'; - p[1] = '9'; - p[2] = '9'; + sectors[0] = '9'; + sectors[1] = '9'; + sectors[2] = '9'; return; } - p[2] = (char)(48U + (size % 10U)); + sectors[2] = (char)(48U + (size % 10U)); size /= 10U; - p[1] = (char)(48U + (size % 10U)); + sectors[1] = (char)(48U + (size % 10U)); size /= 10U; - p[0] = (char)(48U + size); + sectors[0] = (char)(48U + size); } #else #define set_dfu_iface_string(x) @@ -345,10 +350,17 @@ static void get_dev_unique_id(void) { /* Calculated the upper flash limit from the exported data in the parameter block*/ uint32_t fuse_flash_size = desig_get_flash_size(); +#ifdef STM32F1 /* Handle F103x8 as F103xB. */ if (fuse_flash_size == 0x40U) fuse_flash_size = 0x80U; +#endif +#ifdef STM32U5 + /* STM32U5 uses a 16KiB reservation, not 8 for the bootloader. Convert size to 8KiB sectors. */ + set_dfu_iface_string((fuse_flash_size - 16U) / 8U); +#else set_dfu_iface_string(fuse_flash_size - 8U); +#endif max_address = FLASH_BASE + (fuse_flash_size << 10U); read_serial_number(); } From 2abc501a085c0bf3141a1ad57e3e60df3ff8078b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Thu, 27 Nov 2025 17:30:15 +0000 Subject: [PATCH 034/247] common: Cleaned up the core DFU bootloader using the buffer_utils header --- src/include/buffer_utils.h | 1 + src/platforms/common/stm32/dfucore.c | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index ed79f88dba4..0647641a9bf 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -36,6 +36,7 @@ #include #include +#include static inline void write_le2(uint8_t *const buffer, const size_t offset, const uint16_t value) { diff --git a/src/platforms/common/stm32/dfucore.c b/src/platforms/common/stm32/dfucore.c index c2b78e70285..bdd052d40b9 100644 --- a/src/platforms/common/stm32/dfucore.c +++ b/src/platforms/common/stm32/dfucore.c @@ -22,6 +22,7 @@ #include "platform.h" #include "version.h" #include "serialno.h" +#include "buffer_utils.h" #include #include @@ -149,18 +150,12 @@ static const char *const usb_strings[] = { if_string, }; -static uint32_t get_le32(const void *vp) -{ - const uint8_t *p = vp; - return ((uint32_t)p[3] << 24U) + ((uint32_t)p[2] << 16U) + (p[1] << 8U) + p[0]; -} - static uint8_t usbdfu_getstatus(uint32_t *poll_timeout) { switch (usbdfu_state) { case STATE_DFU_DNLOAD_SYNC: usbdfu_state = STATE_DFU_DNBUSY; - *poll_timeout = dfu_poll_timeout(prog.buf[0], get_le32(prog.buf + 1U), prog.blocknum); + *poll_timeout = dfu_poll_timeout(prog.buf[0], read_le4(prog.buf, 1U), prog.blocknum); return DFU_STATUS_OK; case STATE_DFU_MANIFEST_SYNC: @@ -184,7 +179,7 @@ static void usbdfu_getstatus_complete(usbd_device *dev, usb_setup_data_s *req) flash_unlock(); if (prog.blocknum == 0) { - const uint32_t addr = get_le32(prog.buf + 1U); + const uint32_t addr = read_le4(prog.buf, 1U); switch (prog.buf[0]) { case CMD_ERASE: if (addr < app_address || addr >= max_address) { @@ -240,7 +235,7 @@ static usbd_request_return_codes_e usbdfu_control_request(usbd_device *dev, usb_ prog.len = *len; memcpy(prog.buf, data, *len); if (req->wValue == 0 && prog.buf[0] == CMD_SETADDR) { - uint32_t addr = get_le32(prog.buf + 1U); + uint32_t addr = read_le4(prog.buf, 1U); if (addr < app_address || addr >= max_address) { current_error = DFU_STATUS_ERR_TARGET; usbdfu_state = STATE_DFU_ERROR; From cdee66492058d7ca0b872400f621643d76d5c12c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 22 Dec 2025 07:22:56 +0000 Subject: [PATCH 035/247] bmp-v3: Initialise the UART subsystem and configure the UART pins appropriately --- src/platforms/bmp-v3/platform.c | 14 ++++++++++++++ src/platforms/bmp-v3/platform.h | 10 +--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 7940dd4f90e..dedd0433af6 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -35,6 +35,7 @@ #include "platform.h" #include "usb.h" #include "rcc_clocking.h" +#include "aux_serial.h" #include #include @@ -89,12 +90,25 @@ void platform_init(void) gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); + gpio_set_output_options(AUX_UART1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); + gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); + gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART1_RX_PIN); + + gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + gpio_set_output_options(AUX_UART2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); + /* Bring up the ADC */ adc_init(); /* Bring up timing and USB */ platform_timing_init(); blackmagic_usb_init(); + + /* Bring up the aux serial interface */ + aux_serial_init(); } static void adc_init(void) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index f366d287a63..21d5cd4a5ec 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -188,15 +188,7 @@ extern int hwversion; gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); \ } while (0) -#define UART_PIN_SETUP() \ - do { \ - gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); \ - gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); \ - gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART1_RX_PIN); \ - gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); \ - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); \ - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); \ - } while (0) +#define UART_PIN_SETUP() #define USB_DRIVER otgfs_usb_driver #define USB_IRQ NVIC_USB_IRQ From 646882123b5726ca396562f53ba0e950922896a1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 22 Dec 2025 07:35:20 +0000 Subject: [PATCH 036/247] misc: Turn off a clang-analyzer-core lint that we cannot avoid due to writing firmware --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 46a746ecb1c..fcf4eab2feb 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'bugprone-*,cert-*,clang-analyzer-*,misc-*,modernize-*,portability-*,performance-*,readability-*,-readability-magic-numbers,-readability-braces-around-statements,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-modernize-macro-to-enum,-bugprone-easily-swappable-parameters,-misc-include-cleaner,-misc-no-recursion,-misc-header-include-cycle' +Checks: 'bugprone-*,cert-*,clang-analyzer-*,misc-*,modernize-*,portability-*,performance-*,readability-*,-readability-magic-numbers,-readability-braces-around-statements,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-modernize-macro-to-enum,-bugprone-easily-swappable-parameters,-misc-include-cleaner,-misc-no-recursion,-misc-header-include-cycle,-clang-analyzer-core.FixedAddressDereference' FormatStyle: 'none' HeaderFilterRegex: '(src|upgrade)/.+' #AnalyzeTemporaryDtors: false From 64c1527cd7e38e0bc5fd9a7c1338118967d4f13b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 22 Dec 2025 07:57:55 +0000 Subject: [PATCH 037/247] bmp-v3: Initialise the LED pins so we can see the GDB machinary state --- src/platforms/bmp-v3/platform.c | 9 +++++++++ src/platforms/bmp-v3/platform.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index dedd0433af6..68d272668f3 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -90,6 +90,15 @@ void platform_init(void) gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + gpio_set_output_options(LED0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED0_PIN); + gpio_mode_setup(LED0_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED0_PIN); + gpio_set_output_options(LED1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED1_PIN); + gpio_mode_setup(LED1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED1_PIN); + gpio_set_output_options(LED2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED2_PIN); + gpio_mode_setup(LED2_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED2_PIN); + gpio_set_output_options(LED3_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED3_PIN); + gpio_mode_setup(LED3_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED3_PIN); + gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); gpio_set_output_options(AUX_UART1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 21d5cd4a5ec..a6be77e000d 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -267,6 +267,6 @@ extern int hwversion; #define SET_RUN_STATE(state) running_status = (state) #define SET_IDLE_STATE(state) gpio_set_val(LED_IDLE_RUN_PORT, LED_IDLE_RUN_PIN, state) -#define SET_ERROR_STATE(state) gpio_set_val(LED_ERROR_PORT, LED_ERROR_PIN, state) +#define SET_ERROR_STATE(state) gpio_set_val(LED_ERROR_PORT, LED_ERROR_PIN, !state) #endif /* PLATFORMS_BMP_V3_PLATFORM_H */ From 5beecc914a47277d75bb5b840922f2689ce9e8e2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 22 Dec 2025 08:06:25 +0000 Subject: [PATCH 038/247] common: Enable the USB DFU stub to properly reboot cores that are ARMv8-M main profile --- src/platforms/bmp-v3/platform.c | 15 +++++++++++++++ src/platforms/common/usb_dfu_stub.c | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 68d272668f3..1bbb8a0ae21 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -46,6 +46,9 @@ #include #include #include +#include + +#define BOOTLOADER_ADDRESS 0x08000000U static void adc_init(void); @@ -216,6 +219,18 @@ void platform_request_boot(void) /* Drive boot request pin */ gpio_mode_setup(BNT_BOOT_REQ_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BTN_BOOT_REQ_PIN); gpio_clear(BNT_BOOT_REQ_PORT, BTN_BOOT_REQ_PIN); + + /* Reset core to enter bootloader */ + /* Reload PC and SP with their POR values from the start of Flash */ + const uint32_t stack_pointer = *((uint32_t *)BOOTLOADER_ADDRESS); + /* clang-format off */ + __asm__( + "msr msp, %1\n" /* Load the system stack register with the new stack pointer */ + "ldr pc, [%0, 4]\n" /* Jump to the bootloader */ + : : "l"(BOOTLOADER_ADDRESS), "l"(stack_pointer) : "r0" + ); + /* clang-format on */ + cm3_assert_not_reached(); } void platform_target_clk_output_enable(bool enable) diff --git a/src/platforms/common/usb_dfu_stub.c b/src/platforms/common/usb_dfu_stub.c index 0e1ca2ace6f..b21b60ababc 100644 --- a/src/platforms/common/usb_dfu_stub.c +++ b/src/platforms/common/usb_dfu_stub.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2022-2025 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify From 28c3bcf945581cacfff59f0d09c217ba10a51954 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 23 Dec 2025 06:01:00 +0000 Subject: [PATCH 039/247] common/usb_serial: Cleanup to improve nomenclature consistency and readability in the USB configuration setup code --- src/include/gdb_if.h | 2 +- src/platforms/common/stm32/gdb_if.c | 2 +- src/platforms/common/tm4c/gdb_if.c | 4 ++-- src/platforms/common/usb_serial.c | 31 ++++++++++++++------------ src/platforms/ctxlink/ctxlink_gdb_if.c | 2 +- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/include/gdb_if.h b/src/include/gdb_if.h index bf9d0964365..a31bf6a5c91 100644 --- a/src/include/gdb_if.h +++ b/src/include/gdb_if.h @@ -23,7 +23,7 @@ #if CONFIG_BMDA == 0 && !defined(NO_LIBOPENCM3) #include -void gdb_usb_out_cb(usbd_device *dev, uint8_t ep); +void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep); #endif int gdb_if_init(void); diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 16e4851d11d..7f10317d1cb 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -77,7 +77,7 @@ void gdb_if_flush(const bool force) } #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) -void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) +void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep) { (void)ep; usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); diff --git a/src/platforms/common/tm4c/gdb_if.c b/src/platforms/common/tm4c/gdb_if.c index d1ddca2ee5e..961a8053327 100644 --- a/src/platforms/common/tm4c/gdb_if.c +++ b/src/platforms/common/tm4c/gdb_if.c @@ -57,7 +57,7 @@ void gdb_if_flush(const bool force) /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ if (force && count_in == CDCACM_PACKET_SIZE) { - /* + /* * libopencm3 needs a change for us to confirm when that transfer is complete, * so we just send a packet containing a null character for now. */ @@ -69,7 +69,7 @@ void gdb_if_flush(const bool force) count_in = 0U; } -void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) +void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep) { (void)ep; static char buf[CDCACM_PACKET_SIZE]; diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index ac9a9b76e48..068c6f3db1d 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -61,6 +61,12 @@ #include #endif +#ifdef USB_HS +#define DEBUG_SERIAL_RECEIVE_SIZE CDCACM_PACKET_SIZE +#else +#define DEBUG_SERIAL_RECEIVE_SIZE (CDCACM_PACKET_SIZE / 2U) +#endif + static bool gdb_serial_dtr = true; static void usb_serial_set_state(usbd_device *dev, uint16_t iface, uint8_t ep); @@ -159,46 +165,43 @@ void usb_serial_set_state(usbd_device *const dev, const uint16_t iface, const ui uint8_t buf[10]; usb_cdc_notification_s *notif = (void *)buf; /* We echo signals back to host as notification */ - notif->bmRequestType = 0xa1; + notif->bmRequestType = 0xa1U; notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; - notif->wValue = 0; + notif->wValue = 0U; notif->wIndex = iface; - notif->wLength = 2; + notif->wLength = 2U; buf[8] = 3U; buf[9] = 0U; usbd_ep_write_packet(dev, ep, buf, sizeof(buf)); } -void usb_serial_set_config(usbd_device *dev, uint16_t value) +void usb_serial_set_config(usbd_device *const dev, const uint16_t value) { usb_config = value; /* GDB interface */ #if defined(STM32F4) || defined(LM4F) || defined(STM32F7) || defined(STM32U5) - usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, gdb_usb_out_cb); + usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT | USB_REQ_TYPE_OUT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, + gdb_usb_receive_callback); #else - usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); + usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT | USB_REQ_TYPE_OUT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); #endif usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); #if defined(STM32F4) && CDCACM_GDB_NOTIF_ENDPOINT >= 4 /* skip setup for unimplemented EP */ #else - usbd_ep_setup(dev, CDCACM_GDB_NOTIF_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + usbd_ep_setup(dev, CDCACM_GDB_NOTIF_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_INTERRUPT, 16U, NULL); #endif /* Serial interface */ -#if defined(USB_HS) - const uint16_t uart_epout_size = CDCACM_PACKET_SIZE; -#else - const uint16_t uart_epout_size = CDCACM_PACKET_SIZE / 2U; -#endif - usbd_ep_setup(dev, CDCACM_UART_ENDPOINT, USB_ENDPOINT_ATTR_BULK, uart_epout_size, debug_serial_receive_callback); + usbd_ep_setup(dev, CDCACM_UART_ENDPOINT | USB_REQ_TYPE_OUT, USB_ENDPOINT_ATTR_BULK, DEBUG_SERIAL_RECEIVE_SIZE, + debug_serial_receive_callback); usbd_ep_setup(dev, CDCACM_UART_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, debug_serial_send_callback); #if defined(STM32F4) && CDCACM_UART_NOTIF_ENDPOINT >= 4 /* skip setup for unimplemented EP */ #else - usbd_ep_setup(dev, CDCACM_UART_NOTIF_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + usbd_ep_setup(dev, CDCACM_UART_NOTIF_ENDPOINT | USB_REQ_TYPE_IN, USB_ENDPOINT_ATTR_INTERRUPT, 16U, NULL); #endif #ifdef PLATFORM_HAS_TRACESWO diff --git a/src/platforms/ctxlink/ctxlink_gdb_if.c b/src/platforms/ctxlink/ctxlink_gdb_if.c index 2205920c070..96152582346 100644 --- a/src/platforms/ctxlink/ctxlink_gdb_if.c +++ b/src/platforms/ctxlink/ctxlink_gdb_if.c @@ -77,7 +77,7 @@ void gdb_usb_putchar(const char ch, const bool flush) gdb_usb_flush(flush); } -void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) +void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep) { (void)ep; usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); From e33293dcfe8cfc49008ff2dec581406f33c6eddd Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 31 Dec 2025 05:15:04 +0000 Subject: [PATCH 040/247] common/stm32/gdb_if: Upgraded the receive callback mechanism with proper use of atomics, resulting in smaller and more correct code --- src/platforms/common/stm32/gdb_if.c | 43 +++++++++++++++-------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 7f10317d1cb..50c57c80f97 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -31,14 +31,17 @@ #include "usb_serial.h" #include "gdb_if.h" +#include + static uint32_t count_out; static uint32_t count_in; static uint32_t out_ptr; static char buffer_out[CDCACM_PACKET_SIZE]; static char buffer_in[CDCACM_PACKET_SIZE]; #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) -static volatile uint32_t count_new; -static char double_buffer_out[CDCACM_PACKET_SIZE]; +/* Variables used to get data out from the USB controller in interrupt context */ +static _Atomic uint32_t irq_count_received = ATOMIC_VAR_INIT(0U); +static char irq_buffer_received[CDCACM_PACKET_SIZE]; #endif void gdb_if_putchar(const char c, const bool flush) @@ -79,11 +82,11 @@ void gdb_if_flush(const bool force) #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep) { - (void)ep; - usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); - count_new = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT, double_buffer_out, CDCACM_PACKET_SIZE); - if (!count_new) - usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 0); + /* We're here because of new data, so read it out the controller into our intermedate buffer */ + irq_count_received = usbd_ep_read_packet(dev, ep, irq_buffer_received, CDCACM_PACKET_SIZE); + /* Now if that worked, mark the endpoint for NAK for the time being (undone in gdb_if_update_buf()) */ + if (irq_count_received) + usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); } #endif @@ -94,26 +97,24 @@ static void gdb_if_update_buf(void) #if !defined(STM32F4) && !defined(STM32F7) && !defined(STM32U5) count_out = usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_out, CDCACM_PACKET_SIZE); out_ptr = 0; + /* If this didn't dequeue any data, wait for more so the next loop around is more successfull */ + if (!count_out) + __WFI(); #else - cm_disable_interrupts(); - __asm__ volatile("isb"); - /* count_new will become 0 by the time of decision to WFI, so save a copy at entry */ - const uint32_t count_new_saved = count_new; - if (count_new) { - memcpy(buffer_out, double_buffer_out, count_new); - count_out = count_new; - count_new = 0; + /* Grab the amount of data presently available per the IRQ, and reset that value to 0 atomically */ + const uint32_t bytes_available = atomic_exchange(&irq_count_received, 0U); + /* If there's data waiting for us, move it into the main buffer and prep the endpoint for more */ + if (bytes_available) { + memcpy(buffer_out, irq_buffer_received, bytes_available); + /* Save the amount available and reset the read index */ + count_out = bytes_available; out_ptr = 0; usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0); } - cm_enable_interrupts(); - __asm__ volatile("isb"); - /* Wait for Host OUT packets (count_new is 0 by now, so use the copy saved at entry) */ - if (!count_new_saved) + /* If this didn't dequeue any data, wait for more so the next loop around is more successfull */ + else __WFI(); #endif - if (!count_out) - __WFI(); } char gdb_if_getchar(void) From b54782cd9df0bb356a3f5a37ef679da93abd7e93 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 31 Dec 2025 05:16:23 +0000 Subject: [PATCH 041/247] common/stm32/gdb_if: Nomenclature fixes to make things easier to read and track --- src/platforms/common/stm32/gdb_if.c | 49 +++++++++++++------------- src/platforms/ctxlink/ctxlink_gdb_if.c | 42 +++++++++++----------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 50c57c80f97..2edfe2cf1a3 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -33,40 +33,40 @@ #include -static uint32_t count_out; -static uint32_t count_in; -static uint32_t out_ptr; -static char buffer_out[CDCACM_PACKET_SIZE]; -static char buffer_in[CDCACM_PACKET_SIZE]; +static uint32_t gdb_receive_amount_available; +static uint32_t gdb_send_amount_queued; +static uint32_t gdb_receive_index; +static char gdb_receive_buffer[CDCACM_PACKET_SIZE]; +static char gdb_send_buffer[CDCACM_PACKET_SIZE]; #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) /* Variables used to get data out from the USB controller in interrupt context */ static _Atomic uint32_t irq_count_received = ATOMIC_VAR_INIT(0U); static char irq_buffer_received[CDCACM_PACKET_SIZE]; #endif -void gdb_if_putchar(const char c, const bool flush) +void gdb_if_putchar(const char ch, const bool flush) { - buffer_in[count_in++] = c; - if (flush || count_in == CDCACM_PACKET_SIZE) + gdb_send_buffer[gdb_send_amount_queued++] = ch; + if (flush || gdb_send_amount_queued == CDCACM_PACKET_SIZE) gdb_if_flush(flush); } void gdb_if_flush(const bool force) { /* Flush only if there is data to flush */ - if (count_in == 0U) + if (gdb_send_amount_queued == 0U) return; /* Refuse to send if USB isn't configured, and don't bother if nobody's listening */ if (usb_get_config() != 1U || !gdb_serial_get_dtr()) { - count_in = 0U; + gdb_send_amount_queued = 0U; return; } - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0U) + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, gdb_send_buffer, gdb_send_amount_queued) <= 0U) continue; /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ - if (force && count_in == CDCACM_PACKET_SIZE) { + if (force && gdb_send_amount_queued == CDCACM_PACKET_SIZE) { /* * libopencm3 needs a change for us to confirm when that transfer is complete, * so we just send a packet containing a null character for now. @@ -76,7 +76,7 @@ void gdb_if_flush(const bool force) } /* Reset the buffer */ - count_in = 0U; + gdb_send_amount_queued = 0U; } #if defined(STM32F4) || defined(STM32F7) || defined(STM32U5) @@ -95,20 +95,21 @@ static void gdb_if_update_buf(void) while (usb_get_config() != 1) continue; #if !defined(STM32F4) && !defined(STM32F7) && !defined(STM32U5) - count_out = usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_out, CDCACM_PACKET_SIZE); - out_ptr = 0; + gdb_receive_amount_available = + usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT, gdb_receive_buffer, CDCACM_PACKET_SIZE); + gdb_receive_index = 0; /* If this didn't dequeue any data, wait for more so the next loop around is more successfull */ - if (!count_out) + if (!gdb_receive_amount_available) __WFI(); #else /* Grab the amount of data presently available per the IRQ, and reset that value to 0 atomically */ const uint32_t bytes_available = atomic_exchange(&irq_count_received, 0U); /* If there's data waiting for us, move it into the main buffer and prep the endpoint for more */ if (bytes_available) { - memcpy(buffer_out, irq_buffer_received, bytes_available); + memcpy(gdb_receive_buffer, irq_buffer_received, bytes_available); /* Save the amount available and reset the read index */ - count_out = bytes_available; - out_ptr = 0; + gdb_receive_amount_available = bytes_available; + gdb_receive_index = 0; usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0); } /* If this didn't dequeue any data, wait for more so the next loop around is more successfull */ @@ -119,7 +120,7 @@ static void gdb_if_update_buf(void) char gdb_if_getchar(void) { - while (out_ptr >= count_out) { + while (gdb_receive_index >= gdb_receive_amount_available) { /* * Detach if port closed * @@ -134,7 +135,7 @@ char gdb_if_getchar(void) gdb_if_update_buf(); } - return buffer_out[out_ptr++]; + return gdb_receive_buffer[gdb_receive_index++]; } char gdb_if_getchar_to(const uint32_t timeout) @@ -143,7 +144,7 @@ char gdb_if_getchar_to(const uint32_t timeout) platform_timeout_set(&receive_timeout, timeout); /* Wait while we need more data or until the timeout expires */ - while (out_ptr >= count_out && !platform_timeout_is_expired(&receive_timeout)) { + while (gdb_receive_index >= gdb_receive_amount_available && !platform_timeout_is_expired(&receive_timeout)) { /* * Detach if port closed * @@ -157,8 +158,8 @@ char gdb_if_getchar_to(const uint32_t timeout) gdb_if_update_buf(); } - if (out_ptr < count_out) - return buffer_out[out_ptr++]; + if (gdb_receive_index < gdb_receive_amount_available) + return gdb_receive_buffer[gdb_receive_index++]; /* XXX: Need to find a better way to error return than this. This provides '\xff' characters. */ return -1; } diff --git a/src/platforms/ctxlink/ctxlink_gdb_if.c b/src/platforms/ctxlink/ctxlink_gdb_if.c index 96152582346..89568dbf241 100644 --- a/src/platforms/ctxlink/ctxlink_gdb_if.c +++ b/src/platforms/ctxlink/ctxlink_gdb_if.c @@ -34,30 +34,30 @@ #include "gdb_if.h" #include "WiFi_Server.h" -static uint32_t count_out; -static uint32_t count_in; -static uint32_t out_ptr; -static char buffer_out[CDCACM_PACKET_SIZE]; -static char buffer_in[CDCACM_PACKET_SIZE]; +static uint32_t gdb_receive_amount_available; +static uint32_t gdb_send_amount_queued; +static uint32_t gdb_receive_index; +static char gdb_receive_buffer[CDCACM_PACKET_SIZE]; +static char gdb_send_buffer[CDCACM_PACKET_SIZE]; static volatile uint32_t count_new; static char double_buffer_out[CDCACM_PACKET_SIZE]; void gdb_usb_flush(const bool force) { /* Flush only if there is data to flush */ - if (count_in == 0U) + if (gdb_send_amount_queued == 0U) return; /* Refuse to send if USB isn't configured, and don't bother if nobody's listening */ if (usb_get_config() != 1U || !gdb_serial_get_dtr()) { - count_in = 0U; + gdb_send_amount_queued = 0U; return; } - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0U) + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, gdb_send_buffer, gdb_send_amount_queued) <= 0U) continue; /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ - if (force && count_in == CDCACM_PACKET_SIZE) { + if (force && gdb_send_amount_queued == CDCACM_PACKET_SIZE) { /* * libopencm3 needs a change for us to confirm when that transfer is complete, * so we just send a packet containing a null character for now. @@ -67,13 +67,13 @@ void gdb_usb_flush(const bool force) } /* Reset the buffer */ - count_in = 0U; + gdb_send_amount_queued = 0U; } void gdb_usb_putchar(const char ch, const bool flush) { - buffer_in[count_in++] = ch; - if (flush || count_in == CDCACM_PACKET_SIZE) + gdb_send_buffer[gdb_send_amount_queued++] = ch; + if (flush || gdb_send_amount_queued == CDCACM_PACKET_SIZE) gdb_usb_flush(flush); } @@ -93,21 +93,21 @@ static void gdb_if_update_buf(void) cm_disable_interrupts(); __asm__ volatile("isb"); if (count_new) { - memcpy(buffer_out, double_buffer_out, count_new); - count_out = count_new; + memcpy(gdb_receive_buffer, double_buffer_out, count_new); + gdb_receive_amount_available = count_new; count_new = 0; - out_ptr = 0; + gdb_receive_index = 0; usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0); } cm_enable_interrupts(); __asm__ volatile("isb"); - if (!count_out) + if (!gdb_receive_amount_available) __WFI(); } char gdb_usb_getchar(void) { - while (out_ptr >= count_out) { + while (gdb_receive_index >= gdb_receive_amount_available) { /* * Detach if port closed * @@ -123,7 +123,7 @@ char gdb_usb_getchar(void) gdb_if_update_buf(); } - return buffer_out[out_ptr++]; + return gdb_receive_buffer[gdb_receive_index++]; } char gdb_usb_getchar_to(const uint32_t timeout) @@ -132,7 +132,7 @@ char gdb_usb_getchar_to(const uint32_t timeout) platform_timeout_set(&receive_timeout, timeout); /* Wait while we need more data or until the timeout expires */ - while (out_ptr >= count_out && !platform_timeout_is_expired(&receive_timeout)) { + while (gdb_receive_index >= gdb_receive_amount_available && !platform_timeout_is_expired(&receive_timeout)) { /* * Detach if port closed * @@ -146,8 +146,8 @@ char gdb_usb_getchar_to(const uint32_t timeout) gdb_if_update_buf(); } - if (out_ptr < count_out) - return buffer_out[out_ptr++]; + if (gdb_receive_index < gdb_receive_amount_available) + return gdb_receive_buffer[gdb_receive_index++]; /* TODO Need to find a better way to error return than this. This provides '\xff' characters. */ return -1; } From 11084a59d487457e68c3d50719ad8e899288b2cd Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 31 Dec 2025 05:17:10 +0000 Subject: [PATCH 042/247] common/usb_serial: Fixed some clang-tidy lints --- src/platforms/common/usb_serial.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index 068c6f3db1d..cf132dfd7cf 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -108,12 +108,14 @@ static usbd_request_return_codes_e gdb_serial_control_request(usbd_device *dev, return USBD_REQ_NOTSUPP; usb_cdc_line_coding_s *line_coding = (usb_cdc_line_coding_s *)*buf; /* This tells the host that we talk 1MBaud, 8-bit no parity w/ 1 stop bit */ - line_coding->dwDTERate = 1 * 1000 * 1000; + line_coding->dwDTERate = UINT32_C(1) * 1000U * 1000U; line_coding->bCharFormat = USB_CDC_1_STOP_BITS; line_coding->bParityType = USB_CDC_NO_PARITY; - line_coding->bDataBits = 8; + line_coding->bDataBits = 8U; return USBD_REQ_HANDLED; } + default: + break; } return USBD_REQ_NOTSUPP; } @@ -152,6 +154,8 @@ static usbd_request_return_codes_e debug_serial_control_request(usbd_device *dev return USBD_REQ_NOTSUPP; aux_serial_get_encoding((usb_cdc_line_coding_s *)*buf); return USBD_REQ_HANDLED; + default: + break; } return USBD_REQ_NOTSUPP; } From f635cac22a570bc3fcd2b2fe801486326ba8b137 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 31 Dec 2025 05:19:47 +0000 Subject: [PATCH 043/247] common/ctxlink/gdb_if: Upgraded the receive callback mechanism with proper use of atomics, resulting in smaller and more correct code --- src/platforms/ctxlink/ctxlink_gdb_if.c | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/platforms/ctxlink/ctxlink_gdb_if.c b/src/platforms/ctxlink/ctxlink_gdb_if.c index 89568dbf241..4a1f19ebda8 100644 --- a/src/platforms/ctxlink/ctxlink_gdb_if.c +++ b/src/platforms/ctxlink/ctxlink_gdb_if.c @@ -34,13 +34,16 @@ #include "gdb_if.h" #include "WiFi_Server.h" +#include + static uint32_t gdb_receive_amount_available; static uint32_t gdb_send_amount_queued; static uint32_t gdb_receive_index; static char gdb_receive_buffer[CDCACM_PACKET_SIZE]; static char gdb_send_buffer[CDCACM_PACKET_SIZE]; -static volatile uint32_t count_new; -static char double_buffer_out[CDCACM_PACKET_SIZE]; +/* Variables used to get data out from the USB controller in interrupt context */ +static _Atomic uint32_t irq_count_received = ATOMIC_VAR_INIT(0U); +static char irq_buffer_received[CDCACM_PACKET_SIZE]; void gdb_usb_flush(const bool force) { @@ -79,29 +82,27 @@ void gdb_usb_putchar(const char ch, const bool flush) void gdb_usb_receive_callback(usbd_device *dev, uint8_t ep) { - (void)ep; - usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); - count_new = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT, double_buffer_out, CDCACM_PACKET_SIZE); - if (!count_new) - usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 0); + /* We're here because of new data, so read it out the controller into our intermedate buffer */ + irq_count_received = usbd_ep_read_packet(dev, ep, irq_buffer_received, CDCACM_PACKET_SIZE); + /* Now if that worked, mark the endpoint for NAK for the time being (undone in gdb_if_update_buf()) */ + if (irq_count_received) + usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1); } static void gdb_if_update_buf(void) { while (usb_get_config() != 1) continue; - cm_disable_interrupts(); - __asm__ volatile("isb"); - if (count_new) { - memcpy(gdb_receive_buffer, double_buffer_out, count_new); - gdb_receive_amount_available = count_new; - count_new = 0; + /* Grab the amount of data presently available per the IRQ, and reset that value to 0 atomically */ + const uint32_t bytes_available = atomic_exchange(&irq_count_received, 0U); + /* If there's data waiting for us, move it into the main buffer and prep the endpoint for more */ + if (bytes_available) { + memcpy(gdb_receive_buffer, irq_buffer_received, bytes_available); + /* Save the amount available and reset the read index */ + gdb_receive_amount_available = bytes_available; gdb_receive_index = 0; usbd_ep_nak_set(usbdev, CDCACM_GDB_ENDPOINT, 0); - } - cm_enable_interrupts(); - __asm__ volatile("isb"); - if (!gdb_receive_amount_available) + } else __WFI(); } @@ -173,10 +174,9 @@ char gdb_if_getchar(void) platform_tasks(); if (is_gdb_client_connected()) return wifi_get_next(); - else if (usb_get_config() == 1) + if (usb_get_config() == 1) return gdb_usb_getchar(); - else - return 0xff; + return (char)0xff; } char gdb_if_getchar_to(uint32_t timeout) From 02833c4db646330ffcacc1f99698b4bb381142c3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 2 Jan 2026 06:12:11 +0000 Subject: [PATCH 044/247] common/usb_descriptors: Switch to a 125ms polling interval for state change notifications so it's a little more responsive without being overwhelming for traffic --- src/platforms/common/usb_descriptors.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platforms/common/usb_descriptors.h b/src/platforms/common/usb_descriptors.h index 0eb7316d3e3..52581f85a3c 100644 --- a/src/platforms/common/usb_descriptors.h +++ b/src/platforms/common/usb_descriptors.h @@ -71,7 +71,8 @@ static const usb_endpoint_descriptor_s gdb_comm_endp = { .bEndpointAddress = CDCACM_GDB_NOTIF_ENDPOINT | USB_REQ_TYPE_IN, .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, .wMaxPacketSize = 16, - .bInterval = USB_MAX_INTERVAL, + /* Poll for notifications only once every 125ms */ + .bInterval = 125U, }; static const usb_endpoint_descriptor_s gdb_data_endp[] = { From 63d26f574dc2656667844a03aafd5ca15619a0e1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 2 Jan 2026 08:38:35 +0000 Subject: [PATCH 045/247] bmp-v3: Implemented target power support --- src/platforms/bmp-v3/platform.c | 78 ++++++++++++++++++++++++++++++++- src/platforms/bmp-v3/platform.h | 1 + 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 1bbb8a0ae21..49505e6c7e9 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -45,11 +45,14 @@ #include #include #include +#include #include #include -#define BOOTLOADER_ADDRESS 0x08000000U +#define BOOTLOADER_ADDRESS 0x08000000U +#define TPWR_SOFT_START_STEPS 64U +static void power_timer_init(void); static void adc_init(void); int hwversion = -1; @@ -83,6 +86,8 @@ void platform_init(void) rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOC); rcc_periph_clock_enable(RCC_GPIOH); + /* Power up timer that's used for tpwr soft start */ + rcc_periph_clock_enable(RCC_TIM2); /* Make sure to power up the timer used for trace */ rcc_periph_clock_enable(RCC_TIM5); rcc_periph_clock_enable(RCC_CRC); @@ -112,6 +117,14 @@ void platform_init(void) gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); + gpio_clear(TPWR_EN_PORT, TPWR_EN_PIN); + gpio_set_af(TPWR_EN_PORT, GPIO_AF1, TPWR_EN_PIN); + gpio_set_output_options(TPWR_EN_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, TPWR_EN_PIN); + gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TPWR_EN_PIN); + + /* Set up the timer used for controlling tpwr soft start */ + power_timer_init(); + /* Bring up the ADC */ adc_init(); @@ -123,6 +136,35 @@ void platform_init(void) aux_serial_init(); } +/* Configure Timer 2 Channel 1 to allow tpwr to be soft start */ +static void power_timer_init(void) +{ + /* + * Configure Timer 2 to run the power control pin PWM and switch the timer on + * NB: We don't configure the pin mode here, but rather we configure it to the alt-mode and back in + * platform_target_set_power() below. + */ + timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + /* Use PWM mode 1 so the signal generated is low till it exceeds the set value */ + timer_set_oc1_mode(TIM2, TIM_OCM_PWM1); + /* Mark the output active-high due to how this drives the target pin */ + timer_set_oc_polarity_high(TIM2, TIM_OC1); + timer_enable_oc_output(TIM2, TIM_OC1); + timer_set_oc_value(TIM2, TIM_OC1, 0U); + /* Make sure dead-time is switched off as this interferes with correct waveform generation */ + timer_set_deadtime(TIM2, 0U); + /* + * Configure for 64 steps which also makes this output a 500kHz PWM signal + * with the prescaling from APB1 (160MHz) to 32MHz (/5) + */ + timer_set_prescaler(TIM2, 4U); + timer_set_period(TIM2, TPWR_SOFT_START_STEPS - 1U); + timer_enable_break_main_output(TIM2); + timer_continuous_mode(TIM2); + timer_update_on_overflow(TIM2); + timer_enable_counter(TIM2); +} + static void adc_init(void) { /* @@ -173,6 +215,40 @@ bool platform_target_get_power(void) return !gpio_get(TPWR_EN_PORT, TPWR_EN_PIN); } +static inline void platform_wait_pwm_cycle(void) +{ + while (!timer_get_flag(TIM2, TIM_SR_UIF)) + continue; + timer_clear_flag(TIM2, TIM_SR_UIF); +} + +bool platform_target_set_power(const bool power) +{ + /* If we're turning power on */ + if (power) { + /* Configure the pin to be driven by Timer 2 */ + gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, TPWR_EN_PIN); + timer_clear_flag(TIM2, TIM_SR_UIF); + /* Wait for one PWM cycle to have taken place */ + platform_wait_pwm_cycle(); + /* Soft start power on the target */ + for (size_t step = 1U; step < TPWR_SOFT_START_STEPS; ++step) { + /* Set the new PWM value */ + timer_set_oc_value(TIM2, TIM_OC1, step); + /* Wait for one PWM cycle to have taken place */ + platform_wait_pwm_cycle(); + } + } + /* Set the pin state */ + gpio_set_val(TPWR_EN_PORT, TPWR_EN_PIN, power); + /* If we're turning power on, switch the pin back over to GPIO and reset the timer */ + if (power) { + gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TPWR_EN_PIN); + timer_set_oc_value(TIM2, TIM_OC1, 0U); + } + return true; +} + uint32_t platform_target_voltage_sense(void) { /* diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index a6be77e000d..20a862ede58 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -41,6 +41,7 @@ #include "timing_stm32.h" #define PLATFORM_HAS_TRACESWO +#define PLATFORM_HAS_POWER_SWITCH #define PLATFORM_MULTI_UART #define PLATFORM_IDENT "v3 " From 2e60902e344214aeacda25159122f1992e497f49 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 3 Jan 2026 10:20:15 +0000 Subject: [PATCH 046/247] bmp-v3: Refactor the GPIO init into its own function --- src/platforms/bmp-v3/platform.c | 37 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 49505e6c7e9..1a57a7da484 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -52,6 +52,7 @@ #define BOOTLOADER_ADDRESS 0x08000000U #define TPWR_SOFT_START_STEPS 64U +static void gpio_init(void); static void power_timer_init(void); static void adc_init(void); @@ -93,11 +94,31 @@ void platform_init(void) rcc_periph_clock_enable(RCC_CRC); /* Setup GPIO ports */ + gpio_init(); + + /* Set up the timer used for controlling tpwr soft start */ + power_timer_init(); + + /* Bring up the ADC */ + adc_init(); + + /* Bring up timing and USB */ + platform_timing_init(); + blackmagic_usb_init(); + + /* Bring up the aux serial interface */ + aux_serial_init(); +} + +static void gpio_init(void) +{ + /* Configure the pins used to interface to the debug interface of a target */ gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + /* Configure the pins used to drive the LEDs */ gpio_set_output_options(LED0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED0_PIN); gpio_mode_setup(LED0_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED0_PIN); gpio_set_output_options(LED1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED1_PIN); @@ -107,33 +128,23 @@ void platform_init(void) gpio_set_output_options(LED3_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED3_PIN); gpio_mode_setup(LED3_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED3_PIN); + /* Configure the first UART used for the AUX serial interface */ gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); gpio_set_output_options(AUX_UART1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART1_RX_PIN); + /* Configure the second UART used for the AUX serial interface */ gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); gpio_set_output_options(AUX_UART2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); + /* Configure the pin used for tpwr control */ gpio_clear(TPWR_EN_PORT, TPWR_EN_PIN); gpio_set_af(TPWR_EN_PORT, GPIO_AF1, TPWR_EN_PIN); gpio_set_output_options(TPWR_EN_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, TPWR_EN_PIN); gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TPWR_EN_PIN); - - /* Set up the timer used for controlling tpwr soft start */ - power_timer_init(); - - /* Bring up the ADC */ - adc_init(); - - /* Bring up timing and USB */ - platform_timing_init(); - blackmagic_usb_init(); - - /* Bring up the aux serial interface */ - aux_serial_init(); } /* Configure Timer 2 Channel 1 to allow tpwr to be soft start */ From 34b8f2e188a882d2ee607cc9faba87990582f62e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 4 Jan 2026 03:57:54 +0000 Subject: [PATCH 047/247] bmp-v3: More GPIO initialisation work, making sure nRST is bought up correctly and the pins are driven suitably hard --- src/platforms/bmp-v3/platform.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 1a57a7da484..af9eb922bb4 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -113,10 +113,16 @@ void platform_init(void) static void gpio_init(void) { /* Configure the pins used to interface to the debug interface of a target */ + gpio_set_output_options(TCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TCK_PIN); gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); + gpio_set_output_options(TMS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TMS_PIN); gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); + gpio_set_output_options(TDI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TDI_PIN); gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + gpio_clear(NRST_PORT, NRST_PIN); + gpio_set_output_options(NRST_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, NRST_PIN); + gpio_mode_setup(NRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, NRST_PIN); /* Configure the pins used to drive the LEDs */ gpio_set_output_options(LED0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, LED0_PIN); @@ -145,6 +151,8 @@ static void gpio_init(void) gpio_set_af(TPWR_EN_PORT, GPIO_AF1, TPWR_EN_PIN); gpio_set_output_options(TPWR_EN_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, TPWR_EN_PIN); gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TPWR_EN_PIN); + /* And the one used to read back the voltage that's presently on the Vtgt pin */ + gpio_mode_setup(TPWR_SENSE_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, TPWR_SENSE_PIN); } /* Configure Timer 2 Channel 1 to allow tpwr to be soft start */ @@ -187,8 +195,6 @@ static void adc_init(void) adc_ungate_power(ADC1); adc_set_common_prescaler(ADC12_CCR_PRESC_DIV4); - gpio_mode_setup(TPWR_SENSE_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, TPWR_SENSE_PIN); - adc_power_off(ADC1); adc_set_single_conversion_mode(ADC1); adc_disable_external_trigger_regular(ADC1); From 92812d5059d69793e8f506c0f6d8dfac58bc1f5e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 4 Jan 2026 06:19:10 +0000 Subject: [PATCH 048/247] native: Fixed up some nomenclature and warnings in the tpwr voltage read machinary --- src/platforms/native/platform.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index cb505a649cf..7babd42ce0a 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -402,10 +402,10 @@ uint32_t platform_target_voltage_sense(void) while (!adc_eoc(ADC1)) continue; - uint32_t val = adc_read_regular(ADC1); /* 0-4095 */ + uint32_t voltage = adc_read_regular(ADC1); /* 0-4095 */ /* Clear EOC bit. The GD32F103 does not automatically reset it on ADC read. */ ADC_SR(ADC1) &= ~ADC_SR_EOC; - return (val * 99U) / 8191U; + return (voltage * 99U) / 8191U; } const char *platform_target_voltage(void) @@ -413,12 +413,12 @@ const char *platform_target_voltage(void) if (hwversion == 0) return gpio_get(GPIOB, GPIO0) ? "Present" : "Absent"; - static char ret[] = "0.0V"; - uint32_t val = platform_target_voltage_sense(); - ret[0] = '0' + val / 10U; - ret[2] = '0' + val % 10U; + static char result[] = "0.0V"; + uint32_t voltage = platform_target_voltage_sense(); + result[0] = (char)('0' + (voltage / 10U)); + result[2] = (char)('0' + (voltage % 10U)); - return ret; + return result; } void platform_request_boot(void) From 3457755ad03edd1b28a7854cfc643f8fd560e86f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 5 Jan 2026 09:49:29 +0000 Subject: [PATCH 049/247] bmp-v3: Handle proper initialisation of the bus direction pins --- src/platforms/bmp-v3/platform.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index af9eb922bb4..28dbd3ce5ad 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -114,12 +114,20 @@ static void gpio_init(void) { /* Configure the pins used to interface to the debug interface of a target */ gpio_set_output_options(TCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TCK_PIN); - gpio_mode_setup(TCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_PIN); + gpio_mode_setup(TCK_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TCK_PIN); gpio_set_output_options(TMS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TMS_PIN); gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); gpio_set_output_options(TDI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TDI_PIN); gpio_mode_setup(TDI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TDI_PIN); gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + /* Handle the direction pins */ + gpio_clear(TCK_DIR_PORT, TCK_DIR_PIN); + gpio_set_output_options(TCK_DIR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TCK_DIR_PIN); + gpio_mode_setup(TCK_DIR_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TCK_DIR_PIN); + gpio_clear(TMS_DIR_PORT, TMS_DIR_PIN); + gpio_set_output_options(TMS_DIR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TMS_DIR_PIN); + gpio_mode_setup(TMS_DIR_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_DIR_PIN); + /* Handle the nRST pin */ gpio_clear(NRST_PORT, NRST_PIN); gpio_set_output_options(NRST_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, NRST_PIN); gpio_mode_setup(NRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, NRST_PIN); From b6811200d3359a48a4c3eb6f5a4d8e2815bcaa7a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 6 Jan 2026 00:51:49 +0000 Subject: [PATCH 050/247] bmp-v3: Defined the pin setup for the QSPI Flash memory interface to the on-board Flash --- src/platforms/bmp-v3/platform.c | 18 ++++++++++++++++++ src/platforms/bmp-v3/platform.h | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 28dbd3ce5ad..4f97d674d59 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -161,6 +161,24 @@ static void gpio_init(void) gpio_mode_setup(TPWR_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TPWR_EN_PIN); /* And the one used to read back the voltage that's presently on the Vtgt pin */ gpio_mode_setup(TPWR_SENSE_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, TPWR_SENSE_PIN); + + /* Configure the pins used for the on-board SPI Flash */ + gpio_set_af(INT_SPI_SCLK_PORT, GPIO_AF10, INT_SPI_SCLK_PIN); + gpio_set_output_options(INT_SPI_SCLK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_SCLK_PIN); + gpio_mode_setup(INT_SPI_SCLK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_SCLK_PIN); + gpio_set_af(INT_SPI_CS_PORT, GPIO_AF3, INT_SPI_CS_PIN); + gpio_set_output_options(INT_SPI_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_CS_PIN); + gpio_mode_setup(INT_SPI_CS_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_CS_PIN); + gpio_set_af(INT_SPI_IO0_PORT, GPIO_AF10, INT_SPI_IO0_PIN | INT_SPI_IO1_PIN); + gpio_set_output_options(INT_SPI_IO0_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_IO0_PIN); + gpio_mode_setup(INT_SPI_IO0_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_IO0_PIN); + gpio_set_output_options(INT_SPI_IO1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_IO1_PIN); + gpio_mode_setup(INT_SPI_IO1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_IO1_PIN); + gpio_set_af(INT_SPI_IO2_PORT, GPIO_AF10, INT_SPI_IO2_PIN | INT_SPI_IO3_PIN); + gpio_set_output_options(INT_SPI_IO2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_IO2_PIN); + gpio_mode_setup(INT_SPI_IO2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_IO2_PIN); + gpio_set_output_options(INT_SPI_IO3_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, INT_SPI_IO3_PIN); + gpio_mode_setup(INT_SPI_IO3_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, INT_SPI_IO3_PIN); } /* Configure Timer 2 Channel 1 to allow tpwr to be soft start */ diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 20a862ede58..0ef5d8616ef 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -171,6 +171,20 @@ extern int hwversion; #define LED_ERROR_PORT LED2_PORT #define LED_ERROR_PIN LED2_PIN +#define INT_QSPI OCTOSPIM +#define INT_SPI_SCLK_PORT GPIOB +#define INT_SPI_SCLK_PIN GPIO10 +#define INT_SPI_CS_PORT GPIOA +#define INT_SPI_CS_PIN GPIO4 +#define INT_SPI_IO0_PORT GPIOB +#define INT_SPI_IO0_PIN GPIO1 +#define INT_SPI_IO1_PORT GPIOB +#define INT_SPI_IO1_PIN GPIO0 +#define INT_SPI_IO2_PORT GPIOA +#define INT_SPI_IO2_PIN GPIO7 +#define INT_SPI_IO3_PORT GPIOA +#define INT_SPI_IO3_PIN GPIO8 + #define TMS_SET_MODE() \ do { \ gpio_set(TMS_DIR_PORT, TMS_DIR_PIN); \ From 4a331f808077e9b65695b8b6a31a314f9bb45d0b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 6 Jan 2026 03:38:09 +0000 Subject: [PATCH 051/247] common/swdptap: Small tweak to where the read happens in the I/O cycle for SWD in --- src/platforms/common/stm32/gpio.h | 2 +- src/platforms/common/swdptap.c | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/platforms/common/stm32/gpio.h b/src/platforms/common/stm32/gpio.h index 0974d2fb058..bd0899a4dfd 100644 --- a/src/platforms/common/stm32/gpio.h +++ b/src/platforms/common/stm32/gpio.h @@ -58,7 +58,7 @@ static inline void bmp_gpio_clear(const uint32_t gpioport, const uint16_t gpios) static inline uint16_t bmp_gpio_get(const uint32_t gpioport, const uint16_t gpios) { /* NOLINTNEXTLINE(clang-diagnostic-int-to-pointer-cast) */ - return GPIO_IDR(gpioport) & gpios; + return (uint16_t)GPIO_IDR(gpioport) & gpios; } #define gpio_get bmp_gpio_get diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index 1f171f83dd0..a462d1c18f3 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -1,8 +1,10 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2022-2026 1BitSquared * Written by Gareth McMullin + * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -107,9 +109,9 @@ static uint32_t swdptap_seq_in_clk_delay(const size_t clock_cycles) * to a faster down-count that uses SUBS followed by BCS/BCC. */ for (size_t cycle = clock_cycles; cycle--;) { + const bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) continue; - const bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); for (volatile uint32_t counter = target_clk_divider; counter > 0; --counter) continue; @@ -121,7 +123,7 @@ static uint32_t swdptap_seq_in_clk_delay(const size_t clock_cycles) /* Reordering barrier */ __asm__("" ::: "memory"); } - value >>= (32U - clock_cycles); + value >>= 32U - clock_cycles; return value; } @@ -138,9 +140,9 @@ static uint32_t swdptap_seq_in_no_delay(const size_t clock_cycles) * to a faster down-count that uses SUBS followed by BCS/BCC. */ for (size_t cycle = clock_cycles; cycle--;) { + const bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); /* Reordering barrier */ __asm__("" ::: "memory"); - bool bit = gpio_get(SWDIO_IN_PORT, SWDIO_IN_PIN); gpio_set(SWCLK_PORT, SWCLK_PIN); __asm__("nop" ::: "memory"); value >>= 1U; @@ -151,7 +153,7 @@ static uint32_t swdptap_seq_in_no_delay(const size_t clock_cycles) /* Reordering barrier */ __asm__("" ::: "memory"); } - value >>= (32U - clock_cycles); + value >>= 32U - clock_cycles; return value; } From 7c28617d56428983dd3b6c77375af5b77fc46c4e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 11 Jan 2026 09:38:11 +0000 Subject: [PATCH 052/247] common/aux_serial: Implemented more of the multi-UART logic for hooking the dual target serial interfaces up to the second host serial interface --- src/platforms/bmp-v3/platform.h | 20 +++++---- src/platforms/common/aux_serial.c | 73 +++++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 0ef5d8616ef..d9b09ae71a9 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -237,14 +237,18 @@ extern int hwversion; #define AUX_UART2_TX_PIN GPIO6 #define AUX_UART2_RX_PIN GPIO7 -#define AUX_UART_DMA_BUS GPDMA1 -#define AUX_UART_DMA_CLK RCC_GPDMA1 -#define AUX_UART_DMA_TX_CHAN DMA_CHANNEL0 -#define AUX_UART_DMA_RX_CHAN DMA_CHANNEL1 -#define AUX_UART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ -#define AUX_UART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) -#define AUX_UART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ -#define AUX_UART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) +#define AUX_UART_DMA_BUS GPDMA1 +#define AUX_UART_DMA_CLK RCC_GPDMA1 +#define AUX_UART_DMA_TX_CHAN DMA_CHANNEL0 +#define AUX_UART_DMA_RX_CHAN DMA_CHANNEL1 +#define AUX_UART_DMA_TX_IRQ NVIC_GPDMA1_CH0_IRQ +#define AUX_UART_DMA_TX_ISR(x) gpdma1_ch0_isr(x) +#define AUX_UART_DMA_RX_IRQ NVIC_GPDMA1_CH1_IRQ +#define AUX_UART_DMA_RX_ISR(x) gpdma1_ch1_isr(x) +#define AUX_UART1_DMA_REQSEL_TX GPDMA1_CxTR2_REQSEL_USART2_TX +#define AUX_UART1_DMA_REQSEL_RX GPDMA1_CxTR2_REQSEL_USART2_RX +#define AUX_UART2_DMA_REQSEL_TX GPDMA1_CxTR2_REQSEL_USART1_TX +#define AUX_UART2_DMA_REQSEL_RX GPDMA1_CxTR2_REQSEL_USART1_RX /* Use TIM5 Input 2 (from PA1/SWO) for Manchester data recovery */ #define SWO_TIM TIM5 diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index bea5aaa600a..a4ff80f24ba 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -128,7 +128,7 @@ void bmd_usart_set_baudrate(const uintptr_t usart, const uint32_t baud_rate) if (baud_rate < baud_lowest) /* Too low */ return; /* less-than-or-equal: Prefer OVER16 at exactly /16 */ - else if (baud_rate > baud_lowest && baud_rate <= baud_highest_16x) + if (baud_rate > baud_lowest && baud_rate <= baud_highest_16x) usart_set_oversampling(usart, USART_OVERSAMPLING_16); else if (baud_rate > baud_highest_16x && baud_rate <= baud_highest_8x) usart_set_oversampling(usart, USART_OVERSAMPLING_8); @@ -159,18 +159,36 @@ void aux_serial_uart_init(const uintptr_t uart_base) USART_CR1(uart_base) |= USART_CR1_IDLEIE; } +#ifdef PLATFORM_MULTI_UART +void aux_serial_activate_uart(const uintptr_t uart_base) +{ + dma_set_destination_address(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, (uintptr_t)&USART_TDR(uart_base)); + if (uart_base == AUX_UART1) + dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, AUX_UART1_DMA_REQSEL_TX); + else if (uart_base == AUX_UART2) + dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, AUX_UART2_DMA_REQSEL_TX); + + dma_disable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + dma_set_source_address(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, (uintptr_t)&USART_RDR(uart_base)); + if (uart_base == AUX_UART1) + dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, AUX_UART1_DMA_REQSEL_RX); + else if (uart_base == AUX_UART2) + dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, AUX_UART2_DMA_REQSEL_RX); + dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); + + active_uart = uart_base; +} +#endif + void aux_serial_init(void) { -/* Enable clocks */ + /* Enable clocks */ #ifndef PLATFORM_MULTI_UART rcc_periph_clock_enable(USBUSART_CLK); + rcc_periph_clock_enable(USBUSART_DMA_CLK); #else rcc_periph_clock_enable(AUX_UART1_CLK); rcc_periph_clock_enable(AUX_UART2_CLK); -#endif -#ifndef PLATFORM_MULTI_UART - rcc_periph_clock_enable(USBUSART_DMA_CLK); -#else rcc_periph_clock_enable(AUX_UART_DMA_CLK); #endif @@ -271,12 +289,13 @@ void aux_serial_init(void) dma_set_priority(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_PL_HIGH); dma_enable_interrupts(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_HTIF | DMA_TCIF); dma_set_transfer_complete_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_TRANSFER_COMPLETE_MODE_BLOCK); - // dma_request_select(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, ); dma_set_hardware_request(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); dma_set_source_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); dma_set_burst_flow_control(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); #endif +#ifndef PLATFORM_MULTI_UART dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#endif /* Enable interrupts */ #ifndef PLATFORM_MULTI_UART @@ -303,6 +322,9 @@ void aux_serial_init(void) nvic_enable_irq(AUX_UART2_IRQ); nvic_enable_irq(AUX_UART_DMA_TX_IRQ); nvic_enable_irq(AUX_UART_DMA_RX_IRQ); + + /* Activate the default UART (UART1) */ + aux_serial_activate_uart(AUX_UART1); #endif /* Finally enable the USART(s) */ @@ -565,7 +587,9 @@ static void aux_serial_receive_isr(const uint32_t usart, const uint8_t dma_irq) static void aux_serial_dma_transmit_isr(const uint8_t dma_tx_channel) { +#ifndef STM32U5 nvic_disable_irq(USB_IRQ); +#endif /* Stop DMA */ dma_disable_channel(USBUSART_DMA_BUS, dma_tx_channel); @@ -583,7 +607,9 @@ static void aux_serial_dma_transmit_isr(const uint8_t dma_tx_channel) aux_serial_transmit_complete = true; } +#ifndef STM32U5 nvic_enable_irq(USB_IRQ); +#endif } static void aux_serial_dma_receive_isr(const uint8_t usart_irq, const uint8_t dma_rx_channel) @@ -642,49 +668,68 @@ void AUX_UART2_ISR(void) } #endif -#if defined(USBUSART_DMA_TX_ISR) +#ifdef USBUSART_DMA_TX_ISR void USBUSART_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART_DMA_TX_CHAN); } #endif -#if defined(USBUSART1_DMA_TX_ISR) +#ifdef USBUSART1_DMA_TX_ISR void USBUSART1_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART1_DMA_TX_CHAN); } #endif -#if defined(USBUSART2_DMA_TX_ISR) +#ifdef USBUSART2_DMA_TX_ISR void USBUSART2_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART2_DMA_TX_CHAN); } #endif -#if defined(USBUSART_DMA_RX_ISR) +#ifdef AUX_UART_DMA_TX_ISR +void AUX_UART_DMA_TX_ISR(void) +{ + aux_serial_dma_transmit_isr(AUX_UART_DMA_TX_CHAN); +} +#endif + +#ifdef USBUSART_DMA_RX_ISR void USBUSART_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART_IRQ, USBUSART_DMA_RX_CHAN); } #endif -#if defined(USBUSART1_DMA_RX_ISR) +#ifdef USBUSART1_DMA_RX_ISR void USBUSART1_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART1_IRQ, USBUSART1_DMA_RX_CHAN); } #endif -#if defined(USBUSART2_DMA_RX_ISR) +#ifdef USBUSART2_DMA_RX_ISR void USBUSART2_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART2_IRQ, USBUSART2_DMA_RX_CHAN); } #endif -#if defined(USBUSART_DMA_RXTX_ISR) +#ifdef AUX_UART_DMA_RX_ISR +void AUX_UART_DMA_RX_ISR(void) +{ + uint8_t rx_irq = UINT8_MAX; + if (active_uart == AUX_UART1) + rx_irq = AUX_UART1_IRQ; + else if (active_uart == AUX_UART2) + rx_irq = AUX_UART2_IRQ; + aux_serial_dma_receive_isr(rx_irq, AUX_UART_DMA_RX_CHAN); +} +#endif + +#ifdef USBUSART_DMA_RXTX_ISR void USBUSART_DMA_RXTX_ISR(void) { if (dma_get_interrupt_flag(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_CGIF)) From a9c8fdc6f21b4a407c29e7c6a73ada0bd8eab359 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 19 Jan 2026 07:23:50 +0000 Subject: [PATCH 053/247] common/aux_serial: Handle errors that can occur on the UARTs so DMA doesn't get upset by them --- src/platforms/common/aux_serial.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index a4ff80f24ba..52892e4083e 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -157,6 +157,9 @@ void aux_serial_uart_init(const uintptr_t uart_base) usart_set_parity(uart_base, USART_PARITY_NONE); usart_set_flow_control(uart_base, USART_FLOWCONTROL_NONE); USART_CR1(uart_base) |= USART_CR1_IDLEIE; +#ifdef STM32U5 + USART_CR3(uart_base) |= USART_CR3_EIE; +#endif } #ifdef PLATFORM_MULTI_UART @@ -566,23 +569,43 @@ void aux_serial_stage_receive_buffer(void) aux_serial_receive_buffer, aux_serial_receive_read_index, aux_serial_receive_write_index); } -static void aux_serial_receive_isr(const uint32_t usart, const uint8_t dma_irq) +static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) { +#ifndef STM32U5 nvic_disable_irq(dma_irq); /* Get IDLE flag and reset interrupt flags */ - const bool is_idle = usart_get_flag(usart, USART_FLAG_IDLE); - usart_recv(usart); + const bool is_idle = usart_get_flag(uart, USART_FLAG_IDLE); + usart_recv(uart); +#else + (void)dma_irq; + // Inspect the status register for errors, and reset those bits so DMA can continue + const uint32_t status = USART_ISR(uart); + // Handle noise errors + if ((status & USART_ISR_NF) != 0U) + USART_ICR(uart) = USART_ICR_NCF; + // Handle framing errors + if ((status & USART_ISR_FE) != 0U) + USART_ICR(uart) = USART_ICR_FECF; + // Handle overrun errors + if ((status & USART_ISR_ORE) != 0U) + USART_ICR(uart) = USART_ICR_ORECF; + + // Decode if idle happened + const bool is_idle = (status & USART_ISR_IDLE) != 0; +#endif /* If line is now idle, then transmit a packet */ if (is_idle) { #ifdef USART_ICR_IDLECF - USART_ICR(usart) = USART_ICR_IDLECF; + USART_ICR(uart) = USART_ICR_IDLECF; #endif debug_serial_run(); } +#ifndef STM32U5 nvic_enable_irq(dma_irq); +#endif } static void aux_serial_dma_transmit_isr(const uint8_t dma_tx_channel) From 0fb4bd8744a1362d847de72a42181b961c176c93 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 19 Jan 2026 09:53:41 +0000 Subject: [PATCH 054/247] common/aux_serial: More tweaks for the IRQ handling to make things safer and more reasonable --- src/platforms/common/aux_serial.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 52892e4083e..5bb66f1aed4 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -60,7 +60,7 @@ static volatile uint8_t aux_serial_led_state = 0; #define DMA_PL_HIGH DMA_SxCR_PL_HIGH #define DMA_CGIF DMA_ISR_FLAGS #elif defined(STM32U5) -#define DMA_PL_HIGH DMA_CxCR_PRIO_HIGH +#define DMA_PL_HIGH DMA_CxCR_PRIO_VERY_HIGH #define DMA_CGIF DMA_ISR_FLAGS #define USBUSART_DMA_BUS AUX_UART_DMA_BUS #define USBUSART_DMA_TX_CHAN AUX_UART_DMA_TX_CHAN @@ -605,6 +605,8 @@ static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) #ifndef STM32U5 nvic_enable_irq(dma_irq); +#else + dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #endif } @@ -637,13 +639,19 @@ static void aux_serial_dma_transmit_isr(const uint8_t dma_tx_channel) static void aux_serial_dma_receive_isr(const uint8_t usart_irq, const uint8_t dma_rx_channel) { +#ifndef STM32U5 nvic_disable_irq(usart_irq); +#else + (void)usart_irq; +#endif /* Clear flags and transmit a packet */ dma_clear_interrupt_flags(USBUSART_DMA_BUS, dma_rx_channel, DMA_CGIF); debug_serial_run(); +#ifndef STM32U5 nvic_enable_irq(usart_irq); +#endif } #ifndef PLATFORM_MULTI_UART From 45f10fb45c747808968fb639742ac59988decef4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 26 Jan 2026 14:27:08 +0000 Subject: [PATCH 055/247] common/aux_serial: Fixed an order of includes error that meant certain steering macros weren't available when they should be --- src/platforms/common/aux_serial.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 5bb66f1aed4..6d3f66e0639 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022-2025 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -18,6 +18,11 @@ * along with this program. If not, see . */ +#include "general.h" +#include "platform.h" +#include "usb_serial.h" +#include "aux_serial.h" + #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) #include #include @@ -31,11 +36,6 @@ #endif #include -#include "general.h" -#include "platform.h" -#include "usb_serial.h" -#include "aux_serial.h" - static char aux_serial_receive_buffer[AUX_UART_BUFFER_SIZE]; /* FIFO in pointer, writes assumed to be atomic, should be only incremented within RX ISR */ static uint16_t aux_serial_receive_write_index = 0; From 0ee082b40b4d4c59872577adc4d792fbb38b2931 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 26 Jan 2026 15:16:04 +0000 Subject: [PATCH 056/247] common/aux_serial: Impelemented the UART1 half of the switchable UART mechanism using the EXTI --- src/platforms/bmp-v3/platform.h | 17 ++++++++------- src/platforms/common/aux_serial.c | 35 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index d9b09ae71a9..6a4edd1715b 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -220,13 +220,16 @@ extern int hwversion; #define IRQ_PRI_SWO_DMA (0U << 4U) /* PA2/3 as USART2 TX/RX */ -#define AUX_UART1 USART2 -#define AUX_UART1_CLK RCC_USART2 -#define AUX_UART1_IRQ NVIC_USART2_IRQ -#define AUX_UART1_ISR(x) usart2_isr(x) -#define AUX_UART1_PORT GPIOA -#define AUX_UART1_TX_PIN GPIO2 -#define AUX_UART1_RX_PIN GPIO3 +#define AUX_UART1 USART2 +#define AUX_UART1_CLK RCC_USART2 +#define AUX_UART1_IRQ NVIC_USART2_IRQ +#define AUX_UART1_ISR(x) usart2_isr(x) +#define AUX_UART1_PORT GPIOA +#define AUX_UART1_TX_PIN GPIO2 +#define AUX_UART1_RX_PIN GPIO3 +#define AUX_UART1_RX_DETECT_EXTI EXTI3 +#define AUX_UART1_RX_DETECT_IRQ NVIC_EXTI3_IRQ +#define AUX_UART1_RX_DETECT_ISR(x) exti3_isr(x) /* PB6/7 as USART1 TX/RX */ #define AUX_UART2 USART1 diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 6d3f66e0639..84578a5e3c0 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -28,6 +28,9 @@ #include #include #include +#ifdef PLATFORM_MULTI_UART +#include +#endif #elif defined(LM4F) #include #include @@ -300,6 +303,21 @@ void aux_serial_init(void) dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #endif +#ifdef PLATFORM_MULTI_UART + /* Configure the EXTI logic to listen on the RX pins to determine which is currently active */ + exti_set_trigger(AUX_UART1_RX_DETECT_EXTI, EXTI_TRIGGER_RISING); + exti_select_source(AUX_UART1_RX_DETECT_EXTI, AUX_UART1_PORT); + /* Activate the default UART (UART1) if RX is already high on it */ + if (gpio_get(AUX_UART1_PORT, AUX_UART1_RX_PIN)) + aux_serial_activate_uart(AUX_UART1); + else + /* Otherwise enable the EXTI to determine when the pin goes high */ + exti_enable_request(AUX_UART1_RX_DETECT_EXTI); + + nvic_set_priority(AUX_UART1_RX_DETECT_IRQ, IRQ_PRI_AUX_UART); + nvic_enable_irq(AUX_UART1_RX_DETECT_IRQ); +#endif + /* Enable interrupts */ #ifndef PLATFORM_MULTI_UART nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); @@ -325,9 +343,6 @@ void aux_serial_init(void) nvic_enable_irq(AUX_UART2_IRQ); nvic_enable_irq(AUX_UART_DMA_TX_IRQ); nvic_enable_irq(AUX_UART_DMA_RX_IRQ); - - /* Activate the default UART (UART1) */ - aux_serial_activate_uart(AUX_UART1); #endif /* Finally enable the USART(s) */ @@ -769,6 +784,20 @@ void USBUSART_DMA_RXTX_ISR(void) USBUSART_DMA_TX_ISR(); } #endif + +#ifdef PLATFORM_MULTI_UART +void AUX_UART1_RX_DETECT_ISR(void) +{ + /* + * UART1 just became active, so bring it up and disable the EXTI for it, making sure UART2's is + * active in case the user swaps UARTs over. + */ + aux_serial_activate_uart(AUX_UART1); + exti_reset_request(AUX_UART1_RX_DETECT_EXTI); + exti_disable_request(AUX_UART1_RX_DETECT_EXTI); + /* exti_enable_request(AUX_UART2_RX_DETECT_EXTI); */ +} +#endif #elif defined(LM4F) char *aux_serial_current_transmit_buffer(void) { From 5a207dc7f3731ace6e0635a26f14b656798c9769 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 26 Jan 2026 15:40:12 +0000 Subject: [PATCH 057/247] common/aux_serial: Roughly implemented the UART2 half of the switchable UART mechanism using the EXTI --- src/platforms/bmp-v3/platform.h | 23 +++++++++++----- src/platforms/common/aux_serial.c | 45 ++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 6a4edd1715b..2641d4ea419 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -232,13 +232,22 @@ extern int hwversion; #define AUX_UART1_RX_DETECT_ISR(x) exti3_isr(x) /* PB6/7 as USART1 TX/RX */ -#define AUX_UART2 USART1 -#define AUX_UART2_CLK RCC_USART1 -#define AUX_UART2_IRQ NVIC_USART1_IRQ -#define AUX_UART2_ISR(x) usart1_isr(x) -#define AUX_UART2_PORT GPIOB -#define AUX_UART2_TX_PIN GPIO6 -#define AUX_UART2_RX_PIN GPIO7 +#define AUX_UART2 USART1 +#define AUX_UART2_CLK RCC_USART1 +#define AUX_UART2_IRQ NVIC_USART1_IRQ +#define AUX_UART2_ISR(x) usart1_isr(x) +#define AUX_UART2_PORT GPIOB +#define AUX_UART2_TX_PIN GPIO6 +#define AUX_UART2_RX_PIN GPIO7 +#define AUX_UART2_DIR_PORT GPIOC +#define AUX_UART2_DIR_PIN GPIO13 +#define AUX_UART2_RX_DETECT_EXTI1 EXTI7 +#define AUX_UART2_RX_DETECT_EXTI2 EXTI6 +#define AUX_UART2_RX_DETECT_EXTI (AUX_UART2_RX_DETECT_EXTI1 | AUX_UART2_RX_DETECT_EXTI2) +#define AUX_UART2_RX_DETECT_IRQ1 NVIC_EXTI7_IRQ +#define AUX_UART2_RX_DETECT_IRQ2 NVIC_EXTI6_IRQ +#define AUX_UART2_RX_DETECT_ISR1(x) exti7_isr(x) +#define AUX_UART2_RX_DETECT_ISR2(x) exti6_isr(x) #define AUX_UART_DMA_BUS GPDMA1 #define AUX_UART_DMA_CLK RCC_GPDMA1 diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 84578a5e3c0..6077e4f0ac1 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -306,16 +306,31 @@ void aux_serial_init(void) #ifdef PLATFORM_MULTI_UART /* Configure the EXTI logic to listen on the RX pins to determine which is currently active */ exti_set_trigger(AUX_UART1_RX_DETECT_EXTI, EXTI_TRIGGER_RISING); + exti_set_trigger(AUX_UART2_RX_DETECT_EXTI, EXTI_TRIGGER_RISING); exti_select_source(AUX_UART1_RX_DETECT_EXTI, AUX_UART1_PORT); + exti_select_source(AUX_UART2_RX_DETECT_EXTI, AUX_UART2_PORT); + /* Activate the default UART (UART1) if RX is already high on it */ - if (gpio_get(AUX_UART1_PORT, AUX_UART1_RX_PIN)) + if (gpio_get(AUX_UART1_PORT, AUX_UART1_RX_PIN)) { aux_serial_activate_uart(AUX_UART1); - else - /* Otherwise enable the EXTI to determine when the pin goes high */ + exti_enable_request(AUX_UART2_RX_DETECT_EXTI); + } + /* Activate the secondary UART (UART2) if either of the pins is already high */ + else if (gpio_get(AUX_UART2_PORT, AUX_UART2_RX_PIN | AUX_UART2_TX_PIN)) { + aux_serial_activate_uart(AUX_UART2); + exti_enable_request(AUX_UART1_RX_DETECT_EXTI); + } else { + /* Otherwise just enable the EXTIs for both to see which comes up first */ exti_enable_request(AUX_UART1_RX_DETECT_EXTI); + exti_enable_request(AUX_UART2_RX_DETECT_EXTI); + } nvic_set_priority(AUX_UART1_RX_DETECT_IRQ, IRQ_PRI_AUX_UART); + nvic_set_priority(AUX_UART2_RX_DETECT_IRQ1, IRQ_PRI_AUX_UART); + nvic_set_priority(AUX_UART2_RX_DETECT_IRQ2, IRQ_PRI_AUX_UART); nvic_enable_irq(AUX_UART1_RX_DETECT_IRQ); + nvic_enable_irq(AUX_UART2_RX_DETECT_IRQ1); + nvic_enable_irq(AUX_UART2_RX_DETECT_IRQ2); #endif /* Enable interrupts */ @@ -795,7 +810,29 @@ void AUX_UART1_RX_DETECT_ISR(void) aux_serial_activate_uart(AUX_UART1); exti_reset_request(AUX_UART1_RX_DETECT_EXTI); exti_disable_request(AUX_UART1_RX_DETECT_EXTI); - /* exti_enable_request(AUX_UART2_RX_DETECT_EXTI); */ + exti_enable_request(AUX_UART2_RX_DETECT_EXTI); +} + +void aux_uart2_rx_detect_isr(void) +{ + /* + * UART2 just became active, so bring it up and disable the EXTI for it, making sure UART1's is + * active in case the user swaps UARTs over + */ + aux_serial_activate_uart(AUX_UART2); + exti_reset_request(AUX_UART2_RX_DETECT_EXTI); + exti_disable_request(AUX_UART2_RX_DETECT_EXTI); + exti_enable_request(AUX_UART1_RX_DETECT_EXTI); +} + +void AUX_UART2_RX_DETECT_ISR1(void) +{ + aux_uart2_rx_detect_isr(); +} + +void AUX_UART2_RX_DETECT_ISR2(void) +{ + aux_uart2_rx_detect_isr(); } #endif #elif defined(LM4F) From dd8ec3d8f7411812ec515c8e352873dfad38b614 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 4 Feb 2026 11:49:57 +0000 Subject: [PATCH 058/247] bmp-v3: Implemented platform support for UART RX/TX switching for the second UART --- src/include/platform_support.h | 7 ++++++ src/platforms/bmp-v3/platform.c | 43 ++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/include/platform_support.h b/src/include/platform_support.h index ed49fb5166e..2b8ee5f78e8 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -84,4 +84,11 @@ uint8_t platform_spi_xfer(spi_bus_e bus, uint8_t value); const char *platform_ident(void); #endif +#ifdef PLATFORM_MULTI_UART +void platform_enable_uart2(void); +void platform_disable_uart2(void); +bool platform_is_uart2_enabled(void); +void platform_switch_dir_uart2(void); +#endif + #endif /* INCLUDE_PLATFORM_SUPPORT_H */ diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 4f97d674d59..c7d526b87e5 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -145,14 +146,16 @@ static void gpio_init(void) /* Configure the first UART used for the AUX serial interface */ gpio_set_af(AUX_UART1_PORT, GPIO_AF7, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); gpio_set_output_options(AUX_UART1_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); - gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN); - gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART1_RX_PIN); + gpio_mode_setup(AUX_UART1_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART1_TX_PIN | AUX_UART1_RX_PIN); /* Configure the second UART used for the AUX serial interface */ gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); gpio_set_output_options(AUX_UART2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN); - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, AUX_UART2_RX_PIN); + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + /* Start with the pins configured the "correct" way around (unswapped) */ + gpio_set(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); + gpio_set_output_options(AUX_UART2_DIR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, AUX_UART2_DIR_PIN); + gpio_mode_setup(AUX_UART2_DIR_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, AUX_UART2_DIR_PIN); /* Configure the pin used for tpwr control */ gpio_clear(TPWR_EN_PORT, TPWR_EN_PIN); @@ -435,3 +438,35 @@ uint8_t platform_spi_xfer(const spi_bus_e bus, const uint8_t value) return 0xffU; return spi_xfer8(EXT_SPI, value); } + +void platform_enable_uart2(void) +{ + /* Reconfigure the GPIOs to connect to the UART */ + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + /* Use the current direction setting to determine how to enable the UART */ + if (gpio_get(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN)) + usart_set_swap_tx_rx(AUX_UART2, false); + else + usart_set_swap_tx_rx(AUX_UART2, true); + /* Now pin swapping is configured, enable the UART */ + usart_enable(AUX_UART2); +} + +void platform_disable_uart2(void) +{ + /* Dsiable the UART (so we can go back into being able to change the pin swapping) */ + usart_disable(AUX_UART2); + /* Reconfigure the GPIOs back to inputs so we can listen for which is high to watch for new connections */ + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); +} + +bool platform_is_uart2_enabled(void) +{ + return (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; +} + +void platform_switch_dir_uart2(void) +{ + /* Swap the directions of the BMPU connector UART pins */ + gpio_toggle(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); +} From 6dfa0ece3dbdcbaf8433df5f634e7e80f654e382 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 4 Feb 2026 11:53:46 +0000 Subject: [PATCH 059/247] common/timing_stm32: Implemented handling for seeing which way around the UART needs to be switched by toggling the direction signal on the level translation once every 10ms --- src/platforms/common/stm32/timing_stm32.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index a3b3cc7f8d3..2a064898ef6 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -29,13 +29,13 @@ #include bool running_status = false; -static volatile uint32_t time_ms = 0; +static _Atomic uint32_t time_ms = 0; uint32_t target_clk_divider = 0; static size_t morse_tick = 0; #if defined(PLATFORM_HAS_POWER_SWITCH) && defined(STM32F1) -static uint8_t monitor_ticks = 0; -static uint8_t monitor_error_count = 0; +static uint8_t monitor_ticks = 0U; +static uint8_t monitor_error_count = 0U; /* Derived from calculating (1.2V / 3.0V) * 4096 */ #define ADC_VREFINT_MAX 1638U @@ -45,6 +45,9 @@ static uint8_t monitor_error_count = 0; */ #define ADC_VREFINT_MIN 1404U #endif +#ifdef PLATFORM_MULTI_UART +static uint8_t uart_ticks = 0U; +#endif static void usb_config_morse_msg_update(void) { @@ -147,6 +150,19 @@ void sys_tick_handler(void) } else monitor_ticks = 0; #endif + +#ifdef PLATFORM_MULTI_UART + /* Only do the toggling if the UART is not currently enabled */ + if (!platform_is_uart2_enabled()) { + /* Every 10th tick, swap the direction of the UART */ + if (++uart_ticks == 10U) { + platform_switch_dir_uart2(); + /* And reset the counter back to 0 */ + uart_ticks = 0U; + } + } else + uart_ticks = 0U; +#endif } uint32_t platform_time_ms(void) From 6e6545e5e145cdacd81c03564d2e1dc7a7ff166a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 17 Feb 2026 02:58:10 +0000 Subject: [PATCH 060/247] bmp-v3: Implemented logic for enabling and disabling the secondary UART appropriately based on state changes --- src/include/platform_support.h | 8 ++++++++ src/platforms/bmp-v3/platform.c | 19 ++++++++++++++++++- src/platforms/common/aux_serial.c | 5 +++++ src/platforms/common/stm32/timing_stm32.c | 19 +++++++++++++++++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/include/platform_support.h b/src/include/platform_support.h index 2b8ee5f78e8..c417fce5e2c 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -85,10 +85,18 @@ const char *platform_ident(void); #endif #ifdef PLATFORM_MULTI_UART +typedef enum uart_state { + UART_STATE_UNKNOWN, + UART_STATE_IDLE, + UART_STATE_LOST, +} uart_state_e; + void platform_enable_uart2(void); void platform_disable_uart2(void); bool platform_is_uart2_enabled(void); void platform_switch_dir_uart2(void); +void platform_uart2_state_change(uint32_t state); +uart_state_e platform_uart2_state(void); #endif #endif /* INCLUDE_PLATFORM_SUPPORT_H */ diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index c7d526b87e5..2a9ccd92e12 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -59,6 +59,8 @@ static void adc_init(void); int hwversion = -1; +static uart_state_e uart2_state = UART_STATE_UNKNOWN; + void platform_init(void) { hwversion = 0; @@ -454,8 +456,9 @@ void platform_enable_uart2(void) void platform_disable_uart2(void) { - /* Dsiable the UART (so we can go back into being able to change the pin swapping) */ + /* Disable the UART (so we can go back into being able to change the pin swapping) */ usart_disable(AUX_UART2); + uart2_state = UART_STATE_UNKNOWN; /* Reconfigure the GPIOs back to inputs so we can listen for which is high to watch for new connections */ gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); } @@ -470,3 +473,17 @@ void platform_switch_dir_uart2(void) /* Swap the directions of the BMPU connector UART pins */ gpio_toggle(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); } + +void platform_uart2_state_change(const uint32_t state) +{ + /* Make a note of whether either Idle or Framing Error have occured */ + if (state & USART_ISR_IDLE) + uart2_state = UART_STATE_IDLE; + else if (state & USART_ISR_FE) + uart2_state = UART_STATE_LOST; +} + +uart_state_e platform_uart2_state(void) +{ + return uart2_state; +} diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 6077e4f0ac1..deb2a135c84 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -633,6 +633,10 @@ static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) debug_serial_run(); } +#ifdef PLATFORM_MULTI_UART + if (uart == AUX_UART2) + platform_uart2_state_change(status); +#endif #ifndef STM32U5 nvic_enable_irq(dma_irq); #else @@ -819,6 +823,7 @@ void aux_uart2_rx_detect_isr(void) * UART2 just became active, so bring it up and disable the EXTI for it, making sure UART1's is * active in case the user swaps UARTs over */ + platform_enable_uart2(); aux_serial_activate_uart(AUX_UART2); exti_reset_request(AUX_UART2_RX_DETECT_EXTI); exti_disable_request(AUX_UART2_RX_DETECT_EXTI); diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 2a064898ef6..702bb210a49 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -160,8 +160,23 @@ void sys_tick_handler(void) /* And reset the counter back to 0 */ uart_ticks = 0U; } - } else - uart_ticks = 0U; + } else { + /* + * If the UART goes into framing error and that persists for more than a milisecond or two, then + * it's probably safe to assume that the wires became disconnected and the UART is no longer active + * in which case we then want to disable the UART and go back into swap scanning. Additionally, we'll + * want to either make the other UART active, or make all UARTs inactive. + */ + const uart_state_e state = platform_uart2_state(); + if (state == UART_STATE_LOST) { + if (++uart_ticks == 2U) { + platform_disable_uart2(); + uart_ticks = 0U; + } + } else + /* Otherwise if the UART state is either not known or the UART is idle, reset the tick counter */ + uart_ticks = 0U; + } #endif } From c78f1f4cbeba9785380152f0cf142bd7aa4bf291 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 18:14:15 +0000 Subject: [PATCH 061/247] github: Enabled BMPv3 in both the PR and build-and-upload flows --- .github/workflows/build-and-upload.yml | 1 + .github/workflows/build-pr.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 486d2e90b6b..2f0db191564 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -39,6 +39,7 @@ jobs: - 'blackpill-f401ce' - 'blackpill-f411ce' - 'bluepill' + - 'bmp-v3' - 'ctxlink' - 'f072' - 'f3' diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index cf2ac9aed31..0b9683ddc69 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -42,6 +42,7 @@ jobs: - 'blackpill-f401ce' - 'blackpill-f411ce' - 'bluepill' + - 'bmp-v3' - 'ctxlink' - 'f072' - 'f3' From b2620d6fc659e6983608bb9052e99594fb54991f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 18:21:15 +0000 Subject: [PATCH 062/247] f072/atomic: Implemented compare-exchange and fetch-add for 32-bit unsigned values --- src/platforms/f072/atomic.c | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/platforms/f072/atomic.c b/src/platforms/f072/atomic.c index 88e652a12c9..0f229897024 100644 --- a/src/platforms/f072/atomic.c +++ b/src/platforms/f072/atomic.c @@ -108,6 +108,28 @@ uint16_t atomic_fetch_add_2(uint16_t *const atomic_value, const uint16_t add_val return current_value; } +uint32_t atomic_fetch_add_4(uint32_t *const atomic_value, const uint32_t add_value, const int model) +{ + /* Create a model-appropriate sync barrier to start */ + pre_barrier(model); + /* Now grab the current value of the atomic to be modified */ + uint32_t new_value; + uint32_t current_value = *atomic_value; + /* Try, in a loop, doing the addition to the value */ + do { + new_value = current_value + add_value; + /* + * Try to replace the value store by the atomic by the updated value computed here - if this fails + * then we get the new value returned in current_value and can try again. + */ + } while (!atomic_compare_exchange_weak_explicit( + atomic_value, ¤t_value, new_value, memory_order_relaxed, memory_order_relaxed)); + /* Create a model-appropriate sync barrier to finish */ + post_barrier(model); + /* Finally, return the value that was in the atomic to complete the operation's contract */ + return current_value; +} + uint16_t atomic_fetch_sub_2(uint16_t *const atomic_value, const uint16_t sub_value, const int model) { /* Create a model-appropriate sync barrier to start */ @@ -155,14 +177,43 @@ bool atomic_compare_exchange_2(uint16_t *const atomic_value, uint16_t *const exp return result; } +bool atomic_compare_exchange_4(uint32_t *const atomic_value, uint32_t *const expected_value, const uint32_t new_value, + const bool weak, const int success_model, const int failure_model) +{ + (void)weak; + (void)failure_model; + /* Create a model-appropriate sequence barrier to start, and begin a protected block */ + pre_seq_barrier(success_model); + const uint32_t protect_state = protect_begin(atomic_value); + + /* Read out the current value of the atomic, compare it to the expected */ + const uint32_t old_value = *atomic_value; + const bool result = old_value == *expected_value; + /* If it's the expected value, write the new value to complete the RMW cycle */ + if (result) + *atomic_value = new_value; + /* Otherwise, uphold the contract required and write the current value to the expected value pointer */ + else + *expected_value = old_value; + + /* Finish up with a model-appropriate sequence barrier having ended the protected block */ + protect_end(atomic_value, protect_state); + post_seq_barrier(success_model); + return result; +} + /* Alias the functions defined to their special names to satisfy the compiler */ /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming) */ uint16_t __atomic_fetch_add_2(volatile void *atomic_value, uint16_t add_value, int swap_model) __attribute__((alias("atomic_fetch_add_2"))); +unsigned int __atomic_fetch_add_4(volatile void *atomic_value, unsigned int add_value, int swap_model) + __attribute__((alias("atomic_fetch_add_4"))); uint16_t __atomic_fetch_sub_2(volatile void *atomic_value, uint16_t add_value, int swap_model) __attribute__((alias("atomic_fetch_sub_2"))); bool __atomic_compare_exchange_2(volatile void *atomic_value, void *expected_value, uint16_t new_value, bool weak, int success_model, int failure_model) __attribute__((alias("atomic_compare_exchange_2"))); +bool __atomic_compare_exchange_4(volatile void *atomic_value, void *expected_value, unsigned int new_value, bool weak, + int success_model, int failure_model) __attribute__((alias("atomic_compare_exchange_4"))); /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming) */ /* GCC 14 and newer don't provide __atomic_test_and_set, so we have to here */ From 1abd3fbe77fbe338a7745fe9ea14b92b4eed9cb5 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 9 Mar 2026 05:00:21 +0000 Subject: [PATCH 063/247] common/stm32/dfucore: Removed all the extra unnecessary spaces from the DfuSe interface strings --- src/platforms/common/stm32/dfucore.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platforms/common/stm32/dfucore.c b/src/platforms/common/stm32/dfucore.c index bdd052d40b9..b0ebb88347b 100644 --- a/src/platforms/common/stm32/dfucore.c +++ b/src/platforms/common/stm32/dfucore.c @@ -28,21 +28,21 @@ #include #if defined(STM32F1HD) -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/4*002Ka,000*002Kg" -#define DFU_IFACE_STRING_OFFSET 38 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/4*002Ka,000*002Kg" +#define DFU_IFACE_STRING_OFFSET 35 #define DFU_IFACE_PAGESIZE 2 #elif defined(STM32F1) -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,000*001Kg" -#define DFU_IFACE_STRING_OFFSET 38 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/8*001Ka,000*001Kg" +#define DFU_IFACE_STRING_OFFSET 35 #define DFU_IFACE_PAGESIZE 1 #elif defined(STM32F4) || defined(STM32F7) #define DFU_IFACE_PAGESIZE 128 #if APP_START == 0x08020000 -#define DFU_IFACE_STRING_OFFSET 62 -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Ka,1*064Ka,1*128Kg,002*128Kg" +#define DFU_IFACE_STRING_OFFSET 59 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/1*016Ka,3*016Ka,1*064Ka,1*128Kg,002*128Kg" #elif APP_START == 0x08004000 -#define DFU_IFACE_STRING_OFFSET 54 -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,000*128Kg" +#define DFU_IFACE_STRING_OFFSET 51 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/1*016Ka,3*016Kg,1*064Kg,000*128Kg" #endif #elif defined(STM32U5) #define DFU_IFACE_STRING "@Internal Flash/0x08000000/2*8Ka,000*8Kg" From 5a371b2e922a671db9d427f90a29c2e643238870 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 06:22:35 +0000 Subject: [PATCH 064/247] stm32l4: Implemented option bytes support for the STM32U5 --- src/target/stm32l4.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 0bb0a4e55f8..29bfb50c1de 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -493,6 +493,8 @@ static const uint8_t stm32l4_opt_reg_offsets[9] = {0x20, 0x24, 0x28, 0x2c, 0x30, static const uint8_t stm32g4_opt_reg_offsets[11] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x70, 0x44, 0x48, 0x4c, 0x50, 0x74}; static const uint8_t stm32wl_opt_reg_offsets[7] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38}; static const uint8_t stm32wb_opt_reg_offsets[10] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c, 0x80, 0x84}; +static const uint8_t stm32u5_opt_reg_offsets[16] = { + 0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c}; static const uint32_t stm32l4_default_options_values[9] = { 0xffeff8aaU, @@ -548,6 +550,25 @@ static const uint32_t stm32wb_default_options_values[10] = { 0x00000000U, // Secure SRAM2 start address and CPU2 reset vector option bytes }; +static const uint32_t stm32u575_default_options_values[16] = { + 0x1feff8aaU, + 0x0800007fU, + 0x0bf9007fU, + 0x0c00007cU, + 0xffffff80U, + 0x7f807f80U, + 0xff80ffffU, + 0xff80ffffU, + 0xffffff80U, + 0x7f807f80U, + 0xff80ffffU, + 0xff80ffffU, + 0x00000000U, + 0x00000000U, + 0x00000000U, + 0x00000000U, +}; + static_assert(ARRAY_LENGTH(stm32l4_opt_reg_offsets) == ARRAY_LENGTH(stm32l4_default_options_values), "Number of stm32l4 option registers must match number of default values"); static_assert(ARRAY_LENGTH(stm32g4_opt_reg_offsets) == ARRAY_LENGTH(stm32g4_default_options_values), @@ -556,6 +577,8 @@ static_assert(ARRAY_LENGTH(stm32wl_opt_reg_offsets) == ARRAY_LENGTH(stm32wl_defa "Number of stm32wl option registers must match number of default values"); static_assert(ARRAY_LENGTH(stm32wb_opt_reg_offsets) == ARRAY_LENGTH(stm32wb_default_options_values), "Number of stm32wb option registers must match number of default values"); +static_assert(ARRAY_LENGTH(stm32u5_opt_reg_offsets) == ARRAY_LENGTH(stm32u575_default_options_values), + "Number of stm32u5 option registers must match number of default values"); /* Retrieve device basic information, just add to the vector to extend */ static const stm32l4_device_info_s *stm32l4_get_device_info(const uint16_t device_id) @@ -1036,6 +1059,12 @@ static stm32l4_option_bytes_info_s stm32l4_get_opt_bytes_info(const uint16_t par .offsets = stm32wb_opt_reg_offsets, .default_values = stm32wb_default_options_values, }; + case ID_STM32U575: + return (stm32l4_option_bytes_info_s){ + .word_count = ARRAY_LENGTH(stm32u575_default_options_values), + .offsets = stm32u5_opt_reg_offsets, + .default_values = stm32u575_default_options_values, + }; default: return (stm32l4_option_bytes_info_s){ .word_count = ARRAY_LENGTH(stm32l4_default_options_values), From 8957f294fded285c2d40b6a1b15304867fae2501 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 06:23:39 +0000 Subject: [PATCH 065/247] stm32l4: Modernised and fixed how erase is done to better fit the STM32U5 as the old method was making erase fail to execute correctly on these devices --- src/target/stm32l4.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 29bfb50c1de..f5403f3c264 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -909,6 +909,7 @@ static bool stm32l4_flash_busy_wait(target_s *const target, platform_timeout_s * static bool stm32l4_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t len) { + (void)len; target_s *target = flash->t; const stm32l4_flash_s *const sf = (stm32l4_flash_s *)flash; @@ -920,21 +921,17 @@ static bool stm32l4_flash_erase(target_flash_s *const flash, const target_addr_t if (!stm32l4_flash_busy_wait(target, NULL)) return false; - /* Erase the requested chunk of flash, one page at a time. */ - for (size_t offset = 0; offset < len; offset += flash->blocksize) { - const uint32_t page = (addr + offset - STM32L4_FLASH_BANK1_BASE) / flash->blocksize; - const uint32_t bank_flags = addr + offset >= sf->bank1_start ? STM32L4_FPEC_CTRL_BANK_ERASE : 0; - const uint32_t ctrl = STM32L4_FPEC_CTRL_PAGE_ERASE | (page << STM32L4_FPEC_CTRL_PAGE_SHIFT) | bank_flags; - /* Flash page erase instruction */ - stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl); - /* write address to FMA */ - stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl | STM32L4_FPEC_CTRL_START); - - /* Wait for completion or an error */ - if (!stm32l4_flash_busy_wait(target, NULL)) - return false; - } - return true; + /* Erase the requested chunk of flash */ + const uint32_t page = (addr - STM32L4_FLASH_BANK1_BASE) / flash->blocksize; + const uint32_t bank_flags = addr >= sf->bank1_start ? STM32L4_FPEC_CTRL_BANK_ERASE : 0; + const uint32_t ctrl = STM32L4_FPEC_CTRL_PAGE_ERASE | (page << STM32L4_FPEC_CTRL_PAGE_SHIFT) | bank_flags; + /* Flash page erase instruction */ + stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl); + /* write address to FMA */ + stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl | STM32L4_FPEC_CTRL_START); + + /* Wait for completion or an error */ + return stm32l4_flash_busy_wait(target, NULL); } static bool stm32l4_flash_write( From 6ab8dbff64101e3bbd0d0fbcd3e036e76c846169 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 06:29:53 +0000 Subject: [PATCH 066/247] stm32l4: Enable non-halting I/O for the STM32U5 parts as there are no noticable ill effects This needs properly documenting and that documentation must be written before this is PR'd. The flag does not cause data corruption and appears to work the correct way, however it's unknown if it's acting like DMA or micro-halting the core. We suspect the latter. --- src/target/stm32l4.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index f5403f3c264..f74efc9088b 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -765,6 +765,12 @@ bool stm32l4_probe(target_s *const target) target->core = "M33+TZ"; } break; + case ID_STM32U535: + case ID_STM32U5Fx: + case ID_STM32U59x: + case ID_STM32U575: + target->target_options |= TOPT_NON_HALTING_MEM_IO; + break; default: break; } From 571a68e79ead15da4f643994192934ecf691ef24 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 8 Mar 2026 08:01:17 +0000 Subject: [PATCH 067/247] hosted/serial_win: Prevent the RegGetValue() call that fills the output string with its value from getting compiled out when assertions are off --- src/platforms/hosted/serial_win.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platforms/hosted/serial_win.c b/src/platforms/hosted/serial_win.c index 0b16f485756..5bf0a9155e9 100644 --- a/src/platforms/hosted/serial_win.c +++ b/src/platforms/hosted/serial_win.c @@ -175,7 +175,7 @@ static char *read_key_from_path(const char *const subpath, const char *const key return NULL; DWORD value_len = 0; - const LSTATUS result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); + LSTATUS result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA) { display_error(result, "retrieving value for key", key_name); RegCloseKey(key_path_handle); @@ -188,7 +188,8 @@ static char *read_key_from_path(const char *const subpath, const char *const key RegCloseKey(key_path_handle); return NULL; } - assert(RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, value, &value_len) == ERROR_SUCCESS); + result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, value, &value_len); + assert(result == ERROR_SUCCESS); RegCloseKey(key_path_handle); return value; } From 6fe4d68e78bac9d7b1827b92bdadb52db591c463 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 8 Mar 2026 08:06:01 +0000 Subject: [PATCH 068/247] hosted/bmp_serial: Prevent the RegGetValue() call that fills the output string with its value from getting compiled out when assertions are off --- src/platforms/hosted/bmp_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platforms/hosted/bmp_serial.c b/src/platforms/hosted/bmp_serial.c index 79a4d15a8c3..e200cce4ef2 100644 --- a/src/platforms/hosted/bmp_serial.c +++ b/src/platforms/hosted/bmp_serial.c @@ -109,7 +109,7 @@ static const char *read_value_str_from_path(HKEY path_handle, const char *const { DWORD value_len = 0U; /* Start by trying to discover how long the string held by the key is */ - const LSTATUS result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); + LSTATUS result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); /* If that didn't work, we have no hoope, so bail */ if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA) { display_error(result, "retrieving registry value", value_name); @@ -126,7 +126,8 @@ static const char *read_value_str_from_path(HKEY path_handle, const char *const } /* Finally, try reading the value and return it to the user if this didn't explode */ - assert(RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, value, &value_len) == ERROR_SUCCESS); + result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, value, &value_len); + assert(result == ERROR_SUCCESS); return value; } From f086915b40ac552dfe34620d82f753964d59acad Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 06:25:48 +0000 Subject: [PATCH 069/247] gdb_main: Fix a comment style issue in exec_q_memory_map() --- src/gdb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gdb_main.c b/src/gdb_main.c index 218b93a9c27..d8f17d4b260 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -506,7 +506,7 @@ static void exec_q_memory_map(const char *packet, const size_t length) return; } char buf[1024]; - target_mem_map(target, buf, sizeof(buf)); /* Fixme: Check size!*/ + target_mem_map(target, buf, sizeof(buf)); /* Fixme: Check size! */ handle_q_string_reply(buf, packet); } From a7421d9a387252655ee8895197b160f9efbca546 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 23 Feb 2026 06:26:45 +0000 Subject: [PATCH 070/247] cortexm: Cleaned up the comment style and some of the logic in the ARMv8-M fault handling in cortexm_halt_poll() --- src/target/cortexm.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 280163f71a2..1ce07e43ecb 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -864,21 +864,21 @@ static target_halt_reason_e cortexm_halt_poll(target_s *target, target_addr64_t priv->dcache_enabled = ccr & CORTEXM_CCR_DCACHE_ENABLE; priv->icache_enabled = ccr & CORTEXM_CCR_ICACHE_ENABLE; - bool fault_state = false; - // the V8 may stop before actually executing the instruction - // so reading dfsr might not work. - // Instead, we check if there are pending faults on ICSR - // meaning we stopped while trying to execute a fault - // but maybe did not execute it - if ((target->target_options & CORTEXM_TOPT_FLAVOUR_V8M)) { + bool fault = false; + /* + * On ARMv8-M, execution may stop before actually retiring the instruction related to a fault, + * so reading DFSR might not work - instead we check if there are pending faults in ICSR, + * meaning we stopped while trying to execute a faulting instruction but maybe that didn't retire + */ + if (target->target_options & CORTEXM_TOPT_FLAVOUR_V8M) { const uint32_t icsr = target_mem32_read32(target, CORTEXM_ICSR); const uint32_t pending = CORTEXM_ICSR_VEC_PENDING(icsr); - // catch all pending faults - if (pending > 0U && pending < 8U) - fault_state = true; + /* Catch all pending exceptions, but not IRQs */ + fault = pending > 0U && pending < 8U; } else - fault_state = (dfsr & CORTEXM_DFSR_VCATCH) != 0U; - if (fault_state && cortexm_fault_unwind(target)) + fault = (dfsr & CORTEXM_DFSR_VCATCH) != 0U; + /* If there was a fault of some kind, unwind and report */ + if (fault && cortexm_fault_unwind(target)) return TARGET_HALT_FAULT; /* Remember if we stopped on a breakpoint */ From 08a43bb982c78f8735788a19b807ec4b4d16010f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 25 Feb 2026 12:02:45 +0000 Subject: [PATCH 071/247] target: Built out a function for building the target memory map in chunks --- src/include/target.h | 9 ++++--- src/target/target.c | 50 ++++++++++++++++++++++++++++++++++++ src/target/target_internal.h | 2 ++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/include/target.h b/src/include/target.h index 6f9151d3503..afc34e60e49 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -69,7 +69,6 @@ target_s *target_attach_n(size_t n, target_controller_s *controller); void target_detach(target_s *target); /* Memory access functions */ -bool target_mem_map(target_s *target, char *buf, size_t len); bool target_mem32_read(target_s *target, void *dest, target_addr_t src, size_t len); bool target_mem64_read(target_s *target, void *dest, target_addr64_t src, size_t len); bool target_mem32_write(target_s *target, target_addr_t dest, const void *src, size_t len); @@ -82,13 +81,17 @@ bool target_flash_complete(target_s *target); bool target_flash_mass_erase(target_s *target); /* Register access functions */ -size_t target_regs_size(target_s *target); -const char *target_regs_description(target_s *target); void target_regs_read(target_s *target, void *data); void target_regs_write(target_s *target, const void *data); size_t target_reg_read(target_s *target, uint32_t reg, void *data, size_t max); size_t target_reg_write(target_s *target, uint32_t reg, const void *data, size_t size); +/* Target metadata functions */ +bool target_mem_map(target_s *target, char *buf, size_t len); +size_t target_mem_map_chunk(target_s *target, char *buffer, size_t length, uint32_t start_offset); +size_t target_regs_size(target_s *target); +const char *target_regs_description(target_s *target); + /* Halt/resume functions */ typedef enum target_halt_reason { TARGET_HALT_RUNNING = 0, /* Target not halted */ diff --git a/src/target/target.c b/src/target/target.c index b358f8d7090..d4a733c5453 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -54,6 +54,9 @@ const command_s target_cmd_list[] = { {NULL, NULL, NULL}, }; +static const char map_begin[] = ""; +static const char map_end[] = ""; + target_s *target_new(void) { target_s *target = calloc(1, sizeof(*target)); @@ -284,6 +287,53 @@ bool target_mem_map(target_s *target, char *tmp, size_t len) return offset < len - 1U; } +size_t target_mem_map_chunk( + target_s *const target, char *const buffer, const size_t length, const uint32_t start_offset) +{ + /* Simple case - the offset for the next block directly follows on from the last */ + if (start_offset == target->map_transfer_offset) { + /* Figure out where the next chunk is - RAM, Flash or top-and-tail */ + if (start_offset == 0U) { + memcpy(buffer, map_begin, ARRAY_LENGTH(map_begin)); + target->map_transfer_offset = ARRAY_LENGTH(map_begin) - 1U; + return ARRAY_LENGTH(map_begin) - 1U; + } + /* It wasn't the top of the map, so let's find an object that ends past the end of the offset */ + size_t offset = ARRAY_LENGTH(map_begin) - 1U; + /* Start with the RAM for the target */ + for (target_ram_s *ram = target->ram; ram; ram = ram->next) { + /* If this is the entry we're at, format it out and return */ + if (offset == target->map_transfer_offset) { + size_t entry_length = map_ram(buffer, length, ram); + target->map_transfer_offset += entry_length; + return entry_length; + } + /* Otherwise see how long it is and skip past it */ + offset += map_ram(NULL, 0U, ram); + } + /* Now the Flash */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + /* If this is the entry we're at, format it out and return */ + if (offset == target->map_transfer_offset) { + size_t entry_length = map_flash(buffer, length, flash); + target->map_transfer_offset += entry_length; + return entry_length; + } + /* Otherwise see how long it is and skip past it */ + offset += map_flash(NULL, 0U, flash); + } + /* If we've processed all that, then it's an end of map request */ + memcpy(buffer, map_end, ARRAY_LENGTH(map_end)); + target->map_transfer_offset = 0U; + return ARRAY_LENGTH(map_end) - 1U; + } + /* For now don't bother handling the complex case - GDB itself will never invoke this */ + DEBUG_WARN("qXfer memory map request asking for data at offset %" PRIu32 ", but last request ended at %" PRIu32 + " - unsupported request", + start_offset, target->map_transfer_offset); + return 0U; +} + void target_print_progress(platform_timeout_s *const timeout) { if (platform_timeout_is_expired(timeout)) { diff --git a/src/target/target_internal.h b/src/target/target_internal.h index f93238e2a75..4b56cdaa245 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -176,6 +176,8 @@ struct target { target_ram_s *ram; target_flash_s *flash; + uint32_t map_transfer_offset; + /* Other stuff */ const char *driver; uint32_t cpuid; From 12e36f0130861af7ca17b42077742e16d8a73c3a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 25 Feb 2026 12:17:59 +0000 Subject: [PATCH 072/247] gdb_main: Switch exec_q_memory_map() to using the new map chunk function so we avoid map truncation problems and buffer overflows for particularly large maps --- src/gdb_main.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/gdb_main.c b/src/gdb_main.c index d8f17d4b260..72d48720aed 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -498,16 +498,30 @@ static void exec_q_memory_map(const char *packet, const size_t length) (void)length; target_s *target = cur_target; - /* Read target XML memory map */ + /* Figure out which target to read the map for, if there is a valid one */ if (!target) target = last_target; if (!target) { gdb_put_packet_error(1U); return; } - char buf[1024]; - target_mem_map(target, buf, sizeof(buf)); /* Fixme: Check size! */ - handle_q_string_reply(buf, packet); + + /* Decode the offset into the map being requested */ + uint32_t offset = 0; + if (!read_hex32(packet, NULL, &offset, ',')) { + gdb_put_packet_error(1U); + return; + } + + /* Grab not more than a GDB packet buffer's worth of data */ + char buffer[GDB_PACKET_BUFFER_SIZE]; + const size_t chunk_length = target_mem_map_chunk(target, buffer, ARRAY_LENGTH(buffer), offset); + + /* Determine if this was the last chunk so we generate the right kind of packet */ + const bool end = target->map_transfer_offset == 0U; + + /* And now send the chunk back to the host */ + gdb_put_packet(end ? "l" : "m", 1U, buffer, chunk_length, false); } static void exec_q_feature_read(const char *packet, const size_t length) From 095818b80414e6795ffd8852fb20b0a5c2214cd7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 25 Feb 2026 12:24:24 +0000 Subject: [PATCH 073/247] target: Removed the old memory map builder function --- src/include/target.h | 1 - src/target/target.c | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/src/include/target.h b/src/include/target.h index afc34e60e49..ce0be5d1b34 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -87,7 +87,6 @@ size_t target_reg_read(target_s *target, uint32_t reg, void *data, size_t max); size_t target_reg_write(target_s *target, uint32_t reg, const void *data, size_t size); /* Target metadata functions */ -bool target_mem_map(target_s *target, char *buf, size_t len); size_t target_mem_map_chunk(target_s *target, char *buffer, size_t length, uint32_t start_offset); size_t target_regs_size(target_s *target); const char *target_regs_description(target_s *target); diff --git a/src/target/target.c b/src/target/target.c index d4a733c5453..2e9279f7aca 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -273,20 +273,6 @@ static ssize_t map_flash(char *buf, size_t len, target_flash_s *flash) return offset; } -bool target_mem_map(target_s *target, char *tmp, size_t len) -{ - size_t offset = 0; - offset = snprintf(tmp + offset, len - offset, ""); - /* Map each defined RAM */ - for (target_ram_s *ram = target->ram; ram; ram = ram->next) - offset += map_ram(tmp + offset, len - offset, ram); - /* Map each defined Flash */ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) - offset += map_flash(tmp + offset, len - offset, flash); - offset += snprintf(tmp + offset, len - offset, ""); - return offset < len - 1U; -} - size_t target_mem_map_chunk( target_s *const target, char *const buffer, const size_t length, const uint32_t start_offset) { From 28862a79b28807cf208e7d11999a6ed005687970 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 09:58:34 +0000 Subject: [PATCH 074/247] github: Updated the OSes built for in the PR flow --- .github/workflows/build-pr.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 0b9683ddc69..636e8cc7af9 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -126,7 +126,8 @@ jobs: strategy: matrix: os: - - windows-2025 + - windows-2025 # Currently latest + - windows-2025-vs2026 fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -270,10 +271,9 @@ jobs: strategy: matrix: os: - - macos-13 - macos-14 - - macos-15 - - macos-latest + - macos-15 # Currently latest + - macos-26 fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job From d707ca83e1fb6b54706ab35c54fa8121fd79c1a4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 09:59:02 +0000 Subject: [PATCH 075/247] github: Switched size-diff over to BMPv3 in the PR workflow --- .github/workflows/build-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 636e8cc7af9..0e6f560ea70 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -425,14 +425,14 @@ jobs: # Build the base ref firmware for the largest Flash target available - name: Build base run: | - meson setup build --cross-file=cross-file/blackpill-f411ce.ini + meson setup build --cross-file=cross-file/bmp-v3.ini meson compile -C build > build.log working-directory: base # Build the PR `HEAD` ref firmware for the largest Flash target available - name: Build head run: | - meson setup build --cross-file=cross-file/blackpill-f411ce.ini + meson setup build --cross-file=cross-file/bmp-v3.ini meson compile -C build > build.log working-directory: head From 5e7069cfa602b3444bb2c2b314d0abdc01717d4b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 10:03:40 +0000 Subject: [PATCH 076/247] github: Updated the OSes built for in the build-and-upload flow --- .github/workflows/build-and-upload.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 2f0db191564..852b8680fc9 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -267,7 +267,7 @@ jobs: strategy: matrix: os: - - windows-2022 + - windows-2025 sys: - {abi: ucrt64, env: ucrt-x86_64, compiler: gcc} fail-fast: false @@ -369,8 +369,9 @@ jobs: strategy: matrix: os: - - {id: macos-13, name: '13'} - {id: macos-14, name: '14'} + - {id: macos-15, name: '15'} + - {id: macos-26, name: '26'} fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job From 81a955eb9884b5395f686e6361adb7b120374676 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 10:05:34 +0000 Subject: [PATCH 077/247] github: Updated the version of the ARM toolchain we CI with (14.3 -> 15.2) --- .github/workflows/build-and-upload.yml | 2 +- .github/workflows/build-pr.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 852b8680fc9..74ef366dcb6 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -32,7 +32,7 @@ jobs: os: - {id: ubuntu-24.04, name: noble} compiler: - - '14.3.Rel1' + - '15.2.Rel1' probe: - '96b_carbon' - 'blackpill-f401cc' diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 0e6f560ea70..bb5172fe0a2 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -35,7 +35,7 @@ jobs: os: - {id: ubuntu-24.04, name: noble} arm-compiler: - - '14.3.Rel1' + - '15.2.Rel1' probe: - '96b_carbon' - 'blackpill-f401cc' @@ -403,7 +403,7 @@ jobs: - name: Setup ARM GCC uses: carlosperate/arm-none-eabi-gcc-action@v1 with: - release: '14.3.Rel1' + release: '15.2.Rel1' # Install and setup a suitable Meson + Ninja - name: Setup Meson + Ninja From 75197fbdd9433f1025a67c893cbe1746088c9963 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 10:16:09 +0000 Subject: [PATCH 078/247] github: Updated the toolchains used in the Linux CI builds for build-and-upload --- .github/workflows/build-and-upload.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 74ef366dcb6..030c1e4df01 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -151,9 +151,9 @@ jobs: - {id: ubuntu-24.04, name: noble} compiler: - 'clang-17' # Native Clang compiler for the CI image - - 'clang-20' # Latest Clang compiler from the apt mirror + - 'clang-21' # Latest Clang compiler from the apt mirror - 'gcc-12' # Native GCC compiler for the CI image - - 'gcc-13' # Latest GCC compiler from the toolchain PPA + - 'gcc-14' # Latest GCC compiler from the toolchain PPA fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -173,7 +173,7 @@ jobs: shell: bash run: | CXX=${CC/#gcc/g++} - sudo apt-add-repository ppa:ubuntu-toolchain-r/test + sudo apt-add-repository ppa:ubuntu-toolchain-r/ppa sudo apt-get update sudo apt-get install $CC $CXX echo "CC=$CC" >> $GITHUB_ENV From 2ee5ca8dc696b2af9fbc01cfa7e0efc56dd83fb8 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 10:19:31 +0000 Subject: [PATCH 079/247] github: Updated the actions version pinnings in the PR flow --- .github/workflows/build-pr.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index bb5172fe0a2..1a403a84518 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -99,7 +99,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true @@ -161,7 +161,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -241,7 +241,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Install the dependencies needed for BMDA build - name: Install extra BMDA dependencies @@ -301,7 +301,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -378,7 +378,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -412,12 +412,12 @@ jobs: sudo python3 -m pip install meson ninja working-directory: ${{ runner.temp }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.base_ref }} path: base - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha }} path: head From cd436f76272d6d94cec057c678f71aeb88cf6111 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 24 Feb 2026 10:22:18 +0000 Subject: [PATCH 080/247] github: Updated the actions version pinnings in the build-and-upload flow --- .github/workflows/build-and-upload.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 030c1e4df01..f6ca637e520 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -91,7 +91,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -113,7 +113,7 @@ jobs: # Package up the artefacts and upload them - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_firmware-${{ matrix.probe }} path: src/artefacts/* @@ -122,7 +122,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-firmware-${{ matrix.probe }} path: ${{ github.workspace }}/build/meson-logs/* @@ -133,7 +133,7 @@ jobs: needs: build-firmware steps: - name: Merge firmware artefacts - uses: actions/upload-artifact/merge@v4 + uses: actions/upload-artifact/merge@v6 with: name: blackmagic_firmware pattern: blackmagic_firmware-* @@ -220,7 +220,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -236,7 +236,7 @@ jobs: # Package up the artefact and upload it - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_linux-${{ matrix.os.id }}-${{ matrix.compiler }} path: src/artefacts/* @@ -245,7 +245,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-linux-${{ matrix.os.id }}-${{ matrix.compiler }} path: ${{ github.workspace }}/build/meson-logs/* @@ -320,7 +320,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -337,7 +337,7 @@ jobs: # Package up all the artefacts and upload them - name: Archive firmware build artefacts as a zip - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_${{ matrix.os }}-${{ matrix.sys.abi }}-${{ matrix.sys.compiler }} path: src/artefacts/* @@ -346,7 +346,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-${{ matrix.os }}-${{ matrix.sys.abi }}-${{ matrix.sys.compiler }} path: ${{ github.workspace }}/build/meson-logs/* @@ -399,7 +399,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -415,7 +415,7 @@ jobs: # Package up the artefact and upload it - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_macos-${{ matrix.os.name }} path: src/artefacts/* @@ -424,7 +424,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-macos-${{ matrix.os.name }} path: ${{ github.workspace }}/build/meson-logs/* From db9692bc44e888ba5df37adc09d67462f479da45 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 15:36:28 +0000 Subject: [PATCH 081/247] target: Improved the naming of the RAM memory map handler --- src/target/target.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/target/target.c b/src/target/target.c index 2e9279f7aca..fc8ce977d20 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -256,10 +256,10 @@ bool target_enter_flash_mode_stub(target_s *target) return true; } -static ssize_t map_ram(char *buf, size_t len, target_ram_s *ram) +static ssize_t mem_map_ram(char *buffer, size_t length, target_ram_s *ram) { - return snprintf(buf, len, "", ram->start, - (uint32_t)ram->length); + return snprintf(buffer, length, "", + ram->start, (uint32_t)ram->length); } static ssize_t map_flash(char *buf, size_t len, target_flash_s *flash) @@ -290,12 +290,12 @@ size_t target_mem_map_chunk( for (target_ram_s *ram = target->ram; ram; ram = ram->next) { /* If this is the entry we're at, format it out and return */ if (offset == target->map_transfer_offset) { - size_t entry_length = map_ram(buffer, length, ram); + size_t entry_length = mem_map_ram(buffer, length, ram); target->map_transfer_offset += entry_length; return entry_length; } /* Otherwise see how long it is and skip past it */ - offset += map_ram(NULL, 0U, ram); + offset += mem_map_ram(NULL, 0U, ram); } /* Now the Flash */ for (target_flash_s *flash = target->flash; flash; flash = flash->next) { From 1295bf74f2baf90cc12027463024f3845b2c6e14 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 15:36:42 +0000 Subject: [PATCH 082/247] target: Improved the naming of the Flash memory map handler --- src/target/target.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/target/target.c b/src/target/target.c index fc8ce977d20..4260bed6b91 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -262,14 +262,14 @@ static ssize_t mem_map_ram(char *buffer, size_t length, target_ram_s *ram) ram->start, (uint32_t)ram->length); } -static ssize_t map_flash(char *buf, size_t len, target_flash_s *flash) +static ssize_t mem_map_flash(char *buffer, size_t length, target_flash_s *flash) { ssize_t offset = 0; - offset += snprintf(&buf[offset], len - offset, + offset += snprintf(buffer + offset, length - offset, "", flash->start, (uint32_t)flash->length); - offset += snprintf(buf + offset, len - offset, "0x%" PRIx32 "", - (uint32_t)flash->blocksize); + offset += snprintf(buffer + offset, length - offset, + "0x%" PRIx32 "", (uint32_t)flash->blocksize); return offset; } @@ -301,12 +301,12 @@ size_t target_mem_map_chunk( for (target_flash_s *flash = target->flash; flash; flash = flash->next) { /* If this is the entry we're at, format it out and return */ if (offset == target->map_transfer_offset) { - size_t entry_length = map_flash(buffer, length, flash); + size_t entry_length = mem_map_flash(buffer, length, flash); target->map_transfer_offset += entry_length; return entry_length; } /* Otherwise see how long it is and skip past it */ - offset += map_flash(NULL, 0U, flash); + offset += mem_map_flash(NULL, 0U, flash); } /* If we've processed all that, then it's an end of map request */ memcpy(buffer, map_end, ARRAY_LENGTH(map_end)); From e524e57ad1eb6a2a1258887503ff8bd7dea3e8e9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 15:43:58 +0000 Subject: [PATCH 083/247] target: Simplified the Flash memory entry formatter, shaving some size off the binary and fixing it being grumpy if the length parameter was 0 --- src/target/target.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/target/target.c b/src/target/target.c index 4260bed6b91..8bfbbba8180 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -264,13 +264,10 @@ static ssize_t mem_map_ram(char *buffer, size_t length, target_ram_s *ram) static ssize_t mem_map_flash(char *buffer, size_t length, target_flash_s *flash) { - ssize_t offset = 0; - offset += snprintf(buffer + offset, length - offset, - "", flash->start, - (uint32_t)flash->length); - offset += snprintf(buffer + offset, length - offset, - "0x%" PRIx32 "", (uint32_t)flash->blocksize); - return offset; + return snprintf(buffer, length, + "0x%" PRIx32 "", + flash->start, (uint32_t)flash->length, (uint32_t)flash->blocksize); } size_t target_mem_map_chunk( From 3f556b0e329220ea5e59e3f0a806c1f983ca59ca Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 17:23:32 +0000 Subject: [PATCH 084/247] target: Added some missing `const` to the parameters of the memory map formatting functions --- src/target/target.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/target.c b/src/target/target.c index 8bfbbba8180..226266ade81 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -256,13 +256,13 @@ bool target_enter_flash_mode_stub(target_s *target) return true; } -static ssize_t mem_map_ram(char *buffer, size_t length, target_ram_s *ram) +static ssize_t mem_map_ram(char *const buffer, const size_t length, const target_ram_s *const ram) { return snprintf(buffer, length, "", ram->start, (uint32_t)ram->length); } -static ssize_t mem_map_flash(char *buffer, size_t length, target_flash_s *flash) +static ssize_t mem_map_flash(char *const buffer, const size_t length, const target_flash_s *const flash) { return snprintf(buffer, length, " Date: Sat, 14 Mar 2026 15:45:44 +0000 Subject: [PATCH 085/247] riscv_debug: Fixed some type conversions lints in the target description builder function --- src/target/riscv_debug.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 0dd02dc76e2..d022b127df6 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -293,7 +293,7 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) /* * The DMI version does not actually matter here, the implementation details have already been * abstracted away at this point and we have a generic DMI to work with - * + * * But the dminfo register (at 0x11) of v0.11 DM is incompatible with dmstatus (also at 0x11) of * later versions, meaning we can't easily/reliably determine the version of the DM. * We ignore all v0.11 DMI's in the hope we don't encounter a v0.11 DM with a later version DMI. @@ -1319,14 +1319,14 @@ static size_t riscv_build_target_description( const char *const name = riscv_gpr_names[i]; const gdb_reg_type_e type = riscv_gpr_types[i]; - offset += (size_t)snprintf(buffer + offset, print_size, "", name, - address_width, gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); + 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 - offset; - offset += (size_t)snprintf(buffer + offset, print_size, "", address_width, + offset += snprintf(buffer + offset, print_size, "", address_width, gdb_reg_type_strings[GDB_TYPE_CODE_PTR]); /* If the target has basic single precision support, generate a block for that */ @@ -1341,13 +1341,13 @@ static size_t riscv_build_target_description( /* Add main CSR registers*/ if (max_length != 0) print_size = max_length - offset; - offset += (size_t)snprintf(buffer + offset, print_size, ""); + offset += snprintf(buffer + offset, print_size, ""); for (size_t i = 0; i < ARRAY_LENGTH(riscv_csrs); i++) { if (max_length != 0) print_size = max_length - offset; - offset += (size_t)snprintf(buffer + offset, print_size, - " ", riscv_csrs[i].name, address_width, - riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); + offset += snprintf(buffer + offset, print_size, " ", + riscv_csrs[i].name, address_width, riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, + gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); } /* Add the closing tags required */ if (max_length != 0) From 0b2d04f5814397f70295418a2195bd7836677f5a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 15:46:16 +0000 Subject: [PATCH 086/247] hosted/probe_info: Fixed the allocation failure error in probe_info_add_by_id() not being an actual error --- src/platforms/hosted/probe_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/hosted/probe_info.c b/src/platforms/hosted/probe_info.c index b068b8dfbd6..1c69d8c221d 100644 --- a/src/platforms/hosted/probe_info.c +++ b/src/platforms/hosted/probe_info.c @@ -50,7 +50,7 @@ probe_info_s *probe_info_add_by_id(probe_info_s *const list, const probe_type_e #endif probe_info_s *probe_info = malloc(sizeof(*probe_info)); if (!probe_info) { - DEBUG_INFO("Fatal: Failed to allocate memory for a probe info structure\n"); + DEBUG_ERROR("Fatal: Failed to allocate memory for a probe info structure\n"); return NULL; } From 37c2393fd7fa59fab60aff0bfc6618d5b9467353 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 14 Mar 2026 15:46:59 +0000 Subject: [PATCH 087/247] hosted/bmp_libusb: Fixed (at least partially) probe_info_add_by_id() usage being able to loose the input pointers if the entry allocation fails --- src/platforms/hosted/bmp_libusb.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index fb5ad14d925..01d6f8feae5 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -537,10 +537,18 @@ static bool process_vid_pid_table_probe( if (version == NULL) version = strdup("---"); - *probe_list = probe_info_add_by_id(*probe_list, debugger_device->type, device, device_descriptor->idVendor, - device_descriptor->idProduct, manufacturer, product, serial, version); + probe_info_s *probe_info = probe_info_add_by_id(*probe_list, debugger_device->type, device, + device_descriptor->idVendor, device_descriptor->idProduct, manufacturer, product, serial, version); + if (probe_info) + *probe_list = probe_info; + else { + free(product); + free(manufacturer); + free(serial); + free(version); + } libusb_close(handle); - return true; + return probe_info != NULL; } static const probe_info_s *scan_for_devices(bmda_probe_s *info) From 94fc3331fffd91e7d7134e7801be811c5374d898 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 16 Mar 2026 21:20:34 +0000 Subject: [PATCH 088/247] remote: Fix a comment carrying the incorrect command characters for a command (JTAG run cycles command) --- src/remote.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote.c b/src/remote.c index d5a8c03fc04..bdb9fac816d 100644 --- a/src/remote.c +++ b/src/remote.c @@ -214,7 +214,7 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa break; } - case REMOTE_CYCLE: { /* JC = clock cycle ============================ */ + case REMOTE_CYCLE: { /* Jc = clock cycle ============================ */ const size_t clock_cycles = hex_string_to_num(8, packet + 4); const bool tms = packet[2] != '0'; const bool tdi = packet[3] != '0'; From 65ef172f1d631e32a7ede17ae1f14f439cfa1c2d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 16 Mar 2026 21:32:18 +0000 Subject: [PATCH 089/247] hosted/remote/protocol_v2: Avoid issuing `Jc` to the probe if there's nothing to do --- src/platforms/hosted/remote/protocol_v2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platforms/hosted/remote/protocol_v2.c b/src/platforms/hosted/remote/protocol_v2.c index b5b70f9e1b2..dc4eee06930 100644 --- a/src/platforms/hosted/remote/protocol_v2.c +++ b/src/platforms/hosted/remote/protocol_v2.c @@ -121,6 +121,10 @@ static inline uint8_t bool_to_int(const bool value) static void remote_v2_jtag_cycle(const bool tms, const bool tdi, const size_t clock_cycles) { + /* If `clock_cycles` is 0, avoid issuing the command to the probe as there's nothing to do */ + if (clock_cycles == 0U) + return; + char buffer[REMOTE_MAX_MSG_SIZE]; int length = snprintf(buffer, REMOTE_MAX_MSG_SIZE, REMOTE_JTAG_CYCLE_STR, bool_to_int(tms), bool_to_int(tdi), clock_cycles); From 7fc79bf954cfbb47d9916de2f6b6f76db7818396 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 16 Mar 2026 21:32:41 +0000 Subject: [PATCH 090/247] common/jtagtap: Avoid running any cycles if we're asked to do nothing with `jtagtap_cycle()` --- src/platforms/common/jtagtap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platforms/common/jtagtap.c b/src/platforms/common/jtagtap.c index 2a6565752f1..ad76f5c0426 100644 --- a/src/platforms/common/jtagtap.c +++ b/src/platforms/common/jtagtap.c @@ -340,6 +340,10 @@ static void jtagtap_cycle_no_delay(const size_t clock_cycles) static void jtagtap_cycle(const bool tms, const bool tdi, const size_t clock_cycles) { + /* If `clock_cycles` is 0, do nothing so we don't wind up doing UINT32_MAX things */ + if (clock_cycles == 0U) + return; + jtagtap_next(tms, tdi); if (target_clk_divider != UINT32_MAX) jtagtap_cycle_clk_delay(clock_cycles - 1U); From dd49a6c65650986fd9e4f915616984b238dffea2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 17 Mar 2026 04:08:07 +0000 Subject: [PATCH 091/247] codeberg: Implemented steps for building the firmware for all the different probe platforms and their variants in CI --- .codeberg/ci/build-firmware.yml | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .codeberg/ci/build-firmware.yml diff --git a/.codeberg/ci/build-firmware.yml b/.codeberg/ci/build-firmware.yml new file mode 100644 index 00000000000..7c118b1102d --- /dev/null +++ b/.codeberg/ci/build-firmware.yml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +matrix: + # Define a matrix of all the probe platforms to get full build coverage for all the firmware possibilities + BMD_PROBE: + - '96b_carbon' + - 'blackpill-f401cc' + - 'blackpill-f401ce' + - 'blackpill-f411ce' + - 'bluepill' + - 'bmp-v3' + - 'ctxlink' + - 'f072' + - 'f3' + - 'f4discovery' + - 'hydrabus' + - 'launchpad-icdi' + - 'native' + - 'native-uncommon' + - 'native-st-clones' + - 'native-riscv' + - 'native-remote' + - 'stlink' + - 'stlinkv3' + - 'swlink' + +steps: + # Build the firmware for a given probe platform w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + export PATH="$$HOME/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi/bin:$$PATH" + + cc --version + arm-none-eabi-gcc --version + meson --version + ninja --version + + meson setup build --cross-file cross-file/${BMD_PROBE}.ini --werror + meson compile -C build From 3699698206c22526d05a37af03879286e1b8c6f2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 17 Mar 2026 05:12:22 +0000 Subject: [PATCH 092/247] codeberg: Implemented steps for building BMDA on Linux with both Clang and GCC --- .codeberg/ci/build-linux.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .codeberg/ci/build-linux.yml diff --git a/.codeberg/ci/build-linux.yml b/.codeberg/ci/build-linux.yml new file mode 100644 index 00000000000..3b0b4e86b21 --- /dev/null +++ b/.codeberg/ci/build-linux.yml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +matrix: + # Define a matrix of compilers to build BMDA with + C_COMPILER: + - gcc + - clang + +steps: + # Build BMDA with a given compiler from the matrix w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + export CC="${C_COMPILER}" + + $$CC --version + meson --version + ninja --version + + meson setup build --werror + meson compile -C build From a267510c85e1704b91717444176fd85f9c73ce9d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 17 Mar 2026 05:55:05 +0000 Subject: [PATCH 093/247] codeberg: Install the pre-requisites for BMDA in the firmware flow so we aren't having to build libftdi and other components and getting spurious errors out the flow --- .codeberg/ci/build-firmware.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.codeberg/ci/build-firmware.yml b/.codeberg/ci/build-firmware.yml index 7c118b1102d..17c4196ef4f 100644 --- a/.codeberg/ci/build-firmware.yml +++ b/.codeberg/ci/build-firmware.yml @@ -43,6 +43,8 @@ steps: pull: true commands: | export PATH="$$HOME/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi/bin:$$PATH" + apt update + apt install -y libftdi1-dev libhidapi-dev libusb-1.0-0-dev cc --version arm-none-eabi-gcc --version From b712c7af42e4f299821473bb732f3f2c3deff73c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 17 Mar 2026 05:57:00 +0000 Subject: [PATCH 094/247] codeberg: Remove `-Werror` from the BMDA flow on Linux for now till our dependencies can be cleaned up a biti --- .codeberg/ci/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codeberg/ci/build-linux.yml b/.codeberg/ci/build-linux.yml index 3b0b4e86b21..5bea0fd2945 100644 --- a/.codeberg/ci/build-linux.yml +++ b/.codeberg/ci/build-linux.yml @@ -30,5 +30,5 @@ steps: meson --version ninja --version - meson setup build --werror + meson setup build meson compile -C build From 1725e193244837302e4cc8055f609967a652cb02 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 18 Mar 2026 01:39:35 +0000 Subject: [PATCH 095/247] codeberg: Enabled LTO for the Linux BMDA builds --- .codeberg/ci/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codeberg/ci/build-linux.yml b/.codeberg/ci/build-linux.yml index 5bea0fd2945..356e17b0610 100644 --- a/.codeberg/ci/build-linux.yml +++ b/.codeberg/ci/build-linux.yml @@ -30,5 +30,5 @@ steps: meson --version ninja --version - meson setup build + meson setup build -Db_lto=true meson compile -C build From 29436d89f5cf79d3f43b65e20a32ec1437ea4f79 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 18 Mar 2026 03:18:36 +0000 Subject: [PATCH 096/247] codeberg: Added a cross-file describing how to build BMDA for Windows from Linux using UCRT64 tooling --- .../ci/x86_64-pc-windows-mingw-ucrt64.ini | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini diff --git a/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini b/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini new file mode 100644 index 00000000000..70a4427ff00 --- /dev/null +++ b/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini @@ -0,0 +1,34 @@ +[constants] +compileFlags = ['--sysroot', '/usr/x86_64-w64-mingw32ucrt'] +linkFlags = ['--sysroot', '/usr/x86_64-w64-mingw32ucrt'] + +[binaries] +c = 'x86_64-w64-mingw32ucrt-gcc' +cpp = 'x86_64-w64-mingw32ucrt-g++' +# c_ld = 'x86_64-w64-mingw32ucrt-ld.bdf' +# cpp_ld = 'x86_64-w64-mingw32ucrt-ld.bdf' +ar = 'x86_64-w64-mingw32ucrt-ar' +as = 'x86_64-w64-mingw32ucrt-as' +rc = 'x86_64-w64-mingw32ucrt-rc' +windres = 'x86_64-w64-mingw32ucrt-windres' +strip = 'x86_64-w64-mingw32ucrt-strip' +objcopy = 'x86_64-w64-mingw32ucrt-objcopy' +objdump = 'x86_64-w64-mingw32ucrt-objdump' +size = 'x86_64-w64-mingw32ucrt-size' +cmake = 'false' +exe_wrapper = 'wine' + +[properties] +needs_exe_wrapper = true + +[built-in options] +c_args = compileFlags +cpp_args = compileFlags +c_link_args = linkFlags +cpp_link_args = linkFlags + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'amd64' +endian = 'little' From b1c1679df6d60dfd9527a3becc18b74a6bb3e86a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 18 Mar 2026 03:19:15 +0000 Subject: [PATCH 097/247] codeberg: Implemented steps for building BMDA for Windows from Linux --- .codeberg/ci/build-windows.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .codeberg/ci/build-windows.yml diff --git a/.codeberg/ci/build-windows.yml b/.codeberg/ci/build-windows.yml new file mode 100644 index 00000000000..c6cdf413402 --- /dev/null +++ b/.codeberg/ci/build-windows.yml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +steps: + # Build BMDA with a Meson config for Windows cross-builds w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + x86_64-w64-mingw32ucrt-gcc --version + meson --version + ninja --version + + meson setup build --cross-file .codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini -Db_lto=true + meson compile -C build From c4ea63ba8fe41e62b91dce32c5297c5e07c9e111 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 18 Mar 2026 03:59:55 +0000 Subject: [PATCH 098/247] meson: Allow BMDA to be cross-compiled when not building the firmware so that CI can function correctly --- meson.build | 2 +- src/platforms/hosted/meson.build | 39 +++++++++++++++++--------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/meson.build b/meson.build index d7d5f49bbf8..8dff9419a47 100644 --- a/meson.build +++ b/meson.build @@ -320,7 +320,7 @@ elif bmda_platform.found() bmda = executable( 'blackmagic', dependencies: [libbmd_core, bmda_platform], - native: is_cross_build, + native: is_cross_build and is_firmware_build, ) alias_target('bmda', bmda) elif not is_firmware_build diff --git a/src/platforms/hosted/meson.build b/src/platforms/hosted/meson.build index 13c0d511113..229ef4a94d3 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -70,7 +70,10 @@ bmda_args = [ bmda_link_args = [] bmda_deps = [] -cc = is_cross_build ? cc_native : cc_host +bmda_native_build = is_cross_build and is_firmware_build +bmda_machine = bmda_native_build ? build_machine : host_machine + +cc = bmda_native_build ? cc_native : cc_host # Ensure that MSVC is switched to standards compliant mode if cc.get_define('_MSC_VER') != '' @@ -84,7 +87,7 @@ endif # Determine if we're on a MSYS2 environment of some kind # If the compiler is MSYS2 GCC or Clang (but not Clang-cl) -if build_machine.system() == 'windows' and cc.get_define('__MINGW32__') == '1' +if bmda_machine.system() == 'windows' and cc.get_define('__MINGW32__') == '1' # It needs custom location for some MinGWs # See https://github.com/msys2/MINGW-packages/issues/10275 ucrt_check = ''' @@ -117,18 +120,18 @@ libusb = dependency( version: '>=1.0.13', method: 'pkg-config', fallback: 'libusb', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'default_library=static', 'install_targets=false', 'docs=disabled', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) -if build_machine.system() in ['windows', 'cygwin'] +if bmda_machine.system() in ['windows', 'cygwin'] subdir('windows') # Make sure we build for Windows Vista and above, where the @@ -150,7 +153,7 @@ else 'libftdi1', method: 'pkg-config', fallback: 'libftdi', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'default_library=static', 'install_targets=false', @@ -159,29 +162,29 @@ else 'examples=disabled', 'ftdi_eeprom=disabled', #'python_bindings=disabled', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) bmda_sources += files('serial_unix.c') endif # Pick the appropriate HIDAPI depending on platform -if build_machine.system() == 'linux' +if bmda_machine.system() == 'linux' hidapi = dependency( 'hidapi-hidraw', method: 'pkg-config', fallback: ['hidapi', 'hidapi_hidraw_dep'], - native: is_cross_build, + native: bmda_native_build, default_options: [ 'c_std=gnu99', 'default_library=static', 'install_targets=false', 'with_libusb=false', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) else @@ -189,24 +192,24 @@ else 'hidapi', method: 'pkg-config', fallback: 'hidapi', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'c_std=c99', 'default_library=static', 'install_targets=false', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) endif -if build_machine.system() == 'linux' +if bmda_machine.system() == 'linux' libgpiod = dependency( 'libgpiod', version: ['>=1.0.0', '<2.0.0'], required: get_option('enable_gpiod'), - native: is_cross_build, + native: bmda_native_build, ) if libgpiod.found() From 50a001a120771110503bfb23933fc0bde657337e Mon Sep 17 00:00:00 2001 From: hardesk Date: Sun, 8 Feb 2026 19:16:10 +0200 Subject: [PATCH 099/247] mspm0: Flash target using on-device core loop stub --- src/target/flashstub/meson.build | 33 +++++++++++++ src/target/flashstub/mspm0.c | 81 ++++++++++++++++++++++++++++++++ src/target/flashstub/mspm0.ld | 13 +++++ src/target/flashstub/mspm0.stub | 1 + src/target/meson.build | 2 +- src/target/mspm0.c | 74 +++++++++++++++-------------- 6 files changed, 168 insertions(+), 36 deletions(-) create mode 100644 src/target/flashstub/mspm0.c create mode 100644 src/target/flashstub/mspm0.ld create mode 100644 src/target/flashstub/mspm0.stub diff --git a/src/target/flashstub/meson.build b/src/target/flashstub/meson.build index d56822d403d..8b9ef16058c 100644 --- a/src/target/flashstub/meson.build +++ b/src/target/flashstub/meson.build @@ -32,6 +32,7 @@ lmi_stub = [] efm32_stub = [] rp2040_stub = [] +mspm0_stub = [] # If we're doing a firmware build, type to find hexdump and objcopy if is_firmware_build @@ -189,3 +190,35 @@ rp2040_stub = custom_target( output: 'rp.stub', capture: true, ) + +# Flash stub for MSPM0 parts +mspm0_stub_elf = executable( + 'mspm0_stub.elf', + 'mspm0.c', + c_args: [ + '-mcpu=cortex-m0plus', + stub_build_args + ], + link_args: [ + '-mcpu=cortex-m0plus', + stub_build_args, + '-T', '@0@/mspm0.ld'.format(meson.current_source_dir()), + ], + link_depends: files('mspm0.ld'), + pie: false, + install: false, +) + +mspm0_stub_bin = custom_target( + command: [ objcopy, '-O', 'binary', '@INPUT@', '@OUTPUT@' ], + input: mspm0_stub_elf, + output: 'mspm0_stub.bin' +) + +mspm0_stub = custom_target( + 'mspm0_stub-hex', + command: [ hexdump, '-v', '-e', '/2 "0x%04X, "' , '@INPUT@' ], + input: mspm0_stub_bin, + output: 'mspm0.stub', + capture: true, +) diff --git a/src/target/flashstub/mspm0.c b/src/target/flashstub/mspm0.c new file mode 100644 index 00000000000..10544b3f43c --- /dev/null +++ b/src/target/flashstub/mspm0.c @@ -0,0 +1,81 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by hardesk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "stub.h" + +#define MSPM0_FLASH_MAIN 0x00000000U +#define MSPM0_FLASH_SECTOR_SZ 1024U + +#define MSPM0_FLASHCTL_BASE 0x400cd000U +#define MSPM0_FLASHCTL_CMDEXEC *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1100U)) +#define MSPM0_FLASHCTL_CMDTYPE *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1104U)) +#define MSPM0_FLASHCTL_CMDCTL *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1108U)) +#define MSPM0_FLASHCTL_CMDADDR *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1120U)) +#define MSPM0_FLASHCTL_BYTEN *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1124U)) +#define MSPM0_FLASHCTL_STATCMD *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x13d0U)) +#define MSPM0_FLASHCTL_CMDDATA0 *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1130U)) +#define MSPM0_FLASHCTL_CMDDATA1 *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1134U)) +#define MSPM0_FLASHCTL_CMDWEPROTA *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d0U)) +#define MSPM0_FLASHCTL_CMDWEPROTB *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d4U)) +#define MSPM0_FLASHCTL_CMDWEPROTC *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d8U)) +#define MSPM0_FLASHCTL_CMDTYPE_PROG 1U +#define MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD (0U << 4U) +#define MSPM0_FLASHCTL_CMDEXEC_EXEC 1U +#define MSPM0_FLASHCTL_STATCMD_DONE 0x01U +#define MSPM0_FLASHCTL_STATCMD_CMDPASS 0x02U + +char __attribute__((aligned(0x04))) stub_stack[0x20]; + +void mspm0_flash_writer(const uint32_t *const dest, const uint32_t *const src, const uint32_t size) +{ + for (uint32_t i = 0U; i < size / 4U; i += 2U) { + uint32_t addr = (uint32_t)(dest + i); + uint32_t sector = (addr - MSPM0_FLASH_MAIN) / MSPM0_FLASH_SECTOR_SZ; + + if (sector < 32U) + MSPM0_FLASHCTL_CMDWEPROTA = ~(1U << sector); + else if (sector < 256U) + MSPM0_FLASHCTL_CMDWEPROTB = 0U; + else + MSPM0_FLASHCTL_CMDWEPROTC = 0U; + + MSPM0_FLASHCTL_CMDCTL = 0U; + MSPM0_FLASHCTL_BYTEN = 0xffffffffU; + MSPM0_FLASHCTL_CMDTYPE = MSPM0_FLASHCTL_CMDTYPE_PROG | MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD; + + MSPM0_FLASHCTL_CMDADDR = addr; + MSPM0_FLASHCTL_CMDDATA0 = src[i]; + MSPM0_FLASHCTL_CMDDATA1 = src[i + 1U]; + MSPM0_FLASHCTL_CMDEXEC = MSPM0_FLASHCTL_CMDEXEC_EXEC; + while (!(MSPM0_FLASHCTL_STATCMD & MSPM0_FLASHCTL_STATCMD_DONE)) + continue; + if (!(MSPM0_FLASHCTL_STATCMD & MSPM0_FLASHCTL_STATCMD_CMDPASS)) + stub_exit(0); + } + + stub_exit(1); +} + +void __attribute__((naked, used, section(".entry"))) mspm0_flash_write_entry( + const uint32_t *const dest, const uint32_t *const src, const uint32_t size) +{ + __asm volatile("msr msp, %0" : : "r"(stub_stack + sizeof(stub_stack)) :); + mspm0_flash_writer(dest, src, size); +} \ No newline at end of file diff --git a/src/target/flashstub/mspm0.ld b/src/target/flashstub/mspm0.ld new file mode 100644 index 00000000000..622de40f83d --- /dev/null +++ b/src/target/flashstub/mspm0.ld @@ -0,0 +1,13 @@ +MEMORY { sram (rwx): ORIGIN = 0x20000000, LENGTH = 0x00000400 } + +SECTIONS +{ + .text : + { + KEEP(*(.entry)) + *(.text.*, .text) + /* include data so we store stack area in objcopy's output */ + *(.data) + *(.bss) + } > sram +} diff --git a/src/target/flashstub/mspm0.stub b/src/target/flashstub/mspm0.stub new file mode 100644 index 00000000000..35dbaa08eaf --- /dev/null +++ b/src/target/flashstub/mspm0.stub @@ -0,0 +1 @@ +0x4B02, 0xF383, 0x8808, 0xF000, 0xF803, 0x46C0, 0x00D8, 0x2000, 0xB5F7, 0x0893, 0x9301, 0x2380, 0x2580, 0x021B, 0x2601, 0x469C, 0x2200, 0x2301, 0x02ED, 0x4276, 0x9C01, 0x4294, 0xD801, 0xBE01, 0xBDF7, 0x4560, 0xD222, 0x001F, 0x0A84, 0x40A7, 0x43FC, 0x4F13, 0x603C, 0x2700, 0x4C12, 0x6027, 0x4C12, 0x6026, 0x4C12, 0x6023, 0x4C12, 0x6020, 0x680F, 0x4C12, 0x6027, 0x684F, 0x4C11, 0x6027, 0x4C11, 0x6023, 0x4C11, 0x6827, 0x421F, 0xD0FC, 0x6824, 0x07A4, 0xD400, 0xBE00, 0x3202, 0x3008, 0x3108, 0xE7D5, 0x2400, 0x42A8, 0xD201, 0x4F0B, 0xE7DC, 0x4F0B, 0xE7DA, 0x46C0, 0xE1D0, 0x400C, 0xE108, 0x400C, 0xE124, 0x400C, 0xE104, 0x400C, 0xE120, 0x400C, 0xE130, 0x400C, 0xE134, 0x400C, 0xE100, 0x400C, 0xE3D0, 0x400C, 0xE1D4, 0x400C, 0xE1D8, 0x400C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, diff --git a/src/target/meson.build b/src/target/meson.build index be10d0d317a..48c5e74bda0 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -370,7 +370,7 @@ target_ti_cortexm = declare_dependency( 'msp432e4.c', 'msp432p4.c', 'mspm0.c' - ) + lmi_stub, + ) + lmi_stub + mspm0_stub, compile_args: ['-DCONFIG_TI=1'], dependencies: target_cortexm, ) diff --git a/src/target/mspm0.c b/src/target/mspm0.c index 922ab044a6a..d5f27c82e6c 100644 --- a/src/target/mspm0.c +++ b/src/target/mspm0.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2024 hardesk + * Copyright (C) 2024-2026 hardesk * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ #include "target_internal.h" #include "buffer_utils.h" #include "jep106.h" -#include "cortex.h" +#include "cortexm.h" #define MSPM0_CONFIG_FLASH_DUMP_SUPPORT (CONFIG_BMDA == 1 || ENABLE_DEBUG == 1) @@ -31,12 +31,13 @@ #define TI_DEVID_MSPM0L_1227_2228 0xbb9fU /* MSPM0L[12]22[78]*/ #define TI_DEVID_MSPM0G 0xbb88U /* MSPM0G310[567], MSPM0G150[567], MSPM0G350[567] */ -#define MSPM0_SRAM_BASE 0x20000000U -#define MSPM0_FLASH_MAIN 0x00000000U -#define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ -#define MSPM0_FLASH_FACTORY 0x41c40000U /* One Sector, BANK0. Non modifiable */ -#define MSPM0_FLASH_DATA 0x41d00000U -#define MSPM0_FLASH_SECTOR_SZ 1024U +#define MSPM0_SRAM_BASE 0x20000000U +#define MSPM0_FLASH_MAIN 0x00000000U +#define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ +#define MSPM0_FLASH_FACTORY 0x41c40000U /* One Sector, BANK0. Non modifiable */ +#define MSPM0_FLASH_DATA 0x41d00000U +#define MSPM0_FLASH_SECTOR_SZ 1024U +#define MSPM0_FLASH_WRITE_CHUNK_SZ MSPM0_FLASH_SECTOR_SZ #define MSPM0_FACTORYREGION_DEVICEID (MSPM0_FLASH_FACTORY + 0x4U) #define MSPM0_FACTORYREGION_SRAMFLASH (MSPM0_FLASH_FACTORY + 0x18U) @@ -91,6 +92,11 @@ typedef struct mspm0_flash { uint32_t banks; } mspm0_flash_s; +static const uint16_t mspm0_flash_write_stub[] = { +#include "flashstub/mspm0.stub" +}; +#define STUB_BUFFER_BASE ALIGN(MSPM0_SRAM_BASE + sizeof(mspm0_flash_write_stub), 4) + #if MSPM0_CONFIG_FLASH_DUMP_SUPPORT static bool mspm0_dump_factory_config(target_s *target, int argc, const char **argv); static bool mspm0_dump_bcr_config(target_s *target, int argc, const char **argv); @@ -185,7 +191,8 @@ static bool mspm0_dump_bcr_config(target_s *const target, const int argc, const } #endif -static void mspm0_add_flash(target_s *const target, const uint32_t base, const size_t length, const uint32_t banks) +static void mspm0_add_flash( + target_s *const target, const uint32_t base, const size_t length, const uint32_t banks, uint32_t write_size) { mspm0_flash_s *const flash = calloc(1, sizeof(*flash)); if (flash == NULL) { @@ -198,7 +205,7 @@ static void mspm0_add_flash(target_s *const target, const uint32_t base, const s target_flash->start = base; target_flash->length = length; target_flash->blocksize = MSPM0_FLASH_SECTOR_SZ; - target_flash->writesize = 8U; + target_flash->writesize = write_size; target_flash->erase = mspm0_flash_erase; target_flash->write = mspm0_flash_write; target_flash->erased = 0xffU; @@ -208,7 +215,6 @@ static void mspm0_add_flash(target_s *const target, const uint32_t base, const s bool mspm0_probe(target_s *const target) { const uint32_t deviceid = target_mem32_read32(target, MSPM0_FACTORYREGION_DEVICEID); - const uint32_t manufacturer = (deviceid & MSPM0_DEVICEID_MANUFACTURER_MASK) >> MSPM0_DEVICEID_MANUFACTURER_SHIFT; if (manufacturer != JEP106_MANUFACTURER_TEXAS) return false; @@ -235,9 +241,16 @@ bool mspm0_probe(target_s *const target) MSPM0_FACTORYREGION_SRAMFLASH_DATAFLASH_SZ_SHIFT); target_add_ram32(target, MSPM0_SRAM_BASE, sram_size); - mspm0_add_flash(target, MSPM0_FLASH_MAIN, mainflash_size, main_num_banks); + + /* Decrease flash write size until it fits within available RAM */ + uint32_t write_size = MSPM0_FLASH_WRITE_CHUNK_SZ; + uint32_t avail_ram = sram_size - (STUB_BUFFER_BASE - MSPM0_SRAM_BASE); + while (write_size > avail_ram) + write_size >>= 1U; + + mspm0_add_flash(target, MSPM0_FLASH_MAIN, mainflash_size, main_num_banks, write_size); if (dataflash_size != 0) - mspm0_add_flash(target, MSPM0_FLASH_DATA, dataflash_size, 1U); + mspm0_add_flash(target, MSPM0_FLASH_DATA, dataflash_size, 1U, write_size); #if MSPM0_CONFIG_FLASH_DUMP_SUPPORT target_add_commands(target, mspm0_cmds_list, "MSPM0"); @@ -246,7 +259,7 @@ bool mspm0_probe(target_s *const target) return true; } -/* Wait for flash command to finish and return the status word or UINT32_MAX if timout */ +/* Wait for a flash command to finish and return the status word or 0 on timeout */ static uint32_t mspm0_flash_wait_done(target_s *const target) { platform_timeout_s timeout; @@ -257,7 +270,7 @@ static uint32_t mspm0_flash_wait_done(target_s *const target) status = target_mem32_read32(target, MSPM0_FLASHCTL_STATCMD); if (platform_timeout_is_expired(&timeout)) return 0U; - }; + } return status; } @@ -278,8 +291,12 @@ static void mspm0_flash_unprotect_sector(target_flash_s *const target_flash, con uint32_t mask = ~(1U << sector); target_mem32_write32(target_flash->t, MSPM0_FLASHCTL_CMDWEPROTA, mask); } else if (sector < 256U) { /* 8 sectors per bit */ - /* When main flash is single bank, PROTB covers sectors starting after PROTA which is 32k. In multibank case - * PROTB bits overlap PROTA and starts at sector 0. */ + /* + * Sectors affected by PROTB depend on the flash configuration. In single-bank + * main flash, PROTB applies to sectors after those affected by PROTA + * (that is, starting at sector 32). In multi-bank configurations, PROTA overlaps + * PROTB, so PROTB applies starting at sector 0. + */ uint32_t start_protb_sector = mspm0_flash->banks > 1U ? 0U : 32U; uint32_t mask = ~(1U << ((sector - start_protb_sector) >> 3U)); target_mem32_write32(target_flash->t, MSPM0_FLASHCTL_CMDWEPROTB, mask); @@ -316,27 +333,14 @@ static bool mspm0_flash_erase(target_flash_s *const target_flash, const target_a static bool mspm0_flash_write( target_flash_s *const target_flash, target_addr_t dest, const void *const src, const size_t length) { -#ifdef DEBUG_TARGET_IS_NOOP - (void)length; -#endif - target_s *const target = target_flash->t; + DEBUG_TARGET( + "%s: Writing flash addr %08" PRIx32 " length %08" PRIx32 "\n", __func__, (uint32_t)dest, (uint32_t)length); - mspm0_flash_unprotect_sector(target_flash, dest); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDTYPE, MSPM0_FLASHCTL_CMDTYPE_PROG | MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDCTL, 0U); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDADDR, dest); - target_mem32_write32(target, MSPM0_FLASHCTL_BYTEN, 0xffffffffU); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDDATA0, read_le4((const uint8_t *)src, 0U)); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDDATA1, read_le4((const uint8_t *)src, 4U)); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDEXEC, MSPM0_FLASHCTL_CMDEXEC_EXEC); - - const uint32_t status = mspm0_flash_wait_done(target); - if (!(status & MSPM0_FLASHCTL_STAT_CMDPASS)) - DEBUG_TARGET("%s: Failed to write to flash, status %08" PRIx32 " addr %08" PRIx32 " length %08" PRIx32 "\n", - __func__, status, dest, (uint32_t)length); + target_mem32_write(target, MSPM0_SRAM_BASE, mspm0_flash_write_stub, sizeof(mspm0_flash_write_stub)); + target_mem32_write(target, STUB_BUFFER_BASE, src, length); - return status & MSPM0_FLASHCTL_STAT_CMDPASS; + return cortexm_run_stub(target, MSPM0_SRAM_BASE, dest, STUB_BUFFER_BASE, length, 0); } static bool mspm0_mass_erase(target_s *const target, platform_timeout_s *const print_progess) From db181ee0569b5f1cfea8f87a3d8f22a5b44c8a30 Mon Sep 17 00:00:00 2001 From: hardesk Date: Sun, 8 Feb 2026 19:31:05 +0200 Subject: [PATCH 100/247] mspm0: Expand the id list of supported mspm0 targets --- src/target/mspm0.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/target/mspm0.c b/src/target/mspm0.c index d5f27c82e6c..95eeab5a439 100644 --- a/src/target/mspm0.c +++ b/src/target/mspm0.c @@ -26,11 +26,6 @@ #define MSPM0_CONFIG_FLASH_DUMP_SUPPORT (CONFIG_BMDA == 1 || ENABLE_DEBUG == 1) -#define TI_DEVID_MSPM0C 0xbba1U /* MSPM0C110[34] */ -#define TI_DEVID_MSPM0L 0xbb82U /* MSPM0L110[56], MSPM0L13[04][456] */ -#define TI_DEVID_MSPM0L_1227_2228 0xbb9fU /* MSPM0L[12]22[78]*/ -#define TI_DEVID_MSPM0G 0xbb88U /* MSPM0G310[567], MSPM0G150[567], MSPM0G350[567] */ - #define MSPM0_SRAM_BASE 0x20000000U #define MSPM0_FLASH_MAIN 0x00000000U #define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ @@ -92,6 +87,22 @@ typedef struct mspm0_flash { uint32_t banks; } mspm0_flash_s; +static uint16_t mspm0_partnums[] = { + 0xbba1U, /* MSPM0C: 1103 1104 1103-Q1 1104-Q1 */ + 0x0bbbU, /* MSPM0C: 1105-Q1 1106-Q1 */ + 0xbbbaU, /* MSPM0C: 1105 1106 */ + 0xbb82U, /* MSPM0L: 1105 1106 1304 1305 1305 1344 1345 1346 1345-Q1 1346-Q1 */ + 0xbb9fU, /* MSPM0L: 1227 1228 2227 2228 1227-Q1 1228-Q1 2227-Q1 2228-Q1 */ + 0xbbb4U, /* MSPM0L: 1116 1117 1116-Q1 1117-Q1 */ + 0xbbc7U, /* MSPM0L: 1126 1127 2116 2117 */ + 0x0bbaU, /* MSPM0H: 3215 3216 */ + 0xbb88U, /* MSPM0G: 1105 1106 1107 1505 1506 1507 3105 3106 3107 3505 3506 3507 3105-Q1 3106-Q1 3107-Q1 */ + /* 3505-Q1 3506-Q1 3507-Q1 */ + 0xbba9U, /* MSPM0G: 1518 1519 3518 3519 3518-Q1 3519-Q1 3529-Q1 */ + 0xbbbcU, /* MSPM0G: 5187 */ + 0xbbceU, /* MSPM0G: 1207 1218 3207 3218 */ +}; + static const uint16_t mspm0_flash_write_stub[] = { #include "flashstub/mspm0.stub" }; @@ -220,8 +231,12 @@ bool mspm0_probe(target_s *const target) return false; const uint32_t partnum = (deviceid & MSPM0_DEVICEID_PARTNUM_MASK) >> MSPM0_DEVICEID_PARTNUM_SHIFT; - if (partnum != TI_DEVID_MSPM0C && partnum != TI_DEVID_MSPM0L && partnum != TI_DEVID_MSPM0L_1227_2228 && - partnum != TI_DEVID_MSPM0G) + size_t partnum_idx = 0; + for (; partnum_idx < ARRAY_LENGTH(mspm0_partnums); ++partnum_idx) { + if (partnum == mspm0_partnums[partnum_idx]) + break; + } + if (partnum_idx >= ARRAY_LENGTH(mspm0_partnums)) return false; target->driver = "MSPM0"; From 9007caba85781b705dff1743a7166da1f1cfdb93 Mon Sep 17 00:00:00 2001 From: hardesk Date: Sun, 8 Feb 2026 19:31:49 +0200 Subject: [PATCH 101/247] mspm0: Remove the FACTORY/NONMAIN register inspection TI added multiple layouts for the registers depending on a part. Implementing and maintaining it all is too much effort (and code) with dubious value. --- src/target/mspm0.c | 96 ---------------------------------------------- 1 file changed, 96 deletions(-) diff --git a/src/target/mspm0.c b/src/target/mspm0.c index 95eeab5a439..9eb1825c530 100644 --- a/src/target/mspm0.c +++ b/src/target/mspm0.c @@ -24,8 +24,6 @@ #include "jep106.h" #include "cortexm.h" -#define MSPM0_CONFIG_FLASH_DUMP_SUPPORT (CONFIG_BMDA == 1 || ENABLE_DEBUG == 1) - #define MSPM0_SRAM_BASE 0x20000000U #define MSPM0_FLASH_MAIN 0x00000000U #define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ @@ -108,100 +106,10 @@ static const uint16_t mspm0_flash_write_stub[] = { }; #define STUB_BUFFER_BASE ALIGN(MSPM0_SRAM_BASE + sizeof(mspm0_flash_write_stub), 4) -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT -static bool mspm0_dump_factory_config(target_s *target, int argc, const char **argv); -static bool mspm0_dump_bcr_config(target_s *target, int argc, const char **argv); - -static command_s mspm0_cmds_list[] = { - {"dump_factory", mspm0_dump_factory_config, "Display FACTORY registers"}, - {"dump_bcr", mspm0_dump_bcr_config, "Display NONMAIN (BCR/BSL) registers"}, - {NULL, NULL, NULL}, -}; -#endif - static bool mspm0_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool mspm0_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); static bool mspm0_mass_erase(target_s *target, platform_timeout_s *print_progess); -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT -typedef struct conf_register { - uint16_t reg_offset; - uint16_t size_words; - const char *id; -} conf_register_s; - -static conf_register_s mspm0_factory_regs[] = { - {0x00U, 1U, "TRACEID"}, - {0x04U, 1U, "DEVICEID"}, - {0x08U, 1U, "USERID"}, - {0x0cU, 1U, "BSLPIN_UART"}, - {0x10U, 1U, "BSLPIN_I2C"}, - {0x14U, 1U, "BSLPIN_INVOKE"}, - {0x18U, 1U, "SRAMFLASH"}, - {0x3cU, 1U, "TEMP_SENSE0"}, - {0x7cU, 1U, "BOOTCRC"}, - {0U, 0U, NULL}, -}; - -static conf_register_s mspm0_bcr_regs[] = { - {0x00U, 1U, "BCRCONFIGID"}, - {0x04U, 1U, "BOOTCFG0"}, - {0x08U, 1U, "BOOTCFG1"}, - {0x0cU, 4U, "PWDDEBUGLOCK"}, - {0x1cU, 4U, "BOOTCFG2"}, - {0x20U, 1U, "BOOTCFG3"}, - {0x24U, 4U, "PWDMASSERASE"}, - {0x34U, 4U, "PWDFACTORYRESET"}, - {0x44U, 1U, "FLASHSWP0"}, - {0x48U, 1U, "FLASHSWP1"}, - {0x4cU, 1U, "BOOTCFG4"}, - {0x50U, 1U, "APPCRCSTART"}, - {0x54U, 1U, "APPCRCLENGTH"}, - {0x58U, 1U, "APPCRC"}, - {0x5cU, 1U, "BOOTCRC"}, - {0x100U, 1U, "BSLCONFIGID"}, - {0x104U, 1U, "BSLPINCFG0"}, - {0x108U, 1U, "BSLPINCFG1"}, - {0x10cU, 1U, "BSLCONFIG0"}, - {0x110U, 8U, "BSLPW"}, - {0x130U, 1U, "BSLPLUGINCFG"}, - {0x134U, 4U, "BSLPLUGINHOOK"}, - {0x144U, 1U, "PATCHHOOKID"}, - {0x148U, 1U, "SBLADDRESS"}, - {0x14cU, 1U, "BSLAPPVER"}, - {0x150U, 1U, "BSLCONFIG1"}, - {0x154U, 1U, "BSLCRC"}, - {0U, 0U, NULL}, -}; - -static void mspm0_dump_regs(target_s *const target, const conf_register_s *const regs, const uint32_t base) -{ - for (const conf_register_s *reg = regs; reg->id; ++reg) { - tc_printf(target, "%15s: ", reg->id); - for (size_t i = 0; i < reg->size_words; ++i) { - uint32_t value = target_mem32_read32(target, base + reg->reg_offset + (uint32_t)(i * 4U)); - tc_printf(target, "0x%08" PRIx32 "%s", value, i == reg->size_words - 1U ? "\n" : " "); - } - } -} - -static bool mspm0_dump_factory_config(target_s *const target, const int argc, const char **const argv) -{ - (void)argc; - (void)argv; - mspm0_dump_regs(target, mspm0_factory_regs, MSPM0_FLASH_FACTORY); - return true; -} - -static bool mspm0_dump_bcr_config(target_s *const target, const int argc, const char **const argv) -{ - (void)argc; - (void)argv; - mspm0_dump_regs(target, mspm0_bcr_regs, MSPM0_FLASH_NONMAIN); - return true; -} -#endif - static void mspm0_add_flash( target_s *const target, const uint32_t base, const size_t length, const uint32_t banks, uint32_t write_size) { @@ -267,10 +175,6 @@ bool mspm0_probe(target_s *const target) if (dataflash_size != 0) mspm0_add_flash(target, MSPM0_FLASH_DATA, dataflash_size, 1U, write_size); -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT - target_add_commands(target, mspm0_cmds_list, "MSPM0"); -#endif - return true; } From 7d51c114150bf7aadba542ab170229b815d30518 Mon Sep 17 00:00:00 2001 From: hardesk Date: Sun, 8 Feb 2026 19:32:55 +0200 Subject: [PATCH 102/247] mspm0: Fix condition check whether flash erase command has finished --- src/target/mspm0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/mspm0.c b/src/target/mspm0.c index 9eb1825c530..6c300d59f23 100644 --- a/src/target/mspm0.c +++ b/src/target/mspm0.c @@ -283,7 +283,7 @@ static bool mspm0_mass_erase(target_s *const target, platform_timeout_s *const p target_mem32_write32(target, MSPM0_FLASHCTL_CMDEXEC, MSPM0_FLASHCTL_CMDEXEC_EXEC); uint32_t status = 0U; - while (status & MSPM0_FLASHCTL_STAT_DONE) { + while (!(status & MSPM0_FLASHCTL_STAT_DONE)) { status = target_mem32_read32(target, MSPM0_FLASHCTL_STATCMD); if (print_progess) target_print_progress(print_progess); From ab56f9fcf4d620f129d3f5a6b7dda6ac2585fe92 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:08:50 +0000 Subject: [PATCH 103/247] ctxlink/WiFi_Server: Cleaned up in the GDB send interface code --- src/platforms/ctxlink/WiFi_Server.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/platforms/ctxlink/WiFi_Server.c b/src/platforms/ctxlink/WiFi_Server.c index 1e2b69e5353..3fad1f4d492 100644 --- a/src/platforms/ctxlink/WiFi_Server.c +++ b/src/platforms/ctxlink/WiFi_Server.c @@ -1629,7 +1629,7 @@ void send_swo_trace_data(uint8_t *buffer, uint8_t length) void wifi_gdb_putchar(const uint8_t ch, const bool flush) { send_buffer[send_count++] = ch; - if (flush || send_count >= sizeof(send_buffer)) + if (flush || send_count == sizeof(send_buffer)) wifi_gdb_flush(flush); } @@ -1641,13 +1641,9 @@ void wifi_gdb_flush(const bool force) if (send_count == 0U) return; - // TODO is this check required now, looks like a debug test left in place? - if (send_count <= 0U) - DEBUG_WARN("WiFi_putchar bad count\r\n"); - DEBUG_WARN("Wifi_putchar %c\r\n", send_buffer[0]); - send(gdb_client_socket, &send_buffer[0], send_count, 0); + DEBUG_WARN("Wifi_putchar %c\n", send_buffer[0]); + send(gdb_client_socket, send_buffer, send_count, 0); /* Reset the buffer */ send_count = 0U; - memset(&send_buffer[0], 0x00, sizeof(send_buffer)); } From 003f96b4d259d01f9bfeb07c4fcf4072c99554aa Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:11:54 +0000 Subject: [PATCH 104/247] gdb_packet: Converted consume_remote_packet() to using the new GDB packet structure --- src/gdb_packet.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 420ed3f3227..0b6cc653138 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -119,36 +119,35 @@ static uint8_t gdb_packet_checksum(const gdb_packet_s *const packet) return checksum; } -packet_state_e consume_remote_packet(char *const packet, const size_t size) +packet_state_e consume_remote_packet(gdb_packet_s *const packet) { #if CONFIG_BMDA == 0 /* We got what looks like probably a remote control packet */ - size_t offset = 0; + packet->size = 0; while (true) { /* Consume bytes until we either have a complete remote control packet or have to leave this mode */ const char rx_char = gdb_if_getchar(); switch (rx_char) { case '\x04': - packet[0] = rx_char; + packet->data[0] = rx_char; /* EOT (end of transmission) - connection was closed */ return PACKET_IDLE; case REMOTE_SOM: /* Oh dear, restart remote packet capture */ - offset = 0; + packet->size = 0; break; case REMOTE_EOM: /* Complete packet for processing */ /* Null terminate packet */ - packet[offset] = '\0'; + packet->data[packet->size] = '\0'; /* Handle packet */ - remote_packet_process(packet, offset); + remote_packet_process(packet->data, packet->size); /* Restart packet capture */ - packet[0] = '\0'; return PACKET_IDLE; case GDB_PACKET_START: @@ -156,18 +155,15 @@ packet_state_e consume_remote_packet(char *const packet, const size_t size) return PACKET_GDB_CAPTURE; default: - if (offset < size) - packet[offset++] = rx_char; - else { - packet[0] = '\0'; + if (packet->size < GDB_PACKET_BUFFER_SIZE) + packet->data[packet->size++] = rx_char; + else /* Buffer overflow, restart packet capture */ return PACKET_IDLE; - } } } #else (void)packet; - (void)size; /* Hosted builds ignore remote control packets */ return PACKET_IDLE; @@ -199,7 +195,7 @@ gdb_packet_s *gdb_packet_receive(void) * Let consume_remote_packet handle this * returns PACKET_IDLE or PACKET_GDB_CAPTURE if it detects the start of a GDB packet */ - state = consume_remote_packet(packet->data, GDB_PACKET_BUFFER_SIZE); + state = consume_remote_packet(packet); packet->size = 0; } #endif @@ -446,10 +442,10 @@ void gdb_out(const char *const str) /** * Program console output packet * See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html#Stop-Reply-Packets - * + * * Format: ‘O XX…’ * ‘XX…’ is hex encoding of ASCII data, to be written as the program’s console output. - * + * * Can happen at any time while the program is running and the debugger should continue to wait for ‘W’, ‘T’, etc. * This reply is not permitted in non-stop mode. */ @@ -462,7 +458,7 @@ void gdb_voutf(const char *const fmt, va_list ap) * We could technically do the formatting and transformation in a single buffer reducing stack usage * But it is a bit more complex and likely slower, we would need to spread the characters out such * that each occupies two bytes, and then we could hex them in place - * + * * If this stack usage proves to be a problem, we can revisit this */ char str_scratch[GDB_OUT_PACKET_MAX_SIZE + 1U]; From ed378c8cdd6c15efa7e94133986e71d4275e9c82 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:14:39 +0000 Subject: [PATCH 105/247] remote: Begun to switch the remote protocol to be expressed in terms of the GDB packet structure --- src/gdb_packet.c | 2 +- src/remote.c | 20 ++++++++++---------- src/remote.h | 3 ++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 0b6cc653138..6ef909d456b 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -145,7 +145,7 @@ packet_state_e consume_remote_packet(gdb_packet_s *const packet) /* Null terminate packet */ packet->data[packet->size] = '\0'; /* Handle packet */ - remote_packet_process(packet->data, packet->size); + remote_packet_process(packet); /* Restart packet capture */ return PACKET_IDLE; diff --git a/src/remote.c b/src/remote.c index bdb9fac816d..dc3ae4e7ba5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -920,34 +920,34 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len } } -void remote_packet_process(char *const packet, const size_t packet_length) +void remote_packet_process(gdb_packet_s *const packet) { /* Check there's at least a request byte */ - if (packet_length < 1U) { + if (packet->size < 1U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); return; } - switch (packet[0]) { + switch (packet->data[0]) { case REMOTE_SWDP_PACKET: - remote_packet_process_swd(packet, packet_length); + remote_packet_process_swd(packet->data, packet->size); break; case REMOTE_JTAG_PACKET: - remote_packet_process_jtag(packet, packet_length); + remote_packet_process_jtag(packet->data, packet->size); break; case REMOTE_GEN_PACKET: - remote_packet_process_general(packet, packet_length); + remote_packet_process_general(packet->data, packet->size); break; case REMOTE_HL_PACKET: - remote_packet_process_high_level(packet, packet_length); + remote_packet_process_high_level(packet->data, packet->size); break; case REMOTE_ADIV5_PACKET: { /* Setup an exception frame to try the ADIv5 operation in */ TRY (EXCEPTION_ALL) { - remote_packet_process_adiv5(packet, packet_length); + remote_packet_process_adiv5(packet->data, packet->size); } CATCH () { /* Handle any exception we've caught by translating it into a remote protocol response */ @@ -959,12 +959,12 @@ void remote_packet_process(char *const packet, const size_t packet_length) #if defined(CONFIG_RISCV_ACCEL) && CONFIG_RISCV_ACCEL == 1 case REMOTE_RISCV_PACKET: - remote_packet_process_riscv(packet, packet_length); + remote_packet_process_riscv(packet->data, packet->size); break; #endif case REMOTE_SPI_PACKET: - remote_packet_process_spi(packet, packet_length); + remote_packet_process_spi(packet->data, packet->size); break; default: /* Oh dear, unrecognised, return an error */ diff --git a/src/remote.h b/src/remote.h index 66094206885..520cb8a3a64 100644 --- a/src/remote.h +++ b/src/remote.h @@ -25,6 +25,7 @@ #include #include "general.h" +#include "gdb_packet.h" #define REMOTE_HL_VERSION 4 @@ -512,6 +513,6 @@ REMOTE_UINT24, REMOTE_EOM, 0 \ } -void remote_packet_process(char *packet, size_t packet_length); +void remote_packet_process(gdb_packet_s *packet); #endif /* REMOTE_H */ From b983da4d788ee580dff168a121cb6589395abd3b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:16:18 +0000 Subject: [PATCH 106/247] remote: Converted the SWD packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/remote.c b/src/remote.c index dc3ae4e7ba5..078a3b27839 100644 --- a/src/remote.c +++ b/src/remote.c @@ -128,11 +128,11 @@ static adiv5_debug_port_s remote_dp = { .mem_write = adiv5_mem_write_bytes, }; -static void remote_packet_process_swd(const char *const packet, const size_t packet_len) +static void remote_packet_process_swd(gdb_packet_s *const packet) { - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_INIT: /* SS = initialise =============================== */ - if (packet_len == 2) { + if (packet->size == 2) { remote_dp.write_no_check = adiv5_swd_write_no_check; remote_dp.read_no_check = adiv5_swd_read_no_check; remote_dp.dp_read = adiv5_swd_read; @@ -146,7 +146,7 @@ static void remote_packet_process_swd(const char *const packet, const size_t pac break; case REMOTE_IN_PAR: { /* SI = In parity ============================= */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); uint32_t result = 0; const bool parity_ok = swd_proc.seq_in_parity(&result, clock_cycles); remote_respond(parity_ok ? REMOTE_RESP_OK : REMOTE_RESP_PARERR, result); @@ -154,23 +154,23 @@ static void remote_packet_process_swd(const char *const packet, const size_t pac } case REMOTE_IN: { /* Si = In ======================================= */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); const uint32_t result = swd_proc.seq_in(clock_cycles); remote_respond(REMOTE_RESP_OK, result); break; } case REMOTE_OUT: { /* So = Out ====================================== */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t data = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t data = hex_string_to_num(-1, packet->data + 4); swd_proc.seq_out(data, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; } case REMOTE_OUT_PAR: { /* SO = Out parity ========================== */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t data = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t data = hex_string_to_num(-1, packet->data + 4); swd_proc.seq_out_parity(data, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; @@ -929,7 +929,7 @@ void remote_packet_process(gdb_packet_s *const packet) } switch (packet->data[0]) { case REMOTE_SWDP_PACKET: - remote_packet_process_swd(packet->data, packet->size); + remote_packet_process_swd(packet); break; case REMOTE_JTAG_PACKET: From 3da9427f4bf25aa8f6caa543d79eb6842a18f529 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:20:17 +0000 Subject: [PATCH 107/247] remote: Converted the JTAG packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/remote.c b/src/remote.c index 078a3b27839..70032fc7711 100644 --- a/src/remote.c +++ b/src/remote.c @@ -182,9 +182,9 @@ static void remote_packet_process_swd(gdb_packet_s *const packet) } } -static void remote_packet_process_jtag(const char *const packet, const size_t packet_len) +static void remote_packet_process_jtag(gdb_packet_s *const packet) { - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_INIT: /* JS = initialise ============================= */ remote_dp.write_no_check = NULL; remote_dp.read_no_check = NULL; @@ -202,10 +202,10 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa break; case REMOTE_TMS: { /* JT = TMS Sequence ============================ */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t tms_states = hex_string_to_num(2, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t tms_states = hex_string_to_num(2, packet->data + 4); - if (packet_len < 4U) + if (packet->size < 4U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { jtag_proc.jtagtap_tms_seq(tms_states, clock_cycles); @@ -215,9 +215,9 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa } case REMOTE_CYCLE: { /* Jc = clock cycle ============================ */ - const size_t clock_cycles = hex_string_to_num(8, packet + 4); - const bool tms = packet[2] != '0'; - const bool tdi = packet[3] != '0'; + const size_t clock_cycles = hex_string_to_num(8, packet->data + 4); + const bool tms = packet->data[2] != '0'; + const bool tdi = packet->data[3] != '0'; jtag_proc.jtagtap_cycle(tms, tdi, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; @@ -225,14 +225,14 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa case REMOTE_TDITDO_TMS: /* JD = TDI/TDO ========================================= */ case REMOTE_TDITDO_NOTMS: { - if (packet_len < 5U) + if (packet->size < 5U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint64_t data_in = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint64_t data_in = hex_string_to_num(-1, packet->data + 4); uint64_t data_out = 0; jtag_proc.jtagtap_tdi_tdo_seq( - (uint8_t *)&data_out, packet[1] == REMOTE_TDITDO_TMS, (const uint8_t *)&data_in, clock_cycles); + (uint8_t *)&data_out, packet->data[1] == REMOTE_TDITDO_TMS, (const uint8_t *)&data_in, clock_cycles); remote_respond(REMOTE_RESP_OK, data_out); } @@ -240,10 +240,10 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa } case REMOTE_NEXT: { /* JN = NEXT ======================================== */ - if (packet_len != 4U) + if (packet->size != 4U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { - const bool tdo = jtag_proc.jtagtap_next(packet[2] == '1', packet[3] == '1'); + const bool tdo = jtag_proc.jtagtap_next(packet->data[2] == '1', packet->data[3] == '1'); remote_respond(REMOTE_RESP_OK, tdo ? 1U : 0U); } break; @@ -933,7 +933,7 @@ void remote_packet_process(gdb_packet_s *const packet) break; case REMOTE_JTAG_PACKET: - remote_packet_process_jtag(packet->data, packet->size); + remote_packet_process_jtag(packet); break; case REMOTE_GEN_PACKET: From b72e43e5b22e167cfb454dbfd25ca698c1510604 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:21:22 +0000 Subject: [PATCH 108/247] remote: Converted the general packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/remote.c b/src/remote.c index 70032fc7711..c8fa5345f23 100644 --- a/src/remote.c +++ b/src/remote.c @@ -255,22 +255,21 @@ static void remote_packet_process_jtag(gdb_packet_s *const packet) } } -static void remote_packet_process_general(char *packet, const size_t packet_len) +static void remote_packet_process_general(gdb_packet_s *const packet) { - (void)packet_len; - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_VOLTAGE: remote_respond_string(REMOTE_RESP_OK, platform_target_voltage()); break; case REMOTE_NRST_SET: - platform_nrst_set_val(packet[2] == '1'); + platform_nrst_set_val(packet->data[2] == '1'); remote_respond(REMOTE_RESP_OK, 0); break; case REMOTE_NRST_GET: remote_respond(REMOTE_RESP_OK, platform_nrst_get_val()); break; case REMOTE_FREQ_SET: - platform_max_frequency_set(hex_string_to_num(8U, packet + 2U)); + platform_max_frequency_set(hex_string_to_num(8U, packet->data + 2U)); remote_respond(REMOTE_RESP_OK, 0); break; case REMOTE_FREQ_GET: { @@ -280,14 +279,14 @@ static void remote_packet_process_general(char *packet, const size_t packet_len) } case REMOTE_PWR_SET: #ifdef PLATFORM_HAS_POWER_SWITCH - if (packet[2] == '1' && !platform_target_get_power() && + if (packet->data[2] == '1' && !platform_target_get_power() && platform_target_voltage_sense() > POWER_CONFLICT_THRESHOLD) { /* want to enable target power, but voltage > 0.5V sensed * on the pin -> cancel */ remote_respond(REMOTE_RESP_ERR, 0); } else { - const bool result = platform_target_set_power(packet[2] == '1'); + const bool result = platform_target_set_power(packet->data[2] == '1'); remote_respond(result ? REMOTE_RESP_OK : REMOTE_RESP_ERR, 0U); } #else @@ -308,12 +307,12 @@ static void remote_packet_process_general(char *packet, const size_t packet_len) #ifndef PLATFORM_IDENT_DYNAMIC remote_respond_string(REMOTE_RESP_OK, BOARD_IDENT); #else - snprintf(packet, GDB_PACKET_BUFFER_SIZE, BOARD_IDENT, platform_ident()); - remote_respond_string(REMOTE_RESP_OK, packet); + snprintf(packet->data, GDB_PACKET_BUFFER_SIZE, BOARD_IDENT, platform_ident()); + remote_respond_string(REMOTE_RESP_OK, packet->data); #endif break; case REMOTE_TARGET_CLK_OE: - platform_target_clk_output_enable(packet[2] != '0'); + platform_target_clk_output_enable(packet->data[2] != '0'); remote_respond(REMOTE_RESP_OK, 0); break; default: @@ -937,7 +936,7 @@ void remote_packet_process(gdb_packet_s *const packet) break; case REMOTE_GEN_PACKET: - remote_packet_process_general(packet->data, packet->size); + remote_packet_process_general(packet); break; case REMOTE_HL_PACKET: From fe8791ffa83931ca7dc439d25e254f545062f5db Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:22:44 +0000 Subject: [PATCH 109/247] remote: Converted the high-level packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/remote.c b/src/remote.c index c8fa5345f23..64068b82955 100644 --- a/src/remote.c +++ b/src/remote.c @@ -321,29 +321,29 @@ static void remote_packet_process_general(gdb_packet_s *const packet) } } -static void remote_packet_process_high_level(const char *packet, const size_t packet_len) +static void remote_packet_process_high_level(gdb_packet_s *const packet) { SET_IDLE_STATE(0); - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_HL_CHECK: /* HC = check the version of the protocol */ remote_respond(REMOTE_RESP_OK, REMOTE_HL_VERSION); break; case REMOTE_HL_ADD_JTAG_DEV: { /* HJ = fill firmware jtag_devs */ /* Check the packet is an appropriate length */ - if (packet_len < 22U) { + if (packet->size < 22U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); break; } jtag_dev_s jtag_dev = {0}; - const uint8_t index = hex_string_to_num(2, packet + 2); - jtag_dev.dr_prescan = hex_string_to_num(2, packet + 4); - jtag_dev.dr_postscan = hex_string_to_num(2, packet + 6); - jtag_dev.ir_len = hex_string_to_num(2, packet + 8); - jtag_dev.ir_prescan = hex_string_to_num(2, packet + 10); - jtag_dev.ir_postscan = hex_string_to_num(2, packet + 12); - jtag_dev.current_ir = hex_string_to_num(8, packet + 14); + const uint8_t index = hex_string_to_num(2, packet->data + 2); + jtag_dev.dr_prescan = hex_string_to_num(2, packet->data + 4); + jtag_dev.dr_postscan = hex_string_to_num(2, packet->data + 6); + jtag_dev.ir_len = hex_string_to_num(2, packet->data + 8); + jtag_dev.ir_prescan = hex_string_to_num(2, packet->data + 10); + jtag_dev.ir_postscan = hex_string_to_num(2, packet->data + 12); + jtag_dev.current_ir = hex_string_to_num(8, packet->data + 14); jtag_add_device(index, &jtag_dev); remote_respond(REMOTE_RESP_OK, 0); break; @@ -940,7 +940,7 @@ void remote_packet_process(gdb_packet_s *const packet) break; case REMOTE_HL_PACKET: - remote_packet_process_high_level(packet->data, packet->size); + remote_packet_process_high_level(packet); break; case REMOTE_ADIV5_PACKET: { From f111d72daa6177721793f6c77f09f2fd2dc6642f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:24:36 +0000 Subject: [PATCH 110/247] remote: Converted the ADIv5 packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/remote.c b/src/remote.c index 64068b82955..f98c6f3adca 100644 --- a/src/remote.c +++ b/src/remote.c @@ -458,20 +458,20 @@ static void remote_adiv5_respond(const void *const data, const size_t length) } } -static void remote_packet_process_adiv5(const char *const packet, const size_t packet_len) +static void remote_packet_process_adiv5(gdb_packet_s *const packet) { /* Check there's at least an ADI command byte */ - if (packet_len < 2U) { + if (packet->size < 2U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); return; } /* Check if this is a DP version packet and handle it if it is */ - if (packet[1] == REMOTE_DP_VERSION) { + if (packet->data[1] == REMOTE_DP_VERSION) { /* Check there are enough bytes for the request */ - if (packet_len == 4U) { + if (packet->size == 4U) { /* Extract the new version information into the DP */ - remote_dp.version = hex_string_to_num(2U, packet + 2U); + remote_dp.version = hex_string_to_num(2U, packet->data + 2U); remote_respond(REMOTE_RESP_OK, 0); } else /* There weren't enough bytes, so tell the host and get out of here */ @@ -479,11 +479,11 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p return; } /* Check if this is a DP targetsel packet and handle it if it is */ - if (packet[1] == REMOTE_DP_TARGETSEL) { + if (packet->data[1] == REMOTE_DP_TARGETSEL) { /* Check if there are enough bytes for the request */ - if (packet_len == 10U) { + if (packet->size == 10U) { /* Extract the new targetsel information into the DP */ - remote_dp.targetsel = hex_string_to_num(8U, packet + 2U); + remote_dp.targetsel = hex_string_to_num(8U, packet->data + 2U); remote_respond(REMOTE_RESP_OK, 0); } else /* There weren't enough bytes, so tell the host and get out of here */ @@ -492,30 +492,30 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } /* Our shortest ADIv5 packet is 8 bytes long, check that we have at least that */ - if (packet_len < 8U) { + if (packet->size < 8U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Check if this is actually an ADIv6 acceleration packet and dispatch */ - if (packet[1] == REMOTE_ADIV6_PACKET) { - remote_packet_process_adiv6(packet, packet_len); + if (packet->data[1] == REMOTE_ADIV6_PACKET) { + remote_packet_process_adiv6(packet->data, packet->size); return; } /* Set up the DP and a fake AP structure to perform the access with */ - remote_dp.dev_index = hex_string_to_num(2, packet + 2); + remote_dp.dev_index = hex_string_to_num(2, packet->data + 2); remote_dp.fault = 0U; adiv5_access_port_s remote_ap; - remote_ap.apsel = hex_string_to_num(2, packet + 4); + remote_ap.apsel = hex_string_to_num(2, packet->data + 4); remote_ap.dp = &remote_dp; SET_IDLE_STATE(0); - switch (packet[1]) { + switch (packet->data[1]) { /* DP access commands */ case REMOTE_DP_READ: { /* Ad = Read from DP register */ /* Grab the address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); const uint32_t data = adiv5_dp_read(&remote_dp, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU)); remote_adiv5_respond(&data, 4U); @@ -524,8 +524,8 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Raw access comands */ case REMOTE_ADIV5_RAW_ACCESS: { /* AR = Perform a raw ADIv5 access */ /* Grab the address to perform an access against and the value to work with */ - const uint16_t addr = hex_string_to_num(4, packet + 6); - const uint32_t value = hex_string_to_num(8, packet + 10); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); + const uint32_t value = hex_string_to_num(8, packet->data + 10); /* Try to perform the access using the AP selection value as R/!W */ const uint32_t data = adiv5_dp_low_access( &remote_dp, remote_ap.apsel, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU), value); @@ -535,7 +535,7 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* AP access commands */ case REMOTE_AP_READ: { /* Aa = Read from AP register */ /* Grab the AP address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); const uint32_t data = adiv5_ap_read(&remote_ap, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU)); remote_adiv5_respond(&data, 4U); @@ -543,8 +543,8 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } case REMOTE_AP_WRITE: { /* AA = Write to AP register */ /* Grab the AP address to write to and the data to write then try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); - const uint32_t value = hex_string_to_num(8, packet + 10); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); + const uint32_t value = hex_string_to_num(8, packet->data + 10); adiv5_ap_write(&remote_ap, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU), value); remote_adiv5_respond(NULL, 0U); break; @@ -552,11 +552,11 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Memory access commands */ case REMOTE_MEM_READ: { /* Am = Read from memory */ /* Grab the CSW value to use in the access */ - remote_ap.csw = hex_string_to_num(8, packet + 6); + remote_ap.csw = hex_string_to_num(8, packet->data + 6); /* Grab the start address for the read */ - const target_addr64_t address = hex_string_to_num(16, packet + 14U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 14U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 30U); + const uint32_t length = hex_string_to_num(8, packet->data + 30U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_READ_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -571,13 +571,13 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } case REMOTE_MEM_WRITE: { /* AM = Write to memory */ /* Grab the CSW value to use in the access */ - remote_ap.csw = hex_string_to_num(8, packet + 6); + remote_ap.csw = hex_string_to_num(8, packet->data + 6); /* Grab the alignment for the access */ - const align_e align = hex_string_to_num(2, packet + 14U); + const align_e align = hex_string_to_num(2, packet->data + 14U); /* Grab the start address for the write */ - const target_addr64_t address = hex_string_to_num(16, packet + 16U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 16U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 32U); + const uint32_t length = hex_string_to_num(8, packet->data + 32U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_WRITE_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -591,7 +591,7 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 40U, length); + unhexify(data, packet->data + 40U, length); /* Perform the write and report success/failures */ adiv5_mem_write_aligned(&remote_ap, address, data, length, align); remote_adiv5_respond(NULL, 0); @@ -946,7 +946,7 @@ void remote_packet_process(gdb_packet_s *const packet) case REMOTE_ADIV5_PACKET: { /* Setup an exception frame to try the ADIv5 operation in */ TRY (EXCEPTION_ALL) { - remote_packet_process_adiv5(packet->data, packet->size); + remote_packet_process_adiv5(packet); } CATCH () { /* Handle any exception we've caught by translating it into a remote protocol response */ From 4d91de3a448a1fa1e6f9c9cb67ab9688cf2d81b4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:25:55 +0000 Subject: [PATCH 111/247] remote: Converted the ADIv6 packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/remote.c b/src/remote.c index f98c6f3adca..255bef879fe 100644 --- a/src/remote.c +++ b/src/remote.c @@ -43,7 +43,7 @@ #include "hex_utils.h" #if CONFIG_BMDA == 0 -static void remote_packet_process_adiv6(const char *packet, size_t packet_len); +static void remote_packet_process_adiv6(gdb_packet_s *packet); /* hex-ify and send a buffer of data */ static void remote_send_buf(const void *const buffer, const size_t len) @@ -499,7 +499,7 @@ static void remote_packet_process_adiv5(gdb_packet_s *const packet) /* Check if this is actually an ADIv6 acceleration packet and dispatch */ if (packet->data[1] == REMOTE_ADIV6_PACKET) { - remote_packet_process_adiv6(packet->data, packet->size); + remote_packet_process_adiv6(packet); return; } @@ -605,10 +605,10 @@ static void remote_packet_process_adiv5(gdb_packet_s *const packet) SET_IDLE_STATE(1); } -static void remote_packet_process_adiv6(const char *const packet, const size_t packet_len) +static void remote_packet_process_adiv6(gdb_packet_s *const packet) { /* Our shortest ADIv5 packet is 15 bytes long, check that we have at least that */ - if (packet_len < 15U) { + if (packet->size < 15U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } @@ -623,26 +623,26 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p dp.ap_write = adiv6_ap_reg_write; /* Set up the DP and a fake AP structure to perform the access with */ - remote_dp.dev_index = hex_string_to_num(2, packet + 3); + remote_dp.dev_index = hex_string_to_num(2, packet->data + 3); remote_dp.fault = 0U; adiv6_access_port_s remote_ap; - remote_ap.ap_address = hex_string_to_num(16, packet + 5); + remote_ap.ap_address = hex_string_to_num(16, packet->data + 5); remote_ap.base.dp = &dp; SET_IDLE_STATE(0); - switch (packet[2]) { + switch (packet->data[2]) { /* AP access commands */ case REMOTE_AP_READ: { /* A6a = Read from APv2 register */ /* Grab the AP address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 21); + const uint16_t addr = hex_string_to_num(4, packet->data + 21); const uint32_t data = adiv5_ap_read(&remote_ap.base, addr); remote_adiv5_respond(&data, 4U); break; } case REMOTE_AP_WRITE: { /* A6A = Write to APv2 register */ /* Grab the AP address to write to and the data to write then try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 21); - const uint32_t value = hex_string_to_num(8, packet + 25); + const uint16_t addr = hex_string_to_num(4, packet->data + 21); + const uint32_t value = hex_string_to_num(8, packet->data + 25); adiv5_ap_write(&remote_ap.base, addr, value); remote_adiv5_respond(NULL, 0U); break; @@ -650,11 +650,11 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p /* Memory access commands */ case REMOTE_MEM_READ: { /* Am = Read from memory */ /* Grab the CSW value to use in the access */ - remote_ap.base.csw = hex_string_to_num(8, packet + 21); + remote_ap.base.csw = hex_string_to_num(8, packet->data + 21); /* Grab the start address for the read */ - const target_addr64_t address = hex_string_to_num(16, packet + 29U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 29U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 45U); + const uint32_t length = hex_string_to_num(8, packet->data + 45U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV6_MEM_READ_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -669,13 +669,13 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p } case REMOTE_MEM_WRITE: { /* AM = Write to memory */ /* Grab the CSW value to use in the access */ - remote_ap.base.csw = hex_string_to_num(8, packet + 21); + remote_ap.base.csw = hex_string_to_num(8, packet->data + 21); /* Grab the alignment for the access */ - const align_e align = hex_string_to_num(2, packet + 29U); + const align_e align = hex_string_to_num(2, packet->data + 29U); /* Grab the start address for the write */ - const target_addr64_t address = hex_string_to_num(16, packet + 31U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 31U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 47U); + const uint32_t length = hex_string_to_num(8, packet->data + 47U); /* NB: Hex encoding on the request data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_WRITE_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -689,7 +689,7 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 55U, length); + unhexify(data, packet->data + 55U, length); /* Perform the write and report success/failures */ adiv5_mem_write_aligned(&remote_ap.base, address, data, length, align); remote_adiv5_respond(NULL, 0); From f12ce00eb17b1bcbb4e3d59d3a4d07a7ecef999b Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:27:53 +0000 Subject: [PATCH 112/247] remote: Converted the RISC-V packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/remote.c b/src/remote.c index 255bef879fe..4719e7a2d8e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -717,33 +717,33 @@ static riscv_dmi_s remote_dmi = { .write = NULL, }; -void remote_packet_process_riscv(const char *const packet, const size_t packet_len) +void remote_packet_process_riscv(gdb_packet_s *const packet) { /* Our shortest RISC-V Debug protocol packet is 2 bytes long, check that we have at least that */ - if (packet_len < 2U) { + if (packet->size < 2U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Check for and handle the protocols packet */ - if (packet[1U] == REMOTE_RISCV_PROTOCOLS) { + if (packet->data[1U] == REMOTE_RISCV_PROTOCOLS) { /* Validate the length of the packet, then handle it if that checks out */ - if (packet_len != 2U) + if (packet->size != 2U) remote_respond(REMOTE_RESP_PARERR, 0); else remote_respond(REMOTE_RESP_OK, REMOTE_RISCV_PROTOCOL_JTAG); return; } /* Check for and handle the initialisation packet */ - else if (packet[1U] == REMOTE_INIT) { + else if (packet->data[1U] == REMOTE_INIT) { /* Check the length of the packet */ - if (packet_len != 3U) { + if (packet->size != 3U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* We got a good packet, so handle initialisation accordingly */ - switch (packet[2U]) { + switch (packet->data[2U]) { case REMOTE_RISCV_JTAG: remote_dmi.read = riscv_jtag_dmi_read; remote_dmi.write = riscv_jtag_dmi_write; @@ -757,21 +757,21 @@ void remote_packet_process_riscv(const char *const packet, const size_t packet_l return; } /* Our shortest RISC-V protocol packet is 16 bytes long, check that we have at least that */ - else if (packet_len < 16U) { + else if (packet->size < 16U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Having dealt with the other requests, set up the fake DMI structure to perform the access with */ - remote_dmi.dev_index = hex_string_to_num(2, packet + 2); - remote_dmi.idle_cycles = hex_string_to_num(2, packet + 4); - remote_dmi.address_width = hex_string_to_num(2, packet + 6); + remote_dmi.dev_index = hex_string_to_num(2, packet->data + 2); + remote_dmi.idle_cycles = hex_string_to_num(2, packet->data + 4); + remote_dmi.address_width = hex_string_to_num(2, packet->data + 6); remote_dmi.fault = 0U; - switch (packet[1U]) { + switch (packet->data[1U]) { case REMOTE_RISCV_DMI_READ: { /* Grab the DMI address to read from and try to perform the access */ - const uint32_t addr = hex_string_to_num(8, packet + 8); + const uint32_t addr = hex_string_to_num(8, packet->data + 8); uint32_t value = 0; if (!remote_dmi.read(&remote_dmi, addr, &value)) /* If the request didn't work, and caused a fault, tell the host */ @@ -783,13 +783,13 @@ void remote_packet_process_riscv(const char *const packet, const size_t packet_l } case REMOTE_RISCV_DMI_WRITE: { /* Write packets are 24 bytes long, verify we have enough bytes */ - if (packet_len != 24U) { + if (packet->size != 24U) { remote_respond(REMOTE_RESP_PARERR, 0); break; } /* Grab the DMI address to write to and the data to write then try to perform the access */ - const uint32_t addr = hex_string_to_num(8, packet + 8); - const uint32_t value = hex_string_to_num(8, packet + 16); + const uint32_t addr = hex_string_to_num(8, packet->data + 8); + const uint32_t value = hex_string_to_num(8, packet->data + 16); if (!remote_dmi.write(&remote_dmi, addr, value)) /* If the request didn't work, and caused a fault, tell the host */ remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_FAULT | ((uint16_t)remote_dmi.fault << 8U)); @@ -958,7 +958,7 @@ void remote_packet_process(gdb_packet_s *const packet) #if defined(CONFIG_RISCV_ACCEL) && CONFIG_RISCV_ACCEL == 1 case REMOTE_RISCV_PACKET: - remote_packet_process_riscv(packet->data, packet->size); + remote_packet_process_riscv(packet); break; #endif From b7275e6e7847f5973572d2d5d402e330e6277091 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:29:34 +0000 Subject: [PATCH 113/247] remote: Converted the SPI packet handler to be expressed in terms of the GDB packet structure --- src/remote.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/remote.c b/src/remote.c index 4719e7a2d8e..2c80c749786 100644 --- a/src/remote.c +++ b/src/remote.c @@ -815,17 +815,17 @@ static void remote_spi_respond(const bool result) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_FAULT); } -void remote_packet_process_spi(const char *const packet, const size_t packet_len) +void remote_packet_process_spi(gdb_packet_s *const packet) { /* Our shortest SPI packet is 4 bytes long, check that we have at least that */ - if (packet_len < 4U) { + if (packet->size < 4U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } - const uint8_t spi_bus = hex_string_to_num(2, packet + 2); + const uint8_t spi_bus = hex_string_to_num(2, packet->data + 2); - switch (packet[1]) { + switch (packet->data[1]) { /* Bus initialisation/deinitialisation commands */ case REMOTE_SPI_BEGIN: remote_spi_respond(platform_spi_init(spi_bus)); @@ -840,7 +840,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len break; /* Performs a single byte SPI transfer */ case REMOTE_SPI_TRANSFER: { - const uint8_t data_in = hex_string_to_num(2, packet + 4); + const uint8_t data_in = hex_string_to_num(2, packet->data + 4); const uint8_t data_out = platform_spi_xfer(spi_bus, data_in); remote_respond(REMOTE_RESP_OK, data_out); break; @@ -851,10 +851,10 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len * Decode the device to talk to, what command to send, and the addressing * and length information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); - const size_t length = hex_string_to_num(4, packet + 16); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); + const size_t length = hex_string_to_num(4, packet->data + 16); /* Validate the data length isn't overly long */ if (length > 256U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -873,10 +873,10 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len * Decode the device to talk to, what command to send, and the addressing * and length information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); - const size_t length = hex_string_to_num(4, packet + 16); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); + const size_t length = hex_string_to_num(4, packet->data + 16); /* Validate the data length isn't overly long */ if (length > 256U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -885,7 +885,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 20U, length); + unhexify(data, packet->data + 20U, length); /* Perform the write cycle */ bmp_spi_write(spi_bus, spi_device, command, address, data, length); remote_respond(REMOTE_RESP_OK, 0); @@ -894,7 +894,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Get the JEDEC device ID for a Flash device */ case REMOTE_SPI_CHIP_ID: { /* Decoder the device to talk to */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); /* Set up a suitable buffer for and read the JEDEC ID */ spi_flash_id_s flash_id; bmp_spi_read(spi_bus, spi_device, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); @@ -905,9 +905,9 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Run a command against a SPI Flash device */ case REMOTE_SPI_RUN_COMMAND: { /* Decode the device to talk to, what command to send, and the addressing information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); /* Execute the command and signal success */ bmp_spi_run_command(spi_bus, spi_device, command, address); remote_respond(REMOTE_RESP_OK, 0); @@ -963,7 +963,7 @@ void remote_packet_process(gdb_packet_s *const packet) #endif case REMOTE_SPI_PACKET: - remote_packet_process_spi(packet->data, packet->size); + remote_packet_process_spi(packet); break; default: /* Oh dear, unrecognised, return an error */ From cd5f3155de6cc73770a2438a727f5ad0f562d42c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 15 Dec 2024 05:33:28 +0000 Subject: [PATCH 114/247] gdb_packet: Fixed a clang-tidy warning about type conversions in the GDB escape char handling --- src/gdb_packet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 6ef909d456b..b0c95613b8a 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -232,7 +232,7 @@ gdb_packet_s *gdb_packet_receive(void) case PACKET_GDB_ESCAPE: /* Resolve escaped char */ - packet->data[packet->size++] = rx_char ^ GDB_PACKET_ESCAPE_XOR; + packet->data[packet->size++] = (char)((uint8_t)rx_char ^ GDB_PACKET_ESCAPE_XOR); /* Return to normal packet capture */ state = PACKET_GDB_CAPTURE; From 6f901adba8c53144fcdb27523ab502f2661cee22 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 5 Mar 2026 21:03:05 +0300 Subject: [PATCH 115/247] stm32f1: Hardcode GD32VW553 memory map --- src/target/stm32f1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index b51f910ce2b..7501b3e02ac 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -398,10 +398,10 @@ static void gd32vf1_detach(target_s *const target) bool gd32vw5_probe(target_s *const target) { const uint16_t device_id = target_mem32_read32(target, GD32E5_DBGMCU_BASE) & 0xfffU; - const uint32_t signature = target_mem32_read32(target, GD32Fx_FLASHSIZE); - const uint16_t flash_size = signature & 0xffffU; - const uint16_t ram_size = signature >> 16U; - DEBUG_WARN("Stub for detection of GD32VW553. DBG_ID=0x%x, RAM=%u, flash=%u\n", device_id, ram_size, flash_size); + /* Either 2 or 4 MiB of main SiP Flash */ + const uint16_t flash_size = 4096U; + /* SRAM0/1/2 each 64 KiB, SRAM3 128 KiB (96+32 shared) */ + const uint16_t ram_size = 320U; target->driver = "GD32VW5"; target->part_id = device_id; target_add_ram32(target, STM32F1_SRAM_BASE, ram_size * 1024U); From 6ee78d01c28de7505e23fe84b02f9ac97fa61af3 Mon Sep 17 00:00:00 2001 From: Marvin Drescher Date: Thu, 19 Mar 2026 12:28:24 +0100 Subject: [PATCH 116/247] cortexm: wait for halt fixes #2155 --- src/target/cortexm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 1ce07e43ecb..ce59bf5c7ba 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -524,9 +524,13 @@ bool cortexm_attach(target_s *target) /* Try to halt the core, and then check that it worked (which also resets the halt reason) */ target_halt_request(target); - const target_halt_reason_e halt_result = target_halt_poll(target, NULL); - /* If we failed to halt the target somehow, bail */ - if (halt_result == TARGET_HALT_ERROR || halt_result == TARGET_HALT_RUNNING) + platform_timeout_s timeout; + platform_timeout_set(&timeout, 250); + target_halt_reason_e reason = TARGET_HALT_RUNNING; + while (!platform_timeout_is_expired(&timeout) && reason == TARGET_HALT_RUNNING) + reason = target_halt_poll(target, NULL); + /* If we did not succeed, we must abort at this point. */ + if (reason == TARGET_HALT_FAULT || reason == TARGET_HALT_ERROR) return false; /* Request halt on reset */ @@ -570,7 +574,6 @@ bool cortexm_attach(target_s *target) (void)target_mem32_read32(target, CORTEXM_DHCSR); if (target_mem32_read32(target, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST) { platform_nrst_set_val(false); - platform_timeout_s timeout; platform_timeout_set(&timeout, 1000); while (true) { const uint32_t reset_status = target_mem32_read32(target, CORTEXM_DHCSR); From eed71d85b71ec8c63fb21c9222c141c26a1dac3b Mon Sep 17 00:00:00 2001 From: Marvin Drescher Date: Thu, 19 Mar 2026 15:09:31 +0100 Subject: [PATCH 117/247] adiv5: keep trying to read TARGETID --- src/target/adiv5.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 8712e2beeb2..963af409199 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -440,10 +440,26 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) adiv5_dp_clear_sticky_errors(dp); if (dp->version >= 2) { - /* TARGETID is on bank 2 */ - adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK2); - const uint32_t targetid = adiv5_dp_read(dp, ADIV5_DP_TARGETID); - adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK0); + uint32_t targetid = 0U; + uint8_t read_attempts = 0U; + /* + * Retry reading TARGETID until partno is non zero + * On some Nordic devices the TARGETID register isn't fully read on the first attempt + * resulting in the designer code being set while the part no. is still 0x0 + */ + while (targetid == 0U || dp->target_partno == 0U) { + /* TARGETID is on bank 2 */ + adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK2); + targetid = adiv5_dp_read(dp, ADIV5_DP_TARGETID); + adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK0); + + dp->target_partno = (targetid & ADIV5_DP_TARGETID_TPARTNO_MASK) >> ADIV5_DP_TARGETID_TPARTNO_OFFSET; + if (++read_attempts >= 128U && dp->target_partno == 0U) { + DEBUG_WARN("Failed to read TARGETID partno after 128 attempts\n"); + break; + } + + }; /* * Use TARGETID register to identify target and convert it @@ -451,9 +467,6 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) */ dp->target_designer_code = adi_decode_designer((targetid & ADIV5_DP_TARGETID_TDESIGNER_MASK) >> ADIV5_DP_TARGETID_TDESIGNER_OFFSET); - - dp->target_partno = (targetid & ADIV5_DP_TARGETID_TPARTNO_MASK) >> ADIV5_DP_TARGETID_TPARTNO_OFFSET; - DEBUG_INFO("TARGETID 0x%08" PRIx32 " designer 0x%x partno 0x%x\n", targetid, dp->target_designer_code, dp->target_partno); From efecf9dc79c63cbaf2c6305b25c456ebe9383dae Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:01:10 +0300 Subject: [PATCH 118/247] riscv_debug: Log tdata2 from discovered triggers during probe (to detect if any were in use) --- 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 d022b127df6..cc567390a50 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -968,6 +968,13 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) /* 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); +#ifndef DEBUG_TARGET_IS_NOOP + /* Display whatever contents of tdata2 (normally address to match for stale trigger) */ + target_addr64_t tdata2 = 0; + riscv_csr_read(hart, RV_TRIG_DATA_2, &tdata2); + DEBUG_TARGET("Hart trigger slot %" PRIu32 " tdata2 = %08" PRIx32 "%08" PRIx32 "\n", trigger, + (uint32_t)(tdata2 >> 32U), (uint32_t)tdata2); +#endif } } From 9b8f7eaedbf3be6f68d982965ff0e09a16e15789 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:22:25 +0300 Subject: [PATCH 119/247] riscv_debug: Unconditionally disable triggers during attach --- src/target/riscv_debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index cc567390a50..414cdc69d14 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -1052,6 +1052,12 @@ 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; + /* Clear any stale triggers */ + for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { + const uint32_t tdata1 = 0U; + const uint32_t tdata2 = 0U; + riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); + } /* We then need to halt the hart so the attach process can function */ riscv_halt_request(target); return true; From 2ff786f09b084b13f6c71a2439d3195f1a35f677 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:58:08 +0300 Subject: [PATCH 120/247] riscv_debug: Unconditionally disable triggers during detach --- src/target/riscv_debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 414cdc69d14..a51c73daee8 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -1066,6 +1066,12 @@ bool riscv_attach(target_s *const target) void riscv_detach(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); + /* Clear any stale triggers */ + for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { + const uint32_t tdata1 = 0U; + const uint32_t tdata2 = 0U; + riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); + } /* 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 */ From 5cec517f0cb66975c7e233824c5efc924e1ccc2e Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:27:36 +0300 Subject: [PATCH 121/247] riscv_debug: Reorder editing triggers *after* the halting * Any CSR access is only possible on a halted hard * Violating the order results in 4 (wrong state) --- src/target/riscv_debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index a51c73daee8..6db6514ff47 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -1052,14 +1052,14 @@ 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; - /* Clear any stale triggers */ + /* We then need to halt the hart so the attach process can function */ + riscv_halt_request(target); + /* Clear any stale triggers (after halting) */ for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { const uint32_t tdata1 = 0U; const uint32_t tdata2 = 0U; riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); } - /* We then need to halt the hart so the attach process can function */ - riscv_halt_request(target); return true; } From 12d7698d7ab7ab11d444175be6a242aa51fee500 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 26 Oct 2024 11:12:46 +0300 Subject: [PATCH 122/247] stm32g0: Enable non-halting SRAM access for RTT on G0/C0 series * Tested on STM32G071RB --- src/target/stm32g0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32g0.c b/src/target/stm32g0.c index 340da6e14a9..a2d13c5ca27 100644 --- a/src/target/stm32g0.c +++ b/src/target/stm32g0.c @@ -404,6 +404,8 @@ bool stm32g0_probe(target_s *const target) target->attach = stm32g0_attach; target->detach = stm32g0_detach; + /* On this SoC, Cortex-M0+ allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target_add_ram32(target, STM32G0_SRAM_BASE, ram_size); /* Even dual Flash bank devices have a contiguous Flash memory space */ stm32g0_add_flash(target, STM32G0_FLASH_BASE, flash_size, STM32G0_FLASH_PAGE_SIZE); From 92bd1bd76172cb2aa17ece194754c315fa1adc3f Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 16 Nov 2024 21:07:14 +0300 Subject: [PATCH 123/247] stm32f1: Enable non-halting SRAM access for RTT on AT32F403A/F407 --- src/target/stm32f1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 7501b3e02ac..d3c33a55074 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -511,6 +511,8 @@ static bool at32f403a_407_detect(target_s *const target, const uint16_t part_id) } // All parts have 96 KiB SRAM target_add_ram32(target, STM32F1_SRAM_BASE, 96U * 1024U); + /* On AT32F403A/F407 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target->driver = "AT32F403A/407"; target->part_id = part_id; target->target_options |= STM32F1_TOPT_32BIT_WRITES; From 6655d2b6b2a8c1344687ca546235fd5dde86b953 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 23 Nov 2024 15:03:05 +0300 Subject: [PATCH 124/247] stm32f1: Enable non-halting SRAM access for RTT on some GD32 series * Tested chips include GD32F103CB, GD32F303CC, GD32E508ZE --- src/target/stm32f1.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index d3c33a55074..b08aac37e57 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -287,8 +287,10 @@ bool gd32f1_probe(target_s *const target) switch (device_id) { case 0x414U: /* GD32F30x_HD, High density */ case 0x430U: /* GD32F30x_XD, XL-density */ - target->driver = "GD32F3"; + target->driver = "GD32F3 HD/XD"; block_size = 0x800; + /* On this SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; case 0x418U: /* Connectivity Line */ target->driver = "GD32F2"; @@ -298,14 +300,19 @@ bool gd32f1_probe(target_s *const target) if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23) target->driver = "GD32E230"; /* GD32E230, 64 KiB max in 1 KiB pages */ else if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M4) { - target->driver = "GD32F3"; + target->driver = "GD32F3 MD"; block_size = 0x800; - } else + } else { target->driver = "GD32F1"; /* GD32F103, 1 KiB pages */ + /* On this SoC, Cortex-M3 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + } break; case 0x444U: /* GD32E50x_CL, 512 KiB max in 8 KiB pages */ target->driver = "GD32E5"; block_size = 0x2000; + /* On this SoC, Cortex-M33 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; default: return false; From b1ffba4fea537878b9936b8fbc7241bc644cea3b Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 6 Jan 2025 06:40:35 +0300 Subject: [PATCH 125/247] stm32l4: Enable non-halting SRAM access for RTT on STM32G47x --- src/target/stm32l4.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index f74efc9088b..4a7d6db2b02 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -876,6 +876,10 @@ static bool stm32l4_attach(target_s *const target) } else stm32l4_add_flash(target, STM32L4_FLASH_BANK1_BASE, flash_len * 1024U, 0x800, UINT32_MAX); + /* On STM32G47x SoC, Cortex-M4F allows SRAM access without halting */ + if (device->device_id == ID_STM32G47) + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Clear all errors in the status register. */ stm32l4_flash_write32(target, STM32L4_FPEC_STATUS, stm32l4_flash_read32(target, STM32L4_FPEC_STATUS)); return true; From 29ddd4a19e245674a167ed7b9f4cf6b8dc9f21f7 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 11 Jan 2025 17:48:09 +0300 Subject: [PATCH 126/247] stm32f1: Enable non-halting SRAM access for RTT on STM32F1/F3/F0 (blanket) * Tested on STM32F103 and STM32F072 --- src/target/stm32f1.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index b08aac37e57..8e74b1d88da 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -1035,6 +1035,9 @@ bool stm32f1_probe(target_s *const target) stm32f1_add_flash(target, STM32F1_FLASH_BANK1_BASE, flash_size, block_size); target_add_commands(target, stm32f1_cmd_list, target->driver); + /* On STM32F1 (F3, F0) SoC, Cortex-M3 (M4F, M0) allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */ return stm32f1_configure_dbgmcu(target, dbgmcu_config_taddr); } From fab62005a96650717ffe4e641478fe029ad05708 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 19 Jan 2025 20:10:18 +0300 Subject: [PATCH 127/247] stm32f4: Enable non-halting SRAM access for RTT on STM32F4 series --- src/target/stm32f4.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/stm32f4.c b/src/target/stm32f4.c index b8e20f6ad4d..704a413b169 100644 --- a/src/target/stm32f4.c +++ b/src/target/stm32f4.c @@ -486,6 +486,10 @@ static bool stm32f4_attach(target_s *const target) } } + /* On STM32F4 SoC, Cortex-M4F allows SRAM access without halting */ + if (!is_f7 && target->part_id != ID_STM32F20X) + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Now we have a base RAM map, rebuild the Flash map */ uint8_t split = 0; uint32_t bank_length; From 1483f4b5a9ef4b24df84b0252bf683a75f371b9b Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 19 Jan 2025 20:30:40 +0300 Subject: [PATCH 128/247] nrf51: Enable non-halting SRAM access for RTT on nRF52 series --- src/target/nrf51.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/nrf51.c b/src/target/nrf51.c index 6e8c1862139..da9252294fe 100644 --- a/src/target/nrf51.c +++ b/src/target/nrf51.c @@ -157,6 +157,8 @@ bool nrf51_probe(target_s *const target) uint32_t ram_size = target_mem32_read32(target, NRF52_INFO_RAM); target->driver = "nRF52"; target->target_options |= TOPT_INHIBIT_NRST; + /* On nRF52 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target_add_ram32(target, 0x20000000U, ram_size * 1024U); nrf51_add_flash(target, 0, page_size * code_size, page_size); nrf51_add_flash(target, NRF51_UICR, page_size, page_size); From c8cc2e4ae4094ede29506d8c7a26649a935e3bb4 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 17 Dec 2025 21:30:24 +0300 Subject: [PATCH 129/247] nrf54l: Enable non-halting SRAM access for RTT on nRF54L series --- src/target/nrf54l.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/nrf54l.c b/src/target/nrf54l.c index 7d9e16411db..617962d2222 100644 --- a/src/target/nrf54l.c +++ b/src/target/nrf54l.c @@ -118,6 +118,8 @@ bool nrf54l_probe(target_s *const target) case ID_NRF54L: target->driver = "nRF54L"; target->target_options |= TOPT_INHIBIT_NRST; + /* On nRF54L SoC, Cortex-M33 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; default: return false; From 33df9407196183b6366518320f422626069efa50 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 17 Dec 2025 21:45:36 +0300 Subject: [PATCH 130/247] stm32f1: Enable non-halting SRAM access for RTT on AT32F425 --- src/target/stm32f1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index 8e74b1d88da..99f991cfd2e 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -703,6 +703,8 @@ static bool at32f425_detect(target_s *const target, const uint16_t part_id) #endif // All parts have 20 KiB SRAM target_add_ram32(target, 0x20000000, 20U * 1024U); + /* On AT32F425 SoC, Cortex-M4 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target->driver = "AT32F425"; target->part_id = part_id; target->target_options |= STM32F1_TOPT_32BIT_WRITES; From e0f72c108771ec33e2c324ba3ffd94b6c7d27655 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 17 Dec 2025 22:00:24 +0300 Subject: [PATCH 131/247] at32f43x: Enable non-halting SRAM access for RTT on AT32F435/F437 --- src/target/at32f43x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/target/at32f43x.c b/src/target/at32f43x.c index 1b7c052b661..42cfcb98e28 100644 --- a/src/target/at32f43x.c +++ b/src/target/at32f43x.c @@ -300,6 +300,9 @@ static bool at32f43_detect(target_s *const target, const uint16_t part_id) target->attach = at32f43_attach; target->detach = at32f43_detach; + /* On AT32F435/F437 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + at32f43_configure_dbgmcu(target); return true; } From 9dbdf3e8e3f990ea59b8555b88048b7ecd82e228 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 29 Mar 2026 15:42:57 +0100 Subject: [PATCH 132/247] meson: Removed renesas support from the default set on BluePill, F073, native and swlink to solve the out of Flash space problems again --- cross-file/bluepill.ini | 2 +- cross-file/f072.ini | 2 +- cross-file/native.ini | 2 +- cross-file/swlink.ini | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cross-file/bluepill.ini b/cross-file/bluepill.ini index 50028721002..2f09284e334 100644 --- a/cross-file/bluepill.ini +++ b/cross-file/bluepill.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'bluepill' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = true diff --git a/cross-file/f072.ini b/cross-file/f072.ini index 2382d205205..d09b7adc114 100644 --- a/cross-file/f072.ini +++ b/cross-file/f072.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'f072' -targets = 'cortexm,riscv32,riscv64,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,riscv32,riscv64,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = false diff --git a/cross-file/native.ini b/cross-file/native.ini index 2aaae354ad4..44cabd1d5fc 100644 --- a/cross-file/native.ini +++ b/cross-file/native.ini @@ -22,6 +22,6 @@ endian = 'little' [project options] probe = 'native' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = true diff --git a/cross-file/swlink.ini b/cross-file/swlink.ini index 3a744b7675f..f40d75bb151 100644 --- a/cross-file/swlink.ini +++ b/cross-file/swlink.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'swlink' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = false From 6bb58d0f44a396ea08840fb758e006c10b10acde Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Sun, 29 Mar 2026 10:01:17 -0700 Subject: [PATCH 133/247] README: Update the CI badge to use the codeberg CI instead of GH. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4422306385f..837bf00984b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This includes support for ARM and RISC-V devices, the complete list can be found [![Discord](https://img.shields.io/discord/613131135903596547?logo=discord)](https://discord.gg/P7FYThy) [![Current release](https://codeberg.org/blackmagic-debug/blackmagic/badges/release.svg)](https://codeberg.org/blackmagic-debug/blackmagic/releases) -[![CI flow status](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml/badge.svg)](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml) +[![CI status](https://ci.codeberg.org/api/badges/16587/status.svg)](https://ci.codeberg.org/repos/16587) [![AI free project](https://badges.ws/badge/NO-AI-ff0000)](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md#contributing) Table of contents: From df3d79975fe1c76b8786b74c67c1003bd65b5bca Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:37:00 +0300 Subject: [PATCH 134/247] onboard_flash: Don't bail out on non-Winbond flash, detect Macronix --- src/target/onboard_flash.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/target/onboard_flash.c b/src/target/onboard_flash.c index 8c7ffceb244..9f15b98493e 100644 --- a/src/target/onboard_flash.c +++ b/src/target/onboard_flash.c @@ -121,16 +121,26 @@ static bool onboard_flash_add(target_s *const target) spi_flash_id_s flash_id; onboard_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); +#if defined(BLACKMAGIC) || defined(BLACKMAGICPROBE_V3) /* If it doesn't match up to being the expected device (a Winbond Flash), bail */ if (flash_id.manufacturer != 0xefU) { DEBUG_ERROR( "%s: Expecting Winbond SPI Flash device, manufacturer ID is %02x\n", __func__, flash_id.manufacturer); return false; + } else { + target->core = "Winbond"; } +#else + if (flash_id.manufacturer == 0xffU || flash_id.type == 0xffU || flash_id.capacity == 0xffU) { + DEBUG_ERROR("Flash identification failed\n"); + return false; + } else { + target->core = "25-series"; + } +#endif DEBUG_INFO( "Found Flash chip w/ ID: 0x%02x 0x%02x 0x%02x\n", flash_id.manufacturer, flash_id.type, flash_id.capacity); - target->core = "Windbond"; /* Otherwise add it to the providied target */ bmp_spi_add_flash( target, 0U, 1U << flash_id.capacity, onboard_spi_read, onboard_spi_write, onboard_spi_run_command); From 34c5d912ef5416be143eb5714a5341d08fa785ff Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 20 Apr 2025 13:52:08 +0300 Subject: [PATCH 135/247] riscv_debug: Detect Semihosting breakpoints and connect with common support code --- src/target/riscv_debug.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 6db6514ff47..57f8cb0f3ca 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -37,6 +37,7 @@ #include "gdb_reg.h" #include "riscv_debug.h" #include "buffer_utils.h" +#include "semihosting.h" #include @@ -894,6 +895,31 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c return true; } +static target_addr_t riscv_pc_read(riscv_hart_s *const hart) +{ + target_addr_t data = 0; + riscv_csr_read(hart, RV_DPC, &data); + //riscv32_reg_read(target, 32, &data, sizeof(data)); + return data; +} + +static bool riscv_hostio_request(target_s *const target) +{ + /* Read out syscall number from a0/x10 and first argument from a1/x11 */ + uint32_t syscall = 0U; + target_reg_read(target, 10, &syscall, sizeof(syscall)); + uint32_t a1 = 0U; + target_reg_read(target, 11, &a1, sizeof(a1)); + + /* Hand off to the main semihosting implementation */ + const int32_t result = semihosting_request(target, syscall, a1); + + /* Write the result back to the target */ + target_reg_write(target, 10, &result, sizeof(result)); + /* Return if the request was in any way interrupted */ + return target->tc->interrupted; +} + 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 */ @@ -1152,6 +1178,21 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr6 status &= RV_DCSR_CAUSE_MASK; /* Dispatch on the cause code */ switch (status) { + case RV_HALT_CAUSE_EBREAK: { + /* If we've hit a programmed breakpoint, check for semihosting call. */ + const target_addr_t program_counter = riscv_pc_read(hart); + uint32_t instructions[3] = {0}; + target_mem32_read(target, &instructions, program_counter - 4U, 12); + /* A semihosting call is three consecutive uncompressed instructions: slli zero, zero 0x1f; ebreak, srai zero, zero, 7. */ + if (instructions[0] == 0x01f01013 && instructions[1] == RV_EBREAK && instructions[2] == 0x40705013) { + if (riscv_hostio_request(target)) + return TARGET_HALT_REQUEST; + + riscv_halt_resume(target, false); + return TARGET_HALT_RUNNING; + } + return TARGET_HALT_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; From e03e77ad9cbe6cc3e7e0409ea5575ff1549a987a Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:10:05 +0300 Subject: [PATCH 136/247] riscv_debug: Step over ebreak instructions --- src/target/riscv_debug.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 57f8cb0f3ca..2a1aac06079 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -1151,6 +1151,19 @@ static void riscv_halt_resume(target_s *target, const bool step) } if (!riscv_csr_write(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) return; + /* Step over coded breakpoints */ + uint32_t dcsr_cause = 0U; + riscv_csr_read(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &dcsr_cause); + dcsr_cause &= RV_DCSR_CAUSE_MASK; + if (dcsr_cause == RV_HALT_CAUSE_EBREAK) { + /* Read the instruction to resume on */ + uint32_t program_counter = riscv_pc_read(hart); + /* If it actually is a breakpoint instruction, update the program counter one past it. */ + if (target_mem32_read32(target, program_counter) == RV_EBREAK) { + program_counter += 4U; + riscv_csr_write(hart, RV_DPC, &program_counter); + } + } /* Request the hart to resume */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) return; From ce9afca25a452ef8e569d68c3aeeaa02c432340c Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:17:57 +0300 Subject: [PATCH 137/247] riscv_debug: Give names to semihosting NOPs and GPR A1 * Use small regno in riscv_hostio_request --- src/target/riscv_debug.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 2a1aac06079..b691253319d 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -111,8 +111,13 @@ /* tdata2 -> selected trigger configuration register 2 */ #define RV_TRIG_DATA_2 0x7a2U -/* GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O */ +/* + * GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O, + * as semihosting syscall number and result register per ABI + */ #define RV_GPR_A0 0x100aU +/* GPR a1, aka x11, is used as semihosting argument */ +#define RV_GPR_A1 0x100bU /* * Instructions for reading and writing CSRs through a0 @@ -125,6 +130,15 @@ #define RV_CSRW_A0 0x00051073U #define RV_EBREAK 0x00100073U +/* + * A semihosting call is three consecutive uncompressed instructions: + * 0x01f01013 slli zero, zero 0x1f; + * 0x00100073 ebreak; + * 0x40705013 srai zero, zero, 7. + */ +#define RV_ENTRY_NOP 0x01f01013U +#define RV_EXIT_NOP 0x40705013U + #define RV_VENDOR_JEP106_CONT_MASK 0x7fffff80U #define RV_VENDOR_JEP106_CODE_MASK 0x7fU @@ -907,15 +921,15 @@ static bool riscv_hostio_request(target_s *const target) { /* Read out syscall number from a0/x10 and first argument from a1/x11 */ uint32_t syscall = 0U; - target_reg_read(target, 10, &syscall, sizeof(syscall)); + target_reg_read(target, RV_GPR_A0 - RV_GPR_BASE, &syscall, sizeof(syscall)); uint32_t a1 = 0U; - target_reg_read(target, 11, &a1, sizeof(a1)); + target_reg_read(target, RV_GPR_A1 - RV_GPR_BASE, &a1, sizeof(a1)); /* Hand off to the main semihosting implementation */ const int32_t result = semihosting_request(target, syscall, a1); /* Write the result back to the target */ - target_reg_write(target, 10, &result, sizeof(result)); + target_reg_write(target, RV_GPR_A0 - RV_GPR_BASE, &result, sizeof(result)); /* Return if the request was in any way interrupted */ return target->tc->interrupted; } @@ -1197,7 +1211,7 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr6 uint32_t instructions[3] = {0}; target_mem32_read(target, &instructions, program_counter - 4U, 12); /* A semihosting call is three consecutive uncompressed instructions: slli zero, zero 0x1f; ebreak, srai zero, zero, 7. */ - if (instructions[0] == 0x01f01013 && instructions[1] == RV_EBREAK && instructions[2] == 0x40705013) { + if (instructions[0] == RV_ENTRY_NOP && instructions[1] == RV_EBREAK && instructions[2] == RV_EXIT_NOP) { if (riscv_hostio_request(target)) return TARGET_HALT_REQUEST; From df3b165c0fae384b97da9fc16ac6f137fa53bbb5 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:27:47 +0300 Subject: [PATCH 138/247] riscv_debug: Prepare for 64-bit DPC and address space in semihosting --- src/target/riscv_debug.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index b691253319d..12c19609eac 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -909,11 +909,10 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c return true; } -static target_addr_t riscv_pc_read(riscv_hart_s *const hart) +static target_addr64_t riscv_pc_read(riscv_hart_s *const hart) { - target_addr_t data = 0; + target_addr64_t data = 0U; riscv_csr_read(hart, RV_DPC, &data); - //riscv32_reg_read(target, 32, &data, sizeof(data)); return data; } @@ -1171,7 +1170,7 @@ static void riscv_halt_resume(target_s *target, const bool step) dcsr_cause &= RV_DCSR_CAUSE_MASK; if (dcsr_cause == RV_HALT_CAUSE_EBREAK) { /* Read the instruction to resume on */ - uint32_t program_counter = riscv_pc_read(hart); + target_addr64_t program_counter = riscv_pc_read(hart); /* If it actually is a breakpoint instruction, update the program counter one past it. */ if (target_mem32_read32(target, program_counter) == RV_EBREAK) { program_counter += 4U; @@ -1207,9 +1206,9 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr6 switch (status) { case RV_HALT_CAUSE_EBREAK: { /* If we've hit a programmed breakpoint, check for semihosting call. */ - const target_addr_t program_counter = riscv_pc_read(hart); + const target_addr64_t program_counter = riscv_pc_read(hart); uint32_t instructions[3] = {0}; - target_mem32_read(target, &instructions, program_counter - 4U, 12); + target_mem64_read(target, &instructions, program_counter - 4U, sizeof(instructions)); /* A semihosting call is three consecutive uncompressed instructions: slli zero, zero 0x1f; ebreak, srai zero, zero, 7. */ if (instructions[0] == RV_ENTRY_NOP && instructions[1] == RV_EBREAK && instructions[2] == RV_EXIT_NOP) { if (riscv_hostio_request(target)) From 1aa5e4a729bec5e308a2e3588dcb0f2686037d1f Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:47:29 +0300 Subject: [PATCH 139/247] semihosting: Bound-check the open_mode flag before indexing a small array --- src/target/semihosting.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/semihosting.c b/src/target/semihosting.c index b469dfb48a9..c9bac52721d 100644 --- a/src/target/semihosting.c +++ b/src/target/semihosting.c @@ -337,6 +337,10 @@ int32_t semihosting_open(target_s *const target, const semihosting_s *const requ { const target_addr_t file_name_taddr = request->params[0]; const uint32_t file_name_length = request->params[2]; + /* Explicit bounds check for open_mode_flags */ + const uint32_t open_mode_libc = request->params[1]; + if (open_mode_libc > 11) + return -1; /* * Translation table of fopen() modes to GDB-compatible open flags From eea215dd8ead2db0add7dcc2785d6a52a5c9c2b7 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:51:42 +0300 Subject: [PATCH 140/247] riscv_debug: Bypass target_reg_read() indirection in semihosting, use 64-bit --- src/target/riscv_debug.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 12c19609eac..cc305c41045 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -919,16 +919,17 @@ static target_addr64_t riscv_pc_read(riscv_hart_s *const hart) static bool riscv_hostio_request(target_s *const target) { /* Read out syscall number from a0/x10 and first argument from a1/x11 */ - uint32_t syscall = 0U; - target_reg_read(target, RV_GPR_A0 - RV_GPR_BASE, &syscall, sizeof(syscall)); - uint32_t a1 = 0U; - target_reg_read(target, RV_GPR_A1 - RV_GPR_BASE, &a1, sizeof(a1)); + riscv_hart_s *const hart = riscv_hart_struct(target); + uint64_t syscall = 0U; + riscv_csr_read(hart, RV_GPR_A0, &syscall); + uint64_t a1 = 0U; + riscv_csr_read(hart, RV_GPR_A1, &a1); /* Hand off to the main semihosting implementation */ const int32_t result = semihosting_request(target, syscall, a1); /* Write the result back to the target */ - target_reg_write(target, RV_GPR_A0 - RV_GPR_BASE, &result, sizeof(result)); + riscv_csr_write(hart, RV_GPR_A0, &result); /* Return if the request was in any way interrupted */ return target->tc->interrupted; } From 098bb4b739de8f3e938a65cb47d781830dd0d0e2 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 30 Sep 2024 00:06:33 +0300 Subject: [PATCH 141/247] cortexar: Make attach/detach visible in headers --- src/target/cortexar.c | 3 --- src/target/cortexar.h | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/target/cortexar.c b/src/target/cortexar.c index 4ec6ad10806..aac90335df9 100644 --- a/src/target/cortexar.c +++ b/src/target/cortexar.c @@ -402,9 +402,6 @@ static int cortexar_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int cortexar_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); static void cortexar_config_breakpoint(target_s *target, size_t slot, uint32_t mode, target_addr_t addr); -bool cortexar_attach(target_s *target); -void cortexar_detach(target_s *target); - static const char *cortexar_target_description(target_s *target); static void cortexar_banked_dcc_mode(target_s *const target) diff --git a/src/target/cortexar.h b/src/target/cortexar.h index 25551ce9b16..67168aab811 100644 --- a/src/target/cortexar.h +++ b/src/target/cortexar.h @@ -36,6 +36,8 @@ #include "general.h" +bool cortexar_attach(target_s *target); +void cortexar_detach(target_s *target); void cortexar_invalidate_all_caches(target_s *target); #endif /* TARGET_CORTEXAR_H */ From d4a69bf862439426b30b7fc5414e65502b6fe19a Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 23 Dec 2023 13:37:34 +0300 Subject: [PATCH 142/247] stm32mp15: Initial hooks to allow AXI-AP memory accesses stm32mp15: Manage the AXI AP in CA7 target case (experimental) --- src/target/stm32mp15.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 914d0278a31..a2e12cbc934 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -33,6 +33,8 @@ #include "target_internal.h" #include "cortexm.h" #include "stm32_common.h" +#include "adiv5.h" +#include "cortexar.h" /* Memory map constants for STM32MP15x */ #define STM32MP15_CM4_RETRAM_BASE 0x00000000U @@ -167,12 +169,60 @@ bool stm32mp15_cm4_probe(target_s *const target) } #ifdef CONFIG_CORTEXAR +/* + * Override memory r/w operations to go via the AXI (AP0) MEM-AP + * (instead of halting the core and using DTRTX, which cortexar_mem_read/write do by default) + */ +static void stm32mp15_ca7_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len) +{ + adiv5_access_port_s *const ap0 = (adiv5_access_port_s *)target->target_storage; + adiv5_mem_read(ap0, dest, src, len); +} + +static void stm32mp15_ca7_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len) +{ + adiv5_access_port_s *const ap0 = (adiv5_access_port_s *)target->target_storage; + adiv5_mem_write(ap0, dest, src, len); +} + +static void stm32mp15_ca7_setup_axi_ap(target_s *const target) +{ + adiv5_access_port_s *ap0 = calloc(1, sizeof(*ap0)); + if (!ap0) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + adiv5_access_port_s *const ap = cortex_ap(target); + memcpy(ap0, ap, sizeof(*ap0)); + ap0->refcnt = 0; + + ap0->apsel = 0; // Set to AXI-AP + ap0->idr = adiv5_ap_read(ap0, ADIV5_AP_IDR); + ap0->base = adiv5_ap_read(ap0, ADIV5_AP_BASE_LOW); + ap0->csw = adiv5_ap_read(ap0, ADIV5_AP_CSW); + + adiv5_ap_ref(ap0); + target->target_storage = ap0; +} + +static void stm32mp15_ca7_detach(target_s *target) +{ + /* Deallocate any extra AP */ + adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; + adiv5_ap_unref(ap0); + cortexar_detach(target); +} + bool stm32mp15_ca7_probe(target_s *const target) { if (!stm32mp15_ident(target, false)) return false; target->driver = "STM32MP15"; + stm32mp15_ca7_setup_axi_ap(target); + target->mem_read = stm32mp15_ca7_mem_read; + target->mem_write = stm32mp15_ca7_mem_write; + target->detach = stm32mp15_ca7_detach; target_add_commands(target, stm32mp15_cmd_list, target->driver); /* Figure 4. Memory map from §2.5.2 in RM0436 rev 6, pg158 */ From 7aadca583332fa500d2277d8baadc48e46af06da Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:07:38 +0300 Subject: [PATCH 143/247] stm32mp15: Register AP1 for APB-D accesses --- src/target/stm32mp15.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index a2e12cbc934..f1d413df854 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -120,6 +120,26 @@ static bool stm32mp15_ident(target_s *const target, const bool cortexm) return true; } +static void stm32mp15_cm4_setup_apbd_ap(target_s *const target) +{ + adiv5_access_port_s *ap1 = calloc(1, sizeof(*ap1)); + if (!ap1) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + adiv5_access_port_s *const ap2 = cortex_ap(target); + memcpy(ap1, ap2, sizeof(*ap1)); + ap1->refcnt = 0; + + ap1->apsel = 1; // Set to APB-D AP + ap1->idr = adiv5_ap_read(ap1, ADIV5_AP_IDR); + ap1->base = adiv5_ap_read(ap1, ADIV5_AP_BASE_LOW); + ap1->csw = adiv5_ap_read(ap1, ADIV5_AP_CSW); + + adiv5_ap_ref(ap1); + target->target_storage = ap1; +} + static bool stm32mp15_cm4_configure_dbgmcu(target_s *const target) { /* If we're in the probe phase */ @@ -246,7 +266,13 @@ static bool stm32mp15_cm4_attach(target_s *const target) * Try to attach to the part, and then ensure that the WDTs + WFI and WFE * instructions can't cause problems (this is duplicated as it's undone by detach.) */ - return cortexm_attach(target) && stm32mp15_cm4_configure_dbgmcu(target); + if (!cortexm_attach(target)) + return false; + if (!stm32mp15_cm4_configure_dbgmcu(target)) + return false; + /* Reference the APB-D in target storage for External PPB (0xe0000000) manipulations */ + stm32mp15_cm4_setup_apbd_ap(target); + return true; } static void stm32mp15_cm4_detach(target_s *const target) @@ -254,6 +280,11 @@ static void stm32mp15_cm4_detach(target_s *const target) stm32mp15_priv_s *priv = (stm32mp15_priv_s *)target->target_storage; /* Reverse all changes to the DBGMCU config register */ target_mem32_write32(target, STM32MP15_DBGMCU_CONFIG, priv->dbgmcu_config); + + /* Deallocate any extra AP */ + adiv5_access_port_s *ap1 = (adiv5_access_port_s *)target->target_storage; + adiv5_ap_unref(ap1); + /* Now defer to the normal Cortex-M detach routine to complete the detach */ cortexm_detach(target); } From b92174082bbb4d1d4406f5e385cbd5bb646ef066 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:08:26 +0300 Subject: [PATCH 144/247] stm32mp15: Add a command to configure SWO parameters over AP1 --- src/target/stm32mp15.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index f1d413df854..47dd36a96f2 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -76,16 +76,22 @@ /* Taken from CM4ROM_PIDRx in 2.3.21 of ES0438 rev 7, pg18 */ #define ID_STM32MP15x_ERRATA 0x450U +#define SWO_BASE 0xe0083000 +#define SWO_ACPR (SWO_BASE + 0x00010) +#define SWO_SPPR (SWO_BASE + 0x000f0) + typedef struct stm32mp15_priv { uint32_t dbgmcu_config; } stm32mp15_priv_s; static bool stm32mp15_uid(target_s *target, int argc, const char **argv); static bool stm32mp15_cmd_rev(target_s *target, int argc, const char **argv); +static bool stm32mp15_cmd_swo(target_s *target, int argc, const char **argv); const command_s stm32mp15_cmd_list[] = { {"uid", stm32mp15_uid, "Print unique device ID"}, {"revision", stm32mp15_cmd_rev, "Returns the Device ID and Revision"}, + {"conf_swo", stm32mp15_cmd_swo, "Set up SWO mode <1/2> and divisor <0x42>"}, {NULL, NULL, NULL}, }; @@ -120,6 +126,32 @@ static bool stm32mp15_ident(target_s *const target, const bool cortexm) return true; } +static bool stm32mp15_cmd_swo(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* TODO: argv parsing for mode and baudrate */ + adiv5_access_port_s *const ap1 = (adiv5_access_port_s *)target->target_storage; + /* Pin Protocol: change Manchester to UART */ + uint32_t sppr = 0; + adiv5_mem_read(ap1, &sppr, SWO_SPPR, 4); + sppr &= ~(0x3U); + sppr |= 0x2U; + adiv5_mem_write(ap1, SWO_SPPR, &sppr, 4); + + /* + * Prescaler: set to fixed 66; trace clk freq of 133/(66+1) gives ~2Mbaud (+-0.7%) + * assuming AXI clk of 266 and default divisor of 2 + * Or, if you are not restricted by swlink 2.25M, set to fixed 32; 133/(32+1) is ~4Mbaud (+-0.7%) + */ + uint32_t acpr = 0; + adiv5_mem_read(ap1, &acpr, SWO_ACPR, 4); + acpr = 32; + adiv5_mem_write(ap1, SWO_ACPR, &acpr, 4); + + return true; +} + static void stm32mp15_cm4_setup_apbd_ap(target_s *const target) { adiv5_access_port_s *ap1 = calloc(1, sizeof(*ap1)); From ddeff3e1a06a0b5cb3087f2619f0389b91aa8b32 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 30 Mar 2026 21:03:58 +0300 Subject: [PATCH 145/247] common/swdptap: Ensure TDI is driven high, as spec allows it (as-if undriven) --- src/platforms/common/swdptap.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platforms/common/swdptap.c b/src/platforms/common/swdptap.c index a462d1c18f3..64ee94ac6fe 100644 --- a/src/platforms/common/swdptap.c +++ b/src/platforms/common/swdptap.c @@ -66,6 +66,13 @@ void swdptap_init(void) swd_proc.seq_in_parity = swdptap_seq_in_parity; swd_proc.seq_out = swdptap_seq_out; swd_proc.seq_out_parity = swdptap_seq_out_parity; + /* + * IEEE 1149.1-2013, 4.4 Test Data Input (TDI) + * b) The design of the circuitry fed from TDI shall be such that + * an undriven input produces a logical response + * identical to the application of a logic 1. + */ + gpio_set(TDI_PORT, TDI_PIN); } static void swdptap_turnaround(const swdio_status_t dir) From 38bd3648ebd0868e2784877107fc566c0156790b Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:29:10 +0300 Subject: [PATCH 146/247] riscv32: Map three more FPU CSR from 66-68 to 0x001-0x003 --- src/target/riscv32.c | 6 ++++++ src/target/riscv_debug.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f77cf410a43..3d9bc9a5a3f 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -162,6 +162,10 @@ static size_t riscv32_reg_read(target_s *target, const uint32_t reg, void *data, return riscv32_bool_to_4(riscv_csr_read(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, reg - RV_CSR_GDB_OFFSET, data)); + /* GDB registers 66..68 map to FPU CSR 0x001..0x003 */ + if (reg >= RV_FPU_GDB_CSR_OFFSET) + return riscv32_bool_to_4(riscv_csr_read(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); + /* GDB registers 33..65 map to FPU GPR 0x1020..0x1033 */ if (reg >= RV_FPU_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, RV_FP_BASE + reg - RV_FPU_GDB_OFFSET, data)); return 0; @@ -179,6 +183,8 @@ static size_t riscv32_reg_write(target_s *const target, const uint32_t reg, cons return riscv32_bool_to_4(riscv_csr_write(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, reg - RV_CSR_GDB_OFFSET, data)); + if (reg >= RV_FPU_GDB_CSR_OFFSET) + return riscv32_bool_to_4(riscv_csr_write(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); if (reg >= RV_FPU_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, RV_FP_BASE + reg - RV_FPU_GDB_OFFSET, data)); return 0; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 0c89137b06d..2dc5ae5b61f 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -218,6 +218,8 @@ typedef struct riscv_hart { #define RV_GPR_BASE 0x1000U /* The FP base defines the starting register space address for the floating point registers */ #define RV_FP_BASE 0x1020U +/* The FP control base defines the starting register space address for the floating point modes and flags */ +#define RV_FP_CTRL_BASE 0x001U /** * The MXL (Machine XLEN) field encodes the native base integer ISA width From 8d54f2026ec05502bf7867807c1ffe89b4899190 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:51:30 +0300 Subject: [PATCH 147/247] riscv32: Guard against access to non-existent registers below RV_CSR_GDB_OFFSET --- src/target/riscv32.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 3d9bc9a5a3f..8dd063b0081 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -162,6 +162,9 @@ static size_t riscv32_reg_read(target_s *target, const uint32_t reg, void *data, return riscv32_bool_to_4(riscv_csr_read(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, reg - RV_CSR_GDB_OFFSET, data)); + /* GDB register numbers between 69 and 127 don't map to anything */ + if (reg >= RV_FPU_GDB_CSR_OFFSET + 3U) + return 0; /* GDB registers 66..68 map to FPU CSR 0x001..0x003 */ if (reg >= RV_FPU_GDB_CSR_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); @@ -183,6 +186,8 @@ static size_t riscv32_reg_write(target_s *const target, const uint32_t reg, cons return riscv32_bool_to_4(riscv_csr_write(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, reg - RV_CSR_GDB_OFFSET, data)); + if (reg >= RV_FPU_GDB_CSR_OFFSET + 3U) + return 0; if (reg >= RV_FPU_GDB_CSR_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); if (reg >= RV_FPU_GDB_OFFSET) From e6bba26dcc4abcedb31e62d2ee0b97be2e9e3374 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:17:09 +0300 Subject: [PATCH 148/247] stm32mp15: NULL out the target_storage to prevent a double-free --- src/target/stm32mp15.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 47dd36a96f2..9e1a3b8c9af 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -262,6 +262,7 @@ static void stm32mp15_ca7_detach(target_s *target) /* Deallocate any extra AP */ adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; adiv5_ap_unref(ap0); + target->target_storage = NULL; cortexar_detach(target); } @@ -316,6 +317,7 @@ static void stm32mp15_cm4_detach(target_s *const target) /* Deallocate any extra AP */ adiv5_access_port_s *ap1 = (adiv5_access_port_s *)target->target_storage; adiv5_ap_unref(ap1); + target->target_storage = NULL; /* Now defer to the normal Cortex-M detach routine to complete the detach */ cortexm_detach(target); From f54cf446ae41fbb9de242c0bd6efdc905cebeb09 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:43:11 +0300 Subject: [PATCH 149/247] stm32mp15: Postpone allocating AXI-AP to ca7_attach() --- src/target/stm32mp15.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 9e1a3b8c9af..e891db67613 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -257,12 +257,27 @@ static void stm32mp15_ca7_setup_axi_ap(target_s *const target) target->target_storage = ap0; } +static bool stm32mp15_ca7_attach(target_s *const target) +{ + if (!cortexar_attach(target)) + return false; + stm32mp15_ca7_setup_axi_ap(target); + adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; + if (ap0) { + target->mem_read = stm32mp15_ca7_mem_read; + target->mem_write = stm32mp15_ca7_mem_write; + } + return true; +} + static void stm32mp15_ca7_detach(target_s *target) { /* Deallocate any extra AP */ adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; - adiv5_ap_unref(ap0); - target->target_storage = NULL; + if (ap0) { + adiv5_ap_unref(ap0); + ap0 = NULL; + } cortexar_detach(target); } @@ -272,9 +287,7 @@ bool stm32mp15_ca7_probe(target_s *const target) return false; target->driver = "STM32MP15"; - stm32mp15_ca7_setup_axi_ap(target); - target->mem_read = stm32mp15_ca7_mem_read; - target->mem_write = stm32mp15_ca7_mem_write; + target->attach = stm32mp15_ca7_attach; target->detach = stm32mp15_ca7_detach; target_add_commands(target, stm32mp15_cmd_list, target->driver); From 57e856fb7cb7cc84f18d2dbd2d42972a08ebb7ba Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:04:32 +0300 Subject: [PATCH 150/247] stm32mp15: Refactor usage of priv storage --- src/target/stm32mp15.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index e891db67613..b17530c1c61 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -82,6 +82,7 @@ typedef struct stm32mp15_priv { uint32_t dbgmcu_config; + adiv5_access_port_s *ap; } stm32mp15_priv_s; static bool stm32mp15_uid(target_s *target, int argc, const char **argv); @@ -169,7 +170,8 @@ static void stm32mp15_cm4_setup_apbd_ap(target_s *const target) ap1->csw = adiv5_ap_read(ap1, ADIV5_AP_CSW); adiv5_ap_ref(ap1); - target->target_storage = ap1; + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + priv->ap = ap1; } static bool stm32mp15_cm4_configure_dbgmcu(target_s *const target) @@ -227,18 +229,30 @@ bool stm32mp15_cm4_probe(target_s *const target) */ static void stm32mp15_ca7_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len) { - adiv5_access_port_s *const ap0 = (adiv5_access_port_s *)target->target_storage; - adiv5_mem_read(ap0, dest, src, len); + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + adiv5_mem_read(priv->ap, dest, src, len); } static void stm32mp15_ca7_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len) { - adiv5_access_port_s *const ap0 = (adiv5_access_port_s *)target->target_storage; - adiv5_mem_write(ap0, dest, src, len); + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + adiv5_mem_write(priv->ap, dest, src, len); } static void stm32mp15_ca7_setup_axi_ap(target_s *const target) { + stm32mp15_priv_s *priv_storage = (stm32mp15_priv_s *)target->target_storage; + if (!priv_storage) { + /* Allocate target-specific storage */ + priv_storage = calloc(1, sizeof(*priv_storage)); + if (!priv_storage) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + target->target_storage = priv_storage; + } + if (priv_storage->ap) + return; adiv5_access_port_s *ap0 = calloc(1, sizeof(*ap0)); if (!ap0) { /* calloc failed: heap exhaustion */ DEBUG_ERROR("calloc: failed in %s\n", __func__); @@ -254,7 +268,7 @@ static void stm32mp15_ca7_setup_axi_ap(target_s *const target) ap0->csw = adiv5_ap_read(ap0, ADIV5_AP_CSW); adiv5_ap_ref(ap0); - target->target_storage = ap0; + priv_storage->ap = ap0; } static bool stm32mp15_ca7_attach(target_s *const target) @@ -273,10 +287,10 @@ static bool stm32mp15_ca7_attach(target_s *const target) static void stm32mp15_ca7_detach(target_s *target) { /* Deallocate any extra AP */ - adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; - if (ap0) { - adiv5_ap_unref(ap0); - ap0 = NULL; + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + if (priv && priv->ap) { + adiv5_ap_unref(priv->ap); + priv->ap = NULL; } cortexar_detach(target); } @@ -328,9 +342,8 @@ static void stm32mp15_cm4_detach(target_s *const target) target_mem32_write32(target, STM32MP15_DBGMCU_CONFIG, priv->dbgmcu_config); /* Deallocate any extra AP */ - adiv5_access_port_s *ap1 = (adiv5_access_port_s *)target->target_storage; - adiv5_ap_unref(ap1); - target->target_storage = NULL; + adiv5_ap_unref(priv->ap); + priv->ap = NULL; /* Now defer to the normal Cortex-M detach routine to complete the detach */ cortexm_detach(target); From 2e153004bc3ddfcb97897157582a3d0c586439af Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:34:13 +0300 Subject: [PATCH 151/247] cortexar: declare mem_read/write in headers --- src/target/cortexar.c | 7 ++----- src/target/cortexar.h | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/target/cortexar.c b/src/target/cortexar.c index aac90335df9..9d2664de2c5 100644 --- a/src/target/cortexar.c +++ b/src/target/cortexar.c @@ -384,8 +384,6 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) /* clang-format on */ static bool cortexar_check_error(target_s *target); -static void cortexar_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); -static void cortexar_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); static void cortexar_regs_read(target_s *target, void *data); static void cortexar_regs_write(target_s *target, const void *data); @@ -1098,7 +1096,7 @@ static void cortexar_mem_handle_fault(target_s *const target, const char *const * If core is not halted, temporarily halts target and resumes at the end * of the function. */ -static void cortexar_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) +void cortexar_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { /* If system is not halted, halt temporarily within this function. */ const bool halted_in_function = cortexar_halt_and_wait(target); @@ -1220,8 +1218,7 @@ static bool cortexar_mem_write_slow( * If core is not halted, temporarily halts target and resumes at the end * of the function. */ -static void cortexar_mem_write( - target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) +void cortexar_mem_write(target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) { /* If system is not halted, halt temporarily within this function. */ const bool halted_in_function = cortexar_halt_and_wait(target); diff --git a/src/target/cortexar.h b/src/target/cortexar.h index 67168aab811..59c952f903c 100644 --- a/src/target/cortexar.h +++ b/src/target/cortexar.h @@ -38,6 +38,8 @@ bool cortexar_attach(target_s *target); void cortexar_detach(target_s *target); +void cortexar_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); +void cortexar_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); void cortexar_invalidate_all_caches(target_s *target); #endif /* TARGET_CORTEXAR_H */ From af353746a15bb3ddadbcbbadadd1b56567a0f80a Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:14:45 +0300 Subject: [PATCH 152/247] stm32mp15: Restore CA7 memory access methods on detach --- src/target/stm32mp15.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index b17530c1c61..0310653b555 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -291,6 +291,8 @@ static void stm32mp15_ca7_detach(target_s *target) if (priv && priv->ap) { adiv5_ap_unref(priv->ap); priv->ap = NULL; + target->mem_read = cortexar_mem_read; + target->mem_write = cortexar_mem_write; } cortexar_detach(target); } From 24e36ec574019c3c02bb989dc7cc424b2cfdbd33 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:21:45 +0300 Subject: [PATCH 153/247] stm32mp15: Always use AP1 for conf_swo --- src/target/stm32mp15.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 0310653b555..02a117063d6 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -132,7 +132,9 @@ static bool stm32mp15_cmd_swo(target_s *target, int argc, const char **argv) (void)argc; (void)argv; /* TODO: argv parsing for mode and baudrate */ - adiv5_access_port_s *const ap1 = (adiv5_access_port_s *)target->target_storage; + stm32mp15_priv_s *priv = (stm32mp15_priv_s *)target->target_storage; + /* Prefer AP1 over AP0 when attached to CA7 (because no CoreSight components are on AP0) */ + adiv5_access_port_s *const ap1 = (priv->ap->apsel == 1U) ? priv->ap : cortex_ap(target); /* Pin Protocol: change Manchester to UART */ uint32_t sppr = 0; adiv5_mem_read(ap1, &sppr, SWO_SPPR, 4); From 8dfdd8f3e9e3bb5c4becf683486b035fb367c38f Mon Sep 17 00:00:00 2001 From: fufixman Date: Mon, 6 Apr 2026 08:22:20 +0200 Subject: [PATCH 154/247] stlink: document stlink v2-1 bootloader behavior --- src/platforms/stlink/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/platforms/stlink/README.md b/src/platforms/stlink/README.md index 85004c4251a..4c444c2e045 100644 --- a/src/platforms/stlink/README.md +++ b/src/platforms/stlink/README.md @@ -62,6 +62,7 @@ NB: SWDIO/TMS is on P**B**14, not P**A**14. SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", ACTION=="add", RUN+="/stlink-tool" EOF ``` +* For ST-Link v2-1 instead run `stlink-tool -m blackmagic_stlink_firmware.bin` to make the bootloader recognize the firmware as valid thus allowing it to boot on replug ## Reverting to original ST Firmware with running BMP firmware @@ -145,3 +146,15 @@ UART RX/TX On ST-Link v2/2-1 boards with the original bootloader, you can force bootloader entry with asserting [NRST](https://www.carminenoviello.com/2016/02/26/restore-st-link-interface-bad-update-2-26-15-firmware/) of the STM32F103CB of the USB powered board. Several attempts may be needed. + +## ST-Link V2-1 Bootloader Magic + +On boot, the ST-Link V2-1 bootloader checks two conditions to decide whether to jump directly to the application: + +1. Whether the device was reset via Power-on Reset (PoR). If not, it enters DFU mode. +2. Whether the top word of ROM contains the magic number 0xA50027D3. If not, it enters DFU mode. + +For more details, see [this article](https://github.com/GMMan/st-link-hack/blob/master/paper/paper.md#main-function). + +You can either patch this check or include the required value directly in your application. [stlink-tool](https://codeberg.org/blackmagic-debug/stlink-tool) provides the ```-m``` flag to patch the value during firmware upload. + From 32c8fd693018f9b7d7b7651ecebe17a781612ce0 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:42:25 +0300 Subject: [PATCH 155/247] adiv5_jtag: Restore lost JTAG-DP state across resets * In some targets like AT32F403A, a nRST falling edge behaves like TRST. IR is loaded with IDCODE. Next transaction expects DPACC but gets a 8974008e:7. * Mangle internal JTAG IR cache to BYPASS state so that daisy-chaining works. For the active/attached target, BMD logic should run through Capture-IR. * Nothing is needed in SWD transport, so avoid calling a null pointer. --- src/target/adiv5_internal.h | 1 + src/target/adiv5_jtag.c | 12 ++++++++++++ src/target/cortexm.c | 8 ++++++++ 3 files changed, 21 insertions(+) diff --git a/src/target/adiv5_internal.h b/src/target/adiv5_internal.h index b69c0ac51c4..4d529e1062c 100644 --- a/src/target/adiv5_internal.h +++ b/src/target/adiv5_internal.h @@ -203,6 +203,7 @@ struct adiv5_debug_port { uint32_t (*error)(adiv5_debug_port_s *dp, bool protocol_recovery); uint32_t (*low_access)(adiv5_debug_port_s *dp, uint8_t RnW, uint16_t addr, uint32_t value); void (*abort)(adiv5_debug_port_s *dp, uint32_t abort); + void (*ensure_idle)(adiv5_debug_port_s *dp); #if CONFIG_BMDA == 1 void (*ap_regs_read)(adiv5_access_port_s *ap, void *data); diff --git a/src/target/adiv5_jtag.c b/src/target/adiv5_jtag.c index 6667a658866..6bb3ae256b9 100644 --- a/src/target/adiv5_jtag.c +++ b/src/target/adiv5_jtag.c @@ -41,6 +41,17 @@ #define IR_DPACC 0xaU #define IR_APACC 0xbU +static void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp) +{ + /* + * On devices where nRST pulls TRST, the JTAG-DP's IR is reset + * from DPACC/APACC to IDCODE. We want BYPASS in case of daisy-chaining. + */ + jtag_devs[dp->dev_index].current_ir = 0xffU; + /* Go from TLR to RTI. */ + jtagtap_return_idle(1); +} + void adiv5_jtag_dp_handler(const uint8_t dev_index) { adiv5_debug_port_s *dp = calloc(1, sizeof(*dp)); @@ -55,6 +66,7 @@ void adiv5_jtag_dp_handler(const uint8_t dev_index) dp->low_access = adiv5_jtag_raw_access; dp->error = adiv5_jtag_clear_error; dp->abort = adiv5_jtag_abort; + dp->ensure_idle = adiv5_jtag_ensure_idle; #if CONFIG_BMDA == 1 bmda_jtag_dp_init(dp); #endif diff --git a/src/target/cortexm.c b/src/target/cortexm.c index ce59bf5c7ba..97e0fb03df9 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -780,6 +780,9 @@ static void cortexm_pc_write(target_s *target, const uint32_t val) */ static void cortexm_reset(target_s *const target) { + adiv5_access_port_s *ap = cortex_ap(target); + adiv5_debug_port_s *dp = ap->dp; + /* Read DHCSR here to clear S_RESET_ST bit before reset */ target_mem32_read32(target, CORTEXM_DHCSR); /* If the physical reset pin is not inhibited, use it */ @@ -788,6 +791,8 @@ static void cortexm_reset(target_s *const target) platform_nrst_set_val(false); /* Some NRF52840 users saw invalid SWD transaction with native/firmware without this delay.*/ platform_delay(10); + if (dp->ensure_idle) + dp->ensure_idle(dp); } /* Check if the reset succeeded */ @@ -798,6 +803,9 @@ static void cortexm_reset(target_s *const target) * Trigger reset by AIRCR. */ target_mem32_write32(target, CORTEXM_AIRCR, CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ); + platform_delay(10); + if (dp->ensure_idle) + dp->ensure_idle(dp); } /* If target needs to do something extra (see Atmel SAM4L for example) */ From c10a4a4e35bc29ee08d7c923819bb6711a60d0a4 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 9 Feb 2025 23:23:23 +0300 Subject: [PATCH 156/247] adiv5_jtag: Make _ensure_idle method public --- src/target/adiv5.h | 1 + src/target/adiv5_jtag.c | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/target/adiv5.h b/src/target/adiv5.h index 719fd8eec17..ab8f24ff373 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -294,5 +294,6 @@ uint32_t adiv5_jtag_read(adiv5_debug_port_s *dp, uint16_t addr); uint32_t adiv5_jtag_raw_access(adiv5_debug_port_s *dp, uint8_t rnw, uint16_t addr, uint32_t value); uint32_t adiv5_jtag_clear_error(adiv5_debug_port_s *dp, bool protocol_recovery); void adiv5_jtag_abort(adiv5_debug_port_s *dp, uint32_t abort); +void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp); #endif /* TARGET_ADIV5_H */ diff --git a/src/target/adiv5_jtag.c b/src/target/adiv5_jtag.c index 6bb3ae256b9..d91671569af 100644 --- a/src/target/adiv5_jtag.c +++ b/src/target/adiv5_jtag.c @@ -41,17 +41,6 @@ #define IR_DPACC 0xaU #define IR_APACC 0xbU -static void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp) -{ - /* - * On devices where nRST pulls TRST, the JTAG-DP's IR is reset - * from DPACC/APACC to IDCODE. We want BYPASS in case of daisy-chaining. - */ - jtag_devs[dp->dev_index].current_ir = 0xffU; - /* Go from TLR to RTI. */ - jtagtap_return_idle(1); -} - void adiv5_jtag_dp_handler(const uint8_t dev_index) { adiv5_debug_port_s *dp = calloc(1, sizeof(*dp)); @@ -187,3 +176,14 @@ void adiv5_jtag_abort(adiv5_debug_port_s *dp, uint32_t abort) jtag_dev_write_ir(dp->dev_index, IR_ABORT); jtag_dev_shift_dr(dp->dev_index, NULL, (const uint8_t *)&request, 35); } + +void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp) +{ + /* + * On devices where nRST pulls TRST, the JTAG-DP's IR is reset + * from DPACC/APACC to IDCODE. We want BYPASS in case of daisy-chaining. + */ + jtag_devs[dp->dev_index].current_ir = 0xffU; + /* Go from TLR to RTI. */ + jtagtap_return_idle(1); +} From 4e925de3bcc9ad5d3088d0ed8f2000fdd74ea36e Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 9 Feb 2025 23:23:56 +0300 Subject: [PATCH 157/247] remote: Add JTAG ensure idle command (!JI#) --- src/remote.c | 6 ++++++ src/remote.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/remote.c b/src/remote.c index 2c80c749786..77837b0a324 100644 --- a/src/remote.c +++ b/src/remote.c @@ -192,6 +192,7 @@ static void remote_packet_process_jtag(gdb_packet_s *const packet) remote_dp.error = adiv5_jtag_clear_error; remote_dp.low_access = adiv5_jtag_raw_access; remote_dp.abort = adiv5_jtag_abort; + remote_dp.ensure_idle = adiv5_jtag_ensure_idle; jtagtap_init(); remote_respond(REMOTE_RESP_OK, 0); break; @@ -249,6 +250,11 @@ static void remote_packet_process_jtag(gdb_packet_s *const packet) break; } + case REMOTE_JTAG_ENSURE_IDLE: /* JI = re-cycle IR after indirect resets */ + remote_dp.ensure_idle(&remote_dp); + remote_respond(REMOTE_RESP_OK, 0); + break; + default: remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_UNRECOGNISED); break; diff --git a/src/remote.h b/src/remote.h index 520cb8a3a64..e1109eff123 100644 --- a/src/remote.h +++ b/src/remote.h @@ -303,6 +303,7 @@ * need new bits as they are already covered). We will co-ordinate with you in making sure the bit * is unique when going through the PR'ing process. */ +#define REMOTE_JTAG_ENSURE_IDLE 'I' /* ADIv5 accleration protocol elements */ #define REMOTE_ADIV5_PACKET 'A' From 9c93a5b35fcfe8bf19ed8435d0401f9a02e72784 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 9 Feb 2025 23:24:32 +0300 Subject: [PATCH 158/247] hosted/remote/protocol_v4: Add JTAG ensure idle command (!JI#) --- src/platforms/hosted/remote/protocol_v4.c | 15 +++++++++++++++ src/platforms/hosted/remote/protocol_v4.h | 1 + src/platforms/hosted/remote/protocol_v4_defs.h | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/src/platforms/hosted/remote/protocol_v4.c b/src/platforms/hosted/remote/protocol_v4.c index a57ce0d9f34..b4005e6c48e 100644 --- a/src/platforms/hosted/remote/protocol_v4.c +++ b/src/platforms/hosted/remote/protocol_v4.c @@ -108,6 +108,7 @@ bool remote_v4_adiv5_init(adiv5_debug_port_s *const dp) dp->ap_write = remote_v4_adiv5_ap_write; dp->mem_read = remote_v4_adiv5_mem_read_bytes; dp->mem_write = remote_v4_adiv5_mem_write_bytes; + dp->ensure_idle = remote_v4_jtag_ensure_idle; return true; } @@ -172,3 +173,17 @@ uint64_t remote_v4_supported_families(void) return remote_decode_response(buffer + 1U, 8U); return 0U; } + +void remote_v4_jtag_ensure_idle(adiv5_debug_port_s *dp) +{ + /* Ask remote_dp !JI# to set IR cache to BYPASS (because a reset happened) */ + platform_buffer_write(REMOTE_JTAG_ENSURE_IDLE_STR, sizeof(REMOTE_JTAG_ENSURE_IDLE_STR)); + char buffer[REMOTE_MAX_MSG_SIZE]; + /* Read back the answer and check for errors */ + const int length = platform_buffer_read(buffer, REMOTE_MAX_MSG_SIZE); + if (length < 1 || buffer[0U] != REMOTE_RESP_OK) { + DEBUG_ERROR("%s failed, error %s\n", __func__, length ? buffer + 1 : "with communication"); + return; + } + jtag_devs[dp->dev_index].current_ir = 0xffU; +} diff --git a/src/platforms/hosted/remote/protocol_v4.h b/src/platforms/hosted/remote/protocol_v4.h index e55fd078a1c..c42d8f5ee50 100644 --- a/src/platforms/hosted/remote/protocol_v4.h +++ b/src/platforms/hosted/remote/protocol_v4.h @@ -43,6 +43,7 @@ bool remote_v4_init(void); bool remote_v4_adiv5_init(adiv5_debug_port_s *dp); bool remote_v4_adiv6_init(adiv5_debug_port_s *dp); bool remote_v4_riscv_jtag_init(riscv_dmi_s *dmi); +void remote_v4_jtag_ensure_idle(adiv5_debug_port_s *dp); uint64_t remote_v4_supported_architectures(void); uint64_t remote_v4_supported_families(void); diff --git a/src/platforms/hosted/remote/protocol_v4_defs.h b/src/platforms/hosted/remote/protocol_v4_defs.h index 1fac0569254..ac80d2ce84b 100644 --- a/src/platforms/hosted/remote/protocol_v4_defs.h +++ b/src/platforms/hosted/remote/protocol_v4_defs.h @@ -154,6 +154,13 @@ REMOTE_SOM, REMOTE_ADIV5_PACKET, REMOTE_DP_TARGETSEL, REMOTE_ADIV5_DATA, REMOTE_EOM, 0 \ } +#define REMOTE_JTAG_ENSURE_IDLE 'I' +#define REMOTE_JTAG_ENSURE_IDLE_STR \ + (char[]) \ + { \ + REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_JTAG_ENSURE_IDLE, REMOTE_EOM, 0 \ + } + /* ADIv6 acceleration protocol elements */ #define REMOTE_ADIV6_PACKET '6' From ac9948462f9ba5856e5c22e747bff6bbfb25ac90 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Mon, 6 Apr 2026 19:23:52 +0300 Subject: [PATCH 159/247] stm32mp15: Read DBGMCU for CA7 via AP1 --- src/target/stm32mp15.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 02a117063d6..ebf91ed60dc 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -80,6 +80,8 @@ #define SWO_ACPR (SWO_BASE + 0x00010) #define SWO_SPPR (SWO_BASE + 0x000f0) +#define STM32MP15_AP1_DBGMCU_IDCODE 0xe0081000U + typedef struct stm32mp15_priv { uint32_t dbgmcu_config; adiv5_access_port_s *ap; @@ -101,7 +103,7 @@ static void stm32mp15_cm4_detach(target_s *target); static bool stm32mp15_ident(target_s *const target, const bool cortexm) { - const adiv5_access_port_s *const ap = cortex_ap(target); + adiv5_access_port_s *const ap = cortex_ap(target); /* Check if the part's a STM32MP15 */ if (ap->partno != ID_STM32MP15x) { /* If it's not a Cortex-M core or it doesn't match the errata ID code, return false */ @@ -109,6 +111,20 @@ static bool stm32mp15_ident(target_s *const target, const bool cortexm) return false; } + if (!cortexm) { + /* + * After Linux has booted on CA7, 0x50081000 (system bus alias) + * becomes unreadable via cortexar_mem_read, so use debug APB on AP1 + */ + uint32_t idcode = 0; + adiv5_mem_read(ap, &idcode, STM32MP15_AP1_DBGMCU_IDCODE, sizeof(idcode)); + const uint16_t dev_id = idcode & STM32MP15_DBGMCU_IDCODE_DEV_MASK; + DEBUG_TARGET("%s: looking at device ID 0x%03x at 0x%08" PRIx32 "\n", __func__, dev_id, + (uint32_t)STM32MP15_AP1_DBGMCU_IDCODE); + if (dev_id == ID_STM32MP15x) { + return true; + } + } /* By now it's established that this is likely an MP15x_CM4, but check that it's not an H74x */ const uint32_t idcode = target_mem32_read32(target, STM32MP15_DBGMCU_IDCODE); const uint16_t dev_id = idcode & STM32MP15_DBGMCU_IDCODE_DEV_MASK; From e6333cfc524632201db8175efe508ea1dfaae010 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sat, 28 Dec 2024 10:55:07 +0300 Subject: [PATCH 160/247] stm32mp15: Add DRAM (512 MiB) and Boot ROM to CA7 memory map --- src/target/stm32mp15.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index ebf91ed60dc..7be4f39b6d2 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -47,6 +47,8 @@ #define STM32MP15_SYSRAM_SIZE 0x00040000U #define STM32MP15_CAN_SRAM_BASE 0x44011000U #define STM32MP15_CAN_SRAM_SIZE 0x00002800U +#define STM32MP15_DRAM_BASE 0xc0000000U +#define STM32MP15_CA7_BOOTROM_BASE 0x00000000U /* Access from processor address space. * Access via the debug APB is at 0xe0081000 over AP1. */ @@ -336,6 +338,12 @@ bool stm32mp15_ca7_probe(target_s *const target) target_add_ram32(target, STM32MP15_CA7_AHBSRAM_ALIAS_BASE, STM32MP15_AHBSRAM_SIZE); target_add_ram32(target, STM32MP15_SYSRAM_BASE, STM32MP15_SYSRAM_SIZE); target_add_ram32(target, STM32MP15_CAN_SRAM_BASE, STM32MP15_CAN_SRAM_SIZE); + + /* DRAM 512 MiB at identity mapping base */ + target_add_ram32(target, STM32MP15_DRAM_BASE, 512U * 1024U * 1024U); + /* Boot ROM (CA7 stays here in Engi boot) */ + target_add_ram32(target, STM32MP15_CA7_BOOTROM_BASE, 128U * 1024U); + return true; } #endif From f7d50a445fb225ac0d52f37fff8e8ee6e2696eba Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:38:57 +0300 Subject: [PATCH 161/247] riscv32: Prepare memory access via progbuf --- src/target/riscv32.c | 25 +++++++++++++++++++++++++ src/target/riscv_debug.c | 3 +++ src/target/riscv_debug.h | 1 + 3 files changed, 29 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 8dd063b0081..52622cb6913 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -578,6 +578,27 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } +static void riscv32_progbuf_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +{ + /* Figure out the maximal 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; + + (void)access_length; + return; +} + +static void riscv32_progbuf_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + (void)access_length; + return; +} + void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { /* If we're asked to do a 0-byte read, do nothing */ @@ -589,6 +610,8 @@ void riscv32_mem_read(target_s *const target, void *const dest, const target_add riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_read(hart, dest, src, len); + else if (hart->flags & RV_HART_FLAG_MEMORY_PROGBUF) + riscv32_progbuf_mem_read(hart, dest, src, len); else riscv32_abstract_mem_read(hart, dest, src, len); @@ -631,6 +654,8 @@ void riscv32_mem_write(target_s *const target, const target_addr64_t dest, const riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_write(hart, dest, src, len); + else if (hart->flags & RV_HART_FLAG_MEMORY_PROGBUF) + riscv32_progbuf_mem_write(hart, dest, src, len); else riscv32_abstract_mem_write(hart, dest, src, len); } diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index cc305c41045..e9796125538 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -689,6 +689,9 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) /* Now 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 and %u progbuf registers\n", data_registers, hart->progbuf_size); + /* Memory access using less than 4 progbuf slots is not supported yet */ + if (hart->progbuf_size >= 4) + hart->flags |= RV_HART_FLAG_MEMORY_PROGBUF; /* Check we have at least enough data registers for arg0 */ if (data_registers >= 4) hart->access_width = 128U; diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 2dc5ae5b61f..4aec5432e2d 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -101,6 +101,7 @@ typedef enum riscv_match_size { #define RV_HART_FLAG_MEMORY_ABSTRACT (0U << 4U) #define RV_HART_FLAG_MEMORY_SYSBUS (1U << 4U) #define RV_HART_FLAG_DATA_GPR_ONLY (1U << 5U) /* Hart supports Abstract Data commands for GPRs only */ +#define RV_HART_FLAG_MEMORY_PROGBUF (1U << 6U) typedef struct riscv_dmi riscv_dmi_s; From e44fbef07a4e03583c11e43fb6d789e9cb1122c7 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:57:38 +0300 Subject: [PATCH 162/247] riscv32: Implement memory I/O via progbuf (4+1 slots, uncompressed) --- src/target/riscv32.c | 138 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 4 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 52622cb6913..d49845304a0 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -578,6 +578,8 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } +#define RV_DM_PROGBUF_BASE 0x20U + static void riscv32_progbuf_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { @@ -585,8 +587,71 @@ static void riscv32_progbuf_mem_read( const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; - (void)access_length; - return; + /* RV32I opcodes: extract ptr32 from data1, load word/half/byte, store back to data0 */ + static const uint32_t progbuf_read32[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x0005a583U, // lw a1, 0(a1) + 0x00b52023U, // sw a1, 0(a0) + 0x00100073U + }; + static const uint32_t progbuf_read16[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x00059583U, // lh a1, 0(a1) + 0x00b51023U, // sh a1, 0(a0) + 0x00100073U + }; + static const uint32_t progbuf_read8[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x00058583U, // lb a1, 0(a1) + 0x00b50023U, // sb a1, 0(a0) + 0x00100073U + }; + const uint32_t *progbuf_read = progbuf_read32; + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: + progbuf_read = progbuf_read8; + break; + case RV_MEM_ACCESS_16_BIT: + progbuf_read = progbuf_read16; + break; + case RV_MEM_ACCESS_32_BIT: + progbuf_read = progbuf_read32; + break; + default: + return; + } + /* Fill the program buffer */ + for (int i = 0; i < 5; i++) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + i, progbuf_read[i])) + return; + } + + uint32_t a0_save = 0; + uint32_t a1_save = 0; + riscv_csr_read(hart, RV_GPR_BASE + 10, &a0_save); + riscv_csr_read(hart, RV_GPR_BASE + 11, &a1_save); + + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Write the address to read to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src + offset)) + return; + /* Execute progbuf: postexec only, no reg transfer */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_ABST_POSTEXEC) || + !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); + } + + riscv_csr_write(hart, RV_GPR_BASE + 10, &a0_save); + riscv_csr_write(hart, RV_GPR_BASE + 11, &a1_save); } static void riscv32_progbuf_mem_write( @@ -595,8 +660,73 @@ static void riscv32_progbuf_mem_write( /* 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; - (void)access_length; - return; + /* RV32I opcodes: extract ptr32 from data1 and val from data0, store word/half/byte */ + static const uint32_t progbuf_write32[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x00052603U, // lw a2, 0(a0) + 0x00c5a023U, // sw a2, 0(a1) + 0x00100073U + }; + static const uint32_t progbuf_write16[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x00051603U, // lh a2, 0(a0) + 0x00c59023U, // sh a2, 0(a1) + 0x00100073U + }; + static const uint32_t progbuf_write8[5] = { + 0x38000513U, // li a0, 0x380 + 0x00452583U, // lw a1, 4(a0) + 0x00050603U, // lb a2, 0(a0) + 0x00c58023U, // sb a2, 0(a1) + 0x00100073U + }; + const uint32_t *progbuf_write = progbuf_write32; + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: + progbuf_write = progbuf_write8; + break; + case RV_MEM_ACCESS_16_BIT: + progbuf_write = progbuf_write16; + break; + case RV_MEM_ACCESS_32_BIT: + progbuf_write = progbuf_write32; + break; + default: + return; + } + /* Fill the program buffer */ + for (int i = 0; i < 5; i++) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + i, progbuf_write[i])) + return; + } + + uint32_t a0_save = 0; + uint32_t a1_save = 0; + uint32_t a2_save = 0; + riscv_csr_read(hart, RV_GPR_BASE + 10, &a0_save); + riscv_csr_read(hart, RV_GPR_BASE + 11, &a1_save); + riscv_csr_read(hart, RV_GPR_BASE + 12, &a2_save); + + const uint8_t *const data = (const uint8_t *)src; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Write the address to write to arg1 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest + offset)) + return; + /* 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 progbuf: postexec only, no reg transfer */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_ABST_POSTEXEC) || + !riscv_command_wait_complete(hart)) + return; + } + + riscv_csr_write(hart, RV_GPR_BASE + 10, &a0_save); + riscv_csr_write(hart, RV_GPR_BASE + 11, &a1_save); + riscv_csr_write(hart, RV_GPR_BASE + 12, &a2_save); } void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) From 38b92b18c299d94b013f125d4b6b25adaef11271 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:08:08 +0300 Subject: [PATCH 163/247] riscv32: Refactor progbuf-based memory I/O to fit into 1 slot and use GPR --- src/target/riscv32.c | 155 ++++++++++++++++++++++----------------- src/target/riscv_debug.c | 4 +- 2 files changed, 91 insertions(+), 68 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index d49845304a0..1f0332e21fe 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -580,6 +580,10 @@ static void riscv32_sysbus_mem_write( #define RV_DM_PROGBUF_BASE 0x20U +#define RV_GPR_A0 0x100aU + +#define RV_EBREAK 0x00100073U + static void riscv32_progbuf_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { @@ -587,27 +591,15 @@ static void riscv32_progbuf_mem_read( const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; - /* RV32I opcodes: extract ptr32 from data1, load word/half/byte, store back to data0 */ - static const uint32_t progbuf_read32[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x0005a583U, // lw a1, 0(a1) - 0x00b52023U, // sw a1, 0(a0) - 0x00100073U + /* RV32I opcodes: load word/half/byte from address A0 into A0 (clobber) */ + static const uint32_t progbuf_read32[1] = { + 0x00052503U, // lw a0, 0(a0) }; - static const uint32_t progbuf_read16[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x00059583U, // lh a1, 0(a1) - 0x00b51023U, // sh a1, 0(a0) - 0x00100073U + static const uint32_t progbuf_read16[1] = { + 0x00051503U, // lh a0, 0(a0) }; - static const uint32_t progbuf_read8[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x00058583U, // lb a1, 0(a1) - 0x00b50023U, // sb a1, 0(a0) - 0x00100073U + static const uint32_t progbuf_read8[1] = { + 0x00050503U, // lb a0, 0(a0) }; const uint32_t *progbuf_read = progbuf_read32; switch (access_width) { @@ -623,35 +615,62 @@ static void riscv32_progbuf_mem_read( default: return; } +#if 0 + /* assume ptr is in A0, load word/half/byte to A1 (clobber), postincrement A0 */ + static const uint32_t progbuf_read32_autoexec[2] = { + 0x00052583U, // lw a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 + } + DEBUG_TARGET("%s: 0x%08x+%lu width %u\n", __func__, src, len, access_width); +#endif /* Fill the program buffer */ - for (int i = 0; i < 5; i++) { - if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + i, progbuf_read[i])) + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_read[0])) + return; + /* Append literal ebreak (if impebreak is not reached) */ + if (hart->progbuf_size > 1U) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK)) return; } uint32_t a0_save = 0; - uint32_t a1_save = 0; - riscv_csr_read(hart, RV_GPR_BASE + 10, &a0_save); - riscv_csr_read(hart, RV_GPR_BASE + 11, &a1_save); + //uint32_t a1_save = 0; + riscv_csr_read(hart, RV_GPR_A0, &a0_save); + //riscv_csr_read(hart, RV_GPR_A0 + 11, &a1_save); uint8_t *const data = (uint8_t *)dest; for (size_t offset = 0; offset < len; offset += access_length) { - /* Write the address to read to arg1 */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src + offset)) + /* Write the source address to DATA0 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, src + offset)) return; - /* Execute progbuf: postexec only, no reg transfer */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_ABST_POSTEXEC) || - !riscv_command_wait_complete(hart)) + /* Copy the source address from DATA0 to GPR A0 and launch the progbuf postexec */ + bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | + RV_GPR_A0); + /* Wait for both the register write and progbuf execution to complete */ + result &= riscv_command_wait_complete(hart); + if (!result) return; - /* Extract back the data from arg0 */ +#if 1 + /* Copy the read value from GPR A0 to DATA0 */ + const uint32_t abstract_command2 = + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A0; + result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command2); + result &= riscv_command_wait_complete(hart); + if (!result) + return; + /* Extract the read value from DATA0 */ uint32_t value = 0; if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) return; +#else + uint32_t value = 0; + riscv_csr_read(hart, RV_GPR_A0, &value); +#endif riscv32_unpack_data(data + offset, value, access_width); } - riscv_csr_write(hart, RV_GPR_BASE + 10, &a0_save); - riscv_csr_write(hart, RV_GPR_BASE + 11, &a1_save); + riscv_csr_write(hart, RV_GPR_A0, &a0_save); + //riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); } static void riscv32_progbuf_mem_write( @@ -660,27 +679,18 @@ static void riscv32_progbuf_mem_write( /* 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; - /* RV32I opcodes: extract ptr32 from data1 and val from data0, store word/half/byte */ - static const uint32_t progbuf_write32[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x00052603U, // lw a2, 0(a0) - 0x00c5a023U, // sw a2, 0(a1) - 0x00100073U + /* RV32I opcodes: store word/half/byte in a1 into address pointed-by a0 */ + static const uint32_t progbuf_write32[] = { + 0x00b52023U, // sw a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; - static const uint32_t progbuf_write16[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x00051603U, // lh a2, 0(a0) - 0x00c59023U, // sh a2, 0(a1) - 0x00100073U + static const uint32_t progbuf_write16[] = { + 0x00b51023U, // sh a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; - static const uint32_t progbuf_write8[5] = { - 0x38000513U, // li a0, 0x380 - 0x00452583U, // lw a1, 4(a0) - 0x00050603U, // lb a2, 0(a0) - 0x00c58023U, // sb a2, 0(a1) - 0x00100073U + static const uint32_t progbuf_write8[] = { + 0x00b50023U, // sb a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; const uint32_t *progbuf_write = progbuf_write32; switch (access_width) { @@ -697,36 +707,49 @@ static void riscv32_progbuf_mem_write( return; } /* Fill the program buffer */ - for (int i = 0; i < 5; i++) { - if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + i, progbuf_write[i])) + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_write[0])) + return; + /* Append literal ebreak (if impebreak is not reached) */ + if (hart->progbuf_size > 1U) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK)) return; } uint32_t a0_save = 0; uint32_t a1_save = 0; - uint32_t a2_save = 0; - riscv_csr_read(hart, RV_GPR_BASE + 10, &a0_save); - riscv_csr_read(hart, RV_GPR_BASE + 11, &a1_save); - riscv_csr_read(hart, RV_GPR_BASE + 12, &a2_save); + riscv_csr_read(hart, RV_GPR_A0, &a0_save); + riscv_csr_read(hart, RV_GPR_A0 + 1, &a1_save); const uint8_t *const data = (const uint8_t *)src; for (size_t offset = 0; offset < len; offset += access_length) { - /* Write the address to write to arg1 */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest + offset)) + /* Copy the destination address from DATA0 to GPR A0 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, dest + offset)) return; - /* Pack the data to write into arg0 */ + /* Copy the source address from DATA0 to GPR A0 */ + const uint32_t abstract_command1 = + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A0; + bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command1); + result &= riscv_command_wait_complete(hart); + if (!result) + return; + //riscv_csr_write(hart, RV_GPR_A0, dest + offset); + + /* Pack the data to write into GPR A1 */ uint32_t value = riscv32_pack_data(data + offset, access_width); if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) return; - /* Execute progbuf: postexec only, no reg transfer */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_ABST_POSTEXEC) || - !riscv_command_wait_complete(hart)) + /* Copy the write value from DATA0 to GPR A1 and launch the progbuf postexec */ + result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | + (RV_GPR_A0 + 1)); + result &= riscv_command_wait_complete(hart); + if (!result) return; + //riscv_csr_write(hart, RV_GPR_A1, value); } - riscv_csr_write(hart, RV_GPR_BASE + 10, &a0_save); - riscv_csr_write(hart, RV_GPR_BASE + 11, &a1_save); - riscv_csr_write(hart, RV_GPR_BASE + 12, &a2_save); + riscv_csr_write(hart, RV_GPR_A0, &a0_save); + riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); } void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index e9796125538..becfb584ddb 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -689,8 +689,8 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) /* Now 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 and %u progbuf registers\n", data_registers, hart->progbuf_size); - /* Memory access using less than 4 progbuf slots is not supported yet */ - if (hart->progbuf_size >= 4) + /* Memory access using just 1 progbuf slot is possible */ + if (hart->progbuf_size >= 1U) hart->flags |= RV_HART_FLAG_MEMORY_PROGBUF; /* Check we have at least enough data registers for arg0 */ if (data_registers >= 4) From 4399c0cae724f879bb41cc60f3f4ce3575989273 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:36:31 +0300 Subject: [PATCH 164/247] riscv32: Update riscv32_progbuf_mem_read to use both x10 & x11 --- src/target/riscv32.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 1f0332e21fe..5ce0bdeca3d 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -591,15 +591,18 @@ static void riscv32_progbuf_mem_read( const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; - /* RV32I opcodes: load word/half/byte from address A0 into A0 (clobber) */ - static const uint32_t progbuf_read32[1] = { - 0x00052503U, // lw a0, 0(a0) + /* RV32I opcodes: load word/half/byte from address A0 into A1 (clobber), postincrement A0 */ + static const uint32_t progbuf_read32[2] = { + 0x00052583U, // lw a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; - static const uint32_t progbuf_read16[1] = { - 0x00051503U, // lh a0, 0(a0) + static const uint32_t progbuf_read16[2] = { + 0x00051583U, // lh a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; - static const uint32_t progbuf_read8[1] = { - 0x00050503U, // lb a0, 0(a0) + static const uint32_t progbuf_read8[2] = { + 0x00050583U, // lb a1, 0(a0) + 0x00450513U, // addi a0, a0, 4 }; const uint32_t *progbuf_read = progbuf_read32; switch (access_width) { @@ -615,14 +618,6 @@ static void riscv32_progbuf_mem_read( default: return; } -#if 0 - /* assume ptr is in A0, load word/half/byte to A1 (clobber), postincrement A0 */ - static const uint32_t progbuf_read32_autoexec[2] = { - 0x00052583U, // lw a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - } - DEBUG_TARGET("%s: 0x%08x+%lu width %u\n", __func__, src, len, access_width); -#endif /* Fill the program buffer */ if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_read[0])) return; @@ -633,9 +628,9 @@ static void riscv32_progbuf_mem_read( } uint32_t a0_save = 0; - //uint32_t a1_save = 0; + uint32_t a1_save = 0; riscv_csr_read(hart, RV_GPR_A0, &a0_save); - //riscv_csr_read(hart, RV_GPR_A0 + 11, &a1_save); + riscv_csr_read(hart, RV_GPR_A0 + 1, &a1_save); uint8_t *const data = (uint8_t *)dest; for (size_t offset = 0; offset < len; offset += access_length) { @@ -651,9 +646,9 @@ static void riscv32_progbuf_mem_read( if (!result) return; #if 1 - /* Copy the read value from GPR A0 to DATA0 */ + /* Copy the read value from GPR A1 to DATA0 */ const uint32_t abstract_command2 = - RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A0; + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | (RV_GPR_A0 + 1); result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command2); result &= riscv_command_wait_complete(hart); if (!result) @@ -670,7 +665,7 @@ static void riscv32_progbuf_mem_read( } riscv_csr_write(hart, RV_GPR_A0, &a0_save); - //riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); + riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); } static void riscv32_progbuf_mem_write( From ab8ad9007aae94dcec8a4c9a0deb8685645ef381 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 15:55:35 +0300 Subject: [PATCH 165/247] riscv_debug: extract DM ProgBuf Base macro --- src/target/riscv32.c | 2 -- src/target/riscv_debug.c | 7 +++---- src/target/riscv_debug.h | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 5ce0bdeca3d..edf2bdd501b 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -578,8 +578,6 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } -#define RV_DM_PROGBUF_BASE 0x20U - #define RV_GPR_A0 0x100aU #define RV_EBREAK 0x00100073U diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index becfb584ddb..39587f81f81 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -51,10 +51,9 @@ * 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 -#define RV_DM_PROGBUF_BASE 0x20U +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU #define RV_DM_CTRL_ACTIVE (1U << 0U) #define RV_DM_CTRL_SYSTEM_RESET (1U << 1U) diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 4aec5432e2d..effc2e846ba 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -181,6 +181,7 @@ typedef struct riscv_hart { #define RV_DM_DATA3 0x07U #define RV_DM_ABST_CTRLSTATUS 0x16U #define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_PROGBUF_BASE 0x20U #define RV_DM_SYSBUS_CTRLSTATUS 0x38U #define RV_DM_SYSBUS_ADDR0 0x39U #define RV_DM_SYSBUS_ADDR1 0x3aU From e1d5f32a6c7f9a9a142f1b7c6b2869bcbd2790a6 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 16:50:27 +0300 Subject: [PATCH 166/247] riscv_debug: Extract macros for A0/A1 core registers --- src/target/riscv32.c | 2 -- src/target/riscv_debug.c | 8 -------- src/target/riscv_debug.h | 7 +++++++ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index edf2bdd501b..0ab148a3708 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -578,8 +578,6 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } -#define RV_GPR_A0 0x100aU - #define RV_EBREAK 0x00100073U static void riscv32_progbuf_mem_read( diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 39587f81f81..d1486ed6a23 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -110,14 +110,6 @@ /* tdata2 -> selected trigger configuration register 2 */ #define RV_TRIG_DATA_2 0x7a2U -/* - * GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O, - * as semihosting syscall number and result register per ABI - */ -#define RV_GPR_A0 0x100aU -/* GPR a1, aka x11, is used as semihosting argument */ -#define RV_GPR_A1 0x100bU - /* * Instructions for reading and writing CSRs through a0 * CSRR -> CSR Read, abuses the CSRRS atomic read and set bits instruction diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index effc2e846ba..879606cd98c 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -222,6 +222,13 @@ typedef struct riscv_hart { #define RV_FP_BASE 0x1020U /* The FP control base defines the starting register space address for the floating point modes and flags */ #define RV_FP_CTRL_BASE 0x001U +/* + * GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O, + * as semihosting syscall number and result register per ABI + */ +#define RV_GPR_A0 0x100aU +/* GPR a1, aka x11, is used as semihosting argument */ +#define RV_GPR_A1 0x100bU /** * The MXL (Machine XLEN) field encodes the native base integer ISA width From 8f580f261545d5f96b23f5d22d43935c25f1c37d Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 16:51:15 +0300 Subject: [PATCH 167/247] riscv_debug: Extract EBREAK instruction to common header --- src/target/riscv32.c | 2 -- src/target/riscv_debug.c | 1 - src/target/riscv_debug.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 0ab148a3708..aa2339ea915 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -578,8 +578,6 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } -#define RV_EBREAK 0x00100073U - static void riscv32_progbuf_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d1486ed6a23..1649b35c423 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -119,7 +119,6 @@ */ #define RV_CSRR_A0 0x00002573U #define RV_CSRW_A0 0x00051073U -#define RV_EBREAK 0x00100073U /* * A semihosting call is three consecutive uncompressed instructions: diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 879606cd98c..85b2ce52cce 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -230,6 +230,8 @@ typedef struct riscv_hart { /* GPR a1, aka x11, is used as semihosting argument */ #define RV_GPR_A1 0x100bU +#define RV_EBREAK 0x00100073U + /** * The MXL (Machine XLEN) field encodes the native base integer ISA width * From a39d3c41fe8f262af5b55de26a87e6db919186bc Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 16:58:39 +0300 Subject: [PATCH 168/247] riscv32: Use newly added RV_GPR_A1 --- src/target/riscv32.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index aa2339ea915..e5d38a5c6a5 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -624,7 +624,7 @@ static void riscv32_progbuf_mem_read( uint32_t a0_save = 0; uint32_t a1_save = 0; riscv_csr_read(hart, RV_GPR_A0, &a0_save); - riscv_csr_read(hart, RV_GPR_A0 + 1, &a1_save); + riscv_csr_read(hart, RV_GPR_A1, &a1_save); uint8_t *const data = (uint8_t *)dest; for (size_t offset = 0; offset < len; offset += access_length) { @@ -642,7 +642,7 @@ static void riscv32_progbuf_mem_read( #if 1 /* Copy the read value from GPR A1 to DATA0 */ const uint32_t abstract_command2 = - RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | (RV_GPR_A0 + 1); + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A1; result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command2); result &= riscv_command_wait_complete(hart); if (!result) @@ -659,7 +659,7 @@ static void riscv32_progbuf_mem_read( } riscv_csr_write(hart, RV_GPR_A0, &a0_save); - riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); + riscv_csr_write(hart, RV_GPR_A1, &a1_save); } static void riscv32_progbuf_mem_write( @@ -707,7 +707,7 @@ static void riscv32_progbuf_mem_write( uint32_t a0_save = 0; uint32_t a1_save = 0; riscv_csr_read(hart, RV_GPR_A0, &a0_save); - riscv_csr_read(hart, RV_GPR_A0 + 1, &a1_save); + riscv_csr_read(hart, RV_GPR_A1, &a1_save); const uint8_t *const data = (const uint8_t *)src; for (size_t offset = 0; offset < len; offset += access_length) { @@ -730,7 +730,7 @@ static void riscv32_progbuf_mem_write( /* Copy the write value from DATA0 to GPR A1 and launch the progbuf postexec */ result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | - (RV_GPR_A0 + 1)); + RV_GPR_A1); result &= riscv_command_wait_complete(hart); if (!result) return; @@ -738,7 +738,7 @@ static void riscv32_progbuf_mem_write( } riscv_csr_write(hart, RV_GPR_A0, &a0_save); - riscv_csr_write(hart, RV_GPR_A0 + 1, &a1_save); + riscv_csr_write(hart, RV_GPR_A1, &a1_save); } void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) From d46e00035fcbb4b020bd94615e50c86335442463 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 17:50:54 +0300 Subject: [PATCH 169/247] riscv32: Delegate to riscv_csr_read/write in riscv32_progbuf_mem_read/write for AAR --- src/target/riscv32.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index e5d38a5c6a5..ec96058b921 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -639,22 +639,10 @@ static void riscv32_progbuf_mem_read( result &= riscv_command_wait_complete(hart); if (!result) return; -#if 1 - /* Copy the read value from GPR A1 to DATA0 */ - const uint32_t abstract_command2 = - RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A1; - result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command2); - result &= riscv_command_wait_complete(hart); - if (!result) - return; - /* Extract the read value from DATA0 */ uint32_t value = 0; - if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + result = riscv_csr_read(hart, RV_GPR_A1, &value); + if (!result) return; -#else - uint32_t value = 0; - riscv_csr_read(hart, RV_GPR_A0, &value); -#endif riscv32_unpack_data(data + offset, value, access_width); } @@ -711,30 +699,22 @@ static void riscv32_progbuf_mem_write( const uint8_t *const data = (const uint8_t *)src; for (size_t offset = 0; offset < len; offset += access_length) { - /* Copy the destination address from DATA0 to GPR A0 */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, dest + offset)) - return; - /* Copy the source address from DATA0 to GPR A0 */ - const uint32_t abstract_command1 = - RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_REG_ACCESS_32_BIT | RV_GPR_A0; - bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, abstract_command1); - result &= riscv_command_wait_complete(hart); - if (!result) + /* Prepare the destination address in GPR A0 */ + const uint32_t dest_a0 = dest + offset; + if (!riscv_csr_write(hart, RV_GPR_A0, &dest_a0)) return; - //riscv_csr_write(hart, RV_GPR_A0, dest + offset); /* Pack the data to write into GPR A1 */ uint32_t value = riscv32_pack_data(data + offset, access_width); if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) return; /* Copy the write value from DATA0 to GPR A1 and launch the progbuf postexec */ - result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | RV_GPR_A1); result &= riscv_command_wait_complete(hart); if (!result) return; - //riscv_csr_write(hart, RV_GPR_A1, value); } riscv_csr_write(hart, RV_GPR_A0, &a0_save); From 1ea36f8686c51136c4ec75fe4443ef2bf9cb4165 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Mon, 13 Apr 2026 14:19:18 +0300 Subject: [PATCH 170/247] riscv32: Restore saved GPR A0,A1 on progbuf_mem errors in the loop --- src/target/riscv32.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index ec96058b921..618661894bd 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -630,7 +630,7 @@ static void riscv32_progbuf_mem_read( for (size_t offset = 0; offset < len; offset += access_length) { /* Write the source address to DATA0 */ if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, src + offset)) - return; + break; /* Copy the source address from DATA0 to GPR A0 and launch the progbuf postexec */ bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | @@ -638,11 +638,11 @@ static void riscv32_progbuf_mem_read( /* Wait for both the register write and progbuf execution to complete */ result &= riscv_command_wait_complete(hart); if (!result) - return; + break; uint32_t value = 0; result = riscv_csr_read(hart, RV_GPR_A1, &value); if (!result) - return; + break; riscv32_unpack_data(data + offset, value, access_width); } @@ -702,19 +702,19 @@ static void riscv32_progbuf_mem_write( /* Prepare the destination address in GPR A0 */ const uint32_t dest_a0 = dest + offset; if (!riscv_csr_write(hart, RV_GPR_A0, &dest_a0)) - return; + break; /* Pack the data to write into GPR A1 */ uint32_t value = riscv32_pack_data(data + offset, access_width); if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) - return; + break; /* Copy the write value from DATA0 to GPR A1 and launch the progbuf postexec */ bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | RV_GPR_A1); result &= riscv_command_wait_complete(hart); if (!result) - return; + break; } riscv_csr_write(hart, RV_GPR_A0, &a0_save); From a523f16a110d14fe60a77f79cc43ab13e51f12b5 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 12 Apr 2026 18:40:05 +0300 Subject: [PATCH 171/247] riscv32: Compose the progbuf read/write instructions via macros, drop arrays --- src/target/riscv32.c | 68 +++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 618661894bd..a4dca1bef86 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -63,6 +63,27 @@ #define RV32_MATCH_BEFORE 0x00000000U #define RV32_MATCH_AFTER 0x00040000U +/* Uncompressed instruction encodings for progbuf-based memory access */ +#define RV_LOAD_BYTE 0x00000003U +#define RV_LOAD_HALF 0x00001003U +#define RV_LOAD_WORD 0x00002003U +#define RV_STORE_BYTE 0x00000023U +#define RV_STORE_HALF 0x00001023U +#define RV_STORE_WORD 0x00002023U +/* Only GPR A0 & A1 are used for source/destination (and saved-restored) */ +#define RV_RS1_A0 0x00050000U +#define RV_RS2_A1 0x00b00000U +#define RV_RD_A0 0x00000500U +#define RV_RD_A1 0x00000580U +/* RV32I opcodes: load word/half/byte from address pointed-by a0 into a1 */ +#define RV_LW_A1_A0 (RV_LOAD_WORD | RV_RD_A1 | RV_RS1_A0) // 0x00052583U // lw a1, 0(a0) +#define RV_LH_A1_A0 (RV_LOAD_HALF | RV_RD_A1 | RV_RS1_A0) // 0x00051583U // lh a1, 0(a0) +#define RV_LB_A1_A0 (RV_LOAD_BYTE | RV_RD_A1 | RV_RS1_A0) // 0x00050583U // lb a1, 0(a0) +/* RV32I opcodes: store word/half/byte from a1 into address pointed-by a0 */ +#define RV_SW_A1_A0 (RV_STORE_WORD | RV_RS2_A1 | RV_RS1_A0) // 0x00b52023U // sw a1, 0(a0) +#define RV_SH_A1_A0 (RV_STORE_HALF | RV_RS2_A1 | RV_RS1_A0) // 0x00b51023U // sh a1, 0(a0) +#define RV_SB_A1_A0 (RV_STORE_BYTE | RV_RS2_A1 | RV_RS1_A0) // 0x00b50023U // sb a1, 0(a0) + static size_t riscv32_reg_read(target_s *target, uint32_t reg, void *data, size_t max); static size_t riscv32_reg_write(target_s *target, uint32_t reg, const void *data, size_t max); static void riscv32_regs_read(target_s *target, void *data); @@ -585,35 +606,22 @@ static void riscv32_progbuf_mem_read( const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; - /* RV32I opcodes: load word/half/byte from address A0 into A1 (clobber), postincrement A0 */ - static const uint32_t progbuf_read32[2] = { - 0x00052583U, // lw a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - static const uint32_t progbuf_read16[2] = { - 0x00051583U, // lh a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - static const uint32_t progbuf_read8[2] = { - 0x00050583U, // lb a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - const uint32_t *progbuf_read = progbuf_read32; + uint32_t progbuf_read = RV_LOAD_BYTE; switch (access_width) { case RV_MEM_ACCESS_8_BIT: - progbuf_read = progbuf_read8; + progbuf_read = RV_LB_A1_A0; break; case RV_MEM_ACCESS_16_BIT: - progbuf_read = progbuf_read16; + progbuf_read = RV_LH_A1_A0; break; case RV_MEM_ACCESS_32_BIT: - progbuf_read = progbuf_read32; + progbuf_read = RV_LW_A1_A0; break; default: return; } /* Fill the program buffer */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_read[0])) + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_read)) return; /* Append literal ebreak (if impebreak is not reached) */ if (hart->progbuf_size > 1U) { @@ -656,35 +664,23 @@ static void riscv32_progbuf_mem_write( /* 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; - /* RV32I opcodes: store word/half/byte in a1 into address pointed-by a0 */ - static const uint32_t progbuf_write32[] = { - 0x00b52023U, // sw a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - static const uint32_t progbuf_write16[] = { - 0x00b51023U, // sh a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - static const uint32_t progbuf_write8[] = { - 0x00b50023U, // sb a1, 0(a0) - 0x00450513U, // addi a0, a0, 4 - }; - const uint32_t *progbuf_write = progbuf_write32; + + uint32_t progbuf_write = RV_STORE_BYTE; switch (access_width) { case RV_MEM_ACCESS_8_BIT: - progbuf_write = progbuf_write8; + progbuf_write = RV_SB_A1_A0; break; case RV_MEM_ACCESS_16_BIT: - progbuf_write = progbuf_write16; + progbuf_write = RV_SH_A1_A0; break; case RV_MEM_ACCESS_32_BIT: - progbuf_write = progbuf_write32; + progbuf_write = RV_SW_A1_A0; break; default: return; } /* Fill the program buffer */ - if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_write[0])) + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_write)) return; /* Append literal ebreak (if impebreak is not reached) */ if (hart->progbuf_size > 1U) { From d16b5e0f8655db1729ccc2a2a9933ec3039ad4cf Mon Sep 17 00:00:00 2001 From: ALTracer Date: Tue, 14 Apr 2026 22:27:01 +0300 Subject: [PATCH 172/247] hosted/remote/protocol_v4_riscv: Adjust DMI idle cycles for remote "Too soon" responses --- src/platforms/hosted/remote/protocol_v4_riscv.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/platforms/hosted/remote/protocol_v4_riscv.c b/src/platforms/hosted/remote/protocol_v4_riscv.c index 17642d4f148..c061318a698 100644 --- a/src/platforms/hosted/remote/protocol_v4_riscv.c +++ b/src/platforms/hosted/remote/protocol_v4_riscv.c @@ -51,8 +51,15 @@ bool remote_v4_riscv_check_error( const uint64_t response_code = remote_decode_response(buffer + 1, (size_t)length - 1U); const uint8_t error = response_code & 0xffU; /* If the error part of the response code indicates a fault, store the fault value */ - if (error == REMOTE_ERROR_FAULT) + if (error == REMOTE_ERROR_FAULT) { dmi->fault = response_code >> 8U; + /* + * If we got RV_DMI_TOO_SOON and we're under 8 idle cycles, increase the number + * of idle cycles used to compensate and have the outer code re-run the transfers + */ + if (dmi->fault == RV_DMI_TOO_SOON && dmi->idle_cycles < 8U) + ++dmi->idle_cycles; + } /* If the error part indicates an exception had occurred, make that happen here too */ else if (error == REMOTE_ERROR_EXCEPTION) raise_exception(response_code >> 8U, "Remote protocol exception"); @@ -65,6 +72,8 @@ bool remote_v4_riscv_check_error( /* Check if the firmware is reporting some other kind of error */ else if (buffer[0U] != REMOTE_RESP_OK) DEBUG_ERROR("%s: Firmware reported unexpected error: %c\n", func, buffer[0]); + else + dmi->fault = RV_DMI_SUCCESS; /* Return whether the remote indicated the request was successful */ return buffer[0U] == REMOTE_RESP_OK; } From a3f2ad1fe0171b155ba6ed8c7577fd2d7d9ddf19 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Tue, 14 Apr 2026 23:45:01 +0300 Subject: [PATCH 173/247] riscv_debug: Preserve already set hart flags when enabling SysBus --- 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 1649b35c423..d797e8052ba 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -1024,7 +1024,7 @@ static void riscv_hart_memory_access_type(target_s *const target) !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) return; /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ - hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + hart->flags |= RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); /* System Bus also means the target can have memory read without halting */ target->target_options |= TOPT_NON_HALTING_MEM_IO; /* Make sure the system bus is not in any kind of error state */ From 742c62c0199b2686ae51338d778875a9f5ac6a9d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Fri, 17 Apr 2026 04:03:30 +0100 Subject: [PATCH 174/247] hosted/ftdi_bmp: Added the requisite board definition for the ECP5-5G Versa so we can see and use its FTDI interface properly --- src/platforms/hosted/ftdi_bmp.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index b9915866df4..f2b78fa8f90 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -395,10 +395,29 @@ const cable_desc_s cable_desc[] = { .vendor = 0x15baU, .product = 0x0003U, .interface = INTERFACE_A, + .init.data[0] = 0U, .init.dirs[0] = PIN4, .description = "Olimex OpenOCD JTAG", .name = "arm-usb-ocd", }, + { + /* + * https://www.latticesemi.com/view_document?document_id=51396 + * This interface is JTAG-only. + * DBUS 4 is an activity indicator + * MPSSE_SK (DB0) ----------- TCK + * MPSSE-DO (DB1) ----------- TDI + * MPSSE-DI (DB2) ----------- TDO + * MPSSE-CS (DB3) ----------- TMS + */ + .vendor = 0x0403U, + .product = 0x6010U, + .interface = INTERFACE_A, + .init.data[0] = 0U, + .init.dirs[0] = PIN4, + .description = "Lattice ECP5_5G VERSA Board", + .name = "ecp5-versa", + }, {0}, }; From ee3e0b832d833241ed9c23712a9b2d6cc658cca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Sundstr=C3=B6m?= Date: Mon, 9 Feb 2026 15:10:25 +0100 Subject: [PATCH 175/247] misc: Support for STM32401CD Blackpill as debug probe --- cross-file/blackpill-f401cd.ini | 24 ++++++++++++++ meson_options.txt | 1 + src/platforms/blackpill-f401cd/README.md | 4 +++ src/platforms/blackpill-f401cd/platform.h | 31 +++++++++++++++++++ .../common/blackpill-f4/blackpill-f401cd.ld | 30 ++++++++++++++++++ src/platforms/meson.build | 1 + 6 files changed, 91 insertions(+) create mode 100644 cross-file/blackpill-f401cd.ini create mode 100644 src/platforms/blackpill-f401cd/README.md create mode 100644 src/platforms/blackpill-f401cd/platform.h create mode 100644 src/platforms/common/blackpill-f4/blackpill-f401cd.ld diff --git a/cross-file/blackpill-f401cd.ini b/cross-file/blackpill-f401cd.ini new file mode 100644 index 00000000000..bc286c80245 --- /dev/null +++ b/cross-file/blackpill-f401cd.ini @@ -0,0 +1,24 @@ +# This a cross-file for the blackpill-f401cd probe, providing sane default options for it. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'blackpill-f401cd' +targets = 'cortexar,cortexm,riscv32,riscv64,apollo3,at32f4,ch32,ch579,efm,gd32,hc32,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' +rtt_support = true +bmd_bootloader = true diff --git a/meson_options.txt b/meson_options.txt index 5f4e5253a03..2937112e7d7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,6 +5,7 @@ option( '', '96b_carbon', 'blackpill-f401cc', + 'blackpill-f401cd', 'blackpill-f401ce', 'blackpill-f411ce', 'bluepill', diff --git a/src/platforms/blackpill-f401cd/README.md b/src/platforms/blackpill-f401cd/README.md new file mode 100644 index 00000000000..fa4e2106eb8 --- /dev/null +++ b/src/platforms/blackpill-f401cd/README.md @@ -0,0 +1,4 @@ +This platform supports the Black Pill F401CD, with STM32F401CD. + +Detailed information about this platform can be found found in the [readme of the common blackpill-f4 code](./../common/blackpill-f4/README.md). + diff --git a/src/platforms/blackpill-f401cd/platform.h b/src/platforms/blackpill-f401cd/platform.h new file mode 100644 index 00000000000..34c767c3dbc --- /dev/null +++ b/src/platforms/blackpill-f401cd/platform.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file provides the platform specific declarations for the blackpill-f401cd implementation. */ + +#ifndef PLATFORMS_BLACKPILL_F401CD_PLATFORM_H +#define PLATFORMS_BLACKPILL_F401CD_PLATFORM_H + +#define PLATFORM_IDENT "(BlackPill-F401CD) " +#define PLATFORM_CLOCK_FREQ RCC_CLOCK_3V3_84MHZ + +#include "blackpill-f4.h" + +#endif /* PLATFORMS_BLACKPILL_F401CD_PLATFORM_H */ diff --git a/src/platforms/common/blackpill-f4/blackpill-f401cd.ld b/src/platforms/common/blackpill-f4/blackpill-f401cd.ld new file mode 100644 index 00000000000..ea032a38af9 --- /dev/null +++ b/src/platforms/common/blackpill-f4/blackpill-f401cd.ld @@ -0,0 +1,30 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * Copyright (C) 2024 1BitSquared + * Modified by Rachel Mant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 384K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K +} + +/* Include the platform common linker script. */ +INCLUDE ../blackmagic.ld diff --git a/src/platforms/meson.build b/src/platforms/meson.build index 5232c208ca8..3e4e6210127 100644 --- a/src/platforms/meson.build +++ b/src/platforms/meson.build @@ -44,6 +44,7 @@ subdir('common/tm4c') # This is used to allow multiple probes share the same source code probe_alias_map = { 'blackpill-f401cc': 'common/blackpill-f4', + 'blackpill-f401cd': 'common/blackpill-f4', 'blackpill-f401ce': 'common/blackpill-f4', 'blackpill-f411ce': 'common/blackpill-f4', 'bluepill': 'stlink', From cb7f8f4b84f88fa7dd82b52c01e6d2771b5abd5e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 22:34:34 +0100 Subject: [PATCH 176/247] common/stm32/timing_stm32: Removed the auto-tx-rx-switch logic for the second UART as it's interfereing with detecting which UART should be enabled and working --- src/platforms/common/stm32/timing_stm32.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 702bb210a49..0dc25ed305d 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -152,15 +152,8 @@ void sys_tick_handler(void) #endif #ifdef PLATFORM_MULTI_UART - /* Only do the toggling if the UART is not currently enabled */ - if (!platform_is_uart2_enabled()) { - /* Every 10th tick, swap the direction of the UART */ - if (++uart_ticks == 10U) { - platform_switch_dir_uart2(); - /* And reset the counter back to 0 */ - uart_ticks = 0U; - } - } else { + /* If one of the UARTs is presently enabled but we loose lock, consider disabling it */ + if (platform_is_uart2_enabled()) { /* * If the UART goes into framing error and that persists for more than a milisecond or two, then * it's probably safe to assume that the wires became disconnected and the UART is no longer active From 5b5a75bbfe9884890e84cba0970b99461fe33503 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 22:36:10 +0100 Subject: [PATCH 177/247] platform_support: Renamed the functions the timing and serial logic use to control the UART state handling/tracking ahead of fixing things up --- src/include/platform_support.h | 8 ++++---- src/platforms/bmp-v3/platform.c | 20 ++++++++++---------- src/platforms/common/aux_serial.c | 3 +-- src/platforms/common/stm32/timing_stm32.c | 6 +++--- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/include/platform_support.h b/src/include/platform_support.h index c417fce5e2c..2360b0d918d 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -92,11 +92,11 @@ typedef enum uart_state { } uart_state_e; void platform_enable_uart2(void); -void platform_disable_uart2(void); -bool platform_is_uart2_enabled(void); +void platform_disable_uart(void); +bool platform_are_uarts_enabled(void); void platform_switch_dir_uart2(void); -void platform_uart2_state_change(uint32_t state); -uart_state_e platform_uart2_state(void); +void platform_uart_state_change(uint32_t state); +uart_state_e platform_uart_state(void); #endif #endif /* INCLUDE_PLATFORM_SUPPORT_H */ diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 2a9ccd92e12..b47389bb95d 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -59,7 +59,7 @@ static void adc_init(void); int hwversion = -1; -static uart_state_e uart2_state = UART_STATE_UNKNOWN; +static uart_state_e uart_state = UART_STATE_UNKNOWN; void platform_init(void) { @@ -454,18 +454,18 @@ void platform_enable_uart2(void) usart_enable(AUX_UART2); } -void platform_disable_uart2(void) +void platform_disable_uart(void) { /* Disable the UART (so we can go back into being able to change the pin swapping) */ usart_disable(AUX_UART2); - uart2_state = UART_STATE_UNKNOWN; + uart_state = UART_STATE_UNKNOWN; /* Reconfigure the GPIOs back to inputs so we can listen for which is high to watch for new connections */ gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); } -bool platform_is_uart2_enabled(void) +bool platform_are_uarts_enabled(void) { - return (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; + return (USART_CR1(AUX_UART1) & USART_CR1_UE) != 0U || (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; } void platform_switch_dir_uart2(void) @@ -474,16 +474,16 @@ void platform_switch_dir_uart2(void) gpio_toggle(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); } -void platform_uart2_state_change(const uint32_t state) +void platform_uart_state_change(const uint32_t state) { /* Make a note of whether either Idle or Framing Error have occured */ if (state & USART_ISR_IDLE) - uart2_state = UART_STATE_IDLE; + uart_state = UART_STATE_IDLE; else if (state & USART_ISR_FE) - uart2_state = UART_STATE_LOST; + uart_state = UART_STATE_LOST; } -uart_state_e platform_uart2_state(void) +uart_state_e platform_uart_state(void) { - return uart2_state; + return uart_state; } diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index deb2a135c84..9d8b4f5e606 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -634,8 +634,7 @@ static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) } #ifdef PLATFORM_MULTI_UART - if (uart == AUX_UART2) - platform_uart2_state_change(status); + platform_uart_state_change(status); #endif #ifndef STM32U5 nvic_enable_irq(dma_irq); diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 0dc25ed305d..52e5fd71a0b 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -153,17 +153,17 @@ void sys_tick_handler(void) #ifdef PLATFORM_MULTI_UART /* If one of the UARTs is presently enabled but we loose lock, consider disabling it */ - if (platform_is_uart2_enabled()) { + if (platform_are_uarts_enabled()) { /* * If the UART goes into framing error and that persists for more than a milisecond or two, then * it's probably safe to assume that the wires became disconnected and the UART is no longer active * in which case we then want to disable the UART and go back into swap scanning. Additionally, we'll * want to either make the other UART active, or make all UARTs inactive. */ - const uart_state_e state = platform_uart2_state(); + const uart_state_e state = platform_uart_state(); if (state == UART_STATE_LOST) { if (++uart_ticks == 2U) { - platform_disable_uart2(); + platform_disable_uart(); uart_ticks = 0U; } } else From 5b38f1c8234dc722195a2509d54452a611c1cab4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 23:38:31 +0100 Subject: [PATCH 178/247] bmp-v3: Properly handle disabling either UART with the platform function for disabling them --- src/platforms/bmp-v3/platform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index b47389bb95d..960c2dc977d 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -457,7 +457,10 @@ void platform_enable_uart2(void) void platform_disable_uart(void) { /* Disable the UART (so we can go back into being able to change the pin swapping) */ - usart_disable(AUX_UART2); + if ((USART_CR1(AUX_UART1) & USART_CR1_UE) != 0U) + usart_disable(AUX_UART1); + else if ((USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U) + usart_disable(AUX_UART2); uart_state = UART_STATE_UNKNOWN; /* Reconfigure the GPIOs back to inputs so we can listen for which is high to watch for new connections */ gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); From 2bc4dab2c0dc222f29d2cdff657ec6a880cf5c1f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 23:39:37 +0100 Subject: [PATCH 179/247] command: Moved the declaration of the platform command list into the target internal header so it can be kept in sync between the platforms that make use of it, and the command code --- src/command.c | 4 ---- src/target/target_internal.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/command.c b/src/command.c index c86dedb34f7..e9adcebcc5c 100644 --- a/src/command.c +++ b/src/command.c @@ -153,10 +153,6 @@ const command_s cmd_list[] = { {NULL, NULL, NULL}, }; -#ifdef PLATFORM_HAS_CUSTOM_COMMANDS -extern const command_s platform_cmd_list[]; -#endif - bool connect_assert_nrst; #if defined(PLATFORM_HAS_DEBUG) && CONFIG_BMDA == 0 bool debug_bmp; diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 4b56cdaa245..33d23debcb3 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -201,6 +201,10 @@ struct target { uint16_t part_id; }; +#ifdef PLATFORM_HAS_CUSTOM_COMMANDS +extern const command_s platform_cmd_list[]; +#endif + void target_print_progress(platform_timeout_s *timeout); void target_ram_map_free(target_s *target); void target_flash_map_free(target_s *target); From edfff63c337a4e46489be54e21d67e530c709c63 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 23:40:03 +0100 Subject: [PATCH 180/247] command: Fixed some `clang-tidy` diagnostics in the command header --- src/include/command.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/command.h b/src/include/command.h index 311324ed7fd..fab346e828c 100644 --- a/src/include/command.h +++ b/src/include/command.h @@ -25,7 +25,7 @@ #include "target.h" -int command_process(target_s *t, char *cmd_buffer); +int command_process(target_s *target, char *cmd_buffer); /* * Attempts to parse a string as either being "enable" or "disable". @@ -33,7 +33,7 @@ int command_process(target_s *t, char *cmd_buffer); * indicate what was parsed. If not successful, emits a warning to the * gdb port, returns false and leaves out untouched. */ -bool parse_enable_or_disable(const char *s, bool *out); +bool parse_enable_or_disable(const char *value, bool *out); #if CONFIG_BMDA == 1 extern bool shutdown_bmda; From 566501c0528451111762807729a1da79f286fb42 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Mon, 13 Apr 2026 23:48:51 +0100 Subject: [PATCH 181/247] bmp-v3: Implemented a command for setting the BMDU connector's UART pin swapping state --- src/platforms/bmp-v3/platform.c | 40 +++++++++++++++++++++++++++++++++ src/platforms/bmp-v3/platform.h | 1 + 2 files changed, 41 insertions(+) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index 960c2dc977d..c51a34eed25 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -36,6 +36,9 @@ #include "usb.h" #include "rcc_clocking.h" #include "aux_serial.h" +#include "command.h" +#include "gdb_packet.h" +#include "target_internal.h" #include #include @@ -61,6 +64,13 @@ int hwversion = -1; static uart_state_e uart_state = UART_STATE_UNKNOWN; +static bool cmd_switched_txrx(target_s *target, int argc, const char **argv); + +const command_s platform_cmd_list[] = { + {"switched_txrx", cmd_switched_txrx, "Switched TX/RX on BMDU connector: [enable|disable]"}, + {NULL, NULL, NULL}, +}; + void platform_init(void) { hwversion = 0; @@ -490,3 +500,33 @@ uart_state_e platform_uart_state(void) { return uart_state; } + +static bool cmd_switched_txrx(target_s *target, int argc, const char **argv) +{ + (void)target; + /* If invoked without arguments, display the state of the switching */ + if (argc == 1) { + gdb_outf("BMDU connector UART pin swapping %s\n", + gpio_get(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN) ? "disabled" : "enabled"); + return true; + } + if (argc == 2) { + bool swap_pins = false; + /* Try and parse the state to put the pins into, failing if we can't */ + if (!parse_enable_or_disable(argv[1], &swap_pins)) + return false; + /* Check and see if the UART is presently enabled, disabling it if it is */ + const bool uart2_state = (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; + if (uart2_state) + usart_disable(AUX_UART2); + /* Set the pin swapping up */ + gpio_set_val(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN, !swap_pins); + usart_set_swap_tx_rx(AUX_UART2, swap_pins); + /* If the UART was previously enabled, re-enable it */ + if (uart2_state) + usart_enable(AUX_UART2); + return true; + } + gdb_out("Unrecognized command format\n"); + return false; +} diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h index 2641d4ea419..9e4fc58ffc1 100644 --- a/src/platforms/bmp-v3/platform.h +++ b/src/platforms/bmp-v3/platform.h @@ -43,6 +43,7 @@ #define PLATFORM_HAS_TRACESWO #define PLATFORM_HAS_POWER_SWITCH #define PLATFORM_MULTI_UART +#define PLATFORM_HAS_CUSTOM_COMMANDS #define PLATFORM_IDENT "v3 " From 5f2c9f78514e3722d3851789a3065320c7aaa386 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 14 Apr 2026 01:46:02 +0100 Subject: [PATCH 182/247] common/aux_serial: Switched the UART enable scheme for the second back to match the first --- src/platforms/common/aux_serial.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 9d8b4f5e606..6dc739d3929 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -367,7 +367,7 @@ void aux_serial_init(void) usart_enable_rx_dma(USBUSART); #else usart_enable(AUX_UART1); - /* Don't enable UART2 though because it has switchable TX/RX and must be handled differently */ + usart_enable(AUX_UART2); usart_enable_tx_dma(AUX_UART1); usart_enable_rx_dma(AUX_UART1); usart_enable_tx_dma(AUX_UART2); @@ -822,7 +822,6 @@ void aux_uart2_rx_detect_isr(void) * UART2 just became active, so bring it up and disable the EXTI for it, making sure UART1's is * active in case the user swaps UARTs over */ - platform_enable_uart2(); aux_serial_activate_uart(AUX_UART2); exti_reset_request(AUX_UART2_RX_DETECT_EXTI); exti_disable_request(AUX_UART2_RX_DETECT_EXTI); From e276eb4ee6aa6acda65a6da144c33d7275a430a2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 14 Apr 2026 01:46:49 +0100 Subject: [PATCH 183/247] bmp-v3: Pruned the now unused functions for UART control and cleaned up in the initialisation logic --- src/include/platform_support.h | 2 -- src/platforms/bmp-v3/platform.c | 23 +---------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/include/platform_support.h b/src/include/platform_support.h index 2360b0d918d..0e787847c2e 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -91,10 +91,8 @@ typedef enum uart_state { UART_STATE_LOST, } uart_state_e; -void platform_enable_uart2(void); void platform_disable_uart(void); bool platform_are_uarts_enabled(void); -void platform_switch_dir_uart2(void); void platform_uart_state_change(uint32_t state); uart_state_e platform_uart_state(void); #endif diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index c51a34eed25..f5e7c4b5356 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -163,7 +163,7 @@ static void gpio_init(void) /* Configure the second UART used for the AUX serial interface */ gpio_set_af(AUX_UART2_PORT, GPIO_AF7, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); gpio_set_output_options(AUX_UART2_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100MHZ, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); /* Start with the pins configured the "correct" way around (unswapped) */ gpio_set(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); gpio_set_output_options(AUX_UART2_DIR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, AUX_UART2_DIR_PIN); @@ -451,19 +451,6 @@ uint8_t platform_spi_xfer(const spi_bus_e bus, const uint8_t value) return spi_xfer8(EXT_SPI, value); } -void platform_enable_uart2(void) -{ - /* Reconfigure the GPIOs to connect to the UART */ - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); - /* Use the current direction setting to determine how to enable the UART */ - if (gpio_get(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN)) - usart_set_swap_tx_rx(AUX_UART2, false); - else - usart_set_swap_tx_rx(AUX_UART2, true); - /* Now pin swapping is configured, enable the UART */ - usart_enable(AUX_UART2); -} - void platform_disable_uart(void) { /* Disable the UART (so we can go back into being able to change the pin swapping) */ @@ -472,8 +459,6 @@ void platform_disable_uart(void) else if ((USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U) usart_disable(AUX_UART2); uart_state = UART_STATE_UNKNOWN; - /* Reconfigure the GPIOs back to inputs so we can listen for which is high to watch for new connections */ - gpio_mode_setup(AUX_UART2_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); } bool platform_are_uarts_enabled(void) @@ -481,12 +466,6 @@ bool platform_are_uarts_enabled(void) return (USART_CR1(AUX_UART1) & USART_CR1_UE) != 0U || (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; } -void platform_switch_dir_uart2(void) -{ - /* Swap the directions of the BMPU connector UART pins */ - gpio_toggle(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); -} - void platform_uart_state_change(const uint32_t state) { /* Make a note of whether either Idle or Framing Error have occured */ From f3f04ab43b1dbceb9e86b191b1ed59f3a6108835 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Tue, 14 Apr 2026 01:56:18 +0100 Subject: [PATCH 184/247] command: Fixed a `clang-tidy` lint and a capitalisation error in the main command table --- src/command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/command.c b/src/command.c index e9adcebcc5c..d280ddd4a44 100644 --- a/src/command.c +++ b/src/command.c @@ -4,7 +4,7 @@ * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin * Copyright (C) 2021 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) - * Copyright (C) 2023-2025 1BitSquared + * Copyright (C) 2023-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -100,7 +100,7 @@ static bool cmd_shutdown_bmda(target_s *target, int argc, const char **argv); #define strtok_r strtok_s #endif -const command_s cmd_list[] = { +static const command_s cmd_list[] = { {"version", cmd_version, "Display firmware version info"}, {"help", cmd_help, "Display help for monitor commands"}, {"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"}, @@ -111,7 +111,7 @@ const command_s cmd_list[] = { #endif {"spi_scan", cmd_onboard_flash_scan, "Scan for on-board SPI Flash devices"}, {"auto_scan", cmd_auto_scan, "Automatically scan all chain types for devices"}, - {"frequency", cmd_frequency, "set minimum high and low times: [FREQ]"}, + {"frequency", cmd_frequency, "Set minimum high and low times: [FREQ]"}, {"targets", cmd_targets, "Display list of available targets"}, {"morse", cmd_morse, "Display morse error message"}, {"halt_timeout", cmd_halt_timeout, "Timeout to wait until Cortex-M is halted: [TIMEOUT, default 2000ms]"}, From dce31412f607db880d73536e7894b4b89247044a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 15 Apr 2026 02:37:11 +0100 Subject: [PATCH 185/247] common/aux_serial: Moved the UART state tracking into the main UART implementation where it belongs, simplifying and finishing correcting the UART handling for BMPv3 --- src/include/platform_support.h | 15 +------- src/platforms/bmp-v3/platform.c | 44 +++-------------------- src/platforms/common/aux_serial.c | 22 ++++++++++-- src/platforms/common/aux_serial.h | 14 +++++++- src/platforms/common/stm32/timing_stm32.c | 33 ++++++++--------- 5 files changed, 54 insertions(+), 74 deletions(-) diff --git a/src/include/platform_support.h b/src/include/platform_support.h index 0e787847c2e..a4b4ff6eb7c 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -2,7 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015 Gareth McMullin - * Copyright (C) 2022-2025 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -84,17 +84,4 @@ uint8_t platform_spi_xfer(spi_bus_e bus, uint8_t value); const char *platform_ident(void); #endif -#ifdef PLATFORM_MULTI_UART -typedef enum uart_state { - UART_STATE_UNKNOWN, - UART_STATE_IDLE, - UART_STATE_LOST, -} uart_state_e; - -void platform_disable_uart(void); -bool platform_are_uarts_enabled(void); -void platform_uart_state_change(uint32_t state); -uart_state_e platform_uart_state(void); -#endif - #endif /* INCLUDE_PLATFORM_SUPPORT_H */ diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index f5e7c4b5356..b0658629859 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2025 1BitSquared + * Copyright (C) 2025-2026 1BitSquared * Written by Rachel Mant * All rights reserved. * @@ -62,8 +62,6 @@ static void adc_init(void); int hwversion = -1; -static uart_state_e uart_state = UART_STATE_UNKNOWN; - static bool cmd_switched_txrx(target_s *target, int argc, const char **argv); const command_s platform_cmd_list[] = { @@ -451,35 +449,6 @@ uint8_t platform_spi_xfer(const spi_bus_e bus, const uint8_t value) return spi_xfer8(EXT_SPI, value); } -void platform_disable_uart(void) -{ - /* Disable the UART (so we can go back into being able to change the pin swapping) */ - if ((USART_CR1(AUX_UART1) & USART_CR1_UE) != 0U) - usart_disable(AUX_UART1); - else if ((USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U) - usart_disable(AUX_UART2); - uart_state = UART_STATE_UNKNOWN; -} - -bool platform_are_uarts_enabled(void) -{ - return (USART_CR1(AUX_UART1) & USART_CR1_UE) != 0U || (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; -} - -void platform_uart_state_change(const uint32_t state) -{ - /* Make a note of whether either Idle or Framing Error have occured */ - if (state & USART_ISR_IDLE) - uart_state = UART_STATE_IDLE; - else if (state & USART_ISR_FE) - uart_state = UART_STATE_LOST; -} - -uart_state_e platform_uart_state(void) -{ - return uart_state; -} - static bool cmd_switched_txrx(target_s *target, int argc, const char **argv) { (void)target; @@ -494,16 +463,13 @@ static bool cmd_switched_txrx(target_s *target, int argc, const char **argv) /* Try and parse the state to put the pins into, failing if we can't */ if (!parse_enable_or_disable(argv[1], &swap_pins)) return false; - /* Check and see if the UART is presently enabled, disabling it if it is */ - const bool uart2_state = (USART_CR1(AUX_UART2) & USART_CR1_UE) != 0U; - if (uart2_state) - usart_disable(AUX_UART2); + /* Disable the UART so we can alter the pin swapping settings */ + usart_disable(AUX_UART2); /* Set the pin swapping up */ gpio_set_val(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN, !swap_pins); usart_set_swap_tx_rx(AUX_UART2, swap_pins); - /* If the UART was previously enabled, re-enable it */ - if (uart2_state) - usart_enable(AUX_UART2); + /* Re-enable it now that configuration is updated */ + usart_enable(AUX_UART2); return true; } gdb_out("Unrecognized command format\n"); diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 6dc739d3929..338de2137c6 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -101,6 +101,9 @@ static char aux_serial_transmit_buffer[AUX_UART_BUFFER_SIZE]; #else static uintptr_t active_uart = 0U; #define AUX_UART active_uart + +/* UART state tracking so we can properly handle when a UART goes into error and might need disengaging from */ +static uart_state_e uart_state = UART_STATE_UNKNOWN; #endif #ifdef STM32U5 @@ -166,7 +169,7 @@ void aux_serial_uart_init(const uintptr_t uart_base) } #ifdef PLATFORM_MULTI_UART -void aux_serial_activate_uart(const uintptr_t uart_base) +static void aux_serial_activate_uart(const uintptr_t uart_base) { dma_set_destination_address(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, (uintptr_t)&USART_TDR(uart_base)); if (uart_base == AUX_UART1) @@ -184,6 +187,17 @@ void aux_serial_activate_uart(const uintptr_t uart_base) active_uart = uart_base; } + +void aux_serial_deactivate_uart(void) +{ + active_uart = 0U; + uart_state = UART_STATE_UNKNOWN; +} + +uart_state_e aux_serial_uart_state(void) +{ + return uart_state; +} #endif void aux_serial_init(void) @@ -634,7 +648,11 @@ static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) } #ifdef PLATFORM_MULTI_UART - platform_uart_state_change(status); + /* Make a note of whether either Idle or Framing Error have occured */ + if (status & USART_ISR_IDLE) + uart_state = UART_STATE_IDLE; + else if (status & USART_ISR_FE) + uart_state = UART_STATE_LOST; #endif #ifndef STM32U5 nvic_enable_irq(dma_irq); diff --git a/src/platforms/common/aux_serial.h b/src/platforms/common/aux_serial.h index 3fe2cdefe89..3827030bab9 100644 --- a/src/platforms/common/aux_serial.h +++ b/src/platforms/common/aux_serial.h @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #ifndef PLATFORMS_COMMON_AUX_SERIAL_H #define PLATFORMS_COMMON_AUX_SERIAL_H +#include "general.h" #include #include #include @@ -78,4 +79,15 @@ void aux_serial_stage_debug_buffer(void); void aux_serial_stage_receive_buffer(void); #endif +#ifdef PLATFORM_MULTI_UART +typedef enum uart_state { + UART_STATE_UNKNOWN, + UART_STATE_IDLE, + UART_STATE_LOST, +} uart_state_e; + +uart_state_e aux_serial_uart_state(void); +void aux_serial_deactivate_uart(void); +#endif + #endif /* PLATFORMS_COMMON_AUX_SERIAL_H */ diff --git a/src/platforms/common/stm32/timing_stm32.c b/src/platforms/common/stm32/timing_stm32.c index 52e5fd71a0b..3a4e70377e7 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -2,7 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015 Gareth McMullin - * Copyright (C) 2023 1BitSquared + * Copyright (C) 2023-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "platform.h" #include "morse.h" #include "usb.h" +#include "aux_serial.h" #include #include @@ -152,24 +153,20 @@ void sys_tick_handler(void) #endif #ifdef PLATFORM_MULTI_UART - /* If one of the UARTs is presently enabled but we loose lock, consider disabling it */ - if (platform_are_uarts_enabled()) { - /* - * If the UART goes into framing error and that persists for more than a milisecond or two, then - * it's probably safe to assume that the wires became disconnected and the UART is no longer active - * in which case we then want to disable the UART and go back into swap scanning. Additionally, we'll - * want to either make the other UART active, or make all UARTs inactive. - */ - const uart_state_e state = platform_uart_state(); - if (state == UART_STATE_LOST) { - if (++uart_ticks == 2U) { - platform_disable_uart(); - uart_ticks = 0U; - } - } else - /* Otherwise if the UART state is either not known or the UART is idle, reset the tick counter */ + /* + * If a UART goes into framing error and that persists for more than a milisecond or two, then + * it's probably safe to assume that the wires became disconnected and the UART is no longer active + * in which case we then want to disable that UART and go back to waiting for a UART to become active. + */ + const uart_state_e state = aux_serial_uart_state(); + if (state == UART_STATE_LOST) { + if (++uart_ticks == 2U) { + aux_serial_deactivate_uart(); uart_ticks = 0U; - } + } + } else + /* Otherwise if the UART state is either not known or the UART is idle, reset the tick counter */ + uart_ticks = 0U; #endif } From 92f664e51eb2db873b3f7c85131e03091e9c7646 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Wed, 15 Apr 2026 02:39:32 +0100 Subject: [PATCH 186/247] common/aux_serial: Protect the state change logic from spurious changes from the inactive UART --- src/platforms/common/aux_serial.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 338de2137c6..a277165cfb5 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -648,11 +648,14 @@ static void aux_serial_receive_isr(const uintptr_t uart, const uint8_t dma_irq) } #ifdef PLATFORM_MULTI_UART - /* Make a note of whether either Idle or Framing Error have occured */ - if (status & USART_ISR_IDLE) - uart_state = UART_STATE_IDLE; - else if (status & USART_ISR_FE) - uart_state = UART_STATE_LOST; + /* If this is a state change on the active UART */ + if (active_uart == uart) { + /* Make a note of whether either Idle or Framing Error have occured */ + if (status & USART_ISR_IDLE) + uart_state = UART_STATE_IDLE; + else if (status & USART_ISR_FE) + uart_state = UART_STATE_LOST; + } #endif #ifndef STM32U5 nvic_enable_irq(dma_irq); From 731f0106481c289beed85842449a4072a48303a7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 19 Apr 2026 13:53:40 +0100 Subject: [PATCH 187/247] common/usb_serial: Documented how `debug_serial_debug_write()` works --- src/platforms/common/usb_serial.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index cf132dfd7cf..35088fa0256 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -346,15 +346,20 @@ static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep) } #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) -static void debug_serial_append_char(const char c) +static void debug_serial_append_char(const char ch) { - debug_serial_debug_buffer[debug_serial_debug_write_index] = c; + debug_serial_debug_buffer[debug_serial_debug_write_index] = ch; ++debug_serial_debug_write_index; debug_serial_debug_write_index %= AUX_UART_BUFFER_SIZE; } size_t debug_serial_debug_write(const char *buf, const size_t len) { + /* + * Gate actually writing the byte if there are pending USB or UART interrupts + * XXX: Why? What's the point of these checks - that's not to say they're wrong, just + * this needs documenting why this is okay and what its purpose is. + */ if (nvic_get_active_irq(USB_IRQ) || #ifndef PLATFORM_MULTI_UART nvic_get_active_irq(USBUSART_IRQ) || nvic_get_active_irq(USBUSART_DMA_RX_IRQ) @@ -365,9 +370,14 @@ size_t debug_serial_debug_write(const char *buf, const size_t len) ) return 0; + /* Do something to make this atomic? */ CM_ATOMIC_CONTEXT(); size_t offset = 0; + /* + * Loop through all the bytes in the buffer to send, and if there would be a new line, insert the carridge return + * too so that terminal emulation on the receiving end works correctly. + */ for (; offset < len && (debug_serial_debug_write_index + 1U) % AUX_UART_BUFFER_SIZE != debug_serial_debug_read_index; ++offset) { @@ -380,6 +390,7 @@ size_t debug_serial_debug_write(const char *buf, const size_t len) debug_serial_append_char(buf[offset]); } + /* Try to send out the resulting buffer of data now that as much as we've got space for has been queued */ debug_serial_run(); return offset; } From befe8e6506240877c22ea12225ba7d861d498f10 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 19 Apr 2026 14:00:22 +0100 Subject: [PATCH 188/247] common/usb_serial: Documentation for parts of how the debug and aux serial transmit components work --- src/platforms/common/aux_serial.c | 1 + src/platforms/common/usb_serial.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index a277165cfb5..e1b1da0a986 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -591,6 +591,7 @@ void aux_serial_send(const size_t len) void aux_serial_update_receive_buffer_fullness(void) { + /* Extract from the DMA controller where it is in the FIFO buffer and turn that back into a write index */ aux_serial_receive_write_index = AUX_UART_BUFFER_SIZE - dma_get_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); aux_serial_receive_write_index %= AUX_UART_BUFFER_SIZE; diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index 35088fa0256..a98bfd89711 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -274,22 +274,25 @@ static void debug_serial_send_data(void) aux_serial_update_receive_buffer_fullness(); /* Forcibly empty fifo if no USB endpoint. If fifo empty, nothing further to do. */ - if (usb_get_config() != 1 || + if (usb_get_config() != 1U || (aux_serial_receive_buffer_empty() #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) && debug_serial_fifo_buffer_empty() #endif )) { + /* Mark the FIFOs as empty */ #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) debug_serial_debug_read_index = debug_serial_debug_write_index; #endif aux_serial_drain_receive_buffer(); debug_serial_send_complete = true; } else { + /* We have a good USB link and data to send, so queue up anything that's in the debug buffer */ #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) debug_serial_debug_read_index = debug_serial_fifo_send( debug_serial_debug_buffer, debug_serial_debug_read_index, debug_serial_debug_write_index); #endif + /* And anything that's in the AUX serial buffer */ aux_serial_stage_receive_buffer(); } } @@ -348,8 +351,10 @@ static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep) #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) static void debug_serial_append_char(const char ch) { + /* Write the character into the buffer and increment the write index */ debug_serial_debug_buffer[debug_serial_debug_write_index] = ch; ++debug_serial_debug_write_index; + /* Bound the index on the size of the buffer so it remains a FIFO */ debug_serial_debug_write_index %= AUX_UART_BUFFER_SIZE; } From 0a5284343321c2301539167f1d7018c39d6943b2 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 19 Apr 2026 14:39:02 +0100 Subject: [PATCH 189/247] common/usb_serial: Fix the debug serial logic doing a bad by turning USB interrupts off while trying to send data (this breaks DWC2 packet queueing) --- src/platforms/common/usb_serial.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index a98bfd89711..b00e494834b 100644 --- a/src/platforms/common/usb_serial.c +++ b/src/platforms/common/usb_serial.c @@ -75,7 +75,7 @@ static void debug_serial_send_callback(usbd_device *dev, uint8_t ep); static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep); #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) -static bool debug_serial_send_complete = true; +static _Atomic bool debug_serial_send_complete = true; #endif #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) @@ -299,14 +299,11 @@ static void debug_serial_send_data(void) void debug_serial_run(void) { - nvic_disable_irq(USB_IRQ); aux_serial_set_led(AUX_SERIAL_LED_RX); /* Try to send a packet if usb is idle */ if (debug_serial_send_complete) debug_serial_send_data(); - - nvic_enable_irq(USB_IRQ); } #endif From b79287d75936a3ceee2426b6313b12323ed72a01 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 19 Apr 2026 14:39:48 +0100 Subject: [PATCH 190/247] common/aux_serial: Fixed DMA going off the rails because the number-of-data field winds up wholey the wrong value after UART switching --- src/platforms/common/aux_serial.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index e1b1da0a986..33f4af21325 100644 --- a/src/platforms/common/aux_serial.c +++ b/src/platforms/common/aux_serial.c @@ -110,6 +110,11 @@ static uart_state_e uart_state = UART_STATE_UNKNOWN; /* NOLINTBEGIN(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ /* Defines a linked list of things to be done at the completion of RX DMA */ static const uintptr_t aux_serial_dma_receive_ll[] = { + /* + * Make sure we reset the number of bytes we can write so that if we've been doing UART swapping we don't + * wind up screwed and unable to take more data + */ + (uintptr_t)AUX_UART_BUFFER_SIZE, /* This controls the next RX destination address to use */ (uintptr_t)aux_serial_receive_buffer, }; @@ -302,7 +307,8 @@ void aux_serial_init(void) dma_set_number_of_data(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, AUX_UART_BUFFER_SIZE); dma_disable_source_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); dma_enable_destination_increment_mode(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN); - dma_setup_linked_list(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, aux_serial_dma_receive_ll, DMA_CxLLR_UDA); + dma_setup_linked_list( + AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, aux_serial_dma_receive_ll, DMA_CxLLR_UDA | DMA_CxLLR_UB1); dma_set_source_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); dma_set_destination_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); From 39cf143849986dc5da0fd40d6d41e6cf9ed9a715 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Fri, 27 Feb 2026 21:03:30 -0800 Subject: [PATCH 191/247] buffer-utils: Added `read_le8` --- src/include/buffer_utils.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index 0647641a9bf..383eb86fdf6 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -74,6 +74,15 @@ static inline uint32_t read_le4(const uint8_t *const buffer, const size_t offset return data[0U] | ((uint32_t)data[1U] << 8U) | ((uint32_t)data[2U] << 16U) | ((uint32_t)data[3U] << 24U); } +static inline uint64_t read_le8(const uint8_t *const buffer, const size_t offset) +{ + uint8_t data[8U]; + memcpy(data, buffer + offset, 8U); + return data[0U] | ((uint64_t)data[1U] << 8U) | ((uint64_t)data[2U] << 16U) | ((uint64_t)data[3U] << 24U) | + ((uint64_t)data[4U] << 32U) | ((uint64_t)data[5U] << 40U) | ((uint64_t)data[6U] << 48U) | + ((uint64_t)data[7U] << 56U); +} + static inline uint32_t read_be4(const uint8_t *const buffer, const size_t offset) { uint8_t data[4U]; From 9cdd17085084106f5b43c70818318d84e8267d8e Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Fri, 27 Feb 2026 21:04:09 -0800 Subject: [PATCH 192/247] lattice_ecp5: Added target for the Lattice ECP5 FPGA --- meson_options.txt | 1 + src/target/jtag_devs.c | 133 ++++++++++ src/target/lattice_common.h | 121 +++++++++ src/target/lattice_ecp5.c | 516 ++++++++++++++++++++++++++++++++++++ src/target/lattice_ecp5.h | 41 +++ src/target/meson.build | 9 + 6 files changed, 821 insertions(+) create mode 100644 src/target/lattice_common.h create mode 100644 src/target/lattice_ecp5.c create mode 100644 src/target/lattice_ecp5.h diff --git a/meson_options.txt b/meson_options.txt index 2937112e7d7..18c78e47b01 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -40,6 +40,7 @@ option( 'efm', 'gd32', 'hc32', + 'lattice', 'lpc', 'mm32', 'nrf', diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 1830de43e86..5993c65c01a 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -25,6 +25,7 @@ #include "adiv5.h" #include "riscv_debug.h" #include "icepick.h" +#include "lattice_ecp5.h" #include "jtag_devs.h" const jtag_dev_descr_s dev_descr[] = { @@ -433,6 +434,138 @@ const jtag_dev_descr_s dev_descr[] = { .handler = icepick_router_handler, }, #endif +#ifdef CONFIG_LATTICE + { + .idcode = 0x21111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-12", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x41111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x41112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x41113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x01111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x01112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x01113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x81111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x81112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, + { + .idcode = 0x81113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x5U, + }, + }, +#endif #if ENABLE_DEBUG == 1 { .idcode = 0x000007a3U, diff --git a/src/target/lattice_common.h b/src/target/lattice_common.h new file mode 100644 index 00000000000..17ec60cbffa --- /dev/null +++ b/src/target/lattice_common.h @@ -0,0 +1,121 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * 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 LIABLEs + * 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_LATTICE_COMMON_H +#define TARGET_LATTICE_COMMON_H + +#include + +// Read out the 32-bit IDCODE of the device +#define CMD_READ_ID 0xE0U +// Read 32-bit user code +#define CMD_USERCODE 0xC0U +// Read out internal status +#define CMD_LSC_READ_STATUS 0x3CU +// Read 1 bit busy flag to check the command execution status +#define CMD_LSC_CHECK_BSY 0xF0U +// Equivalent to toggle PROGRAMN pin +#define CMD_LSC_REFRESH 0x79U +// Enable the Offline configuration mode +#define CMD_ISC_ENABLE 0xC6U +// Enable the Transparent configuration mode +#define CMD_ISC_ENABLE_X 0x74U +// Disable the configuration operation +#define CMD_ISC_DISABLE 0x26U +// Write the 32-bit new USERCODE data to USERCODE register +#define CMD_ISC_PROGRAM_USERCODE 0xC2U +// Bulk erase the memory array base on the access mode and array selection +#define CMD_ISC_ERASE 0x0EU +// Program the DONE bit if the device is in Configuration state. +#define CMD_ISC_PROGRAM_DONE 0x5EU +// Program the Security bit if the device is in Configuration state. +#define CMD_ISC_PROGRAM_SECURITY 0xCEU +// Initialize the Address Shift Register +#define CMD_LSC_INIT_ADDRESS 0x46U +// Write the 16 bit Address Register to move the address quickly +#define CMD_LSC_WRITE_ADDRESS 0xB4U +// Program the device the whole bitstream sent in as the command operand +#define CMD_LSC_BITSTREAM_BURST 0x7AU +/* + * Write configuration data to the configuration memory frame at current address + * and post increment the address. + * + * Byte 2~0 of the opcode indicate number of the frames included in the operand field. + */ +#define CMD_LSC_PROG_INCR_RTI 0x82U +// Encrypt the configuration data then write +#define CMD_LSC_PROG_INCR_ENC 0xB6U +// Decompress the configuration data, then write +#define CMD_LSC_PROG_INCR_CMP 0xB8U +// Decompress and Encrypt the configuration data, then write +#define CMD_LSC_PROG_INCR_CNE 0xBAU +// Read back the configuration memory frame selected by the address register and post increment the address +#define CMD_LSC_VERIFY_INCR_RTI 0x6AU +// Modify the Control Register 0 +#define CMD_LSC_PROG_CTRL0 0x22U +// Read the Control Register 0 +#define CMD_LSC_READ_CTRL0 0x20U +// Reset 16-bit frame CRC register to 0x0000 +#define CMD_LSC_RESET_CRC 0x3BU +// Read 16-bit frame CRC register content +#define CMD_LSC_READ_CRC 0x60U +// Program the calculated 32-bit CRC based on configuration bit values only into overall CRC register +#define CMD_LSC_PROG_SED_CRC 0xA2U +// Read the 32-bit SED CRC +#define CMD_LSC_READ_SED_CRC 0xA4U +// Program 64-bit password into the non-volatile memory (Efuse) +#define CMD_LSC_PROG_PASSWORD 0xF1U +// Read out the 64-bit password before activated for verification +#define CMD_LSC_READ_PASSWORD 0xF2U +// Shift in the password to unlock for re-configuration (necessary when password protection feature is active). +#define CMD_LSC_SHIFT_PASSWORD 0xBCU +// Program the 128-bit cipher key into Efuse +#define CMD_LSC_PROG_CIPHER_KEY 0xF3U +// Read out the 128-bit cipher key before activated for verification +#define CMD_LSC_READ_CIPHER_KEY 0xF4U +// Program User Feature, such as Customer ID, I2C Slave Address, Unique ID Header +#define CMD_LSC_PROG_FEATURE 0xE4U +// Read User Feature, such as Customer ID, I2C Slave Address, Unique ID Header +#define CMD_LSC_READ_FEATURE 0xE7U +// Program User Feature Bits, such as CFG port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. +#define CMD_LSC_PROG_FEABITS 0xF8U +// Read User Feature Bits, such as CFH port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. +#define CMD_LSC_READ_FEABITS 0xFBU +// Program OTP bits, to set Memory Sectors One Time Programmable +#define CMD_LSC_PROG_OTP 0xF9U +// Read OTP bits setting +#define CMD_LSC_READ_OTP 0xFAU +// Enable SPI Programming via JTAG +#define CMD_LSC_BACKGROUND_SPI 0x3AU + +#endif /* TARGET_LATTICE_COMMON_H */ diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c new file mode 100644 index 00000000000..dd232655d5f --- /dev/null +++ b/src/target/lattice_ecp5.c @@ -0,0 +1,516 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * 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 LIABLEs + * 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 "buffer_utils.h" +#include "jtag_scan.h" +#include "jtagtap.h" +#include "spi.h" +#include "sfdp.h" +#include "gdb_packet.h" +#include "lattice_common.h" +#include "lattice_ecp5.h" + +#define REGISTER_FIELD(reg, mask, shift) (((reg) >> (shift)) & (mask)) + +#define ECP5_STATUS_TRANSPARENT_MASK 1U +#define ECP5_STATUS_TRANSPARENT_SHIFT 0U +#define ECP5_STATUS_TRANSPARENT(reg) REGISTER_FIELD(reg, ECP5_STATUS_TRANSPARENT_MASK, ECP5_STATUS_TRANSPARENT_SHIFT) +#define ECP5_STATUS_TARGET_MASK 0x7U +#define ECP5_STATUS_TARGET_SHIFT 1U +#define ECP5_STATUS_TARGET(reg) REGISTER_FIELD(reg, ECP5_STATUS_TARGET_MASK, ECP5_STATUS_TARGET_SHIFT) +#define ECP5_STATUS_JTAG_ACTIVE_MASK 1U +#define ECP5_STATUS_JTAG_ACTIVE_SHIFT 4U +#define ECP5_STATUS_JTAG_ACTIVE(reg) REGISTER_FIELD(reg, ECP5_STATUS_JTAG_ACTIVE_MASK, ECP5_STATUS_JTAG_ACTIVE_SHIFT) +#define ECP5_STATUS_PASSWORD_PROTECTED_MASK 1U +#define ECP5_STATUS_PASSWORD_PROTECTED_SHIFT 5U +#define ECP5_STATUS_PASSWORD_PROTECTED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PASSWORD_PROTECTED_MASK, ECP5_STATUS_PASSWORD_PROTECTED_SHIFT) +#define ECP5_STATUS_INTERNAL0_MASK 1U +#define ECP5_STATUS_INTERNAL0_SHIFT 6U +#define ECP5_STATUS_INTERNAL0(reg) REGISTER_FIELD(reg, ECP5_STATUS_INTERNAL0_MASK, ECP5_STATUS_INTERNAL0_SHIFT) +#define ECP5_STATUS_DECRYPT_ENABLED_MASK 1U +#define ECP5_STATUS_DECRYPT_ENABLED_SHIFT 7U +#define ECP5_STATUS_DECRYPT_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_DECRYPT_ENABLED_MASK, ECP5_STATUS_DECRYPT_ENABLED_SHIFT) +#define ECP5_STATUS_DONE_MASK 1U +#define ECP5_STATUS_DONE_SHIFT 8U +#define ECP5_STATUS_DONE(reg) REGISTER_FIELD(reg, ECP5_STATUS_DONE_MASK, ECP5_STATUS_DONE_SHIFT) +#define ECP5_STATUS_ISC_ENABLED_MASK 1U +#define ECP5_STATUS_ISC_ENABLED_SHIFT 9U +#define ECP5_STATUS_ISC_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_ISC_ENABLED_MASK, ECP5_STATUS_ISC_ENABLED_SHIFT) +#define ECP5_STATUS_WRITE_ENABLED_MASK 1U +#define ECP5_STATUS_WRITE_ENABLED_SHIFT 10U +#define ECP5_STATUS_WRITE_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_WRITE_ENABLED_MASK, ECP5_STATUS_WRITE_ENABLED_SHIFT) +#define ECP5_STATUS_READ_ENABLED_MASK 1U +#define ECP5_STATUS_READ_ENABLED_SHIFT 11U +#define ECP5_STATUS_READ_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_READ_ENABLED_MASK, ECP5_STATUS_READ_ENABLED_SHIFT) +#define ECP5_STATUS_BUSY_MASK 1U +#define ECP5_STATUS_BUSY_SHIFT 12U +#define ECP5_STATUS_BUSY(reg) REGISTER_FIELD(reg, ECP5_STATUS_BUSY_MASK, ECP5_STATUS_BUSY_SHIFT) +#define ECP5_STATUS_FAILURE_MASK 1U +#define ECP5_STATUS_FAILURE_SHIFT 13U +#define ECP5_STATUS_FAILURE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FAILURE_MASK, ECP5_STATUS_FAILURE_SHIFT) +#define ECP5_STATUS_FEATURES_OTP_MASK 1U +#define ECP5_STATUS_FEATURES_OTP_SHIFT 14U +#define ECP5_STATUS_FEATURES_OTP(reg) REGISTER_FIELD(reg, ECP5_STATUS_FEATURES_OTP_MASK, ECP5_STATUS_FEATURES_OTP_SHIFT) +#define ECP5_STATUS_ENCRYPTED_ONLY_MASK 1U +#define ECP5_STATUS_ENCRYPTED_ONLY_SHIFT 15U +#define ECP5_STATUS_ENCRYPTED_ONLY(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_ENCRYPTED_ONLY_MASK, ECP5_STATUS_ENCRYPTED_ONLY_SHIFT) +#define ECP5_STATUS_PASSWORD_ENABLED_MASK 1U +#define ECP5_STATUS_PASSWORD_ENABLED_SHIFT 16U +#define ECP5_STATUS_PASSWORD_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PASSWORD_ENABLED_MASK, ECP5_STATUS_PASSWORD_ENABLED_SHIFT) +#define ECP5_STATUS_INTERNAL1_MASK 0x7U +#define ECP5_STATUS_INTERNAL1_SHIFT 17U +#define ECP5_STATUS_INTERNAL1(reg) REGISTER_FIELD(reg, ECP5_STATUS_INTERNAL1_MASK, ECP5_STATUS_INTERNAL1_SHIFT) +#define ECP5_STATUS_ENCRYPT_PREAMBLE_MASK 1U +#define ECP5_STATUS_ENCRYPT_PREAMBLE_SHIFT 20U +#define ECP5_STATUS_ENCRYPT_PREAMBLE(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_ENCRYPT_PREAMBLE_MASK, ECP5_STATUS_ENCRYPT_PREAMBLE_SHIFT) +#define ECP5_STATUS_STANDARD_PREAMBLE_MASK 1U +#define ECP5_STATUS_STANDARD_PREAMBLE_SHIFT 21U +#define ECP5_STATUS_STANDARD_PREAMBLE(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_STANDARD_PREAMBLE_MASK, ECP5_STATUS_STANDARD_PREAMBLE_SHIFT) +#define ECP5_STATUS_PRIMARY_CFG_FAIL_MASK 1U +#define ECP5_STATUS_PRIMARY_CFG_FAIL_SHIFT 22U +#define ECP5_STATUS_PRIMARY_CFG_FAIL(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PRIMARY_CFG_FAIL_MASK, ECP5_STATUS_PRIMARY_CFG_FAIL_SHIFT) +#define ECP5_STATUS_BSE_ERROR_MASK 0x7U +#define ECP5_STATUS_BSE_ERROR_SHIFT 23U +#define ECP5_STATUS_BSE_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_BSE_ERROR_MASK, ECP5_STATUS_BSE_ERROR_SHIFT) +#define ECP5_STATUS_EXEC_ERROR_MASK 1U +#define ECP5_STATUS_EXEC_ERROR_SHIFT 26U +#define ECP5_STATUS_EXEC_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_EXEC_ERROR_MASK, ECP5_STATUS_EXEC_ERROR_SHIFT) +#define ECP5_STATUS_ID_ERROR_MASK 1U +#define ECP5_STATUS_ID_ERROR_SHIFT 27U +#define ECP5_STATUS_ID_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_ID_ERROR_MASK, ECP5_STATUS_ID_ERROR_SHIFT) +#define ECP5_STATUS_INVALID_COMMAND_MASK 1U +#define ECP5_STATUS_INVALID_COMMAND_SHIFT 28U +#define ECP5_STATUS_INVALID_COMMAND(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_INVALID_COMMAND_MASK, ECP5_STATUS_INVALID_COMMAND_SHIFT) +#define ECP5_STATUS_SED_ERROR_MASK 1U +#define ECP5_STATUS_SED_ERROR_SHIFT 29U +#define ECP5_STATUS_SED_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_SED_ERROR_MASK, ECP5_STATUS_SED_ERROR_SHIFT) +#define ECP5_STATUS_BYPASS_MODE_MASK 1U +#define ECP5_STATUS_BYPASS_MODE_SHIFT 30U +#define ECP5_STATUS_BYPASS_MODE(reg) REGISTER_FIELD(reg, ECP5_STATUS_BYPASS_MODE_MASK, ECP5_STATUS_BYPASS_MODE_SHIFT) +#define ECP5_STATUS_FLOW_MODE_MASK 1U +#define ECP5_STATUS_FLOW_MODE_SHIFT 31U +#define ECP5_STATUS_FLOW_MODE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FLOW_MODE_MASK, ECP5_STATUS_FLOW_MODE_SHIFT) + +#define ECP5_CTRL0_MSPI_CLK_MASK 0x1FU +#define ECP5_CTRL0_MSPI_CLK_SHIFT 0U +#define ECP5_CTRL0_MSPI_CLK(reg) REGISTER_FIELD(reg, ECP5_CTRL0_MSPI_CLK_MASK, ECP5_CTRL0_MSPI_CLK_SHIFT) +#define ECP5_CTRL0_SLEW_MASK 0x3U +#define ECP5_CTRL0_SLEW_SHIFT 6U +#define ECP5_CTRL0_SLEW(reg) REGISTER_FIELD(reg, ECP5_CTRL0_SLEW_MASK, ECP5_CTRL0_SLEW_SHIFT) +#define ECP5_CTRL0_RSVD0_MASK 0x1FFU +#define ECP5_CTRL0_RSVD0_SHIFT 8U +#define ECP5_CTRL0_RSVD0(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD0_MASK, ECP5_CTRL0_RSVD0_SHIFT) +#define ECP5_CTRL0_PDONE_MASK 0x3U +#define ECP5_CTRL0_PDONE_SHIFT 18U +#define ECP5_CTRL0_PDONE(reg) REGISTER_FIELD(reg, ECP5_CTRL0_PDONE_MASK, ECP5_CTRL0_PDONE_SHIFT) +#define ECP5_CTRL0_RSVD1_MASK 0x80U +#define ECP5_CTRL0_RSVD1_SHIFT 20U +#define ECP5_CTRL0_RSVD1(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD1_MASK, ECP5_CTRL0_RSVD1_SHIFT) +#define ECP5_CTRL0_NDR_MASK 1U +#define ECP5_CTRL0_NDR_SHIFT 28U +#define ECP5_CTRL0_NDR(reg) REGISTER_FIELD(reg, ECP5_CTRL0_NDR_MASK, ECP5_CTRL0_NDR_SHIFT) +#define ECP5_CTRL0_WAKEUP_TRANS_MASK 1U +#define ECP5_CTRL0_WAKEUP_TRANS_SHIFT 29U +#define ECP5_CTRL0_WAKEUP_TRANS(reg) REGISTER_FIELD(reg, ECP5_CTRL0_WAKEUP_TRANS_MASK, ECP5_CTRL0_WAKEUP_TRANS_SHIFT) +#define ECP5_CTRL0_RSVD2_MASK 0x3U +#define ECP5_CTRL0_RSVD2_SHIFT 30U +#define ECP5_CTRL0_RSVD2(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD2_MASK, ECP5_CTRL0_RSVD2_SHIFT) + +#define ECP5_FLASH_BASE 0x04000000U + +static const uint8_t ecp5_spi_unlock[2U] = {0xfeU, 0x68U}; + +typedef struct ecp5_ctx { + uint8_t device_index; +} ecp5_ctx_s; + +static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv); +static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv); +static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv); + +const command_s ecp5_cmd_list[] = { + {"status", ecp5_read_reg_status, "Read FPGA status register"}, + {"control", ecp5_read_reg_control, "Read FPGA control register"}, + {"usercode", ecp5_read_reg_usercode, "Read FPGA USERCODE register"}, + {NULL, NULL, NULL}, +}; + +static uint8_t reverse_bits(uint8_t data); + +static void ecp5_free_ctx(void *priv); +static uint32_t ecp5_read32(uint8_t dev_index, uint8_t cmd); + +static bool ecp5_attach(target_s *target); +static bool ecp5_check_error(target_s *target); +static void ecp5_reset(target_s *target); +static bool ecp5_enter_flash(target_s *target); +static bool ecp5_exit_flash(target_s *target); + +static void ecp5_spi_read(target_s *target, uint16_t command, target_addr_t address, void *buffer, size_t length); +static void ecp5_spi_write( + target_s *target, uint16_t command, target_addr_t address, const void *buffer, size_t length); +static void ecp5_spi_run_command(target_s *target, uint16_t command, target_addr_t address); +static void ecp5_spi_xfr_jtag(target_s *target, uint8_t *data_out, const uint8_t *data_in, size_t length); + +void lattice_ecp5_handler(const uint8_t dev_index) +{ + target_s *target = target_new(); + target->driver = "Lattice"; + target->core = "ECP5"; + target->priv = calloc(1U, sizeof(ecp5_ctx_s)); + + if (!target->priv) { + DEBUG_ERROR("calloc: %s: failed to allocate target context\n", __func__); + return; + } + + target->priv_free = free; + target->attach = ecp5_attach; + target->check_error = ecp5_check_error; + target->reset = ecp5_reset; + target->enter_flash_mode = ecp5_enter_flash; + target->exit_flash_mode = ecp5_exit_flash; + target_add_commands(target, ecp5_cmd_list, target->driver); + + ecp5_ctx_s *ctx = target->priv; + ctx->device_index = dev_index; +} + +static uint32_t ecp5_read32(const uint8_t dev_index, const uint8_t cmd) +{ + uint8_t data[4U]; + jtag_dev_write_ir(dev_index, cmd); + jtag_dev_shift_dr(dev_index, data, NULL, 32U); + return read_le4(data, 0U); +} + +static bool ecp5_attach(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + + if (ECP5_STATUS_ENCRYPTED_ONLY(status)) + DEBUG_WARN("This FPGA only accepts encrypted bitstreams!\n"); + + if (ECP5_STATUS_DONE(status)) + DEBUG_INFO("FPGA is configured\n"); + + ecp5_enter_flash(target); + + spi_flash_id_s flash_id; + ecp5_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0U, &flash_id, sizeof(flash_id)); + + /* If we read out valid Flash information, set up a region for it */ + if (flash_id.manufacturer != 0xffU && flash_id.type != 0xffU && flash_id.capacity != 0xffU) { + const uint32_t capacity = 1U << flash_id.capacity; + DEBUG_INFO("SPI Flash: mfr = %02" PRIx8 ", type = %02" PRIx8 ", capacity = %08" PRIx32 "\n", + flash_id.manufacturer, flash_id.type, capacity); + bmp_spi_add_flash(target, ECP5_FLASH_BASE, capacity, ecp5_spi_read, ecp5_spi_write, ecp5_spi_run_command); + } else + DEBUG_INFO("Flash identification failed\n"); + + ecp5_exit_flash(target); + + return true; +} + +static bool ecp5_check_error(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + + return !(ECP5_STATUS_BSE_ERROR(status) || ECP5_STATUS_ID_ERROR(status) || ECP5_STATUS_EXEC_ERROR(status) || + ECP5_STATUS_PRIMARY_CFG_FAIL(status) || ECP5_STATUS_FAILURE(status) || ECP5_STATUS_INVALID_COMMAND(status)); +} + +static void ecp5_reset(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); + jtag_proc.jtagtap_cycle(false, false, 50U); +} + +static bool ecp5_enter_flash(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + // Enter Offline configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_ENABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + // Erase configuration SRAM + jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Wait for the configuration to be erased + while (ECP5_STATUS_BUSY(ecp5_read32(dev_index, CMD_LSC_READ_STATUS))) + platform_delay(100U); + + // Exit Offline configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Enter background SPI programming mode + jtag_dev_write_ir(dev_index, CMD_LSC_BACKGROUND_SPI); + jtag_dev_shift_dr(dev_index, NULL, ecp5_spi_unlock, 16U); + jtag_proc.jtagtap_cycle(false, false, 50U); + + return true; +} + +static bool ecp5_exit_flash(target_s *const target) +{ + /* + * NOTE: The ECP5 doesn't have a known way to exit SPI background mode, so we need to reset the + * whole device. + * + * This is fine for SPI passthrough, but when doing SRAM programming this flushes the SRAM and + * causes the device to load from flash again, so we need to eventually check if we're + * writing to SRAM or not. + */ + ecp5_reset(target); + + return true; +} + +static void ecp5_spi_read(target_s *const target, const uint16_t command, const target_addr_t address, + void *const buffer, const size_t length) +{ + size_t offset = 0U; + uint8_t cmd_buf[4100U]; + uint8_t data_buf[4100U]; + cmd_buf[offset++] = SPI_FLASH_OPCODE(command); + if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { + cmd_buf[offset++] = (address & 0xff0000U) >> 16U; + cmd_buf[offset++] = (address & 0x00ff00U) >> 8U; + cmd_buf[offset++] = address & 0x0000ffU; + } + + const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; + for (size_t dummy = 0U; dummy < dummy_len; ++dummy) + cmd_buf[offset++] = 0U; + + memset(cmd_buf + offset, 0, length); + + ecp5_spi_xfr_jtag(target, data_buf, cmd_buf, length + offset); + memcpy(buffer, data_buf + offset, length); +} + +static void ecp5_spi_write(target_s *const target, const uint16_t command, const target_addr_t address, + const void *const buffer, const size_t length) +{ + size_t offset = 0U; + uint8_t cmd_buf[4100U]; + cmd_buf[offset++] = SPI_FLASH_OPCODE(command); + if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { + cmd_buf[offset++] = (address & 0xff0000U) >> 16U; + cmd_buf[offset++] = (address & 0x00ff00U) >> 8U; + cmd_buf[offset++] = address & 0x0000ffU; + } + + const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; + for (size_t dummy = 0U; dummy < dummy_len; ++dummy) + cmd_buf[offset++] = 0U; + + // Guard in the case buffer is `NULL` + if (buffer) + memcpy(cmd_buf + offset, buffer, length); + + ecp5_spi_xfr_jtag(target, NULL, cmd_buf, length + offset); +} + +static void ecp5_spi_run_command(target_s *const target, const uint16_t command, const target_addr_t address) +{ + ecp5_spi_write(target, command, address, NULL, 0UL); +} + +static void ecp5_spi_xfr_jtag( + target_s *const target, uint8_t *const data_out, const uint8_t *const data_in, const size_t length) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + const jtag_dev_s *const device = &jtag_devs[dev_index]; + + /* Switch into Shift-DR */ + jtagtap_shift_dr(); + /* Now we're in Shift-DR, clock out 1's till we hit the right device in the chain */ + jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); + + uint8_t tap_out; + for (size_t idx = 0U; idx < length; ++idx) { + const uint8_t tap_in = reverse_bits(data_in[idx]); + jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); + if (data_out) + data_out[idx] = reverse_bits(tap_out); + } + + DEBUG_PROTO("%s: %" PRIu32 " cycles\n", __func__, (uint32_t)length); + + /* Make sure we're in Exit1-DR having clocked out 1's for any more devices on the chain */ + jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); + /* Now go through Update-DR and back to Idle */ + jtagtap_return_idle(1U); +} + +static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + + gdb_outf("Transparent: %" PRIu32 "\n", ECP5_STATUS_TRANSPARENT(status_register)); + gdb_outf("Configuration Target: %s\n", ECP5_STATUS_TARGET(status_register) ? "eFUSE" : "SRAM"); + gdb_outf("JTAG Active: %" PRIu32 "\n", ECP5_STATUS_JTAG_ACTIVE(status_register)); + gdb_outf("Password Protected: %" PRIu32 "\n", ECP5_STATUS_PASSWORD_PROTECTED(status_register)); + gdb_outf("Internal: %" PRIu32 "\n", ECP5_STATUS_INTERNAL0(status_register)); + gdb_outf("Encryption Enabled: %" PRIu32 "\n", ECP5_STATUS_DECRYPT_ENABLED(status_register)); + gdb_outf("Configuration Success: %" PRIu32 "\n", ECP5_STATUS_DONE(status_register)); + gdb_outf("ISC Enabled: %" PRIu32 "\n", ECP5_STATUS_ISC_ENABLED(status_register)); + gdb_outf("Configuration Writable: %" PRIu32 "\n", ECP5_STATUS_WRITE_ENABLED(status_register)); + gdb_outf("Configuration Readable: %" PRIu32 "\n", ECP5_STATUS_READ_ENABLED(status_register)); + gdb_outf("Configuration Busy: %" PRIu32 "\n", ECP5_STATUS_BUSY(status_register)); + gdb_outf("Last Command Failed: %" PRIu32 "\n", ECP5_STATUS_FAILURE(status_register)); + gdb_outf("Features are OTP: %" PRIu32 "\n", ECP5_STATUS_FEATURES_OTP(status_register)); + gdb_outf("Encrypted Bitstream Only: %" PRIu32 "\n", ECP5_STATUS_ENCRYPTED_ONLY(status_register)); + gdb_outf("Password Protection Enabled: %" PRIu32 "\n", ECP5_STATUS_PASSWORD_ENABLED(status_register)); + gdb_outf("Internal: %" PRIu32 "\n", ECP5_STATUS_INTERNAL1(status_register)); + gdb_outf("Encrypted Preamble: %" PRIu32 "\n", ECP5_STATUS_ENCRYPT_PREAMBLE(status_register)); + gdb_outf("Standard Preamble: %" PRIu32 "\n", ECP5_STATUS_STANDARD_PREAMBLE(status_register)); + gdb_outf("Primary Bitstream Failure: %" PRIu32 "\n", ECP5_STATUS_PRIMARY_CFG_FAIL(status_register)); + gdb_outf("BSE Status:\n"); + switch (ECP5_STATUS_BSE_ERROR(status_register)) { + case 0x0U: + gdb_outf("\tNo Errors\n"); + break; + case 0x1U: + gdb_outf("\tID Error\n"); + break; + case 0x2U: + gdb_outf("\tIllegal Command\n"); + break; + case 0x3U: + gdb_outf("\tCRC Error\n"); + break; + case 0x4U: + gdb_outf("\tPreamble Error\n"); + break; + case 0x5U: + gdb_outf("\tConfiguration Aborted By User\n"); + break; + case 0x6U: + gdb_outf("\tData Overflow\n"); + break; + case 0x7U: + gdb_outf("\tConfiguration too big for device SRAM\n"); + break; + } + gdb_outf("Execution Error: %" PRIu32 "\n", ECP5_STATUS_EXEC_ERROR(status_register)); + gdb_outf("ID Error: %" PRIu32 "\n", ECP5_STATUS_ID_ERROR(status_register)); + gdb_outf("Invalid Command: %" PRIu32 "\n", ECP5_STATUS_INVALID_COMMAND(status_register)); + gdb_outf("SED Error: %" PRIu32 "\n", ECP5_STATUS_SED_ERROR(status_register)); + gdb_outf("Bypass Mode: %" PRIu32 "\n", ECP5_STATUS_BYPASS_MODE(status_register)); + gdb_outf("Flow Through Mode: %" PRIu32 "\n", ECP5_STATUS_FLOW_MODE(status_register)); + + return true; +} + +static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t control_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_CTRL0); + + gdb_outf("MSPI Clock Divider: %" PRIu32 "\n", ECP5_CTRL0_MSPI_CLK(control_register)); + gdb_outf("\tSlew Rate: "); + switch (ECP5_CTRL0_SLEW(control_register)) { + case 0x0U: + gdb_outf("Slow\n"); + break; + case 0x1U: + gdb_outf("Medium\n"); + break; + default: + gdb_outf("Fast\n"); + break; + } + gdb_outf("\tPROGRAM_DONE: "); + switch (ECP5_CTRL0_PDONE(control_register)) { + case 0x2U: + gdb_outf("Overload with BYPASS\n"); + break; + case 0x3U: + gdb_outf("Overload with FLOW_THROUGH\n"); + break; + default: + gdb_outf("No Overload\n"); + break; + } + gdb_outf("\tNDR/TransFR: %" PRIu32 "\n", ECP5_CTRL0_NDR(control_register)); + gdb_outf("Wakeup Transparent: %" PRIu32 "\n", ECP5_CTRL0_WAKEUP_TRANS(control_register)); + return true; +} + +static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t usercode = ecp5_read32(ctx->device_index, CMD_USERCODE); + + gdb_outf("USERCODE: %08" PRIx32 "\n", usercode); + return true; +} + +static uint8_t reverse_bits(uint8_t data) +{ + return (((data & 0x01U) >> 0U) << 7U) | (((data & 0x02U) >> 1U) << 6U) | (((data & 0x04U) >> 2U) << 5U) | + (((data & 0x08U) >> 3U) << 4U) | (((data & 0x10U) >> 4U) << 3U) | (((data & 0x20U) >> 5U) << 2U) | + (((data & 0x40U) >> 6U) << 1U) | (((data & 0x80U) >> 7U) << 0U); +} diff --git a/src/target/lattice_ecp5.h b/src/target/lattice_ecp5.h new file mode 100644 index 00000000000..9cc884f04e5 --- /dev/null +++ b/src/target/lattice_ecp5.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * 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 LIABLEs + * 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_LATTICE_ECP5_H +#define TARGET_LATTICE_ECP5_H + +#include + +void lattice_ecp5_handler(uint8_t dev_index); + +#endif /* TARGET_LATTICE_ECP5_H */ diff --git a/src/target/meson.build b/src/target/meson.build index 48c5e74bda0..da287df0ad0 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -95,6 +95,7 @@ if is_firmware_build 'stm': 'STM32 (and clones) parts', 'ti': 'Texas Instruments parts', 'xilinx': 'Xilinx parts', + 'lattice': 'Lattice parts', } # Check to see if the set of enabled targets is all of them and the @@ -200,6 +201,13 @@ target_hc32 = declare_dependency( dependencies: target_cortexm, ) +target_lattice = declare_dependency( + sources: files( + 'lattice_ecp5.c', + ), + compile_args: ['-DCONFIG_LATTICE=1'], +) + target_lpc = declare_dependency( sources: files( 'lpc11xx.c', @@ -439,6 +447,7 @@ libbmd_target_deps = [ target_efm, target_gd32, target_hc32, + target_lattice, target_lpc, target_mm32, target_nrf, From 95914f9ef33e7825b0dab0bb1f86a57f20bfc1b5 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Mon, 20 Apr 2026 11:03:00 -0700 Subject: [PATCH 193/247] general: Added `__has_builtin` check for `__builtin_constant_p` --- src/include/general.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/include/general.h b/src/include/general.h index 34278e949c5..27a334318bf 100644 --- a/src/include/general.h +++ b/src/include/general.h @@ -163,6 +163,16 @@ void debug_serial_send_stdout(const uint8_t *data, size_t len); #define BMD_UNUSED __attribute__((unused)) #endif +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_constant_p) +#define BMD_CONSTANT_P __builtin_constant_p +#else +#define BMD_CONSTANT_P(x) 0 +#endif + #ifdef _MSC_VER #define strcasecmp _stricmp #define strncasecmp _strnicmp From 7b6992baaac610c8bbbf46f6834505de75749876 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Mon, 20 Apr 2026 11:06:56 -0700 Subject: [PATCH 194/247] lattice_ecp5: Put command and data buffers on heap The command and data buffers that were being used for the transparent SPI mode of the FPGA were on the stack before, which is fine for BMDA, but when being put on physical hardware it would cause stack exhaustion. This is because each buffer was 4100 bytes, and the stack for the probe hardware is 4KiB total, meaning that in the best case we overflowed the stack by 4 bytes, and in worse case by 4104 bytes. Now that shouldn't be a problem. --- src/target/lattice_ecp5.c | 58 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index dd232655d5f..f35f5fbaad3 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -162,6 +162,9 @@ static const uint8_t ecp5_spi_unlock[2U] = {0xfeU, 0x68U}; typedef struct ecp5_ctx { uint8_t device_index; + uint8_t *cmd_buffer; + uint8_t *data_buffer; + uint16_t buffer_len; } ecp5_ctx_s; static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv); @@ -200,11 +203,11 @@ void lattice_ecp5_handler(const uint8_t dev_index) target->priv = calloc(1U, sizeof(ecp5_ctx_s)); if (!target->priv) { - DEBUG_ERROR("calloc: %s: failed to allocate target context\n", __func__); + DEBUG_ERROR("calloc: failed in %s\n", __func__); return; } - target->priv_free = free; + target->priv_free = ecp5_free_ctx; target->attach = ecp5_attach; target->check_error = ecp5_check_error; target->reset = ecp5_reset; @@ -214,6 +217,22 @@ void lattice_ecp5_handler(const uint8_t dev_index) ecp5_ctx_s *ctx = target->priv; ctx->device_index = dev_index; + // Setup the command/data buffers + ctx->buffer_len = 4100U; + ctx->data_buffer = calloc(1, ctx->buffer_len); + ctx->cmd_buffer = calloc(1, ctx->buffer_len); + + if (!ctx->data_buffer || ctx->cmd_buffer) + DEBUG_ERROR("calloc: failed in %s\n", __func__); +} + +static void ecp5_free_ctx(void *const priv) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)priv; + + free(ctx->cmd_buffer); + free(ctx->data_buffer); + free(priv); } static uint32_t ecp5_read32(const uint8_t dev_index, const uint8_t cmd) @@ -318,47 +337,46 @@ static bool ecp5_exit_flash(target_s *const target) static void ecp5_spi_read(target_s *const target, const uint16_t command, const target_addr_t address, void *const buffer, const size_t length) { + const ecp5_ctx_s *ctx = (ecp5_ctx_s *)target->priv; size_t offset = 0U; - uint8_t cmd_buf[4100U]; - uint8_t data_buf[4100U]; - cmd_buf[offset++] = SPI_FLASH_OPCODE(command); + ctx->cmd_buffer[offset++] = SPI_FLASH_OPCODE(command); if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { - cmd_buf[offset++] = (address & 0xff0000U) >> 16U; - cmd_buf[offset++] = (address & 0x00ff00U) >> 8U; - cmd_buf[offset++] = address & 0x0000ffU; + ctx->cmd_buffer[offset++] = (address & 0xff0000U) >> 16U; + ctx->cmd_buffer[offset++] = (address & 0x00ff00U) >> 8U; + ctx->cmd_buffer[offset++] = address & 0x0000ffU; } const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; for (size_t dummy = 0U; dummy < dummy_len; ++dummy) - cmd_buf[offset++] = 0U; + ctx->cmd_buffer[offset++] = 0U; - memset(cmd_buf + offset, 0, length); + memset(ctx->cmd_buffer + offset, 0, length); - ecp5_spi_xfr_jtag(target, data_buf, cmd_buf, length + offset); - memcpy(buffer, data_buf + offset, length); + ecp5_spi_xfr_jtag(target, ctx->data_buffer, ctx->cmd_buffer, length + offset); + memcpy(buffer, ctx->data_buffer + offset, length); } static void ecp5_spi_write(target_s *const target, const uint16_t command, const target_addr_t address, const void *const buffer, const size_t length) { + const ecp5_ctx_s *ctx = (ecp5_ctx_s *)target->priv; size_t offset = 0U; - uint8_t cmd_buf[4100U]; - cmd_buf[offset++] = SPI_FLASH_OPCODE(command); + ctx->cmd_buffer[offset++] = SPI_FLASH_OPCODE(command); if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { - cmd_buf[offset++] = (address & 0xff0000U) >> 16U; - cmd_buf[offset++] = (address & 0x00ff00U) >> 8U; - cmd_buf[offset++] = address & 0x0000ffU; + ctx->cmd_buffer[offset++] = (address & 0xff0000U) >> 16U; + ctx->cmd_buffer[offset++] = (address & 0x00ff00U) >> 8U; + ctx->cmd_buffer[offset++] = address & 0x0000ffU; } const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; for (size_t dummy = 0U; dummy < dummy_len; ++dummy) - cmd_buf[offset++] = 0U; + ctx->cmd_buffer[offset++] = 0U; // Guard in the case buffer is `NULL` if (buffer) - memcpy(cmd_buf + offset, buffer, length); + memcpy(ctx->cmd_buffer + offset, buffer, length); - ecp5_spi_xfr_jtag(target, NULL, cmd_buf, length + offset); + ecp5_spi_xfr_jtag(target, NULL, ctx->cmd_buffer, length + offset); } static void ecp5_spi_run_command(target_s *const target, const uint16_t command, const target_addr_t address) From bf030801a8d6f9e0c535b662a28dc91fd1473f49 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Mon, 20 Apr 2026 12:07:32 -0700 Subject: [PATCH 195/247] buffer: Added `reverse_bits` helpers for `8`/`16`/`24`/`32` bit widths There is currently only a single optimized path for arm using `rbit` but others for other platforms should be added as these are kinda expensive instruction wise, Not a huge deal on BMDA, but any other probe that is not an Arm with `rbit` is going to be in for some real pain --- src/include/buffer_utils.h | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index 383eb86fdf6..891f5a25e2a 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -38,6 +38,8 @@ #include #include +#include + static inline void write_le2(uint8_t *const buffer, const size_t offset, const uint16_t value) { buffer[offset + 0U] = value & 0xffU; @@ -106,4 +108,78 @@ static inline size_t write_char(char *const buffer, const size_t buffer_size, co return offset + 1U; } +static inline uint8_t reverse_bits8(const uint8_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if defined(__arm__) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xff000000U) >> 24U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x01U) << 7U) | ((data & 0x02U) << 5U) | ((data & 0x04U) << 3U) | ((data & 0x08U) << 1U) | + ((data & 0x10U) >> 1U) | ((data & 0x20U) >> 3U) | ((data & 0x40U) >> 5U) | ((data & 0x80U) >> 7U); +} + +static inline uint16_t reverse_bits16(const uint16_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if defined(__arm__) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xffff0000U) >> 16U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x0001U) << 15U) | ((data & 0x0002U) << 13U) | ((data & 0x0004U) << 11U) | + ((data & 0x0008U) << 9U) | ((data & 0x0010U) << 7U) | ((data & 0x0020U) << 5U) | ((data & 0x0040U) << 3U) | + ((data & 0x0080U) << 1U) | ((data & 0x0100U) >> 1U) | ((data & 0x0200U) >> 3U) | ((data & 0x0400U) >> 5U) | + ((data & 0x0800U) >> 7U) | ((data & 0x1000U) >> 9U) | ((data & 0x2000U) >> 11U) | ((data & 0x4000U) >> 13U) | + ((data & 0x8000U) >> 15U); +} + +static inline uint32_t reverse_bits24(const uint32_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if defined(__arm__) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xffffff00U) >> 8U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x00000001U) << 23U) | ((data & 0x00000002U) << 21U) | ((data & 0x00000004U) << 19U) | + ((data & 0x00000008U) << 17U) | ((data & 0x00000010U) << 15U) | ((data & 0x00000020U) << 13U) | + ((data & 0x00000040U) << 11U) | ((data & 0x00000080U) << 9U) | ((data & 0x00000100U) << 7U) | + ((data & 0x00000200U) << 5U) | ((data & 0x00000400U) << 3U) | ((data & 0x00000800U) << 1U) | + ((data & 0x00001000U) >> 1U) | ((data & 0x00002000U) >> 3U) | ((data & 0x00004000U) >> 5U) | + ((data & 0x00008000U) >> 7U) | ((data & 0x00010000U) >> 9U) | ((data & 0x00020000U) >> 11U) | + ((data & 0x00040000U) >> 13U) | ((data & 0x00080000U) >> 15U) | ((data & 0x00100000U) >> 17U) | + ((data & 0x00200000U) >> 19U) | ((data & 0x00400000U) >> 21U) | ((data & 0x00800000U) >> 23U); +} + +static inline uint32_t reverse_bits32(const uint32_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if defined(__arm__) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return result; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x00000001U) << 31U) | ((data & 0x00000002U) << 29U) | ((data & 0x00000004U) << 27U) | + ((data & 0x00000008U) << 25U) | ((data & 0x00000010U) << 23U) | ((data & 0x00000020U) << 21U) | + ((data & 0x00000040U) << 19U) | ((data & 0x00000080U) << 17U) | ((data & 0x00000100U) << 15U) | + ((data & 0x00000200U) << 13U) | ((data & 0x00000400U) << 11U) | ((data & 0x00000800U) << 9U) | + ((data & 0x00001000U) << 7U) | ((data & 0x00002000U) << 5U) | ((data & 0x00004000U) << 3U) | + ((data & 0x00008000U) << 1U) | ((data & 0x00010000U) >> 1U) | ((data & 0x00020000U) >> 3U) | + ((data & 0x00040000U) >> 5U) | ((data & 0x00080000U) >> 7U) | ((data & 0x00100000U) >> 9U) | + ((data & 0x00200000U) >> 11U) | ((data & 0x00400000U) >> 13U) | ((data & 0x00800000U) >> 15U) | + ((data & 0x01000000U) >> 17U) | ((data & 0x02000000U) >> 19U) | ((data & 0x04000000U) >> 21U) | + ((data & 0x08000000U) >> 23U) | ((data & 0x10000000U) >> 25U) | ((data & 0x20000000U) >> 27U) | + ((data & 0x40000000U) >> 29U) | ((data & 0x80000000U) >> 31U); +} + #endif /*INCLUDE_BUFFER_UTILS_H*/ From fd035cd8111b9b84056450f998ab65369989465e Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Mon, 20 Apr 2026 12:07:59 -0700 Subject: [PATCH 196/247] lattice_ecp5: Use the new `reverse_bits8` helper --- src/target/lattice_ecp5.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index f35f5fbaad3..060791cb38e 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -178,8 +178,6 @@ const command_s ecp5_cmd_list[] = { {NULL, NULL, NULL}, }; -static uint8_t reverse_bits(uint8_t data); - static void ecp5_free_ctx(void *priv); static uint32_t ecp5_read32(uint8_t dev_index, uint8_t cmd); @@ -398,10 +396,10 @@ static void ecp5_spi_xfr_jtag( uint8_t tap_out; for (size_t idx = 0U; idx < length; ++idx) { - const uint8_t tap_in = reverse_bits(data_in[idx]); + const uint8_t tap_in = reverse_bits8(data_in[idx]); jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); if (data_out) - data_out[idx] = reverse_bits(tap_out); + data_out[idx] = reverse_bits8(tap_out); } DEBUG_PROTO("%s: %" PRIu32 " cycles\n", __func__, (uint32_t)length); @@ -525,10 +523,3 @@ static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv gdb_outf("USERCODE: %08" PRIx32 "\n", usercode); return true; } - -static uint8_t reverse_bits(uint8_t data) -{ - return (((data & 0x01U) >> 0U) << 7U) | (((data & 0x02U) >> 1U) << 6U) | (((data & 0x04U) >> 2U) << 5U) | - (((data & 0x08U) >> 3U) << 4U) | (((data & 0x10U) >> 4U) << 3U) | (((data & 0x20U) >> 5U) << 2U) | - (((data & 0x40U) >> 6U) << 1U) | (((data & 0x80U) >> 7U) << 0U); -} From aa776a4793392f81be310d216cda1e2eb0f5cff4 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Mon, 20 Apr 2026 13:39:49 -0700 Subject: [PATCH 197/247] lattice_ecp5: Mark things that were supposed to be static actually static --- src/target/lattice_ecp5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 060791cb38e..ba9646f6fa6 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -171,7 +171,7 @@ static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv); static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv); static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv); -const command_s ecp5_cmd_list[] = { +static const command_s ecp5_cmd_list[] = { {"status", ecp5_read_reg_status, "Read FPGA status register"}, {"control", ecp5_read_reg_control, "Read FPGA control register"}, {"usercode", ecp5_read_reg_usercode, "Read FPGA USERCODE register"}, From af280e5981f059356a4ec945c452b25669355288 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Thu, 23 Apr 2026 19:11:44 -0700 Subject: [PATCH 198/247] lattice_ecp5: Add `ecp5_decode_registers` option This options lets you turn off the verbose decoding of the ECP5's status and control registers. This is mostly for eliminating a whole bunch of strings that inflate the ROM image a bit, it only saves a bit over 1KiB (1568B) but every little bit can help --- meson_options.txt | 6 ++++++ src/target/lattice_ecp5.c | 11 +++++++++++ src/target/meson.build | 7 ++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 18c78e47b01..460d7b707d4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -157,3 +157,9 @@ option( type: 'boolean', value: false ) +option( + 'ecp5_decode_registers', + type: 'boolean', + value: true, + description: 'Enable decoding of Lattice ECP5 registers where applicable, rather than printing them raw' +) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index ba9646f6fa6..d2645898eed 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -418,6 +418,7 @@ static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv) const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; const uint32_t status_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); +#if CONFIG_LATTICE_ECP5_DECODE gdb_outf("Transparent: %" PRIu32 "\n", ECP5_STATUS_TRANSPARENT(status_register)); gdb_outf("Configuration Target: %s\n", ECP5_STATUS_TARGET(status_register) ? "eFUSE" : "SRAM"); gdb_outf("JTAG Active: %" PRIu32 "\n", ECP5_STATUS_JTAG_ACTIVE(status_register)); @@ -470,6 +471,9 @@ static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv) gdb_outf("SED Error: %" PRIu32 "\n", ECP5_STATUS_SED_ERROR(status_register)); gdb_outf("Bypass Mode: %" PRIu32 "\n", ECP5_STATUS_BYPASS_MODE(status_register)); gdb_outf("Flow Through Mode: %" PRIu32 "\n", ECP5_STATUS_FLOW_MODE(status_register)); +#else /* CONFIG_LATTICE_ECP5_DECODE */ + gdb_outf("Status: %08" PRIx32 "\n", status_register); +#endif return true; } @@ -482,6 +486,7 @@ static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv) const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; const uint32_t control_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_CTRL0); +#if CONFIG_LATTICE_ECP5_DECODE gdb_outf("MSPI Clock Divider: %" PRIu32 "\n", ECP5_CTRL0_MSPI_CLK(control_register)); gdb_outf("\tSlew Rate: "); switch (ECP5_CTRL0_SLEW(control_register)) { @@ -509,6 +514,11 @@ static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv) } gdb_outf("\tNDR/TransFR: %" PRIu32 "\n", ECP5_CTRL0_NDR(control_register)); gdb_outf("Wakeup Transparent: %" PRIu32 "\n", ECP5_CTRL0_WAKEUP_TRANS(control_register)); + +#else /* CONFIG_LATTICE_ECP5_DECODE */ + gdb_outf("Control: %08" PRIx32 "\n", control_register); +#endif + return true; } @@ -521,5 +531,6 @@ static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv const uint32_t usercode = ecp5_read32(ctx->device_index, CMD_USERCODE); gdb_outf("USERCODE: %08" PRIx32 "\n", usercode); + return true; } diff --git a/src/target/meson.build b/src/target/meson.build index da287df0ad0..78570942292 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -201,11 +201,16 @@ target_hc32 = declare_dependency( dependencies: target_cortexm, ) +target_lattice_defs = ['-DCONFIG_LATTICE=1'] +if get_option('ecp5_decode_registers') + target_lattice_defs += '-DCONFIG_LATTICE_ECP5_DECODE=1' +endif + target_lattice = declare_dependency( sources: files( 'lattice_ecp5.c', ), - compile_args: ['-DCONFIG_LATTICE=1'], + compile_args: [target_lattice_defs], ) target_lpc = declare_dependency( From 762b270029937af9191002f493df7dc11f379d97 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 12:33:40 -0700 Subject: [PATCH 199/247] meson: Add `lattice` target to the bmp-v3 cross file --- cross-file/bmp-v3.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cross-file/bmp-v3.ini b/cross-file/bmp-v3.ini index c6cc2c319eb..584ddb86306 100644 --- a/cross-file/bmp-v3.ini +++ b/cross-file/bmp-v3.ini @@ -19,7 +19,7 @@ endian = 'little' [project options] probe = 'bmp-v3' -targets = 'cortexar,cortexm,riscv32,riscv64,apollo3,at32f4,ch32,ch32v,ch579,efm,gd32,hc32,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' +targets = 'cortexar,cortexm,riscv32,riscv64,apollo3,at32f4,ch32,ch32v,ch579,efm,gd32,hc32,lattice,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' rtt_support = true bmd_bootloader = true enable_riscv_accel = true From 4d5027abc0e7da535c503807633d73d79055202c Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Thu, 23 Apr 2026 23:12:22 -0700 Subject: [PATCH 200/247] lattice_ecp5: Initial direct SRAM programming support --- src/target/lattice_ecp5.c | 201 +++++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 12 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index d2645898eed..7c3ea847d48 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -156,6 +156,7 @@ #define ECP5_CTRL0_RSVD2_SHIFT 30U #define ECP5_CTRL0_RSVD2(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD2_MASK, ECP5_CTRL0_RSVD2_SHIFT) +#define ECP5_SRAM_BASE 0x00000000U #define ECP5_FLASH_BASE 0x04000000U static const uint8_t ecp5_spi_unlock[2U] = {0xfeU, 0x68U}; @@ -165,8 +166,38 @@ typedef struct ecp5_ctx { uint8_t *cmd_buffer; uint8_t *data_buffer; uint16_t buffer_len; + bool wrote_to_sram; } ecp5_ctx_s; +typedef struct ecp5_device { + uint32_t idcode; + uint32_t bitstream_len; + uint8_t frame_len; +} ecp5_device_s; + +static const ecp5_device_s devices[] = { + // LEF5-12 + {.idcode = 0x21111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5-25 + {.idcode = 0x41111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5UM-25 + {.idcode = 0x01111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5UM5G-25 + {.idcode = 0x81111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5-45 + {.idcode = 0x41112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5UM-45 + {.idcode = 0x01112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5UM5G-45 + {.idcode = 0x81112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5-85 + {.idcode = 0x41113043U, .bitstream_len = 2293750U, .frame_len = 142U}, + // LEF5UM-85 + {.idcode = 0x01113043U, .bitstream_len = 2293750U, .frame_len = 142U}, + // LEF5UM5G-85 + {.idcode = 0x81113043U, .bitstream_len = 2293750U, .frame_len = 142U}, +}; + static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv); static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv); static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv); @@ -187,12 +218,19 @@ static void ecp5_reset(target_s *target); static bool ecp5_enter_flash(target_s *target); static bool ecp5_exit_flash(target_s *target); +static bool ecp5_spi_flash_prepare(target_flash_s *flash); +static bool ecp5_spi_flash_done(target_flash_s *flash); static void ecp5_spi_read(target_s *target, uint16_t command, target_addr_t address, void *buffer, size_t length); static void ecp5_spi_write( target_s *target, uint16_t command, target_addr_t address, const void *buffer, size_t length); static void ecp5_spi_run_command(target_s *target, uint16_t command, target_addr_t address); static void ecp5_spi_xfr_jtag(target_s *target, uint8_t *data_out, const uint8_t *data_in, size_t length); +static bool ecp5_sram_prepare(target_flash_s *flash); +static bool ecp5_sram_done(target_flash_s *flash); +static bool ecp5_sram_erase(target_flash_s *flash, target_addr_t addr, size_t length); +static bool ecp5_sram_write(target_flash_s *flash, target_addr_t dest, const void *buffer, size_t length); + void lattice_ecp5_handler(const uint8_t dev_index) { target_s *target = target_new(); @@ -213,6 +251,28 @@ void lattice_ecp5_handler(const uint8_t dev_index) target->exit_flash_mode = ecp5_exit_flash; target_add_commands(target, ecp5_cmd_list, target->driver); + for (size_t dev = 0U; dev < ARRAY_LENGTH(devices); ++dev) { + if (devices[dev].idcode == jtag_devs[dev_index].jd_idcode) { + target_flash_s *flash = calloc(1U, sizeof(*flash)); + + if (!flash) { + DEBUG_ERROR("calloc: %s: failed to allocate flash\n", __func__); + return; + } + + flash->length = devices[dev].bitstream_len; + flash->start = ECP5_SRAM_BASE; + flash->blocksize = flash->length; + flash->writesize = flash->length; // devices[dev].frame_len; + flash->prepare = ecp5_sram_prepare; + flash->done = ecp5_sram_done; + flash->erase = ecp5_sram_erase; + flash->write = ecp5_sram_write; + + target_add_flash(target, flash); + } + } + ecp5_ctx_s *ctx = target->priv; ctx->device_index = dev_index; // Setup the command/data buffers @@ -262,7 +322,11 @@ static bool ecp5_attach(target_s *const target) const uint32_t capacity = 1U << flash_id.capacity; DEBUG_INFO("SPI Flash: mfr = %02" PRIx8 ", type = %02" PRIx8 ", capacity = %08" PRIx32 "\n", flash_id.manufacturer, flash_id.type, capacity); - bmp_spi_add_flash(target, ECP5_FLASH_BASE, capacity, ecp5_spi_read, ecp5_spi_write, ecp5_spi_run_command); + spi_flash_s *const spi_flash = + bmp_spi_add_flash(target, ECP5_FLASH_BASE, capacity, ecp5_spi_read, ecp5_spi_write, ecp5_spi_run_command); + target_flash_s *const flash = &spi_flash->flash; + flash->prepare = ecp5_spi_flash_prepare; + flash->done = ecp5_spi_flash_done; } else DEBUG_INFO("Flash identification failed\n"); @@ -282,11 +346,15 @@ static bool ecp5_check_error(target_s *const target) static void ecp5_reset(target_s *const target) { - const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; const uint8_t dev_index = ctx->device_index; - jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); - jtag_proc.jtagtap_cycle(false, false, 50U); + // If we wrote to the device SRAM, then exit out of programming mode + // otherwise do the normal reset + if (!ctx->wrote_to_sram) { + jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); + jtag_proc.jtagtap_cycle(false, false, 50U); + } } static bool ecp5_enter_flash(target_s *const target) @@ -300,11 +368,46 @@ static bool ecp5_enter_flash(target_s *const target) // Erase configuration SRAM jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); jtag_proc.jtagtap_cycle(false, false, 50U); + // Reset the CRC + jtag_dev_write_ir(dev_index, CMD_LSC_READ_CRC); + jtag_proc.jtagtap_cycle(false, false, 50U); // Wait for the configuration to be erased while (ECP5_STATUS_BUSY(ecp5_read32(dev_index, CMD_LSC_READ_STATUS))) platform_delay(100U); + return true; +} + +static bool ecp5_exit_flash(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + if (ctx->wrote_to_sram) { + // Exit configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + } + + const uint32_t status = ecp5_read32(dev_index, CMD_LSC_READ_STATUS); + + const uint32_t result = + (ECP5_STATUS_BSE_ERROR(status) || ECP5_STATUS_ID_ERROR(status) || ECP5_STATUS_EXEC_ERROR(status) || + ECP5_STATUS_PRIMARY_CFG_FAIL(status) || ECP5_STATUS_FAILURE(status) || ECP5_STATUS_INVALID_COMMAND(status)); + + if (result != 0) { + DEBUG_ERROR("Bitstream programming failed: %" PRIu32 "\n", result); + } + + return result != 0; +} + +static bool ecp5_spi_flash_prepare(target_flash_s *flash) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)flash->t->priv; + const uint8_t dev_index = ctx->device_index; + // Exit Offline configuration mode jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); jtag_proc.jtagtap_cycle(false, false, 50U); @@ -317,17 +420,13 @@ static bool ecp5_enter_flash(target_s *const target) return true; } -static bool ecp5_exit_flash(target_s *const target) +static bool ecp5_spi_flash_done(target_flash_s *flash) { /* - * NOTE: The ECP5 doesn't have a known way to exit SPI background mode, so we need to reset the - * whole device. - * - * This is fine for SPI passthrough, but when doing SRAM programming this flushes the SRAM and - * causes the device to load from flash again, so we need to eventually check if we're - * writing to SRAM or not. + * The ECP5 doesn't have a known way to exit SPI background mode, so we need to reset the whole + * device. */ - ecp5_reset(target); + ecp5_reset(flash->t); return true; } @@ -410,6 +509,84 @@ static void ecp5_spi_xfr_jtag( jtagtap_return_idle(1U); } +static bool ecp5_sram_prepare(target_flash_s *const flash) +{ + const target_s *const target = flash->t; + ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + + ctx->wrote_to_sram = true; + + return true; +} + +static bool ecp5_sram_done(target_flash_s *const flash) +{ + (void)flash; + return true; +} + +static bool ecp5_sram_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + (void)addr; + (void)length; + + const target_s *const target = flash->t; + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + // Erase configuration SRAM + jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Wait for the configuration to be erased + while (ECP5_STATUS_BUSY(ecp5_read32(dev_index, CMD_LSC_READ_STATUS))) + platform_delay(100U); + + // Reset the configuration CRC SRAM + jtag_dev_write_ir(dev_index, CMD_LSC_RESET_CRC); + jtag_proc.jtagtap_cycle(false, false, 50U); + + return true; +} + +static bool ecp5_sram_write( + target_flash_s *const flash, const target_addr_t dest, const void *const buffer, const size_t length) +{ + (void)dest; + + const target_s *const target = flash->t; + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + const jtag_dev_s *const device = &jtag_devs[dev_index]; + + // Write bitstream to SRAM + jtag_dev_write_ir(dev_index, CMD_LSC_BITSTREAM_BURST); + jtag_proc.jtagtap_cycle(false, false, 50U); + + /* Switch into Shift-DR */ + jtagtap_shift_dr(); + /* Now we're in Shift-DR, clock out 1's till we hit the right device in the chain */ + jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); + + uint8_t tap_out; + const uint8_t *const data_in = buffer; + for (size_t idx = 0U; idx < length; ++idx) { + const uint8_t tap_in = reverse_bits8(data_in[idx]); + jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); + + if (idx % 8192U) { + DEBUG_PROTO("%s: %zu/%zu bytes written\n", __func__, idx, length); + } + } + + /* Make sure we're in Exit1-DR having clocked out 1's for any more devices on the chain */ + jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); + /* Now go through Update-DR and back to Idle */ + jtagtap_return_idle(1U); + + return true; +} + static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv) { (void)argc; From d37de0579ccf5ba0e71fef8d394c62c4ed22fc5b Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 17:23:49 -0700 Subject: [PATCH 201/247] lattice_ecp5: Fixed up the order of flash operations to ensure SRAM programming succeeds --- src/target/lattice_ecp5.c | 53 ++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 7c3ea847d48..6abba4940cd 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -166,7 +166,6 @@ typedef struct ecp5_ctx { uint8_t *cmd_buffer; uint8_t *data_buffer; uint16_t buffer_len; - bool wrote_to_sram; } ecp5_ctx_s; typedef struct ecp5_device { @@ -226,7 +225,6 @@ static void ecp5_spi_write( static void ecp5_spi_run_command(target_s *target, uint16_t command, target_addr_t address); static void ecp5_spi_xfr_jtag(target_s *target, uint8_t *data_out, const uint8_t *data_in, size_t length); -static bool ecp5_sram_prepare(target_flash_s *flash); static bool ecp5_sram_done(target_flash_s *flash); static bool ecp5_sram_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool ecp5_sram_write(target_flash_s *flash, target_addr_t dest, const void *buffer, size_t length); @@ -264,7 +262,6 @@ void lattice_ecp5_handler(const uint8_t dev_index) flash->start = ECP5_SRAM_BASE; flash->blocksize = flash->length; flash->writesize = flash->length; // devices[dev].frame_len; - flash->prepare = ecp5_sram_prepare; flash->done = ecp5_sram_done; flash->erase = ecp5_sram_erase; flash->write = ecp5_sram_write; @@ -346,14 +343,10 @@ static bool ecp5_check_error(target_s *const target) static void ecp5_reset(target_s *const target) { - ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; - const uint8_t dev_index = ctx->device_index; - - // If we wrote to the device SRAM, then exit out of programming mode - // otherwise do the normal reset - if (!ctx->wrote_to_sram) { - jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); - jtag_proc.jtagtap_cycle(false, false, 50U); + // NOTE: BMDA doesn't handle flash finalization properly when in CLI mode, so we need to do so manually + for (target_flash_s *flash = target->flash; flash != NULL; flash = flash->next) { + if (flash->operation != FLASH_OPERATION_NONE) + flash->done(flash); } } @@ -384,12 +377,6 @@ static bool ecp5_exit_flash(target_s *const target) const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; const uint8_t dev_index = ctx->device_index; - if (ctx->wrote_to_sram) { - // Exit configuration mode - jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); - jtag_proc.jtagtap_cycle(false, false, 50U); - } - const uint32_t status = ecp5_read32(dev_index, CMD_LSC_READ_STATUS); const uint32_t result = @@ -422,13 +409,18 @@ static bool ecp5_spi_flash_prepare(target_flash_s *flash) static bool ecp5_spi_flash_done(target_flash_s *flash) { + target_s *const target = flash->t; + ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + /* - * The ECP5 doesn't have a known way to exit SPI background mode, so we need to reset the whole + * The ECP5 doesn't have any way to exit SPI background mode, so we need to reset the whole * device. */ - ecp5_reset(flash->t); + jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); + jtag_proc.jtagtap_cycle(false, false, 50U); - return true; + return ecp5_check_error(target); } static void ecp5_spi_read(target_s *const target, const uint16_t command, const target_addr_t address, @@ -509,20 +501,19 @@ static void ecp5_spi_xfr_jtag( jtagtap_return_idle(1U); } -static bool ecp5_sram_prepare(target_flash_s *const flash) +static bool ecp5_sram_done(target_flash_s *const flash) { - const target_s *const target = flash->t; + target_s *const target = flash->t; ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; - ctx->wrote_to_sram = true; - - return true; -} + if (flash->operation == FLASH_OPERATION_WRITE) { + // Exit configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + } -static bool ecp5_sram_done(target_flash_s *const flash) -{ - (void)flash; - return true; + return ecp5_check_error(target); } static bool ecp5_sram_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) @@ -575,7 +566,7 @@ static bool ecp5_sram_write( jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); if (idx % 8192U) { - DEBUG_PROTO("%s: %zu/%zu bytes written\n", __func__, idx, length); + DEBUG_TARGET("%s: %" PRIu32 "/%" PRIu32 " bytes written\n", __func__, idx, length); } } From 6f4ecb7dcdc9044d53ad2f989f892dfcd6933824 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 17:35:28 -0700 Subject: [PATCH 202/247] buffer_utils: Fixed `rbit` guard against ARMv6-M --- src/include/buffer_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/buffer_utils.h b/src/include/buffer_utils.h index 891f5a25e2a..25e713dc673 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -111,7 +111,7 @@ static inline size_t write_char(char *const buffer, const size_t buffer_size, co static inline uint8_t reverse_bits8(const uint8_t data) { if (!BMD_CONSTANT_P(data)) { -#if defined(__arm__) || defined(__aarch64__) +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) uint32_t result; __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); return (result & 0xff000000U) >> 24U; @@ -125,7 +125,7 @@ static inline uint8_t reverse_bits8(const uint8_t data) static inline uint16_t reverse_bits16(const uint16_t data) { if (!BMD_CONSTANT_P(data)) { -#if defined(__arm__) || defined(__aarch64__) +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) uint32_t result; __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); return (result & 0xffff0000U) >> 16U; @@ -142,7 +142,7 @@ static inline uint16_t reverse_bits16(const uint16_t data) static inline uint32_t reverse_bits24(const uint32_t data) { if (!BMD_CONSTANT_P(data)) { -#if defined(__arm__) || defined(__aarch64__) +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) uint32_t result; __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); return (result & 0xffffff00U) >> 8U; @@ -162,7 +162,7 @@ static inline uint32_t reverse_bits24(const uint32_t data) static inline uint32_t reverse_bits32(const uint32_t data) { if (!BMD_CONSTANT_P(data)) { -#if defined(__arm__) || defined(__aarch64__) +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) uint32_t result; __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); return result; From fb9911fedb35005193713090e4514d7a71601ad0 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 17:44:38 -0700 Subject: [PATCH 203/247] lattice_ecp5: Fix identification of attached SPI flash --- src/target/lattice_ecp5.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 6abba4940cd..86e412f9dd8 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -311,6 +311,11 @@ static bool ecp5_attach(target_s *const target) ecp5_enter_flash(target); + // Create a synthetic flash object so we can shell out to `spi_flash_prepare` to enter transparent SPI mode + target_flash_s flash; + flash.t = target; + ecp5_spi_flash_prepare(&flash); + spi_flash_id_s flash_id; ecp5_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0U, &flash_id, sizeof(flash_id)); @@ -321,9 +326,9 @@ static bool ecp5_attach(target_s *const target) flash_id.manufacturer, flash_id.type, capacity); spi_flash_s *const spi_flash = bmp_spi_add_flash(target, ECP5_FLASH_BASE, capacity, ecp5_spi_read, ecp5_spi_write, ecp5_spi_run_command); - target_flash_s *const flash = &spi_flash->flash; - flash->prepare = ecp5_spi_flash_prepare; - flash->done = ecp5_spi_flash_done; + target_flash_s *const target_flash = &spi_flash->flash; + target_flash->prepare = ecp5_spi_flash_prepare; + target_flash->done = ecp5_spi_flash_done; } else DEBUG_INFO("Flash identification failed\n"); From fd7ee8d4bca6823a00383905cd272ad36375ef4a Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 17:44:55 -0700 Subject: [PATCH 204/247] lattice_ecp5: Codestyle cleanup --- src/target/lattice_ecp5.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 86e412f9dd8..30b461e9738 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -267,6 +267,7 @@ void lattice_ecp5_handler(const uint8_t dev_index) flash->write = ecp5_sram_write; target_add_flash(target, flash); + break; } } @@ -384,15 +385,14 @@ static bool ecp5_exit_flash(target_s *const target) const uint32_t status = ecp5_read32(dev_index, CMD_LSC_READ_STATUS); - const uint32_t result = + const bool result = (ECP5_STATUS_BSE_ERROR(status) || ECP5_STATUS_ID_ERROR(status) || ECP5_STATUS_EXEC_ERROR(status) || ECP5_STATUS_PRIMARY_CFG_FAIL(status) || ECP5_STATUS_FAILURE(status) || ECP5_STATUS_INVALID_COMMAND(status)); - if (result != 0) { - DEBUG_ERROR("Bitstream programming failed: %" PRIu32 "\n", result); - } + if (result) + DEBUG_ERROR("Bitstream programming failed: %" PRIu32 "\n", status); - return result != 0; + return !result; } static bool ecp5_spi_flash_prepare(target_flash_s *flash) @@ -570,9 +570,8 @@ static bool ecp5_sram_write( const uint8_t tap_in = reverse_bits8(data_in[idx]); jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); - if (idx % 8192U) { - DEBUG_TARGET("%s: %" PRIu32 "/%" PRIu32 " bytes written\n", __func__, idx, length); - } + if (idx % 8192U) + DEBUG_TARGET("%s: %" PRIu32 "/%" PRIu32 " bytes written\n", __func__, (uint32_t)idx, (uint32_t)length); } /* Make sure we're in Exit1-DR having clocked out 1's for any more devices on the chain */ From 5057f2fd28c21acb489fb06aeaf37354d16ab208 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 18:25:33 -0700 Subject: [PATCH 205/247] lattice_ecp5: Prevent the ECP5 from becoming a zombie --- src/target/lattice_ecp5.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 30b461e9738..4766db7cfd2 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -333,6 +333,8 @@ static bool ecp5_attach(target_s *const target) } else DEBUG_INFO("Flash identification failed\n"); + // Make sure to reset the FPGA so we don't leave it in a coma + ecp5_spi_flash_done(&flash); ecp5_exit_flash(target); return true; From b150f5eb76e6c388d2d5981e54dccd0cdd744475 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Tue, 9 Dec 2025 17:00:36 -0700 Subject: [PATCH 206/247] stm32h7rs: Adding zyp's old STM32H7R/S code. Updated for newer API. Almost works. --- src/target/cortexm.c | 1 + src/target/meson.build | 1 + src/target/stm32h7rs.c | 583 ++++++++++++++++++++++++++++++++++++++ src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 5 files changed, 587 insertions(+) create mode 100644 src/target/stm32h7rs.c diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 97e0fb03df9..9a5c88a0296 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -395,6 +395,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) PROBE(stm32f4_probe); PROBE(stm32h5_probe); PROBE(stm32h7_probe); + PROBE(stm32h7rs_probe); PROBE(stm32mp15_cm4_probe); PROBE(stm32l0_probe); PROBE(stm32l1_probe); diff --git a/src/target/meson.build b/src/target/meson.build index 78570942292..c6ead651359 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -343,6 +343,7 @@ target_stm = declare_dependency( 'stm32g0.c', 'stm32h5.c', 'stm32h7.c', + 'stm32h7rs.c', 'stm32l0.c', 'stm32l4.c', 'stm32mp15.c', diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c new file mode 100644 index 00000000000..9be5c9af0dd --- /dev/null +++ b/src/target/stm32h7rs.c @@ -0,0 +1,583 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017-2020 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de + * Copyright (C) 2022-2023 1BitSquared + * Modified by Rachel Mant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file implements STM32H7R/S target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * References: + * RM0477 - STM32H7Rx/7Sx Arm®-based 32-bit MCUs, Rev. 6 + * https://www.st.com/resource/en/reference_manual/rm0477-stm32h7rx7sx-armbased-32bit-mcus-stmicroelectronics.pdf + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "stm32_common.h" + +#define FLASH_ACR 0x000U +#define FLASH_KEYR 0x004U +#define FLASH_CR 0x010U +#define FLASH_SR 0x014U +#define FLASH_IER 0x020U +#define FLASH_ISR 0x024U +#define FLASH_ICR 0x028U +//#define FLASH_OPTKEYR 0x008U +//#define FLASH_CCR 0x014U +//#define FLASH_OPTCR 0x018U +//#define FLASH_OPTSR_CUR 0x01cU +//#define FLASH_OPTSR 0x020U +//#define FLASH_CRCCR 0x050U +//#define FLASH_CRCDATA 0x05cU + +/* Flash Program and Erase Controller Register Map */ +#define FPEC1_BASE 0x52002000U +#define FLASH_SR_BSY (1U << 0U) +#define FLASH_SR_WBNE (1U << 1U) +#define FLASH_SR_QW (1U << 2U) +#define FLASH_SR_CRC_BUSY (1U << 3U) +#define FLASH_ISR_EOP (1U << 16U) +#define FLASH_ISR_WRPERR (1U << 17U) +#define FLASH_ISR_PGSERR (1U << 18U) +#define FLASH_ISR_STRBERR (1U << 19U) +#define FLASH_ISR_INCERR (1U << 21U) +#define FLASH_ISR_RDSERR (1U << 24U) +#define FLASH_ISR_SNECCERR (1U << 25U) +#define FLASH_ISR_DBECCERR (1U << 26U) +#define FLASH_ISR_CRCEND (1U << 27U) +#define FLASH_ISR_CRCRDERR (1U << 28U) +#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) +#define FLASH_ISR_ERROR_MASK \ + (FLASH_ISR_WRPERR | FLASH_ISR_PGSERR | FLASH_ISR_STRBERR | FLASH_ISR_INCERR | FLASH_ISR_ERROR_READ) +#define FLASH_CR_LOCK (1U << 0U) +#define FLASH_CR_PG (1U << 1U) +#define FLASH_CR_SER (1U << 2U) +#define FLASH_CR_BER (1U << 3U) +//#define FLASH_CR_PSIZE8 (0U << 4U) +//#define FLASH_CR_PSIZE16 (1U << 4U) +//#define FLASH_CR_PSIZE32 (2U << 4U) +//#define FLASH_CR_PSIZE64 (3U << 4U) +#define FLASH_CR_FW (1U << 4U) +#define FLASH_CR_START (1U << 5U) +#define FLASH_CR_SSN_SHIFT 6U +//#define FLASH_CR_SNB_1 (1U << 8U) +//#define FLASH_CR_SNB (3U << 8U) +//#define FLASH_CR_CRC_EN (1U << 15U) +// +//#define FLASH_OPTCR_OPTLOCK (1U << 0U) +//#define FLASH_OPTCR_OPTSTRT (1U << 1U) +// +//#define FLASH_OPTSR_IWDG1_SW (1U << 4U) +// +//#define FLASH_CRCCR_ALL_BANK (1U << 7U) +//#define FLASH_CRCCR_START_CRC (1U << 16U) +//#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) +//#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) + +#define STM32H7RS_FLASH_KEY1 0x45670123U +#define STM32H7RS_FLASH_KEY2 0xcdef89abU + +#define STM32H7RS_OPT_KEY1 0x08192a3bU +#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU + +#define DBGMCU_IDCODE 0x5c001000U +#define STM32H7RS_FLASH_SIZE 0x1ff1e880U +#define STM32H7Bx_FLASH_SIZE 0x08fff80cU +/* Access from processor address space. + * Access via the APB-D is at 0xe00e1000 */ +#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) +#define DBGMCU_CR (DBGMCU_IDCODE + 4U) +#define DBGSLEEP_D1 (1U << 0U) +#define DBGSTOP_D1 (1U << 1U) +#define DBGSTBY_D1 (1U << 2U) +#define DBGSTOP_D3 (1U << 7U) +#define DBGSTBY_D3 (1U << 8U) +#define D1DBGCKEN (1U << 21U) +#define D3DBGCKEN (1U << 22U) + +#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU +#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U + +#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00100000U +#define NUM_SECTOR_PER_BANK 8U +#define FLASH_SECTOR_SIZE 0x20000U + +#define ID_STM32H7RS 0x485U /* RM0477 */ + +typedef struct stm32h7rs_flash { + target_flash_s target_flash; + align_e psize; + uint32_t regbase; +} stm32h7rs_flash_s; + +typedef struct stm32h7rs_priv { + uint32_t dbg_cr; +} stm32h7rs_priv_s; + +static bool stm32h7rs_uid(target_s *target, int argc, const char **argv); +//static bool stm32h7rs_crc(target_s *target, int argc, const char **argv); +static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv); +static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv); + +const command_s stm32h7rs_cmd_list[] = { + {"psize", stm32h7rs_cmd_psize, "Configure flash write parallelism: (x8|x16|x32|x64(default))"}, + {"uid", stm32h7rs_uid, "Print unique device ID"}, + //{"crc", stm32h7rs_crc, "Print CRC of both banks"}, + {"revision", stm32h7rs_cmd_rev, "Returns the Device ID and Revision"}, + {NULL, NULL, NULL}, +}; + +static bool stm32h7rs_attach(target_s *target); +static void stm32h7rs_detach(target_s *target); +static bool stm32h7rs_flash_erase(target_flash_s *target_flash, target_addr_t addr, size_t len); +static bool stm32h7rs_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len); +static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess); + +static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, size_t blocksize) +{ + stm32h7rs_flash_s *flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + target_flash_s *target_flash = &flash->target_flash; + target_flash->start = addr; + target_flash->length = length; + target_flash->blocksize = blocksize; + target_flash->erase = stm32h7rs_flash_erase; + target_flash->write = stm32h7rs_flash_write; + target_flash->writesize = 2048; + target_flash->erased = 0xffU; + flash->regbase = FPEC1_BASE; + flash->psize = ALIGN_64BIT; + target_add_flash(target, target_flash); +} + +bool stm32h7rs_probe(target_s *target) +{ + const adiv5_access_port_s *const ap = cortex_ap(target); + if (ap->partno != ID_STM32H7RS) + return false; + + target->part_id = ap->partno; + + /* Save private storage */ + stm32h7rs_priv_s *priv = calloc(1, sizeof(*priv)); + if (!priv) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return false; + } + target->target_storage = priv; + priv->dbg_cr = target_mem32_read32(target, DBGMCU_CR); + + target->driver = "STM32H7R/S"; + target->attach = stm32h7rs_attach; + target->detach = stm32h7rs_detach; + target->mass_erase = stm32h7rs_mass_erase; + target_add_commands(target, stm32h7rs_cmd_list, target->driver); + + /* EMEB - Decide which of these is correct */ +#if 1 + /* RM0433 Rev 4 is not really clear, what bits are needed in DBGMCU_CR. Maybe more flags needed? */ + const uint32_t dbgmcu_ctrl = DBGSLEEP_D1 | D1DBGCKEN; + target_mem32_write32(target, DBGMCU_CR, dbgmcu_ctrl); +#else + /* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */ + target_mem32_write32(target, STM32H7_DBGMCU_APB3FREEZE, STM32H7_DBGMCU_APB3FREEZE_WWDG1); + target_mem32_write32(target, STM32H7_DBGMCU_APB4FREEZE, STM32H7_DBGMCU_APB4FREEZE_IWDG1); + /* + * Make sure that both domain D1 and D3 debugging are enabled and that we can keep + * debugging through sleep, stop and standby states for domain D1 + */ + target_mem32_write32(target, STM32H7_DBGMCU_CONFIG, + target_mem32_read32(target, STM32H7_DBGMCU_CONFIG) | STM32H7_DBGMCU_CONFIG_DBGSLEEP_D1 | + STM32H7_DBGMCU_CONFIG_DBGSTOP_D1 | STM32H7_DBGMCU_CONFIG_DBGSTBY_D1 | STM32H7_DBGMCU_CONFIG_D1DBGCKEN | + STM32H7_DBGMCU_CONFIG_D3DBGCKEN); + stm32h7_configure_wdts(target); +#endif + + /* Build the RAM map - EMEB: ITCM/DTCM are too big -shared w/ AXI */ + switch (target->part_id) { + case ID_STM32H7RS: { + /* Table 6. Memory map and default device memory area attributes RM0477, pg151 */ + target_add_ram32(target, 0x00000000, 0x30000); /* ITCM RAM, 192 KiB */ + target_add_ram32(target, 0x20000000, 0x30000); /* DTCM RAM, 192 KiB */ + target_add_ram32(target, 0x24000000, 0x72000); /* AXI RAM1+2+3+4, 456 KiB [128+128+128+72] contiguous, */ + target_add_ram32(target, 0x30000000, 0x8000); /* AHB SRAM1+2, 32 KiB [16+16] contiguous, */ + break; + } + default: + break; + } + + /* + * Note on SRD from AN5293, 3. System architecture differences between STM32F7 and STM32H7 Series + * > The D3 domain evolved into a domain called SRD domain (or smart-run domain). + */ + + /* Build the Flash map */ + switch (target->part_id) { + case ID_STM32H7RS: + stm32h7rs_add_flash(target, STM32H7RS_FLASH_BANK1_BASE, 0x10000U, 0x2000U); + break; + default: + break; + } + + return true; +} + +static bool stm32h7rs_attach(target_s *target) +{ + if (!cortexm_attach(target)) + return false; + /* + * If IWDG runs as HARDWARE watchdog (§44.3.4) erase + * will be aborted by the Watchdog and erase fails! + * Setting IWDG_KR to 0xaaaa does not seem to help! + */ + //const uint32_t optsr = target_mem_read32(target, FPEC1_BASE + FLASH_OPTSR); + //if (!(optsr & FLASH_OPTSR_IWDG1_SW)) + // tc_printf(target, "Hardware IWDG running. Expect failure. Set IWDG1_SW!"); + return true; +} + +static void stm32h7rs_detach(target_s *target) +{ + //stm32h7rs_priv_s *ps = (stm32h7rs_priv_s *)target->target_storage; + //target_mem_write32(target, DBGMCU_CR, ps->dbg_cr); + cortexm_detach(target); +} + +static bool stm32h7rs_flash_busy_wait(target_s *const target, const uint32_t regbase) +{ + uint32_t status = FLASH_SR_BSY | FLASH_SR_QW; + while (status & (FLASH_SR_BSY | FLASH_SR_QW)) { + status = target_mem32_read32(target, regbase + FLASH_SR); + uint32_t istatus = target_mem32_read32(target, regbase + FLASH_ISR); + if ((istatus & FLASH_ISR_ERROR_MASK) || target_check_error(target)) { + DEBUG_ERROR("%s: error status %08" PRIx32 "\n", __func__, istatus); + target_mem32_write32(target, regbase + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); + return false; + } + } + return true; +} + +static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) +{ + (void)addr; + const uint32_t regbase = FPEC1_BASE; + /* Wait for any pending operations to complete */ + if (!stm32h7rs_flash_busy_wait(target, regbase)) + return false; + /* Unlock the device Flash if not already unlocked (it's an error to re-key the controller if it is) */ + if (target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK) { + /* Enable Flash controller access */ + target_mem32_write32(target, regbase + FLASH_KEYR, STM32H7RS_FLASH_KEY1); + target_mem32_write32(target, regbase + FLASH_KEYR, STM32H7RS_FLASH_KEY2); + } + /* Return whether we were able to put the device into unlocked mode */ + return !(target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK); +} + +///* Helper for offsetting FLASH_CR bits correctly */ +//static uint32_t stm32h7rs_flash_cr(uint32_t sector_size, const uint32_t ctrl, int snb) +//{ +// uint32_t command = ctrl; +// /* H74x, H72x IP: 128 KiB and has PSIZE */ +// if (sector_size == FLASH_SECTOR_SIZE) { +// command = ctrl | (snb * FLASH_CR_SNB_1); +// DEBUG_TARGET("%s: patching FLASH_CR from 0x%08" PRIx32 " to 0x%08" PRIx32 "\n", __func__, ctrl, command); +// return command; +// } +// +// /* H7Bx IP: 8 KiB and no PSIZE */ +// /* Save and right-shift FW, START bits */ +// const uint32_t temp_fw_start = command & (FLASH_CR_FW | FLASH_CR_START); +// /* Parallelism is ignored */ +// command &= ~(FLASH_CR_PSIZE64 | FLASH_CR_FW | FLASH_CR_START); +// /* Restore FW, START to H7Bx-correct bits */ +// command |= (temp_fw_start >> 2U); +// /* SNB offset is different, too */ +// command |= (snb << 6U); +// DEBUG_TARGET("%s: patching FLASH_CR from 0x%08" PRIx32 " to 0x%08" PRIx32 "\n", __func__, ctrl, command); +// return command; +//} + +static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) +{ + const uint32_t sector_size = target_flash->blocksize; + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + /* Unlock the Flash */ + if (!stm32h7rs_flash_unlock(target, addr)) + return false; + /* We come out of reset with HSI 64 MHz. Adapt FLASH_ACR.*/ + target_mem32_write32(target, flash->regbase + FLASH_ACR, 0); + /* Calculate SNB span */ + addr &= target_flash->length - 1U; + const size_t end_sector = (addr + len - 1U) / sector_size; + //const align_e psize = flash->psize; + const uint32_t reg_base = flash->regbase; + + for (size_t begin_sector = addr / sector_size; begin_sector <= end_sector; ++begin_sector) { + /* Erase the current Flash sector */ + //const uint32_t ctrl = stm32h7rs_flash_cr(sector_size, (psize * FLASH_CR_PSIZE16) | FLASH_CR_SER, begin_sector); + const uint32_t ctrl = FLASH_CR_SER | (begin_sector << FLASH_CR_SSN_SHIFT); + target_mem32_write32(target, reg_base + FLASH_CR, ctrl); + //const uint32_t ctrl_start = stm32h7rs_flash_cr(sector_size, ctrl | FLASH_CR_START, begin_sector); + const uint32_t ctrl_start = ctrl | FLASH_CR_START; + target_mem32_write32(target, reg_base + FLASH_CR, ctrl_start); + + /* Wait for the operation to complete and report errors */ + DEBUG_INFO("Erasing, ctrl = %08" PRIx32 " status = %08" PRIx32 "\n", + target_mem32_read32(target, reg_base + FLASH_CR), target_mem32_read32(target, reg_base + FLASH_SR)); + + if (!stm32h7rs_flash_busy_wait(target, reg_base)) + return false; + } + return true; +} + +static bool stm32h7rs_flash_write( + target_flash_s *const target_flash, const target_addr_t dest, const void *const src, const size_t len) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + /* Unlock the Flash */ + if (!stm32h7rs_flash_unlock(target, dest)) + return false; + + /* Prepare the Flash write operation */ + //const uint32_t ctrl = stm32h7rs_flash_cr(target_flash->blocksize, flash->psize * FLASH_CR_PSIZE16, 0); + //target_mem_write32(target, flash->regbase + FLASH_CR, ctrl); + //const uint32_t ctrl_pg = stm32h7rs_flash_cr(target_flash->blocksize, ctrl | FLASH_CR_PG, 0); + const uint32_t ctrl_pg = FLASH_CR_PG; + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg); + /* does H7 stall?*/ + + /* Write the data to the Flash */ + target_mem32_write(target, dest, src, len); + + /* Wait for the operation to complete and report errors */ + if (!stm32h7rs_flash_busy_wait(target, flash->regbase)) + return false; + + /* Close write windows */ + target_mem32_write32(target, flash->regbase + FLASH_CR, 0); + return true; +} + +static bool stm32h7rs_erase_bank( + target_s *const target, const align_e psize, const uint32_t start_addr, const uint32_t reg_base) +{ + if (!stm32h7rs_flash_unlock(target, start_addr)) { + DEBUG_ERROR("Bank erase: Unlock bank failed\n"); + return false; + } + /* BER and start can be merged (§3.3.10). */ + //const uint32_t ctrl = stm32h7rs_flash_cr(target->flash->blocksize, (psize * FLASH_CR_PSIZE16) | FLASH_CR_BER | FLASH_CR_START, 0); + const uint32_t ctrl = FLASH_CR_BER | FLASH_CR_START; + target_mem32_write32(target, reg_base + FLASH_CR, ctrl); + DEBUG_INFO("Mass erase of bank started\n"); + return true; +} + +static bool stm32h7rs_wait_erase_bank(target_s *const target, platform_timeout_s *const timeout, const uint32_t reg_base) +{ + while (target_mem32_read32(target, reg_base + FLASH_SR) & FLASH_SR_QW) { + if (target_check_error(target)) { + DEBUG_ERROR("mass erase bank: comm failed\n"); + return false; + } + target_print_progress(timeout); + } + return true; +} + +static bool stm32h7rs_check_bank(target_s *const target, const uint32_t reg_base) +{ + uint32_t status = target_mem32_read32(target, reg_base + FLASH_ISR); + if (status & FLASH_ISR_ERROR_MASK) + DEBUG_ERROR("mass erase bank: error sr %" PRIx32 "\n", status); + return !(status & FLASH_ISR_ERROR_MASK); +} + +/* Both banks are erased in parallel.*/ +static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess) +{ + align_e psize = ALIGN_64BIT; + /* + * XXX: What is this and why does it exist? + * A dry-run walk-through says it'll pull out the psize for the first Flash region added by stm32h7rs_probe() + * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. + */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + if (flash->write == stm32h7rs_flash_write) + psize = ((struct stm32h7rs_flash *)flash)->psize; + } + /* Send mass erase Flash start instruction */ + if (!stm32h7rs_erase_bank(target, psize, STM32H7RS_FLASH_BANK1_BASE, FPEC1_BASE)) + return false; + + /* Wait for the banks to finish erasing */ + if (!stm32h7rs_wait_erase_bank(target, print_progess, FPEC1_BASE)) + return false; + + /* Check the banks for final errors */ + return stm32h7rs_check_bank(target, FPEC1_BASE); +} + +/* + * Print the Unique device ID. + * Can be reused for other STM32 devices with uid as parameter. + */ +static bool stm32h7rs_uid(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const uint32_t uid_addr = 0x08fff800U; + + tc_printf(target, "0x"); + for (size_t i = 0; i < 12U; i += 4U) { + const uint32_t value = target_mem32_read32(target, uid_addr + i); + tc_printf(target, "%02X%02X%02X%02X", (value >> 24U) & 0xffU, (value >> 16U) & 0xffU, (value >> 8U) & 0xffU, + value & 0xffU); + } + tc_printf(target, "\n"); + return true; +} + +//static bool stm32h7rs_crc_bank(target_s *target, uint32_t addr) +//{ +// const uint32_t reg_base = FPEC1_BASE; +// if (!stm32h7rs_flash_unlock(target, addr)) +// return false; +// +// target_mem_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); +// const uint32_t crc_ctrl = FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_ALL_BANK; +// target_mem_write32(target, reg_base + FLASH_CRCCR, crc_ctrl); +// target_mem_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); +// uint32_t status = FLASH_SR_CRC_BUSY; +//#if ENABLE_DEBUG == 1 +// const uint8_t bank = reg_base == FPEC1_BASE ? 1 : 2; +//#endif +// while (status & FLASH_SR_CRC_BUSY) { +// status = target_mem_read32(target, reg_base + FLASH_SR); +// if (target_check_error(target)) { +// DEBUG_ERROR("CRC bank %u: comm failed\n", bank); +// return false; +// } +// if (status & FLASH_SR_ERROR_READ) { +// DEBUG_ERROR("CRC bank %u: error status %08" PRIx32 "\n", bank, status); +// return false; +// } +// } +// return true; +//} +// +//static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) +//{ +// (void)argc; +// (void)argv; +// if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK1_BASE)) +// return false; +// uint32_t crc1 = target_mem_read32(target, FPEC1_BASE + FLASH_CRCDATA); +// if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK2_BASE)) +// return false; +// uint32_t crc2 = 0;//TODO: remove: target_mem_read32(target, FPEC2_BASE + FLASH_CRCDATA); +// tc_printf(target, "CRC: bank1 0x%08lx, bank2 0x%08lx\n", crc1, crc2); +// return true; +//} + +static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv) +{ + if (argc == 1) { + align_e psize = ALIGN_64BIT; + /* + * XXX: What is this and why does it exist? + * A dry-run walk-through says it'll pull out the psize for the first Flash region added by stm32h7rs_probe() + * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. + */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + if (flash->write == stm32h7rs_flash_write) + psize = ((stm32h7rs_flash_s *)flash)->psize; + } + tc_printf(target, "Flash write parallelism: %s\n", stm32_psize_to_string(psize)); + } else { + align_e psize; + if (!stm32_psize_from_string(target, argv[1], &psize)) + return false; + + /* + * XXX: What is this and why does it exist? + * A dry-run walk-through says it'll overwrite psize for every Flash region added by stm32h7rs_probe() + * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. + */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + if (flash->write == stm32h7rs_flash_write) + ((stm32h7rs_flash_s *)flash)->psize = psize; + } + } + return true; +} + +static const struct { + uint16_t rev_id; + char revision; +} stm32h7rs_revisions[] = { + {0x1003U, 'Y'}, +}; + +static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* DBGMCU identity code register */ + const uint32_t dbgmcu_idc = target_mem32_read32(target, DBGMCU_IDC); + const uint16_t rev_id = dbgmcu_idc >> STM32H7RS_DBGMCU_IDCODE_REV_SHIFT; + const uint16_t dev_id = dbgmcu_idc & STM32H7RS_DBGMCU_IDCODE_DEV_MASK; + + /* Print device */ + switch (dev_id) { + case ID_STM32H7RS: + tc_printf(target, "STM32H7Rx/Sx\n"); + break; + default: + tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); + return false; + } + /* Print revision */ + char rev = '?'; + for (size_t i = 0; i < ARRAY_LENGTH(stm32h7rs_revisions); i++) { + /* Check for matching revision */ + if (stm32h7rs_revisions[i].rev_id == rev_id) + rev = stm32h7rs_revisions[i].revision; + } + tc_printf(target, "Revision %c\n", rev); + + return true; +} diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 49bc3fc0845..0344c360cfe 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -151,6 +151,7 @@ TARGET_PROBE_WEAK_NOP(stm32f4_probe) TARGET_PROBE_WEAK_NOP(stm32g0_probe) TARGET_PROBE_WEAK_NOP(stm32h5_probe) TARGET_PROBE_WEAK_NOP(stm32h7_probe) +TARGET_PROBE_WEAK_NOP(stm32h7rs_probe) TARGET_PROBE_WEAK_NOP(stm32l0_probe) TARGET_PROBE_WEAK_NOP(stm32l1_probe) TARGET_PROBE_WEAK_NOP(stm32l4_probe) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 932fe8f0632..249aed67ec1 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -102,6 +102,7 @@ bool stm32f4_probe(target_s *target); bool stm32g0_probe(target_s *target); bool stm32h5_probe(target_s *target); bool stm32h7_probe(target_s *target); +bool stm32h7rs_probe(target_s *target); bool stm32l0_probe(target_s *target); bool stm32l1_probe(target_s *target); bool stm32l4_probe(target_s *target); From d91e0fba7f2eefeaf4423c4cf45647f9cecf30bd Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Wed, 10 Dec 2025 14:27:08 -0700 Subject: [PATCH 207/247] stm32h7rs: Added code to clear FLASH_ISR bits during unlock. Helps. --- src/target/stm32h7rs.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 9be5c9af0dd..6089b0cedd8 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -273,9 +273,19 @@ static void stm32h7rs_detach(target_s *target) static bool stm32h7rs_flash_busy_wait(target_s *const target, const uint32_t regbase) { uint32_t status = FLASH_SR_BSY | FLASH_SR_QW; + + /* EMEB - clear any pending flash interrupts that could hurt us */ + uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR) & + FLASH_ISR_ERROR_MASK; + if(istatus != 0U) + { + DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); + target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus); + } + while (status & (FLASH_SR_BSY | FLASH_SR_QW)) { status = target_mem32_read32(target, regbase + FLASH_SR); - uint32_t istatus = target_mem32_read32(target, regbase + FLASH_ISR); + istatus = target_mem32_read32(target, regbase + FLASH_ISR); if ((istatus & FLASH_ISR_ERROR_MASK) || target_check_error(target)) { DEBUG_ERROR("%s: error status %08" PRIx32 "\n", __func__, istatus); target_mem32_write32(target, regbase + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); @@ -365,7 +375,7 @@ static bool stm32h7rs_flash_write( target_flash_s *const target_flash, const target_addr_t dest, const void *const src, const size_t len) { target_s *target = target_flash->t; - const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; /* Unlock the Flash */ if (!stm32h7rs_flash_unlock(target, dest)) return false; From fe8fb8df4781f3acc899ae5eda2613110250fab5 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 11 Dec 2025 16:21:03 -0700 Subject: [PATCH 208/247] stm32h7rs: Bringing old code up to latest standards. Starting to work! --- src/target/stm32h7rs.c | 133 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 15 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 6089b0cedd8..ae9a32443cc 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -118,9 +118,9 @@ #define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U #define STM32H7RS_FLASH_BANK1_BASE 0x08000000U -#define STM32H7RS_FLASH_BANK_SIZE 0x00100000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U #define NUM_SECTOR_PER_BANK 8U -#define FLASH_SECTOR_SIZE 0x20000U +#define FLASH_SECTOR_SIZE 0x2000U #define ID_STM32H7RS 0x485U /* RM0477 */ @@ -151,6 +151,8 @@ static bool stm32h7rs_attach(target_s *target); static void stm32h7rs_detach(target_s *target); static bool stm32h7rs_flash_erase(target_flash_s *target_flash, target_addr_t addr, size_t len); static bool stm32h7rs_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len); +static bool stm32h7rs_flash_prepare(target_flash_s *target_flash); +static bool stm32h7rs_flash_done(target_flash_s *target_flash); static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess); static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, size_t blocksize) @@ -167,6 +169,8 @@ static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, target_flash->blocksize = blocksize; target_flash->erase = stm32h7rs_flash_erase; target_flash->write = stm32h7rs_flash_write; + target_flash->prepare = stm32h7rs_flash_prepare; + target_flash->done = stm32h7rs_flash_done; target_flash->writesize = 2048; target_flash->erased = 0xffU; flash->regbase = FPEC1_BASE; @@ -239,7 +243,7 @@ bool stm32h7rs_probe(target_s *target) /* Build the Flash map */ switch (target->part_id) { case ID_STM32H7RS: - stm32h7rs_add_flash(target, STM32H7RS_FLASH_BANK1_BASE, 0x10000U, 0x2000U); + stm32h7rs_add_flash(target, STM32H7RS_FLASH_BANK1_BASE, STM32H7RS_FLASH_BANK_SIZE, FLASH_SECTOR_SIZE); break; default: break; @@ -274,18 +278,9 @@ static bool stm32h7rs_flash_busy_wait(target_s *const target, const uint32_t reg { uint32_t status = FLASH_SR_BSY | FLASH_SR_QW; - /* EMEB - clear any pending flash interrupts that could hurt us */ - uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR) & - FLASH_ISR_ERROR_MASK; - if(istatus != 0U) - { - DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); - target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus); - } - while (status & (FLASH_SR_BSY | FLASH_SR_QW)) { status = target_mem32_read32(target, regbase + FLASH_SR); - istatus = target_mem32_read32(target, regbase + FLASH_ISR); + uint32_t istatus = target_mem32_read32(target, regbase + FLASH_ISR); if ((istatus & FLASH_ISR_ERROR_MASK) || target_check_error(target)) { DEBUG_ERROR("%s: error status %08" PRIx32 "\n", __func__, istatus); target_mem32_write32(target, regbase + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); @@ -295,13 +290,57 @@ static bool stm32h7rs_flash_busy_wait(target_s *const target, const uint32_t reg return true; } +static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t regbase) +{ + uint32_t status = FLASH_SR_QW, istatus = 0U; + /* Loop waiting for the queuewait bit to clear and EOP to set, indicating completion of all ongoing operations */ + while (!(istatus & FLASH_ISR_EOP) && (status & FLASH_SR_QW)) { + status = target_mem32_read32(target, regbase + FLASH_SR); + istatus = target_mem32_read32(target, regbase + FLASH_ISR); + /* If an error occurs, make noises */ + if (target_check_error(target)) { + DEBUG_ERROR("%s: error reading status\n", __func__); + return false; + } + } + /* Now the operation's complete, we can check the error bits */ + if (istatus & FLASH_ISR_ERROR_MASK) + DEBUG_ERROR("%s: Flash error: %08" PRIx32 "\n", __func__, istatus); + target_mem32_write32(target, regbase + FLASH_ICR, + istatus & (FLASH_ISR_EOP | FLASH_ISR_ERROR_MASK)); + /* Return whether any errors occured */ + return !(istatus & FLASH_ISR_ERROR_MASK); +} + static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) { (void)addr; const uint32_t regbase = FPEC1_BASE; + /* EMEB - clear any pending flash interrupts that could hurt us */ + uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR) & + FLASH_ISR_ERROR_MASK; + if(istatus != 0U) + { + DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); + target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus); + } + +#if 0 + // EMEB original zyp code /* Wait for any pending operations to complete */ if (!stm32h7rs_flash_busy_wait(target, regbase)) return false; +#else + // EMEB new code based on H7 driver + /* Read out the Flash status and tend to any pending conditions */ + const uint32_t status = target_mem32_read32(target, regbase + FLASH_SR); + /* Start by checking if there are any pending ongoing operations */ + if (status & FLASH_SR_QW) { + /* Wait for any pending operations to complete */ + if (!stm32h7rs_flash_wait_complete(target, regbase)) + return false; + } +#endif /* Unlock the device Flash if not already unlocked (it's an error to re-key the controller if it is) */ if (target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK) { /* Enable Flash controller access */ @@ -312,7 +351,25 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) return !(target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK); } -///* Helper for offsetting FLASH_CR bits correctly */ +static bool stm32h7rs_flash_prepare(target_flash_s *const target_flash) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + + /* Unlock the Flash controller to prepare it for operations */ + return stm32h7rs_flash_unlock(target, flash->regbase); +} + +static bool stm32h7rs_flash_done(target_flash_s *const target_flash) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + /* Lock the Flash controller to complete operations */ + target_mem32_write32(target, flash->regbase + FLASH_CR, FLASH_CR_LOCK); + return true; +} + +///* Helper for offsetting FLASH_CR bits correctly EMEB - not needed for just H7R/S */ //static uint32_t stm32h7rs_flash_cr(uint32_t sector_size, const uint32_t ctrl, int snb) //{ // uint32_t command = ctrl; @@ -338,6 +395,8 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) { +#if 0 + // EMEB original zyp code const uint32_t sector_size = target_flash->blocksize; target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; @@ -368,7 +427,28 @@ static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_add if (!stm32h7rs_flash_busy_wait(target, reg_base)) return false; } - return true; + return stm32h7rs_flash_wait_complete(target, flash->regbase); +#else + // EMEB new code based on latest H7 driver + (void)len; + /* Erases are always done one sector at a time - the target Flash API guarantees this */ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + + /* Calculate the sector to erase and set the operation runnning */ + const uint32_t sector = (addr - target_flash->start) / target_flash->blocksize; + const uint32_t ctrl = FLASH_CR_SER | (sector << FLASH_CR_SSN_SHIFT); + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl); + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl | FLASH_CR_START); + + /* Wait for the operation to complete and report errors */ + DEBUG_INFO("Erasing, ctrl = %08" PRIx32 " status = %08" PRIx32 "\n", + target_mem32_read32(target, flash->regbase + FLASH_CR), target_mem32_read32(target, flash->regbase + FLASH_SR)); + + /* Wait for the operation to complete and report errors */ + return stm32h7rs_flash_wait_complete(target, flash->regbase); + +#endif } static bool stm32h7rs_flash_write( @@ -389,6 +469,8 @@ static bool stm32h7rs_flash_write( /* does H7 stall?*/ /* Write the data to the Flash */ +#if 0 + /* original from zyp's 2024 version w/o queue waiting */ target_mem32_write(target, dest, src, len); /* Wait for the operation to complete and report errors */ @@ -398,6 +480,27 @@ static bool stm32h7rs_flash_write( /* Close write windows */ target_mem32_write32(target, flash->regbase + FLASH_CR, 0); return true; +#else + /* New stuff from H7 driver - adapted for 16 byte flash wordsize */ + for (size_t offset = 0U; offset < len; offset += 16U) { + const size_t amount = MIN(len - offset, 16U); + target_mem32_write(target, dest + offset, ((const uint8_t *)src) + offset, amount); + /* + * If this is the final chunk and the amount is not a multiple of 16 + * bytes, make sure the write is forced to complete per RM0468 §5.3.8 + * "Single write sequence" pg215 + */ + if (amount < 16U) + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg | FLASH_CR_FW); + + /* wait for QW bit to clear */ + while (target_mem32_read32(target, flash->regbase + FLASH_SR) & FLASH_SR_QW) + continue; + } + + /* Wait for the operation to complete and report errors */ + return stm32h7rs_flash_wait_complete(target, flash->regbase); +#endif } static bool stm32h7rs_erase_bank( From fda61ffddc07a2c8454eb56b491e89562d228558 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 11 Dec 2025 16:42:53 -0700 Subject: [PATCH 209/247] stm32h7rs: Cleaned out dead code & comments --- src/target/stm32h7rs.c | 110 +---------------------------------------- 1 file changed, 1 insertion(+), 109 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index ae9a32443cc..83f57e9a117 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -274,22 +274,6 @@ static void stm32h7rs_detach(target_s *target) cortexm_detach(target); } -static bool stm32h7rs_flash_busy_wait(target_s *const target, const uint32_t regbase) -{ - uint32_t status = FLASH_SR_BSY | FLASH_SR_QW; - - while (status & (FLASH_SR_BSY | FLASH_SR_QW)) { - status = target_mem32_read32(target, regbase + FLASH_SR); - uint32_t istatus = target_mem32_read32(target, regbase + FLASH_ISR); - if ((istatus & FLASH_ISR_ERROR_MASK) || target_check_error(target)) { - DEBUG_ERROR("%s: error status %08" PRIx32 "\n", __func__, istatus); - target_mem32_write32(target, regbase + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); - return false; - } - } - return true; -} - static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t regbase) { uint32_t status = FLASH_SR_QW, istatus = 0U; @@ -325,13 +309,6 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus); } -#if 0 - // EMEB original zyp code - /* Wait for any pending operations to complete */ - if (!stm32h7rs_flash_busy_wait(target, regbase)) - return false; -#else - // EMEB new code based on H7 driver /* Read out the Flash status and tend to any pending conditions */ const uint32_t status = target_mem32_read32(target, regbase + FLASH_SR); /* Start by checking if there are any pending ongoing operations */ @@ -340,7 +317,7 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) if (!stm32h7rs_flash_wait_complete(target, regbase)) return false; } -#endif + /* Unlock the device Flash if not already unlocked (it's an error to re-key the controller if it is) */ if (target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK) { /* Enable Flash controller access */ @@ -369,68 +346,8 @@ static bool stm32h7rs_flash_done(target_flash_s *const target_flash) return true; } -///* Helper for offsetting FLASH_CR bits correctly EMEB - not needed for just H7R/S */ -//static uint32_t stm32h7rs_flash_cr(uint32_t sector_size, const uint32_t ctrl, int snb) -//{ -// uint32_t command = ctrl; -// /* H74x, H72x IP: 128 KiB and has PSIZE */ -// if (sector_size == FLASH_SECTOR_SIZE) { -// command = ctrl | (snb * FLASH_CR_SNB_1); -// DEBUG_TARGET("%s: patching FLASH_CR from 0x%08" PRIx32 " to 0x%08" PRIx32 "\n", __func__, ctrl, command); -// return command; -// } -// -// /* H7Bx IP: 8 KiB and no PSIZE */ -// /* Save and right-shift FW, START bits */ -// const uint32_t temp_fw_start = command & (FLASH_CR_FW | FLASH_CR_START); -// /* Parallelism is ignored */ -// command &= ~(FLASH_CR_PSIZE64 | FLASH_CR_FW | FLASH_CR_START); -// /* Restore FW, START to H7Bx-correct bits */ -// command |= (temp_fw_start >> 2U); -// /* SNB offset is different, too */ -// command |= (snb << 6U); -// DEBUG_TARGET("%s: patching FLASH_CR from 0x%08" PRIx32 " to 0x%08" PRIx32 "\n", __func__, ctrl, command); -// return command; -//} - static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) { -#if 0 - // EMEB original zyp code - const uint32_t sector_size = target_flash->blocksize; - target_s *target = target_flash->t; - const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; - /* Unlock the Flash */ - if (!stm32h7rs_flash_unlock(target, addr)) - return false; - /* We come out of reset with HSI 64 MHz. Adapt FLASH_ACR.*/ - target_mem32_write32(target, flash->regbase + FLASH_ACR, 0); - /* Calculate SNB span */ - addr &= target_flash->length - 1U; - const size_t end_sector = (addr + len - 1U) / sector_size; - //const align_e psize = flash->psize; - const uint32_t reg_base = flash->regbase; - - for (size_t begin_sector = addr / sector_size; begin_sector <= end_sector; ++begin_sector) { - /* Erase the current Flash sector */ - //const uint32_t ctrl = stm32h7rs_flash_cr(sector_size, (psize * FLASH_CR_PSIZE16) | FLASH_CR_SER, begin_sector); - const uint32_t ctrl = FLASH_CR_SER | (begin_sector << FLASH_CR_SSN_SHIFT); - target_mem32_write32(target, reg_base + FLASH_CR, ctrl); - //const uint32_t ctrl_start = stm32h7rs_flash_cr(sector_size, ctrl | FLASH_CR_START, begin_sector); - const uint32_t ctrl_start = ctrl | FLASH_CR_START; - target_mem32_write32(target, reg_base + FLASH_CR, ctrl_start); - - /* Wait for the operation to complete and report errors */ - DEBUG_INFO("Erasing, ctrl = %08" PRIx32 " status = %08" PRIx32 "\n", - target_mem32_read32(target, reg_base + FLASH_CR), target_mem32_read32(target, reg_base + FLASH_SR)); - - if (!stm32h7rs_flash_busy_wait(target, reg_base)) - return false; - } - return stm32h7rs_flash_wait_complete(target, flash->regbase); -#else - // EMEB new code based on latest H7 driver - (void)len; /* Erases are always done one sector at a time - the target Flash API guarantees this */ target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; @@ -447,8 +364,6 @@ static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_add /* Wait for the operation to complete and report errors */ return stm32h7rs_flash_wait_complete(target, flash->regbase); - -#endif } static bool stm32h7rs_flash_write( @@ -456,32 +371,12 @@ static bool stm32h7rs_flash_write( { target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; - /* Unlock the Flash */ - if (!stm32h7rs_flash_unlock(target, dest)) - return false; /* Prepare the Flash write operation */ - //const uint32_t ctrl = stm32h7rs_flash_cr(target_flash->blocksize, flash->psize * FLASH_CR_PSIZE16, 0); - //target_mem_write32(target, flash->regbase + FLASH_CR, ctrl); - //const uint32_t ctrl_pg = stm32h7rs_flash_cr(target_flash->blocksize, ctrl | FLASH_CR_PG, 0); const uint32_t ctrl_pg = FLASH_CR_PG; target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg); - /* does H7 stall?*/ /* Write the data to the Flash */ -#if 0 - /* original from zyp's 2024 version w/o queue waiting */ - target_mem32_write(target, dest, src, len); - - /* Wait for the operation to complete and report errors */ - if (!stm32h7rs_flash_busy_wait(target, flash->regbase)) - return false; - - /* Close write windows */ - target_mem32_write32(target, flash->regbase + FLASH_CR, 0); - return true; -#else - /* New stuff from H7 driver - adapted for 16 byte flash wordsize */ for (size_t offset = 0U; offset < len; offset += 16U) { const size_t amount = MIN(len - offset, 16U); target_mem32_write(target, dest + offset, ((const uint8_t *)src) + offset, amount); @@ -500,7 +395,6 @@ static bool stm32h7rs_flash_write( /* Wait for the operation to complete and report errors */ return stm32h7rs_flash_wait_complete(target, flash->regbase); -#endif } static bool stm32h7rs_erase_bank( @@ -511,7 +405,6 @@ static bool stm32h7rs_erase_bank( return false; } /* BER and start can be merged (§3.3.10). */ - //const uint32_t ctrl = stm32h7rs_flash_cr(target->flash->blocksize, (psize * FLASH_CR_PSIZE16) | FLASH_CR_BER | FLASH_CR_START, 0); const uint32_t ctrl = FLASH_CR_BER | FLASH_CR_START; target_mem32_write32(target, reg_base + FLASH_CR, ctrl); DEBUG_INFO("Mass erase of bank started\n"); @@ -538,7 +431,6 @@ static bool stm32h7rs_check_bank(target_s *const target, const uint32_t reg_base return !(status & FLASH_ISR_ERROR_MASK); } -/* Both banks are erased in parallel.*/ static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess) { align_e psize = ALIGN_64BIT; From bb93ef33358e30984b80bd3fac444d2463fa4ccb Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 11 Dec 2025 18:18:10 -0700 Subject: [PATCH 210/247] stm32h7rs: cleanup warnings --- src/target/stm32h7rs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 83f57e9a117..476656a62b8 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -348,6 +348,7 @@ static bool stm32h7rs_flash_done(target_flash_s *const target_flash) static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) { + (void) len; // prevent unused param warning /* Erases are always done one sector at a time - the target Flash API guarantees this */ target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; @@ -400,6 +401,7 @@ static bool stm32h7rs_flash_write( static bool stm32h7rs_erase_bank( target_s *const target, const align_e psize, const uint32_t start_addr, const uint32_t reg_base) { + (void) psize; // prevent unused param warning if (!stm32h7rs_flash_unlock(target, start_addr)) { DEBUG_ERROR("Bank erase: Unlock bank failed\n"); return false; From cf64c718102e8a1d4e11f1060ed81aa0b55b2044 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 11 Dec 2025 21:42:43 -0700 Subject: [PATCH 211/247] stm32h7rs: align w/ latest H7 driver, clean out unused stuff. --- src/target/stm32h7rs.c | 192 ++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 88 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 476656a62b8..6ae11aa3f3e 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -46,8 +46,8 @@ //#define FLASH_OPTCR 0x018U //#define FLASH_OPTSR_CUR 0x01cU //#define FLASH_OPTSR 0x020U -//#define FLASH_CRCCR 0x050U -//#define FLASH_CRCDATA 0x05cU +#define FLASH_CRCCR 0x050U +#define FLASH_CRCDATA 0x05cU /* Flash Program and Erase Controller Register Map */ #define FPEC1_BASE 0x52002000U @@ -81,17 +81,17 @@ #define FLASH_CR_SSN_SHIFT 6U //#define FLASH_CR_SNB_1 (1U << 8U) //#define FLASH_CR_SNB (3U << 8U) -//#define FLASH_CR_CRC_EN (1U << 15U) +#define FLASH_CR_CRC_EN (1U << 15U) // //#define FLASH_OPTCR_OPTLOCK (1U << 0U) //#define FLASH_OPTCR_OPTSTRT (1U << 1U) // //#define FLASH_OPTSR_IWDG1_SW (1U << 4U) // -//#define FLASH_CRCCR_ALL_BANK (1U << 7U) -//#define FLASH_CRCCR_START_CRC (1U << 16U) -//#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) -//#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) +#define FLASH_CRCCR_ALL_BANK (1U << 7U) +#define FLASH_CRCCR_START_CRC (1U << 16U) +#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) +#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) #define STM32H7RS_FLASH_KEY1 0x45670123U #define STM32H7RS_FLASH_KEY2 0xcdef89abU @@ -99,13 +99,29 @@ #define STM32H7RS_OPT_KEY1 0x08192a3bU #define STM32H7RS_OPT_KEY2 0x4c5d6e7fU -#define DBGMCU_IDCODE 0x5c001000U #define STM32H7RS_FLASH_SIZE 0x1ff1e880U -#define STM32H7Bx_FLASH_SIZE 0x08fff80cU +#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U +#define NUM_SECTOR_PER_BANK 8U +#define FLASH_SECTOR_SIZE 0x2000U + +/* WWDG base address and register map */ +#define STM32H7RS_WWDG_BASE 0x40002c00U +#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) +#define STM32H7RS_WWDG_CR_RESET 0x0000007fU + +/* IWDG base address and register map */ +#define STM32H7RS_IWDG_BASE 0x58004800U +#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) +#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU + /* Access from processor address space. * Access via the APB-D is at 0xe00e1000 */ +#define DBGMCU_IDCODE 0x5c001000U #define DBGMCU_IDC (DBGMCU_IDCODE + 0U) #define DBGMCU_CR (DBGMCU_IDCODE + 4U) +#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) +#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) #define DBGSLEEP_D1 (1U << 0U) #define DBGSTOP_D1 (1U << 1U) #define DBGSTBY_D1 (1U << 2U) @@ -113,15 +129,12 @@ #define DBGSTBY_D3 (1U << 8U) #define D1DBGCKEN (1U << 21U) #define D3DBGCKEN (1U << 22U) +#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) +#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) #define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU #define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U -#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U -#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U -#define NUM_SECTOR_PER_BANK 8U -#define FLASH_SECTOR_SIZE 0x2000U - #define ID_STM32H7RS 0x485U /* RM0477 */ typedef struct stm32h7rs_flash { @@ -135,14 +148,14 @@ typedef struct stm32h7rs_priv { } stm32h7rs_priv_s; static bool stm32h7rs_uid(target_s *target, int argc, const char **argv); -//static bool stm32h7rs_crc(target_s *target, int argc, const char **argv); +static bool stm32h7rs_crc(target_s *target, int argc, const char **argv); static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv); static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv); const command_s stm32h7rs_cmd_list[] = { {"psize", stm32h7rs_cmd_psize, "Configure flash write parallelism: (x8|x16|x32|x64(default))"}, {"uid", stm32h7rs_uid, "Print unique device ID"}, - //{"crc", stm32h7rs_crc, "Print CRC of both banks"}, + {"crc", stm32h7rs_crc, "Print CRC of bank 1"}, {"revision", stm32h7rs_cmd_rev, "Returns the Device ID and Revision"}, {NULL, NULL, NULL}, }; @@ -202,23 +215,24 @@ bool stm32h7rs_probe(target_s *target) target_add_commands(target, stm32h7rs_cmd_list, target->driver); /* EMEB - Decide which of these is correct */ -#if 1 +#if 0 /* RM0433 Rev 4 is not really clear, what bits are needed in DBGMCU_CR. Maybe more flags needed? */ const uint32_t dbgmcu_ctrl = DBGSLEEP_D1 | D1DBGCKEN; target_mem32_write32(target, DBGMCU_CR, dbgmcu_ctrl); #else /* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */ - target_mem32_write32(target, STM32H7_DBGMCU_APB3FREEZE, STM32H7_DBGMCU_APB3FREEZE_WWDG1); - target_mem32_write32(target, STM32H7_DBGMCU_APB4FREEZE, STM32H7_DBGMCU_APB4FREEZE_IWDG1); + target_mem32_write32(target, DBGMCU_APB1FREEZE, DBGMCU_APB1FREEZE_WWDG1); + target_mem32_write32(target, DBGMCU_APB4FREEZE, DBGMCU_APB4FREEZE_IWDG1); /* + * EMEB - this is redundant w/ stuff that happens at attach() * Make sure that both domain D1 and D3 debugging are enabled and that we can keep * debugging through sleep, stop and standby states for domain D1 */ - target_mem32_write32(target, STM32H7_DBGMCU_CONFIG, - target_mem32_read32(target, STM32H7_DBGMCU_CONFIG) | STM32H7_DBGMCU_CONFIG_DBGSLEEP_D1 | - STM32H7_DBGMCU_CONFIG_DBGSTOP_D1 | STM32H7_DBGMCU_CONFIG_DBGSTBY_D1 | STM32H7_DBGMCU_CONFIG_D1DBGCKEN | - STM32H7_DBGMCU_CONFIG_D3DBGCKEN); - stm32h7_configure_wdts(target); + target_mem32_write32(target, DBGMCU_CR, + target_mem32_read32(target, DBGMCU_CR) | DBGSLEEP_D1 | + DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); + target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); #endif /* Build the RAM map - EMEB: ITCM/DTCM are too big -shared w/ AXI */ @@ -256,6 +270,7 @@ static bool stm32h7rs_attach(target_s *target) { if (!cortexm_attach(target)) return false; +#if 0 /* * If IWDG runs as HARDWARE watchdog (§44.3.4) erase * will be aborted by the Watchdog and erase fails! @@ -264,13 +279,29 @@ static bool stm32h7rs_attach(target_s *target) //const uint32_t optsr = target_mem_read32(target, FPEC1_BASE + FLASH_OPTSR); //if (!(optsr & FLASH_OPTSR_IWDG1_SW)) // tc_printf(target, "Hardware IWDG running. Expect failure. Set IWDG1_SW!"); +#else + /* + * Make sure that both domain D1 and D3 debugging are enabled and that we can keep + * debugging through sleep, stop and standby states for domain D1 - this is duplicated as it's undone by detach. + */ + target_mem32_write32(target, DBGMCU_CR, + DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); + target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); +#endif return true; } static void stm32h7rs_detach(target_s *target) { +#if 0 //stm32h7rs_priv_s *ps = (stm32h7rs_priv_s *)target->target_storage; //target_mem_write32(target, DBGMCU_CR, ps->dbg_cr); +#else + target_mem32_write32(target, DBGMCU_CR, + target_mem32_read32(target, DBGMCU_CR) & + ~(DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN)); +#endif cortexm_detach(target); } @@ -296,17 +327,14 @@ static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t return !(istatus & FLASH_ISR_ERROR_MASK); } -static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t addr) +static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t regbase) { - (void)addr; - const uint32_t regbase = FPEC1_BASE; - /* EMEB - clear any pending flash interrupts that could hurt us */ - uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR) & - FLASH_ISR_ERROR_MASK; - if(istatus != 0U) + /* clear any pending flash interrupts that could hurt us */ + uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR); + if (istatus & FLASH_ISR_ERROR_MASK) { DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); - target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus); + target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); } /* Read out the Flash status and tend to any pending conditions */ @@ -348,7 +376,7 @@ static bool stm32h7rs_flash_done(target_flash_s *const target_flash) static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) { - (void) len; // prevent unused param warning + (void) len; /* Erases are always done one sector at a time - the target Flash API guarantees this */ target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; @@ -399,10 +427,9 @@ static bool stm32h7rs_flash_write( } static bool stm32h7rs_erase_bank( - target_s *const target, const align_e psize, const uint32_t start_addr, const uint32_t reg_base) + target_s *const target, const uint32_t reg_base) { - (void) psize; // prevent unused param warning - if (!stm32h7rs_flash_unlock(target, start_addr)) { + if (!stm32h7rs_flash_unlock(target, reg_base)) { DEBUG_ERROR("Bank erase: Unlock bank failed\n"); return false; } @@ -435,18 +462,8 @@ static bool stm32h7rs_check_bank(target_s *const target, const uint32_t reg_base static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess) { - align_e psize = ALIGN_64BIT; - /* - * XXX: What is this and why does it exist? - * A dry-run walk-through says it'll pull out the psize for the first Flash region added by stm32h7rs_probe() - * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. - */ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) { - if (flash->write == stm32h7rs_flash_write) - psize = ((struct stm32h7rs_flash *)flash)->psize; - } /* Send mass erase Flash start instruction */ - if (!stm32h7rs_erase_bank(target, psize, STM32H7RS_FLASH_BANK1_BASE, FPEC1_BASE)) + if (!stm32h7rs_erase_bank(target, FPEC1_BASE)) return false; /* Wait for the banks to finish erasing */ @@ -478,47 +495,46 @@ static bool stm32h7rs_uid(target_s *target, int argc, const char **argv) return true; } -//static bool stm32h7rs_crc_bank(target_s *target, uint32_t addr) -//{ -// const uint32_t reg_base = FPEC1_BASE; -// if (!stm32h7rs_flash_unlock(target, addr)) -// return false; -// -// target_mem_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); -// const uint32_t crc_ctrl = FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_ALL_BANK; -// target_mem_write32(target, reg_base + FLASH_CRCCR, crc_ctrl); -// target_mem_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); -// uint32_t status = FLASH_SR_CRC_BUSY; -//#if ENABLE_DEBUG == 1 -// const uint8_t bank = reg_base == FPEC1_BASE ? 1 : 2; -//#endif -// while (status & FLASH_SR_CRC_BUSY) { -// status = target_mem_read32(target, reg_base + FLASH_SR); -// if (target_check_error(target)) { -// DEBUG_ERROR("CRC bank %u: comm failed\n", bank); -// return false; -// } -// if (status & FLASH_SR_ERROR_READ) { -// DEBUG_ERROR("CRC bank %u: error status %08" PRIx32 "\n", bank, status); -// return false; -// } -// } -// return true; -//} -// -//static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) -//{ -// (void)argc; -// (void)argv; -// if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK1_BASE)) -// return false; -// uint32_t crc1 = target_mem_read32(target, FPEC1_BASE + FLASH_CRCDATA); -// if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK2_BASE)) -// return false; -// uint32_t crc2 = 0;//TODO: remove: target_mem_read32(target, FPEC2_BASE + FLASH_CRCDATA); -// tc_printf(target, "CRC: bank1 0x%08lx, bank2 0x%08lx\n", crc1, crc2); -// return true; -//} +static bool stm32h7rs_crc_bank(target_s *target, uint32_t addr) +{ + (void) addr; + const uint32_t reg_base = FPEC1_BASE; + if (!stm32h7rs_flash_unlock(target, reg_base)) + return false; + + target_mem32_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); + const uint32_t crc_ctrl = FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_ALL_BANK; + target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl); + target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); + uint32_t status = FLASH_SR_CRC_BUSY; +#if ENABLE_DEBUG == 1 + const uint8_t bank = reg_base == FPEC1_BASE ? 1 : 2; +#endif + while (status & FLASH_SR_CRC_BUSY) { + status = target_mem32_read32(target, reg_base + FLASH_SR); + if (target_check_error(target)) { + DEBUG_ERROR("CRC bank %u: comm failed\n", bank); + return false; + } + uint32_t istatus = target_mem32_read32(target, reg_base + FLASH_ISR); + if (istatus & FLASH_ISR_ERROR_READ) { + DEBUG_ERROR("CRC bank %u: error status %08" PRIx32 "\n", bank, istatus); + return false; + } + } + return true; +} + +static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK1_BASE)) + return false; + uint32_t crc1 = target_mem32_read32(target, FPEC1_BASE + FLASH_CRCDATA); + tc_printf(target, "CRC: bank1 0x%08" PRIx32 "\n", crc1); + return true; +} static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv) { From eb0322661aa5e93d70538adc948dfd1f620423d4 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 11 Dec 2025 21:58:49 -0700 Subject: [PATCH 212/247] stm32h7rs: DBGMCU stuff in attach/detach was causing problems. Disabled. --- src/target/stm32h7rs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 6ae11aa3f3e..d7be512b6d7 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -270,7 +270,7 @@ static bool stm32h7rs_attach(target_s *target) { if (!cortexm_attach(target)) return false; -#if 0 +#if 1 /* * If IWDG runs as HARDWARE watchdog (§44.3.4) erase * will be aborted by the Watchdog and erase fails! @@ -294,7 +294,7 @@ static bool stm32h7rs_attach(target_s *target) static void stm32h7rs_detach(target_s *target) { -#if 0 +#if 1 //stm32h7rs_priv_s *ps = (stm32h7rs_priv_s *)target->target_storage; //target_mem_write32(target, DBGMCU_CR, ps->dbg_cr); #else From 68d9ab41159ae215320c23683e5bd74117d84d68 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Fri, 12 Dec 2025 15:46:21 -0700 Subject: [PATCH 213/247] stm32h7rs: CRC works, tested breakpoints, erase_mass. Minor cleanups. --- src/target/stm32h7rs.c | 250 +++++++++++++++++------------------------ 1 file changed, 105 insertions(+), 145 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index d7be512b6d7..f2093f9b28c 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -34,108 +34,99 @@ #include "cortexm.h" #include "stm32_common.h" -#define FLASH_ACR 0x000U -#define FLASH_KEYR 0x004U -#define FLASH_CR 0x010U -#define FLASH_SR 0x014U -#define FLASH_IER 0x020U -#define FLASH_ISR 0x024U -#define FLASH_ICR 0x028U -//#define FLASH_OPTKEYR 0x008U -//#define FLASH_CCR 0x014U -//#define FLASH_OPTCR 0x018U -//#define FLASH_OPTSR_CUR 0x01cU -//#define FLASH_OPTSR 0x020U -#define FLASH_CRCCR 0x050U -#define FLASH_CRCDATA 0x05cU - /* Flash Program and Erase Controller Register Map */ -#define FPEC1_BASE 0x52002000U -#define FLASH_SR_BSY (1U << 0U) -#define FLASH_SR_WBNE (1U << 1U) -#define FLASH_SR_QW (1U << 2U) -#define FLASH_SR_CRC_BUSY (1U << 3U) -#define FLASH_ISR_EOP (1U << 16U) -#define FLASH_ISR_WRPERR (1U << 17U) -#define FLASH_ISR_PGSERR (1U << 18U) -#define FLASH_ISR_STRBERR (1U << 19U) -#define FLASH_ISR_INCERR (1U << 21U) -#define FLASH_ISR_RDSERR (1U << 24U) -#define FLASH_ISR_SNECCERR (1U << 25U) -#define FLASH_ISR_DBECCERR (1U << 26U) -#define FLASH_ISR_CRCEND (1U << 27U) -#define FLASH_ISR_CRCRDERR (1U << 28U) -#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) -#define FLASH_ISR_ERROR_MASK \ +#define FPEC1_BASE 0x52002000U +#define FLASH_ACR 0x000U +#define FLASH_KEYR 0x004U +#define FLASH_CR 0x010U +#define FLASH_SR 0x014U +#define FLASH_IER 0x020U +#define FLASH_ISR 0x024U +#define FLASH_ICR 0x028U +#define FLASH_CRCCR 0x030U +#define FLASH_CRCDATA 0x03cU +#define FLASH_SR_BSY (1U << 0U) +#define FLASH_SR_WBNE (1U << 1U) +#define FLASH_SR_QW (1U << 2U) +#define FLASH_SR_CRC_BUSY (1U << 3U) +#define FLASH_ISR_EOP (1U << 16U) +#define FLASH_ISR_WRPERR (1U << 17U) +#define FLASH_ISR_PGSERR (1U << 18U) +#define FLASH_ISR_STRBERR (1U << 19U) +#define FLASH_ISR_INCERR (1U << 21U) +#define FLASH_ISR_RDSERR (1U << 24U) +#define FLASH_ISR_SNECCERR (1U << 25U) +#define FLASH_ISR_DBECCERR (1U << 26U) +#define FLASH_ISR_CRCEND (1U << 27U) +#define FLASH_ISR_CRCRDERR (1U << 28U) +#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) +#define FLASH_ISR_ERROR_MASK \ (FLASH_ISR_WRPERR | FLASH_ISR_PGSERR | FLASH_ISR_STRBERR | FLASH_ISR_INCERR | FLASH_ISR_ERROR_READ) -#define FLASH_CR_LOCK (1U << 0U) -#define FLASH_CR_PG (1U << 1U) -#define FLASH_CR_SER (1U << 2U) -#define FLASH_CR_BER (1U << 3U) -//#define FLASH_CR_PSIZE8 (0U << 4U) -//#define FLASH_CR_PSIZE16 (1U << 4U) -//#define FLASH_CR_PSIZE32 (2U << 4U) -//#define FLASH_CR_PSIZE64 (3U << 4U) -#define FLASH_CR_FW (1U << 4U) -#define FLASH_CR_START (1U << 5U) -#define FLASH_CR_SSN_SHIFT 6U -//#define FLASH_CR_SNB_1 (1U << 8U) -//#define FLASH_CR_SNB (3U << 8U) -#define FLASH_CR_CRC_EN (1U << 15U) -// -//#define FLASH_OPTCR_OPTLOCK (1U << 0U) -//#define FLASH_OPTCR_OPTSTRT (1U << 1U) -// -//#define FLASH_OPTSR_IWDG1_SW (1U << 4U) -// -#define FLASH_CRCCR_ALL_BANK (1U << 7U) -#define FLASH_CRCCR_START_CRC (1U << 16U) -#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) -#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) - -#define STM32H7RS_FLASH_KEY1 0x45670123U -#define STM32H7RS_FLASH_KEY2 0xcdef89abU - -#define STM32H7RS_OPT_KEY1 0x08192a3bU -#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU - -#define STM32H7RS_FLASH_SIZE 0x1ff1e880U -#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U -#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U -#define NUM_SECTOR_PER_BANK 8U -#define FLASH_SECTOR_SIZE 0x2000U +#define FLASH_CR_LOCK (1U << 0U) +#define FLASH_CR_PG (1U << 1U) +#define FLASH_CR_SER (1U << 2U) +#define FLASH_CR_BER (1U << 3U) +#define FLASH_CR_FW (1U << 4U) +#define FLASH_CR_START (1U << 5U) +#define FLASH_CR_SSN_SHIFT 6U +#define FLASH_CR_CRC_EN (1U << 17U) +#define FLASH_CRCCR_ALL_BANK (1U << 7U) +#define FLASH_CRCCR_START_CRC (1U << 16U) +#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) +#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) + +#define STM32H7RS_FLASH_KEY1 0x45670123U +#define STM32H7RS_FLASH_KEY2 0xcdef89abU + +#define STM32H7RS_OPT_KEY1 0x08192a3bU +#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU + +#define STM32H7RS_FLASH_SIZE 0x1ff1e880U +#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U +#define NUM_SECTOR_PER_BANK 8U +#define FLASH_SECTOR_SIZE 0x2000U /* WWDG base address and register map */ -#define STM32H7RS_WWDG_BASE 0x40002c00U -#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) -#define STM32H7RS_WWDG_CR_RESET 0x0000007fU +#define STM32H7RS_WWDG_BASE 0x40002c00U +#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) +#define STM32H7RS_WWDG_CR_RESET 0x0000007fU /* IWDG base address and register map */ -#define STM32H7RS_IWDG_BASE 0x58004800U -#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) -#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU - -/* Access from processor address space. - * Access via the APB-D is at 0xe00e1000 */ -#define DBGMCU_IDCODE 0x5c001000U -#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) -#define DBGMCU_CR (DBGMCU_IDCODE + 4U) -#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) -#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) -#define DBGSLEEP_D1 (1U << 0U) -#define DBGSTOP_D1 (1U << 1U) -#define DBGSTBY_D1 (1U << 2U) -#define DBGSTOP_D3 (1U << 7U) -#define DBGSTBY_D3 (1U << 8U) -#define D1DBGCKEN (1U << 21U) -#define D3DBGCKEN (1U << 22U) -#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) -#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) - -#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU -#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U - -#define ID_STM32H7RS 0x485U /* RM0477 */ +#define STM32H7RS_IWDG_BASE 0x58004800U +#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) +#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU + +/* + * Access from processor address space. + * Access via the APB-D is at 0xe00e1000 + */ +#define DBGMCU_IDCODE 0x5c001000U +#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) +#define DBGMCU_CR (DBGMCU_IDCODE + 4U) +#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) +#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) +#define DBGSLEEP_D1 (1U << 0U) +#define DBGSTOP_D1 (1U << 1U) +#define DBGSTBY_D1 (1U << 2U) +#define DBGSTOP_D3 (1U << 7U) +#define DBGSTBY_D3 (1U << 8U) +#define D1DBGCKEN (1U << 21U) +#define D3DBGCKEN (1U << 22U) +#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) +#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) + +#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU +#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U + +#define ID_STM32H7RS 0x485U /* RM0477 */ + +/* + * Uncomment this to enable DBGMCU setup in attach() and detach() + * This seem to cause problems with reconnecting to the target and is + * also somewhat redundant with similar setup that happens in probe() + */ +//#define CONFIG_EXPERIMENTAL_DBGMCU typedef struct stm32h7rs_flash { target_flash_s target_flash; @@ -214,17 +205,10 @@ bool stm32h7rs_probe(target_s *target) target->mass_erase = stm32h7rs_mass_erase; target_add_commands(target, stm32h7rs_cmd_list, target->driver); - /* EMEB - Decide which of these is correct */ -#if 0 - /* RM0433 Rev 4 is not really clear, what bits are needed in DBGMCU_CR. Maybe more flags needed? */ - const uint32_t dbgmcu_ctrl = DBGSLEEP_D1 | D1DBGCKEN; - target_mem32_write32(target, DBGMCU_CR, dbgmcu_ctrl); -#else /* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */ target_mem32_write32(target, DBGMCU_APB1FREEZE, DBGMCU_APB1FREEZE_WWDG1); target_mem32_write32(target, DBGMCU_APB4FREEZE, DBGMCU_APB4FREEZE_IWDG1); /* - * EMEB - this is redundant w/ stuff that happens at attach() * Make sure that both domain D1 and D3 debugging are enabled and that we can keep * debugging through sleep, stop and standby states for domain D1 */ @@ -233,9 +217,9 @@ bool stm32h7rs_probe(target_s *target) DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); -#endif + - /* Build the RAM map - EMEB: ITCM/DTCM are too big -shared w/ AXI */ + /* Build the RAM map */ switch (target->part_id) { case ID_STM32H7RS: { /* Table 6. Memory map and default device memory area attributes RM0477, pg151 */ @@ -270,16 +254,7 @@ static bool stm32h7rs_attach(target_s *target) { if (!cortexm_attach(target)) return false; -#if 1 - /* - * If IWDG runs as HARDWARE watchdog (§44.3.4) erase - * will be aborted by the Watchdog and erase fails! - * Setting IWDG_KR to 0xaaaa does not seem to help! - */ - //const uint32_t optsr = target_mem_read32(target, FPEC1_BASE + FLASH_OPTSR); - //if (!(optsr & FLASH_OPTSR_IWDG1_SW)) - // tc_printf(target, "Hardware IWDG running. Expect failure. Set IWDG1_SW!"); -#else +#ifdef CONFIG_EXPERIMENTAL_DBGMCU /* * Make sure that both domain D1 and D3 debugging are enabled and that we can keep * debugging through sleep, stop and standby states for domain D1 - this is duplicated as it's undone by detach. @@ -294,10 +269,10 @@ static bool stm32h7rs_attach(target_s *target) static void stm32h7rs_detach(target_s *target) { -#if 1 - //stm32h7rs_priv_s *ps = (stm32h7rs_priv_s *)target->target_storage; - //target_mem_write32(target, DBGMCU_CR, ps->dbg_cr); -#else +#ifdef CONFIG_EXPERIMENTAL_DBGMCU + /* + * undo DBGMCU setup done in attach() + */ target_mem32_write32(target, DBGMCU_CR, target_mem32_read32(target, DBGMCU_CR) & ~(DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN)); @@ -307,7 +282,8 @@ static void stm32h7rs_detach(target_s *target) static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t regbase) { - uint32_t status = FLASH_SR_QW, istatus = 0U; + uint32_t status = FLASH_SR_QW; + uint32_t istatus = 0U; /* Loop waiting for the queuewait bit to clear and EOP to set, indicating completion of all ongoing operations */ while (!(istatus & FLASH_ISR_EOP) && (status & FLASH_SR_QW)) { status = target_mem32_read32(target, regbase + FLASH_SR); @@ -474,51 +450,34 @@ static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const pri return stm32h7rs_check_bank(target, FPEC1_BASE); } -/* - * Print the Unique device ID. - * Can be reused for other STM32 devices with uid as parameter. - */ static bool stm32h7rs_uid(target_s *target, int argc, const char **argv) { (void)argc; (void)argv; - const uint32_t uid_addr = 0x08fff800U; - - tc_printf(target, "0x"); - for (size_t i = 0; i < 12U; i += 4U) { - const uint32_t value = target_mem32_read32(target, uid_addr + i); - tc_printf(target, "%02X%02X%02X%02X", (value >> 24U) & 0xffU, (value >> 16U) & 0xffU, (value >> 8U) & 0xffU, - value & 0xffU); - } - tc_printf(target, "\n"); - return true; + return stm32_uid(target, uid_addr); } -static bool stm32h7rs_crc_bank(target_s *target, uint32_t addr) +static bool stm32h7rs_crc_bank(target_s *target) { - (void) addr; const uint32_t reg_base = FPEC1_BASE; if (!stm32h7rs_flash_unlock(target, reg_base)) return false; target_mem32_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); - const uint32_t crc_ctrl = FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_ALL_BANK; - target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl); + const uint32_t crc_ctrl = + FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_ALL_BANK; target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); uint32_t status = FLASH_SR_CRC_BUSY; -#if ENABLE_DEBUG == 1 - const uint8_t bank = reg_base == FPEC1_BASE ? 1 : 2; -#endif while (status & FLASH_SR_CRC_BUSY) { status = target_mem32_read32(target, reg_base + FLASH_SR); if (target_check_error(target)) { - DEBUG_ERROR("CRC bank %u: comm failed\n", bank); + DEBUG_ERROR("CRC comm failed\n"); return false; } uint32_t istatus = target_mem32_read32(target, reg_base + FLASH_ISR); if (istatus & FLASH_ISR_ERROR_READ) { - DEBUG_ERROR("CRC bank %u: error status %08" PRIx32 "\n", bank, istatus); + DEBUG_ERROR("CRC error status %08" PRIx32 "\n", istatus); return false; } } @@ -529,10 +488,10 @@ static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) { (void)argc; (void)argv; - if (!stm32h7rs_crc_bank(target, STM32H7RS_FLASH_BANK1_BASE)) + if (!stm32h7rs_crc_bank(target)) return false; uint32_t crc1 = target_mem32_read32(target, FPEC1_BASE + FLASH_CRCDATA); - tc_printf(target, "CRC: bank1 0x%08" PRIx32 "\n", crc1); + tc_printf(target, "CRC: 0x%08" PRIx32 "\n", crc1); return true; } @@ -573,6 +532,7 @@ static const struct { char revision; } stm32h7rs_revisions[] = { {0x1003U, 'Y'}, + {0x2000U, 'B'}, }; static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv) From 25191106eb4d503a7f35a65fbf5730cc6dcbfd4b Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Fri, 12 Dec 2025 19:24:18 -0700 Subject: [PATCH 214/247] stm32h7rs: fix formatting issues found by CI --- src/target/stm32h7rs.c | 193 ++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 100 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index f2093f9b28c..176fd80966c 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -35,91 +35,91 @@ #include "stm32_common.h" /* Flash Program and Erase Controller Register Map */ -#define FPEC1_BASE 0x52002000U -#define FLASH_ACR 0x000U -#define FLASH_KEYR 0x004U -#define FLASH_CR 0x010U -#define FLASH_SR 0x014U -#define FLASH_IER 0x020U -#define FLASH_ISR 0x024U -#define FLASH_ICR 0x028U -#define FLASH_CRCCR 0x030U -#define FLASH_CRCDATA 0x03cU -#define FLASH_SR_BSY (1U << 0U) -#define FLASH_SR_WBNE (1U << 1U) -#define FLASH_SR_QW (1U << 2U) -#define FLASH_SR_CRC_BUSY (1U << 3U) -#define FLASH_ISR_EOP (1U << 16U) -#define FLASH_ISR_WRPERR (1U << 17U) -#define FLASH_ISR_PGSERR (1U << 18U) -#define FLASH_ISR_STRBERR (1U << 19U) -#define FLASH_ISR_INCERR (1U << 21U) -#define FLASH_ISR_RDSERR (1U << 24U) -#define FLASH_ISR_SNECCERR (1U << 25U) -#define FLASH_ISR_DBECCERR (1U << 26U) -#define FLASH_ISR_CRCEND (1U << 27U) -#define FLASH_ISR_CRCRDERR (1U << 28U) -#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) -#define FLASH_ISR_ERROR_MASK \ +#define FPEC1_BASE 0x52002000U +#define FLASH_ACR 0x000U +#define FLASH_KEYR 0x004U +#define FLASH_CR 0x010U +#define FLASH_SR 0x014U +#define FLASH_IER 0x020U +#define FLASH_ISR 0x024U +#define FLASH_ICR 0x028U +#define FLASH_CRCCR 0x030U +#define FLASH_CRCDATA 0x03cU +#define FLASH_SR_BSY (1U << 0U) +#define FLASH_SR_WBNE (1U << 1U) +#define FLASH_SR_QW (1U << 2U) +#define FLASH_SR_CRC_BUSY (1U << 3U) +#define FLASH_ISR_EOP (1U << 16U) +#define FLASH_ISR_WRPERR (1U << 17U) +#define FLASH_ISR_PGSERR (1U << 18U) +#define FLASH_ISR_STRBERR (1U << 19U) +#define FLASH_ISR_INCERR (1U << 21U) +#define FLASH_ISR_RDSERR (1U << 24U) +#define FLASH_ISR_SNECCERR (1U << 25U) +#define FLASH_ISR_DBECCERR (1U << 26U) +#define FLASH_ISR_CRCEND (1U << 27U) +#define FLASH_ISR_CRCRDERR (1U << 28U) +#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) +#define FLASH_ISR_ERROR_MASK \ (FLASH_ISR_WRPERR | FLASH_ISR_PGSERR | FLASH_ISR_STRBERR | FLASH_ISR_INCERR | FLASH_ISR_ERROR_READ) -#define FLASH_CR_LOCK (1U << 0U) -#define FLASH_CR_PG (1U << 1U) -#define FLASH_CR_SER (1U << 2U) -#define FLASH_CR_BER (1U << 3U) -#define FLASH_CR_FW (1U << 4U) -#define FLASH_CR_START (1U << 5U) -#define FLASH_CR_SSN_SHIFT 6U -#define FLASH_CR_CRC_EN (1U << 17U) -#define FLASH_CRCCR_ALL_BANK (1U << 7U) -#define FLASH_CRCCR_START_CRC (1U << 16U) -#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) -#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) - -#define STM32H7RS_FLASH_KEY1 0x45670123U -#define STM32H7RS_FLASH_KEY2 0xcdef89abU - -#define STM32H7RS_OPT_KEY1 0x08192a3bU -#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU - -#define STM32H7RS_FLASH_SIZE 0x1ff1e880U -#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U -#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U -#define NUM_SECTOR_PER_BANK 8U -#define FLASH_SECTOR_SIZE 0x2000U +#define FLASH_CR_LOCK (1U << 0U) +#define FLASH_CR_PG (1U << 1U) +#define FLASH_CR_SER (1U << 2U) +#define FLASH_CR_BER (1U << 3U) +#define FLASH_CR_FW (1U << 4U) +#define FLASH_CR_START (1U << 5U) +#define FLASH_CR_SSN_SHIFT 6U +#define FLASH_CR_CRC_EN (1U << 17U) +#define FLASH_CRCCR_ALL_BANK (1U << 7U) +#define FLASH_CRCCR_START_CRC (1U << 16U) +#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) +#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) + +#define STM32H7RS_FLASH_KEY1 0x45670123U +#define STM32H7RS_FLASH_KEY2 0xcdef89abU + +#define STM32H7RS_OPT_KEY1 0x08192a3bU +#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU + +#define STM32H7RS_FLASH_SIZE 0x1ff1e880U +#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U +#define NUM_SECTOR_PER_BANK 8U +#define FLASH_SECTOR_SIZE 0x2000U /* WWDG base address and register map */ -#define STM32H7RS_WWDG_BASE 0x40002c00U -#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) -#define STM32H7RS_WWDG_CR_RESET 0x0000007fU +#define STM32H7RS_WWDG_BASE 0x40002c00U +#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) +#define STM32H7RS_WWDG_CR_RESET 0x0000007fU /* IWDG base address and register map */ -#define STM32H7RS_IWDG_BASE 0x58004800U -#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) -#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU +#define STM32H7RS_IWDG_BASE 0x58004800U +#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) +#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU /* * Access from processor address space. * Access via the APB-D is at 0xe00e1000 */ -#define DBGMCU_IDCODE 0x5c001000U -#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) -#define DBGMCU_CR (DBGMCU_IDCODE + 4U) -#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) -#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) -#define DBGSLEEP_D1 (1U << 0U) -#define DBGSTOP_D1 (1U << 1U) -#define DBGSTBY_D1 (1U << 2U) -#define DBGSTOP_D3 (1U << 7U) -#define DBGSTBY_D3 (1U << 8U) -#define D1DBGCKEN (1U << 21U) -#define D3DBGCKEN (1U << 22U) -#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) -#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) - -#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU -#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U - -#define ID_STM32H7RS 0x485U /* RM0477 */ +#define DBGMCU_IDCODE 0x5c001000U +#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) +#define DBGMCU_CR (DBGMCU_IDCODE + 4U) +#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) +#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) +#define DBGSLEEP_D1 (1U << 0U) +#define DBGSTOP_D1 (1U << 1U) +#define DBGSTBY_D1 (1U << 2U) +#define DBGSTOP_D3 (1U << 7U) +#define DBGSTBY_D3 (1U << 8U) +#define D1DBGCKEN (1U << 21U) +#define D3DBGCKEN (1U << 22U) +#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) +#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) + +#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU +#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U + +#define ID_STM32H7RS 0x485U /* RM0477 */ /* * Uncomment this to enable DBGMCU setup in attach() and detach() @@ -187,9 +187,9 @@ bool stm32h7rs_probe(target_s *target) const adiv5_access_port_s *const ap = cortex_ap(target); if (ap->partno != ID_STM32H7RS) return false; - + target->part_id = ap->partno; - + /* Save private storage */ stm32h7rs_priv_s *priv = calloc(1, sizeof(*priv)); if (!priv) { /* calloc failed: heap exhaustion */ @@ -198,13 +198,13 @@ bool stm32h7rs_probe(target_s *target) } target->target_storage = priv; priv->dbg_cr = target_mem32_read32(target, DBGMCU_CR); - + target->driver = "STM32H7R/S"; target->attach = stm32h7rs_attach; target->detach = stm32h7rs_detach; target->mass_erase = stm32h7rs_mass_erase; target_add_commands(target, stm32h7rs_cmd_list, target->driver); - + /* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */ target_mem32_write32(target, DBGMCU_APB1FREEZE, DBGMCU_APB1FREEZE_WWDG1); target_mem32_write32(target, DBGMCU_APB4FREEZE, DBGMCU_APB4FREEZE_IWDG1); @@ -213,12 +213,10 @@ bool stm32h7rs_probe(target_s *target) * debugging through sleep, stop and standby states for domain D1 */ target_mem32_write32(target, DBGMCU_CR, - target_mem32_read32(target, DBGMCU_CR) | DBGSLEEP_D1 | - DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_read32(target, DBGMCU_CR) | DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); - /* Build the RAM map */ switch (target->part_id) { case ID_STM32H7RS: { @@ -259,8 +257,7 @@ static bool stm32h7rs_attach(target_s *target) * Make sure that both domain D1 and D3 debugging are enabled and that we can keep * debugging through sleep, stop and standby states for domain D1 - this is duplicated as it's undone by detach. */ - target_mem32_write32(target, DBGMCU_CR, - DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_write32(target, DBGMCU_CR, DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); #endif @@ -274,8 +271,7 @@ static void stm32h7rs_detach(target_s *target) * undo DBGMCU setup done in attach() */ target_mem32_write32(target, DBGMCU_CR, - target_mem32_read32(target, DBGMCU_CR) & - ~(DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN)); + target_mem32_read32(target, DBGMCU_CR) & ~(DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN)); #endif cortexm_detach(target); } @@ -297,8 +293,7 @@ static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t /* Now the operation's complete, we can check the error bits */ if (istatus & FLASH_ISR_ERROR_MASK) DEBUG_ERROR("%s: Flash error: %08" PRIx32 "\n", __func__, istatus); - target_mem32_write32(target, regbase + FLASH_ICR, - istatus & (FLASH_ISR_EOP | FLASH_ISR_ERROR_MASK)); + target_mem32_write32(target, regbase + FLASH_ICR, istatus & (FLASH_ISR_EOP | FLASH_ISR_ERROR_MASK)); /* Return whether any errors occured */ return !(istatus & FLASH_ISR_ERROR_MASK); } @@ -307,12 +302,11 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t regbas { /* clear any pending flash interrupts that could hurt us */ uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR); - if (istatus & FLASH_ISR_ERROR_MASK) - { + if (istatus & FLASH_ISR_ERROR_MASK) { DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); } - + /* Read out the Flash status and tend to any pending conditions */ const uint32_t status = target_mem32_read32(target, regbase + FLASH_SR); /* Start by checking if there are any pending ongoing operations */ @@ -321,7 +315,7 @@ static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t regbas if (!stm32h7rs_flash_wait_complete(target, regbase)) return false; } - + /* Unlock the device Flash if not already unlocked (it's an error to re-key the controller if it is) */ if (target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK) { /* Enable Flash controller access */ @@ -352,7 +346,7 @@ static bool stm32h7rs_flash_done(target_flash_s *const target_flash) static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) { - (void) len; + (void)len; /* Erases are always done one sector at a time - the target Flash API guarantees this */ target_s *target = target_flash->t; const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; @@ -375,7 +369,7 @@ static bool stm32h7rs_flash_write( target_flash_s *const target_flash, const target_addr_t dest, const void *const src, const size_t len) { target_s *target = target_flash->t; - const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; /* Prepare the Flash write operation */ const uint32_t ctrl_pg = FLASH_CR_PG; @@ -392,7 +386,7 @@ static bool stm32h7rs_flash_write( */ if (amount < 16U) target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg | FLASH_CR_FW); - + /* wait for QW bit to clear */ while (target_mem32_read32(target, flash->regbase + FLASH_SR) & FLASH_SR_QW) continue; @@ -402,8 +396,7 @@ static bool stm32h7rs_flash_write( return stm32h7rs_flash_wait_complete(target, flash->regbase); } -static bool stm32h7rs_erase_bank( - target_s *const target, const uint32_t reg_base) +static bool stm32h7rs_erase_bank(target_s *const target, const uint32_t reg_base) { if (!stm32h7rs_flash_unlock(target, reg_base)) { DEBUG_ERROR("Bank erase: Unlock bank failed\n"); @@ -416,7 +409,8 @@ static bool stm32h7rs_erase_bank( return true; } -static bool stm32h7rs_wait_erase_bank(target_s *const target, platform_timeout_s *const timeout, const uint32_t reg_base) +static bool stm32h7rs_wait_erase_bank( + target_s *const target, platform_timeout_s *const timeout, const uint32_t reg_base) { while (target_mem32_read32(target, reg_base + FLASH_SR) & FLASH_SR_QW) { if (target_check_error(target)) { @@ -465,8 +459,7 @@ static bool stm32h7rs_crc_bank(target_s *target) return false; target_mem32_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); - const uint32_t crc_ctrl = - FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_ALL_BANK; + const uint32_t crc_ctrl = FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_ALL_BANK; target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); uint32_t status = FLASH_SR_CRC_BUSY; while (status & FLASH_SR_CRC_BUSY) { From a896e6d7532b45992d2a5b0a9d423eeb55613d00 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Sun, 26 Apr 2026 14:24:47 -0700 Subject: [PATCH 215/247] Added copyright and psize changes requested. --- src/target/stm32h7rs.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 176fd80966c..5389c60487d 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -3,7 +3,8 @@ * * Copyright (C) 2017-2020 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de * Copyright (C) 2022-2023 1BitSquared - * Modified by Rachel Mant + * Copyright (C) 2025-2026 Eric Brombaugh based on initial + * work done by Rachel Mant and zyp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -130,12 +131,12 @@ typedef struct stm32h7rs_flash { target_flash_s target_flash; - align_e psize; uint32_t regbase; } stm32h7rs_flash_s; typedef struct stm32h7rs_priv { uint32_t dbg_cr; + align_e psize; } stm32h7rs_priv_s; static bool stm32h7rs_uid(target_s *target, int argc, const char **argv); @@ -178,7 +179,6 @@ static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, target_flash->writesize = 2048; target_flash->erased = 0xffU; flash->regbase = FPEC1_BASE; - flash->psize = ALIGN_64BIT; target_add_flash(target, target_flash); } @@ -197,8 +197,11 @@ bool stm32h7rs_probe(target_s *target) return false; } target->target_storage = priv; + /* Get the current value of the debug control register (and store it for later) */ priv->dbg_cr = target_mem32_read32(target, DBGMCU_CR); - + /* Set up the Flash write/erase parallelism to 64-bit default */ + priv->psize = ALIGN_64BIT; + target->driver = "STM32H7R/S"; target->attach = stm32h7rs_attach; target->detach = stm32h7rs_detach; @@ -491,31 +494,13 @@ static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv) { if (argc == 1) { - align_e psize = ALIGN_64BIT; - /* - * XXX: What is this and why does it exist? - * A dry-run walk-through says it'll pull out the psize for the first Flash region added by stm32h7rs_probe() - * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. - */ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) { - if (flash->write == stm32h7rs_flash_write) - psize = ((stm32h7rs_flash_s *)flash)->psize; - } + align_e psize = ((const stm32h7rs_priv_s *)target->target_storage)->psize;; tc_printf(target, "Flash write parallelism: %s\n", stm32_psize_to_string(psize)); } else { - align_e psize; + align_e psize = ALIGN_64BIT; if (!stm32_psize_from_string(target, argv[1], &psize)) return false; - - /* - * XXX: What is this and why does it exist? - * A dry-run walk-through says it'll overwrite psize for every Flash region added by stm32h7rs_probe() - * because all Flash regions added by stm32h7rs_add_flash match the if condition. This looks redundant and wrong. - */ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) { - if (flash->write == stm32h7rs_flash_write) - ((stm32h7rs_flash_s *)flash)->psize = psize; - } + ((stm32h7rs_priv_s *)target->target_storage)->psize = psize; } return true; } From 3b325641ab2d402d186430f34ca68218a8c4d981 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Tue, 28 Apr 2026 15:08:54 -0700 Subject: [PATCH 216/247] stm32c5: initial commit adding stm32c5 support to stm32h5 driver. --- src/target/cortexm.c | 1 + src/target/stm32h5.c | 161 ++++++++++++++++++++++++++++++++++++-- src/target/target_probe.c | 1 + src/target/target_probe.h | 1 + 4 files changed, 159 insertions(+), 5 deletions(-) diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 9a5c88a0296..95780497f89 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -402,6 +402,7 @@ bool cortexm_probe(adiv5_access_port_s *ap) PROBE(stm32l4_probe); PROBE(stm32g0_probe); PROBE(stm32wb0_probe); + PROBE(stm32c5_probe); break; case JEP106_MANUFACTURER_CYPRESS: DEBUG_WARN("Unhandled Cypress device\n"); diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index f3d592e8437..46306c04f66 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -32,7 +32,7 @@ */ /* - * This file implements support for STM32H5xx series devices, providing + * This file implements support for STM32H5xx and STM32C5xx series devices, providing * memory maps and Flash programming routines. * * References: @@ -40,6 +40,8 @@ * https://www.st.com/resource/en/reference_manual/rm0481-stm32h563h573-and-stm32h562-armbased-32bit-mcus-stmicroelectronics.pdf * RM0492 - STM32H503 Arm®-based 32-bit MCUs, Rev. 2 * https://www.st.com/resource/en/reference_manual/rm0492-stm32h503-line-armbased-32bit-mcus-stmicroelectronics.pdf + * RM0522 - STM32C5xx Arm®-based 32-bit MCUs, Rev. 1 + * https://www.st.com/resource/en/reference_manual/rm0522-stm32c5-series-armbased-32bit-mcus-stmicroelectronics.pdf */ #include "general.h" @@ -82,6 +84,40 @@ #define STM32H523_SRAM3_SIZE 0x00010000U #define STM32H523_SRAM123_SIZE (STM32H523_SRAM1_SIZE + STM32H523_SRAM2_SIZE + STM32H523_SRAM3_SIZE) +/* Memory map constants for STM32C53x/C54x */ +#define STM32C53X_FLASH_BANK1_BASE 0x08000000U +#define STM32C53X_FLASH_BANK2_BASE 0x08020000U +#define STM32C53X_FLASH_BANK_SIZE 0x00020000U +#define STM32C53X_SECTORS_PER_BANK 0x00000010U +#define STM32C53X_SRAM1_BASE 0x0a000000U +#define STM32C53X_SRAM1_SIZE 0x00008000U +#define STM32C53X_SRAM2_BASE 0x0a008000U +#define STM32C53X_SRAM2_SIZE 0x00008000U +#define STM32C53X_SRAM_ALIAS_BASE 0x20000000U +#define STM32C53X_SRAM12_SIZE (STM32C53X_SRAM1_SIZE + STM32C53X_SRAM2_SIZE) +/* Memory map constants for STM32C55x/C56x */ +#define STM32C55X_FLASH_BANK1_BASE 0x08000000U +#define STM32C55X_FLASH_BANK2_BASE 0x08040000U +#define STM32C55X_FLASH_BANK_SIZE 0x00040000U +#define STM32C55X_SECTORS_PER_BANK 0x00000020U +#define STM32C55X_SRAM1_BASE 0x0a000000U +#define STM32C55X_SRAM1_SIZE 0x00010000U +#define STM32C55X_SRAM2_BASE 0x0a010000U +#define STM32C55X_SRAM2_SIZE 0x00010000U +#define STM32C55X_SRAM_ALIAS_BASE 0x20000000U +#define STM32C55X_SRAM12_SIZE (STM32C55X_SRAM1_SIZE + STM32C55X_SRAM2_SIZE) +/* Memory map constants for STM32C59x/C5Ax */ +#define STM32C59X_FLASH_BANK1_BASE 0x08000000U +#define STM32C59X_FLASH_BANK2_BASE 0x08080000U +#define STM32C59X_FLASH_BANK_SIZE 0x00080000U +#define STM32C59X_SECTORS_PER_BANK 0x00000040U +#define STM32C59X_SRAM1_BASE 0x0a000000U +#define STM32C59X_SRAM1_SIZE 0x00020000U +#define STM32C59X_SRAM2_BASE 0x0a020000U +#define STM32C59X_SRAM2_SIZE 0x00020000U +#define STM32C59X_SRAM_ALIAS_BASE 0x20000000U +#define STM32C59X_SRAM12_SIZE (STM32C59X_SRAM1_SIZE + STM32C59X_SRAM2_SIZE) + /* Flash Program and Erase Controller (FPEC) Register Map */ #define STM32H5_FPEC_BASE 0x40022000 #define STM32H5_FPEC_ACCESS_CTRL (STM32H5_FPEC_BASE + 0x000U) @@ -140,6 +176,10 @@ #define ID_STM32H503 0x474U /* Taken from DBGMCU_IDCODE in §59.12.4 of RM0481 rev 2, pg3116 */ #define ID_STM32H523 0x478U +/* Taken from DBGMCU_IDCODE in §49.12.4 of RM0522 rev 1, pg2542 */ +#define ID_STM32C53x 0x44FU +#define ID_STM32C55x 0x44EU +#define ID_STM32C59x 0x45AU typedef struct stm32h5_flash { target_flash_s target_flash; @@ -297,6 +337,88 @@ bool stm32h5_probe(target_s *const target) return true; } +bool stm32c5_probe(target_s *const target) +{ + const adiv5_access_port_s *const ap = cortex_ap(target); + /* Use the partno from the AP always to handle the difference between JTAG and SWD */ + if (ap->partno != ID_STM32C55x && ap->partno != ID_STM32C53x && ap->partno != ID_STM32C59x) + return false; + target->part_id = ap->partno; + + /* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */ + if (!stm32h5_configure_dbgmcu(target)) + return false; + + target->driver = "STM32H5"; + target->attach = stm32h5_attach; + target->detach = stm32h5_detach; + target->mass_erase = stm32h5_mass_erase; + target->enter_flash_mode = stm32h5_enter_flash_mode; + target->exit_flash_mode = stm32h5_exit_flash_mode; + target_add_commands(target, stm32h5_cmd_list, target->driver); + + switch (target->part_id) { + case ID_STM32C53x: + /* + * Build the RAM map. 32+32=64. + * This uses the addresses and sizes found in §2.2.2, Figure 2, pg91 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C53X_SRAM1_BASE, STM32C53X_SRAM1_SIZE); + target_add_ram32(target, STM32C53X_SRAM2_BASE, STM32C53X_SRAM2_SIZE); + target_add_ram32(target, STM32C53X_SRAM_ALIAS_BASE, STM32C53X_SRAM12_SIZE); + + /* + * Build the Flash map. C53X/C54X has one of: + * Flash: 256 KiB as two equal banks (16 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C53X_FLASH_BANK1_BASE, STM32C53X_FLASH_BANK_SIZE, + STM32C53X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C53X_FLASH_BANK2_BASE, STM32C53X_FLASH_BANK_SIZE, + STM32C53X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + case ID_STM32C55x: + /* + * Build the RAM map. 64+64=128. + * This uses the addresses and sizes found in §2.2.2, Figure 3, pg92 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C55X_SRAM1_BASE, STM32C55X_SRAM1_SIZE); + target_add_ram32(target, STM32C55X_SRAM2_BASE, STM32C55X_SRAM2_SIZE); + target_add_ram32(target, STM32C55X_SRAM_ALIAS_BASE, STM32C55X_SRAM12_SIZE); + + /* + * Build the Flash map. C55X/C56X has one of: + * Flash: 512 KiB as two equal banks (32 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C55X_FLASH_BANK1_BASE, STM32C55X_FLASH_BANK_SIZE, + STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C55X_FLASH_BANK2_BASE, STM32C55X_FLASH_BANK_SIZE, + STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + case ID_STM32C59x: + /* + * Build the RAM map. 128+128=256. + * This uses the addresses and sizes found in §2.2.2, Figure 4, pg93 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C59X_SRAM1_BASE, STM32C59X_SRAM1_SIZE); + target_add_ram32(target, STM32C59X_SRAM2_BASE, STM32C59X_SRAM2_SIZE); + target_add_ram32(target, STM32C59X_SRAM_ALIAS_BASE, STM32C59X_SRAM12_SIZE); + + /* + * Build the Flash map. C59X has + * Flash: 1 MiB as two equal banks (64 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C59X_FLASH_BANK1_BASE, STM32C59X_FLASH_BANK_SIZE, + STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C59X_FLASH_BANK2_BASE, STM32C59X_FLASH_BANK_SIZE, + STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + default: + return false; + } + + return true; +} + static bool stm32h5_attach(target_s *const target) { /* @@ -424,6 +546,15 @@ static const struct { {0x1007U, 'X'}, }; +static const struct { + uint16_t rev_id; + char revision; +} stm32c5_revisions[] = { + {0x1000U, 'A'}, + {0x1001U, 'Z'}, + {0x1003U, 'Y'}, +}; + static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) { (void)argc; @@ -432,7 +563,8 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) const uint32_t idcode = target_mem32_read32(target, STM32H5_DBGMCU_IDCODE); const uint16_t rev_id = (idcode & STM32H5_DBGMCU_IDCODE_REV_MASK) >> STM32H5_DBGMCU_IDCODE_REV_SHIFT; const uint16_t dev_id = idcode & STM32H5_DBGMCU_IDCODE_DEV_MASK; - + uint8_t isC5 = 0; + /* Display the device ID */ switch (dev_id) { case ID_STM32H5xx: @@ -444,13 +576,32 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) case ID_STM32H523: tc_printf(target, "STM32H523/533\n"); break; + case ID_STM32C53x: + tc_printf(target, "STM32C53x/54x\n"); + isC5 = 1; + break; + case ID_STM32C55x: + tc_printf(target, "STM32C55x/56x\n"); + isC5 = 1; + break; + case ID_STM32C59x: + tc_printf(target, "STM32C59x/5Ax\n"); + isC5 = 1; + break; default: tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); } char revision = '?'; - for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { - if (stm32h5_revisions[i].rev_id == rev_id) - revision = stm32h5_revisions[i].revision; + if(!isC5) { + for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { + if (stm32h5_revisions[i].rev_id == rev_id) + revision = stm32h5_revisions[i].revision; + } + } else { + for (size_t i = 0; i < ARRAY_LENGTH(stm32c5_revisions); ++i) { + if (stm32c5_revisions[i].rev_id == rev_id) + revision = stm32h5_revisions[i].revision; + } } tc_printf(target, "Revision %c\n", revision); return true; diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 0344c360cfe..8fbc15c6fca 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -158,6 +158,7 @@ TARGET_PROBE_WEAK_NOP(stm32l4_probe) TARGET_PROBE_WEAK_NOP(stm32mp15_ca7_probe) TARGET_PROBE_WEAK_NOP(stm32mp15_cm4_probe) TARGET_PROBE_WEAK_NOP(stm32wb0_probe) +TARGET_PROBE_WEAK_NOP(stm32c5_probe) TARGET_PROBE_WEAK_NOP(zynq7_probe) LPC55_DP_PREPARE_WEAK_NOP(lpc55_dp_prepare) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 249aed67ec1..4463906d1c3 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -109,6 +109,7 @@ bool stm32l4_probe(target_s *target); bool stm32mp15_ca7_probe(target_s *target); bool stm32mp15_cm4_probe(target_s *target); bool stm32wb0_probe(target_s *target); +bool stm32c5_probe(target_s *target); bool zynq7_probe(target_s *target); void lpc55_dp_prepare(adiv5_debug_port_s *dp); From 88434c1255bfce3ac5c09c52cf9fa12d420796a5 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Tue, 28 Apr 2026 15:36:31 -0700 Subject: [PATCH 217/247] stm32c5: Fix bug in C5 revision reporting. --- src/target/stm32h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index 46306c04f66..199507b88d2 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -600,7 +600,7 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) } else { for (size_t i = 0; i < ARRAY_LENGTH(stm32c5_revisions); ++i) { if (stm32c5_revisions[i].rev_id == rev_id) - revision = stm32h5_revisions[i].revision; + revision = stm32c5_revisions[i].revision; } } tc_printf(target, "Revision %c\n", revision); From d22b393945ab26ab4879c33d6283e9a5d0bce058 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Tue, 28 Apr 2026 15:46:19 -0700 Subject: [PATCH 218/247] stm32c5: address issues raised in review --- src/target/stm32h5.c | 114 +++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index 199507b88d2..59862613dc2 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -85,38 +85,38 @@ #define STM32H523_SRAM123_SIZE (STM32H523_SRAM1_SIZE + STM32H523_SRAM2_SIZE + STM32H523_SRAM3_SIZE) /* Memory map constants for STM32C53x/C54x */ -#define STM32C53X_FLASH_BANK1_BASE 0x08000000U -#define STM32C53X_FLASH_BANK2_BASE 0x08020000U -#define STM32C53X_FLASH_BANK_SIZE 0x00020000U -#define STM32C53X_SECTORS_PER_BANK 0x00000010U -#define STM32C53X_SRAM1_BASE 0x0a000000U -#define STM32C53X_SRAM1_SIZE 0x00008000U -#define STM32C53X_SRAM2_BASE 0x0a008000U -#define STM32C53X_SRAM2_SIZE 0x00008000U -#define STM32C53X_SRAM_ALIAS_BASE 0x20000000U -#define STM32C53X_SRAM12_SIZE (STM32C53X_SRAM1_SIZE + STM32C53X_SRAM2_SIZE) +#define STM32C53x_FLASH_BANK1_BASE 0x08000000U +#define STM32C53x_FLASH_BANK2_BASE 0x08020000U +#define STM32C53x_FLASH_BANK_SIZE 0x00020000U +#define STM32C53x_SECTORS_PER_BANK 0x00000010U +#define STM32C53x_SRAM1_BASE 0x0a000000U +#define STM32C53x_SRAM1_SIZE 0x00008000U +#define STM32C53x_SRAM2_BASE 0x0a008000U +#define STM32C53x_SRAM2_SIZE 0x00008000U +#define STM32C53x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C53x_SRAM12_SIZE (STM32C53x_SRAM1_SIZE + STM32C53x_SRAM2_SIZE) /* Memory map constants for STM32C55x/C56x */ -#define STM32C55X_FLASH_BANK1_BASE 0x08000000U -#define STM32C55X_FLASH_BANK2_BASE 0x08040000U -#define STM32C55X_FLASH_BANK_SIZE 0x00040000U -#define STM32C55X_SECTORS_PER_BANK 0x00000020U -#define STM32C55X_SRAM1_BASE 0x0a000000U -#define STM32C55X_SRAM1_SIZE 0x00010000U -#define STM32C55X_SRAM2_BASE 0x0a010000U -#define STM32C55X_SRAM2_SIZE 0x00010000U -#define STM32C55X_SRAM_ALIAS_BASE 0x20000000U -#define STM32C55X_SRAM12_SIZE (STM32C55X_SRAM1_SIZE + STM32C55X_SRAM2_SIZE) +#define STM32C55x_FLASH_BANK1_BASE 0x08000000U +#define STM32C55x_FLASH_BANK2_BASE 0x08040000U +#define STM32C55x_FLASH_BANK_SIZE 0x00040000U +#define STM32C55x_SECTORS_PER_BANK 0x00000020U +#define STM32C55x_SRAM1_BASE 0x0a000000U +#define STM32C55x_SRAM1_SIZE 0x00010000U +#define STM32C55x_SRAM2_BASE 0x0a010000U +#define STM32C55x_SRAM2_SIZE 0x00010000U +#define STM32C55x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C55x_SRAM12_SIZE (STM32C55x_SRAM1_SIZE + STM32C55x_SRAM2_SIZE) /* Memory map constants for STM32C59x/C5Ax */ -#define STM32C59X_FLASH_BANK1_BASE 0x08000000U -#define STM32C59X_FLASH_BANK2_BASE 0x08080000U -#define STM32C59X_FLASH_BANK_SIZE 0x00080000U -#define STM32C59X_SECTORS_PER_BANK 0x00000040U -#define STM32C59X_SRAM1_BASE 0x0a000000U -#define STM32C59X_SRAM1_SIZE 0x00020000U -#define STM32C59X_SRAM2_BASE 0x0a020000U -#define STM32C59X_SRAM2_SIZE 0x00020000U -#define STM32C59X_SRAM_ALIAS_BASE 0x20000000U -#define STM32C59X_SRAM12_SIZE (STM32C59X_SRAM1_SIZE + STM32C59X_SRAM2_SIZE) +#define STM32C59x_FLASH_BANK1_BASE 0x08000000U +#define STM32C59x_FLASH_BANK2_BASE 0x08080000U +#define STM32C59x_FLASH_BANK_SIZE 0x00080000U +#define STM32C59x_SECTORS_PER_BANK 0x00000040U +#define STM32C59x_SRAM1_BASE 0x0a000000U +#define STM32C59x_SRAM1_SIZE 0x00020000U +#define STM32C59x_SRAM2_BASE 0x0a020000U +#define STM32C59x_SRAM2_SIZE 0x00020000U +#define STM32C59x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C59x_SRAM12_SIZE (STM32C59x_SRAM1_SIZE + STM32C59x_SRAM2_SIZE) /* Flash Program and Erase Controller (FPEC) Register Map */ #define STM32H5_FPEC_BASE 0x40022000 @@ -349,7 +349,7 @@ bool stm32c5_probe(target_s *const target) if (!stm32h5_configure_dbgmcu(target)) return false; - target->driver = "STM32H5"; + target->driver = "STM32C5"; target->attach = stm32h5_attach; target->detach = stm32h5_detach; target->mass_erase = stm32h5_mass_erase; @@ -363,54 +363,54 @@ bool stm32c5_probe(target_s *const target) * Build the RAM map. 32+32=64. * This uses the addresses and sizes found in §2.2.2, Figure 2, pg91 of RM0522 Rev. 1 */ - target_add_ram32(target, STM32C53X_SRAM1_BASE, STM32C53X_SRAM1_SIZE); - target_add_ram32(target, STM32C53X_SRAM2_BASE, STM32C53X_SRAM2_SIZE); - target_add_ram32(target, STM32C53X_SRAM_ALIAS_BASE, STM32C53X_SRAM12_SIZE); + target_add_ram32(target, STM32C53x_SRAM1_BASE, STM32C53x_SRAM1_SIZE); + target_add_ram32(target, STM32C53x_SRAM2_BASE, STM32C53x_SRAM2_SIZE); + target_add_ram32(target, STM32C53x_SRAM_ALIAS_BASE, STM32C53x_SRAM12_SIZE); /* * Build the Flash map. C53X/C54X has one of: * Flash: 256 KiB as two equal banks (16 sectors of 8 KiB each) */ - stm32h5_add_flash(target, STM32C53X_FLASH_BANK1_BASE, STM32C53X_FLASH_BANK_SIZE, - STM32C53X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); - stm32h5_add_flash(target, STM32C53X_FLASH_BANK2_BASE, STM32C53X_FLASH_BANK_SIZE, - STM32C53X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + stm32h5_add_flash(target, STM32C53x_FLASH_BANK1_BASE, STM32C53x_FLASH_BANK_SIZE, + STM32C53x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C53x_FLASH_BANK2_BASE, STM32C53x_FLASH_BANK_SIZE, + STM32C53x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); break; case ID_STM32C55x: /* * Build the RAM map. 64+64=128. * This uses the addresses and sizes found in §2.2.2, Figure 3, pg92 of RM0522 Rev. 1 */ - target_add_ram32(target, STM32C55X_SRAM1_BASE, STM32C55X_SRAM1_SIZE); - target_add_ram32(target, STM32C55X_SRAM2_BASE, STM32C55X_SRAM2_SIZE); - target_add_ram32(target, STM32C55X_SRAM_ALIAS_BASE, STM32C55X_SRAM12_SIZE); + target_add_ram32(target, STM32C55x_SRAM1_BASE, STM32C55x_SRAM1_SIZE); + target_add_ram32(target, STM32C55x_SRAM2_BASE, STM32C55x_SRAM2_SIZE); + target_add_ram32(target, STM32C55x_SRAM_ALIAS_BASE, STM32C55x_SRAM12_SIZE); /* * Build the Flash map. C55X/C56X has one of: * Flash: 512 KiB as two equal banks (32 sectors of 8 KiB each) */ - stm32h5_add_flash(target, STM32C55X_FLASH_BANK1_BASE, STM32C55X_FLASH_BANK_SIZE, - STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); - stm32h5_add_flash(target, STM32C55X_FLASH_BANK2_BASE, STM32C55X_FLASH_BANK_SIZE, - STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + stm32h5_add_flash(target, STM32C55x_FLASH_BANK1_BASE, STM32C55x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C55x_FLASH_BANK2_BASE, STM32C55x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); break; case ID_STM32C59x: /* * Build the RAM map. 128+128=256. * This uses the addresses and sizes found in §2.2.2, Figure 4, pg93 of RM0522 Rev. 1 */ - target_add_ram32(target, STM32C59X_SRAM1_BASE, STM32C59X_SRAM1_SIZE); - target_add_ram32(target, STM32C59X_SRAM2_BASE, STM32C59X_SRAM2_SIZE); - target_add_ram32(target, STM32C59X_SRAM_ALIAS_BASE, STM32C59X_SRAM12_SIZE); + target_add_ram32(target, STM32C59x_SRAM1_BASE, STM32C59x_SRAM1_SIZE); + target_add_ram32(target, STM32C59x_SRAM2_BASE, STM32C59x_SRAM2_SIZE); + target_add_ram32(target, STM32C59x_SRAM_ALIAS_BASE, STM32C59x_SRAM12_SIZE); /* * Build the Flash map. C59X has * Flash: 1 MiB as two equal banks (64 sectors of 8 KiB each) */ - stm32h5_add_flash(target, STM32C59X_FLASH_BANK1_BASE, STM32C59X_FLASH_BANK_SIZE, - STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); - stm32h5_add_flash(target, STM32C59X_FLASH_BANK2_BASE, STM32C59X_FLASH_BANK_SIZE, - STM32C55X_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + stm32h5_add_flash(target, STM32C59x_FLASH_BANK1_BASE, STM32C59x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C59x_FLASH_BANK2_BASE, STM32C59x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); break; default: return false; @@ -563,7 +563,7 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) const uint32_t idcode = target_mem32_read32(target, STM32H5_DBGMCU_IDCODE); const uint16_t rev_id = (idcode & STM32H5_DBGMCU_IDCODE_REV_MASK) >> STM32H5_DBGMCU_IDCODE_REV_SHIFT; const uint16_t dev_id = idcode & STM32H5_DBGMCU_IDCODE_DEV_MASK; - uint8_t isC5 = 0; + bool isc5 = false; /* Display the device ID */ switch (dev_id) { @@ -578,21 +578,21 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) break; case ID_STM32C53x: tc_printf(target, "STM32C53x/54x\n"); - isC5 = 1; + isc5 = true; break; case ID_STM32C55x: tc_printf(target, "STM32C55x/56x\n"); - isC5 = 1; + isc5 = true; break; case ID_STM32C59x: tc_printf(target, "STM32C59x/5Ax\n"); - isC5 = 1; + isc5 = true; break; default: tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); } char revision = '?'; - if(!isC5) { + if(!isc5) { for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { if (stm32h5_revisions[i].rev_id == rev_id) revision = stm32h5_revisions[i].revision; From dc3ee037868997da6badb29e686a9c5f7dd1062d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 00:09:30 +0100 Subject: [PATCH 219/247] codeberg: Created a new CI flow for the `lint` step we did on GHA, allowing pre-commit to run again --- .codeberg/ci/lint.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .codeberg/ci/lint.yml diff --git a/.codeberg/ci/lint.yml b/.codeberg/ci/lint.yml new file mode 100644 index 00000000000..75ebbff5e1b --- /dev/null +++ b/.codeberg/ci/lint.yml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +steps: + - name: lint + image: debian:trixie + pull: true + commands: | + # Get Python installed + apt update + apt install -y python3 python3-venv git + # Create a Python venv for pre-commit to be run from and activate it + python3 -m venv --upgrade-deps env + . env/bin/activate + # Now install pre-commit and run it for BMD + pip install pre-commit + pre-commit run --show-diff-on-failure --color=always --all-files From 09438ee5d2b48f8fd17931cb3facd628d9e60ae4 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 00:54:57 +0100 Subject: [PATCH 220/247] stm32h5: Fixed the formatting lints from `pre-commit` --- src/target/stm32h5.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index 59862613dc2..a5a4d1d3a60 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -94,7 +94,7 @@ #define STM32C53x_SRAM2_BASE 0x0a008000U #define STM32C53x_SRAM2_SIZE 0x00008000U #define STM32C53x_SRAM_ALIAS_BASE 0x20000000U -#define STM32C53x_SRAM12_SIZE (STM32C53x_SRAM1_SIZE + STM32C53x_SRAM2_SIZE) +#define STM32C53x_SRAM12_SIZE (STM32C53x_SRAM1_SIZE + STM32C53x_SRAM2_SIZE) /* Memory map constants for STM32C55x/C56x */ #define STM32C55x_FLASH_BANK1_BASE 0x08000000U #define STM32C55x_FLASH_BANK2_BASE 0x08040000U @@ -105,7 +105,7 @@ #define STM32C55x_SRAM2_BASE 0x0a010000U #define STM32C55x_SRAM2_SIZE 0x00010000U #define STM32C55x_SRAM_ALIAS_BASE 0x20000000U -#define STM32C55x_SRAM12_SIZE (STM32C55x_SRAM1_SIZE + STM32C55x_SRAM2_SIZE) +#define STM32C55x_SRAM12_SIZE (STM32C55x_SRAM1_SIZE + STM32C55x_SRAM2_SIZE) /* Memory map constants for STM32C59x/C5Ax */ #define STM32C59x_FLASH_BANK1_BASE 0x08000000U #define STM32C59x_FLASH_BANK2_BASE 0x08080000U @@ -116,7 +116,7 @@ #define STM32C59x_SRAM2_BASE 0x0a020000U #define STM32C59x_SRAM2_SIZE 0x00020000U #define STM32C59x_SRAM_ALIAS_BASE 0x20000000U -#define STM32C59x_SRAM12_SIZE (STM32C59x_SRAM1_SIZE + STM32C59x_SRAM2_SIZE) +#define STM32C59x_SRAM12_SIZE (STM32C59x_SRAM1_SIZE + STM32C59x_SRAM2_SIZE) /* Flash Program and Erase Controller (FPEC) Register Map */ #define STM32H5_FPEC_BASE 0x40022000 @@ -177,7 +177,7 @@ /* Taken from DBGMCU_IDCODE in §59.12.4 of RM0481 rev 2, pg3116 */ #define ID_STM32H523 0x478U /* Taken from DBGMCU_IDCODE in §49.12.4 of RM0522 rev 1, pg2542 */ -#define ID_STM32C53x 0x44FU +#define ID_STM32C53x 0x44FU #define ID_STM32C55x 0x44EU #define ID_STM32C59x 0x45AU @@ -356,7 +356,7 @@ bool stm32c5_probe(target_s *const target) target->enter_flash_mode = stm32h5_enter_flash_mode; target->exit_flash_mode = stm32h5_exit_flash_mode; target_add_commands(target, stm32h5_cmd_list, target->driver); - + switch (target->part_id) { case ID_STM32C53x: /* @@ -564,7 +564,7 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) const uint16_t rev_id = (idcode & STM32H5_DBGMCU_IDCODE_REV_MASK) >> STM32H5_DBGMCU_IDCODE_REV_SHIFT; const uint16_t dev_id = idcode & STM32H5_DBGMCU_IDCODE_DEV_MASK; bool isc5 = false; - + /* Display the device ID */ switch (dev_id) { case ID_STM32H5xx: @@ -592,7 +592,7 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); } char revision = '?'; - if(!isc5) { + if (!isc5) { for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { if (stm32h5_revisions[i].rev_id == rev_id) revision = stm32h5_revisions[i].revision; From 6f6019b2968b7d4b3f0598e4ddf24009e74c5e80 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 00:55:26 +0100 Subject: [PATCH 221/247] lattice_ecp5: Fixed the formatting lints from `pre-commit` --- src/target/lattice_ecp5.c | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 4766db7cfd2..5da6d96207c 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -43,15 +43,15 @@ #define REGISTER_FIELD(reg, mask, shift) (((reg) >> (shift)) & (mask)) -#define ECP5_STATUS_TRANSPARENT_MASK 1U -#define ECP5_STATUS_TRANSPARENT_SHIFT 0U -#define ECP5_STATUS_TRANSPARENT(reg) REGISTER_FIELD(reg, ECP5_STATUS_TRANSPARENT_MASK, ECP5_STATUS_TRANSPARENT_SHIFT) -#define ECP5_STATUS_TARGET_MASK 0x7U -#define ECP5_STATUS_TARGET_SHIFT 1U -#define ECP5_STATUS_TARGET(reg) REGISTER_FIELD(reg, ECP5_STATUS_TARGET_MASK, ECP5_STATUS_TARGET_SHIFT) -#define ECP5_STATUS_JTAG_ACTIVE_MASK 1U -#define ECP5_STATUS_JTAG_ACTIVE_SHIFT 4U -#define ECP5_STATUS_JTAG_ACTIVE(reg) REGISTER_FIELD(reg, ECP5_STATUS_JTAG_ACTIVE_MASK, ECP5_STATUS_JTAG_ACTIVE_SHIFT) +#define ECP5_STATUS_TRANSPARENT_MASK 1U +#define ECP5_STATUS_TRANSPARENT_SHIFT 0U +#define ECP5_STATUS_TRANSPARENT(reg) REGISTER_FIELD(reg, ECP5_STATUS_TRANSPARENT_MASK, ECP5_STATUS_TRANSPARENT_SHIFT) +#define ECP5_STATUS_TARGET_MASK 0x7U +#define ECP5_STATUS_TARGET_SHIFT 1U +#define ECP5_STATUS_TARGET(reg) REGISTER_FIELD(reg, ECP5_STATUS_TARGET_MASK, ECP5_STATUS_TARGET_SHIFT) +#define ECP5_STATUS_JTAG_ACTIVE_MASK 1U +#define ECP5_STATUS_JTAG_ACTIVE_SHIFT 4U +#define ECP5_STATUS_JTAG_ACTIVE(reg) REGISTER_FIELD(reg, ECP5_STATUS_JTAG_ACTIVE_MASK, ECP5_STATUS_JTAG_ACTIVE_SHIFT) #define ECP5_STATUS_PASSWORD_PROTECTED_MASK 1U #define ECP5_STATUS_PASSWORD_PROTECTED_SHIFT 5U #define ECP5_STATUS_PASSWORD_PROTECTED(reg) \ @@ -73,18 +73,18 @@ #define ECP5_STATUS_WRITE_ENABLED_SHIFT 10U #define ECP5_STATUS_WRITE_ENABLED(reg) \ REGISTER_FIELD(reg, ECP5_STATUS_WRITE_ENABLED_MASK, ECP5_STATUS_WRITE_ENABLED_SHIFT) -#define ECP5_STATUS_READ_ENABLED_MASK 1U -#define ECP5_STATUS_READ_ENABLED_SHIFT 11U -#define ECP5_STATUS_READ_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_READ_ENABLED_MASK, ECP5_STATUS_READ_ENABLED_SHIFT) -#define ECP5_STATUS_BUSY_MASK 1U -#define ECP5_STATUS_BUSY_SHIFT 12U -#define ECP5_STATUS_BUSY(reg) REGISTER_FIELD(reg, ECP5_STATUS_BUSY_MASK, ECP5_STATUS_BUSY_SHIFT) -#define ECP5_STATUS_FAILURE_MASK 1U -#define ECP5_STATUS_FAILURE_SHIFT 13U -#define ECP5_STATUS_FAILURE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FAILURE_MASK, ECP5_STATUS_FAILURE_SHIFT) -#define ECP5_STATUS_FEATURES_OTP_MASK 1U -#define ECP5_STATUS_FEATURES_OTP_SHIFT 14U -#define ECP5_STATUS_FEATURES_OTP(reg) REGISTER_FIELD(reg, ECP5_STATUS_FEATURES_OTP_MASK, ECP5_STATUS_FEATURES_OTP_SHIFT) +#define ECP5_STATUS_READ_ENABLED_MASK 1U +#define ECP5_STATUS_READ_ENABLED_SHIFT 11U +#define ECP5_STATUS_READ_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_READ_ENABLED_MASK, ECP5_STATUS_READ_ENABLED_SHIFT) +#define ECP5_STATUS_BUSY_MASK 1U +#define ECP5_STATUS_BUSY_SHIFT 12U +#define ECP5_STATUS_BUSY(reg) REGISTER_FIELD(reg, ECP5_STATUS_BUSY_MASK, ECP5_STATUS_BUSY_SHIFT) +#define ECP5_STATUS_FAILURE_MASK 1U +#define ECP5_STATUS_FAILURE_SHIFT 13U +#define ECP5_STATUS_FAILURE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FAILURE_MASK, ECP5_STATUS_FAILURE_SHIFT) +#define ECP5_STATUS_FEATURES_OTP_MASK 1U +#define ECP5_STATUS_FEATURES_OTP_SHIFT 14U +#define ECP5_STATUS_FEATURES_OTP(reg) REGISTER_FIELD(reg, ECP5_STATUS_FEATURES_OTP_MASK, ECP5_STATUS_FEATURES_OTP_SHIFT) #define ECP5_STATUS_ENCRYPTED_ONLY_MASK 1U #define ECP5_STATUS_ENCRYPTED_ONLY_SHIFT 15U #define ECP5_STATUS_ENCRYPTED_ONLY(reg) \ From df45ebdf6e53bb9a5963cfd57f7438857b587378 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 00:56:53 +0100 Subject: [PATCH 222/247] lattice_common: Fixed the hex literal case lints from `pre-commit` --- src/target/lattice_common.h | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/target/lattice_common.h b/src/target/lattice_common.h index 17ec60cbffa..919a1b9f895 100644 --- a/src/target/lattice_common.h +++ b/src/target/lattice_common.h @@ -37,35 +37,35 @@ #include // Read out the 32-bit IDCODE of the device -#define CMD_READ_ID 0xE0U +#define CMD_READ_ID 0xe0U // Read 32-bit user code -#define CMD_USERCODE 0xC0U +#define CMD_USERCODE 0xc0U // Read out internal status -#define CMD_LSC_READ_STATUS 0x3CU +#define CMD_LSC_READ_STATUS 0x3cU // Read 1 bit busy flag to check the command execution status -#define CMD_LSC_CHECK_BSY 0xF0U +#define CMD_LSC_CHECK_BSY 0xf0U // Equivalent to toggle PROGRAMN pin #define CMD_LSC_REFRESH 0x79U // Enable the Offline configuration mode -#define CMD_ISC_ENABLE 0xC6U +#define CMD_ISC_ENABLE 0xc6U // Enable the Transparent configuration mode #define CMD_ISC_ENABLE_X 0x74U // Disable the configuration operation #define CMD_ISC_DISABLE 0x26U // Write the 32-bit new USERCODE data to USERCODE register -#define CMD_ISC_PROGRAM_USERCODE 0xC2U +#define CMD_ISC_PROGRAM_USERCODE 0xc2U // Bulk erase the memory array base on the access mode and array selection -#define CMD_ISC_ERASE 0x0EU +#define CMD_ISC_ERASE 0x0eU // Program the DONE bit if the device is in Configuration state. -#define CMD_ISC_PROGRAM_DONE 0x5EU +#define CMD_ISC_PROGRAM_DONE 0x5eU // Program the Security bit if the device is in Configuration state. -#define CMD_ISC_PROGRAM_SECURITY 0xCEU +#define CMD_ISC_PROGRAM_SECURITY 0xceU // Initialize the Address Shift Register #define CMD_LSC_INIT_ADDRESS 0x46U // Write the 16 bit Address Register to move the address quickly -#define CMD_LSC_WRITE_ADDRESS 0xB4U +#define CMD_LSC_WRITE_ADDRESS 0xb4U // Program the device the whole bitstream sent in as the command operand -#define CMD_LSC_BITSTREAM_BURST 0x7AU +#define CMD_LSC_BITSTREAM_BURST 0x7aU /* * Write configuration data to the configuration memory frame at current address * and post increment the address. @@ -74,48 +74,48 @@ */ #define CMD_LSC_PROG_INCR_RTI 0x82U // Encrypt the configuration data then write -#define CMD_LSC_PROG_INCR_ENC 0xB6U +#define CMD_LSC_PROG_INCR_ENC 0xb6U // Decompress the configuration data, then write -#define CMD_LSC_PROG_INCR_CMP 0xB8U +#define CMD_LSC_PROG_INCR_CMP 0xb8U // Decompress and Encrypt the configuration data, then write -#define CMD_LSC_PROG_INCR_CNE 0xBAU +#define CMD_LSC_PROG_INCR_CNE 0xbaU // Read back the configuration memory frame selected by the address register and post increment the address -#define CMD_LSC_VERIFY_INCR_RTI 0x6AU +#define CMD_LSC_VERIFY_INCR_RTI 0x6aU // Modify the Control Register 0 #define CMD_LSC_PROG_CTRL0 0x22U // Read the Control Register 0 #define CMD_LSC_READ_CTRL0 0x20U // Reset 16-bit frame CRC register to 0x0000 -#define CMD_LSC_RESET_CRC 0x3BU +#define CMD_LSC_RESET_CRC 0x3bU // Read 16-bit frame CRC register content #define CMD_LSC_READ_CRC 0x60U // Program the calculated 32-bit CRC based on configuration bit values only into overall CRC register -#define CMD_LSC_PROG_SED_CRC 0xA2U +#define CMD_LSC_PROG_SED_CRC 0xa2U // Read the 32-bit SED CRC -#define CMD_LSC_READ_SED_CRC 0xA4U +#define CMD_LSC_READ_SED_CRC 0xa4U // Program 64-bit password into the non-volatile memory (Efuse) -#define CMD_LSC_PROG_PASSWORD 0xF1U +#define CMD_LSC_PROG_PASSWORD 0xf1U // Read out the 64-bit password before activated for verification -#define CMD_LSC_READ_PASSWORD 0xF2U +#define CMD_LSC_READ_PASSWORD 0xf2U // Shift in the password to unlock for re-configuration (necessary when password protection feature is active). -#define CMD_LSC_SHIFT_PASSWORD 0xBCU +#define CMD_LSC_SHIFT_PASSWORD 0xbcU // Program the 128-bit cipher key into Efuse -#define CMD_LSC_PROG_CIPHER_KEY 0xF3U +#define CMD_LSC_PROG_CIPHER_KEY 0xf3U // Read out the 128-bit cipher key before activated for verification -#define CMD_LSC_READ_CIPHER_KEY 0xF4U +#define CMD_LSC_READ_CIPHER_KEY 0xf4U // Program User Feature, such as Customer ID, I2C Slave Address, Unique ID Header -#define CMD_LSC_PROG_FEATURE 0xE4U +#define CMD_LSC_PROG_FEATURE 0xe4U // Read User Feature, such as Customer ID, I2C Slave Address, Unique ID Header -#define CMD_LSC_READ_FEATURE 0xE7U +#define CMD_LSC_READ_FEATURE 0xe7U // Program User Feature Bits, such as CFG port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. -#define CMD_LSC_PROG_FEABITS 0xF8U +#define CMD_LSC_PROG_FEABITS 0xf8U // Read User Feature Bits, such as CFH port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. -#define CMD_LSC_READ_FEABITS 0xFBU +#define CMD_LSC_READ_FEABITS 0xfbU // Program OTP bits, to set Memory Sectors One Time Programmable -#define CMD_LSC_PROG_OTP 0xF9U +#define CMD_LSC_PROG_OTP 0xf9U // Read OTP bits setting -#define CMD_LSC_READ_OTP 0xFAU +#define CMD_LSC_READ_OTP 0xfaU // Enable SPI Programming via JTAG -#define CMD_LSC_BACKGROUND_SPI 0x3AU +#define CMD_LSC_BACKGROUND_SPI 0x3aU #endif /* TARGET_LATTICE_COMMON_H */ From e0d8ef9aaf11b1400faa7237bbb6e1b5a0d0e6aa Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 01:02:05 +0100 Subject: [PATCH 223/247] stm32h7rs: Cleaned up and fixed formatting lints --- src/target/stm32h7rs.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c index 5389c60487d..ae292720dbd 100644 --- a/src/target/stm32h7rs.c +++ b/src/target/stm32h7rs.c @@ -3,8 +3,9 @@ * * Copyright (C) 2017-2020 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de * Copyright (C) 2022-2023 1BitSquared - * Copyright (C) 2025-2026 Eric Brombaugh based on initial - * work done by Rachel Mant and zyp. + * Copyright (C) 2025-2026 Eric Brombaugh + * Written by Eric Brombaugh + * Based on initial work done by Rachel Mant and zyp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -98,7 +99,7 @@ #define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) #define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU -/* +/* * Access from processor address space. * Access via the APB-D is at 0xe00e1000 */ @@ -144,7 +145,7 @@ static bool stm32h7rs_crc(target_s *target, int argc, const char **argv); static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv); static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv); -const command_s stm32h7rs_cmd_list[] = { +static const command_s stm32h7rs_cmd_list[] = { {"psize", stm32h7rs_cmd_psize, "Configure flash write parallelism: (x8|x16|x32|x64(default))"}, {"uid", stm32h7rs_uid, "Print unique device ID"}, {"crc", stm32h7rs_crc, "Print CRC of bank 1"}, @@ -158,7 +159,7 @@ static bool stm32h7rs_flash_erase(target_flash_s *target_flash, target_addr_t ad static bool stm32h7rs_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len); static bool stm32h7rs_flash_prepare(target_flash_s *target_flash); static bool stm32h7rs_flash_done(target_flash_s *target_flash); -static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess); +static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *print_progess); static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, size_t blocksize) { @@ -201,7 +202,7 @@ bool stm32h7rs_probe(target_s *target) priv->dbg_cr = target_mem32_read32(target, DBGMCU_CR); /* Set up the Flash write/erase parallelism to 64-bit default */ priv->psize = ALIGN_64BIT; - + target->driver = "STM32H7R/S"; target->attach = stm32h7rs_attach; target->detach = stm32h7rs_detach; @@ -494,7 +495,7 @@ static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv) { if (argc == 1) { - align_e psize = ((const stm32h7rs_priv_s *)target->target_storage)->psize;; + const align_e psize = ((const stm32h7rs_priv_s *)target->target_storage)->psize; tc_printf(target, "Flash write parallelism: %s\n", stm32_psize_to_string(psize)); } else { align_e psize = ALIGN_64BIT; From 6bcc67d98d1a8503a5b2f9d78290eff3dc3b54c7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 01:02:36 +0100 Subject: [PATCH 224/247] stm32h5: Fixed hex literal case lints from `pre-commit` --- src/target/stm32h5.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index a5a4d1d3a60..a80664a549a 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -177,9 +177,9 @@ /* Taken from DBGMCU_IDCODE in §59.12.4 of RM0481 rev 2, pg3116 */ #define ID_STM32H523 0x478U /* Taken from DBGMCU_IDCODE in §49.12.4 of RM0522 rev 1, pg2542 */ -#define ID_STM32C53x 0x44FU -#define ID_STM32C55x 0x44EU -#define ID_STM32C59x 0x45AU +#define ID_STM32C53x 0x44fU +#define ID_STM32C55x 0x44eU +#define ID_STM32C59x 0x45aU typedef struct stm32h5_flash { target_flash_s target_flash; From d8b83dc8041103850f88cd72c533c775e278b331 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 01:03:26 +0100 Subject: [PATCH 225/247] lattice_ecp5: Fixed hex literal case lints from `pre-commit` --- src/target/lattice_ecp5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 5da6d96207c..7c613789878 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -131,13 +131,13 @@ #define ECP5_STATUS_FLOW_MODE_SHIFT 31U #define ECP5_STATUS_FLOW_MODE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FLOW_MODE_MASK, ECP5_STATUS_FLOW_MODE_SHIFT) -#define ECP5_CTRL0_MSPI_CLK_MASK 0x1FU +#define ECP5_CTRL0_MSPI_CLK_MASK 0x1fU #define ECP5_CTRL0_MSPI_CLK_SHIFT 0U #define ECP5_CTRL0_MSPI_CLK(reg) REGISTER_FIELD(reg, ECP5_CTRL0_MSPI_CLK_MASK, ECP5_CTRL0_MSPI_CLK_SHIFT) #define ECP5_CTRL0_SLEW_MASK 0x3U #define ECP5_CTRL0_SLEW_SHIFT 6U #define ECP5_CTRL0_SLEW(reg) REGISTER_FIELD(reg, ECP5_CTRL0_SLEW_MASK, ECP5_CTRL0_SLEW_SHIFT) -#define ECP5_CTRL0_RSVD0_MASK 0x1FFU +#define ECP5_CTRL0_RSVD0_MASK 0x1ffU #define ECP5_CTRL0_RSVD0_SHIFT 8U #define ECP5_CTRL0_RSVD0(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD0_MASK, ECP5_CTRL0_RSVD0_SHIFT) #define ECP5_CTRL0_PDONE_MASK 0x3U From 0898e1dfc2ade521fe39b2480422e88a70bfdb40 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 2 May 2026 01:05:39 +0100 Subject: [PATCH 226/247] adiv5: Fixed the formatting lint from `pre-commit` --- src/target/adiv5.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 963af409199..7b254df24f4 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -458,7 +458,6 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) DEBUG_WARN("Failed to read TARGETID partno after 128 attempts\n"); break; } - }; /* From 5a4e73956a971be016a5f567d2971503f8cb0610 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Sat, 2 May 2026 18:04:43 -0700 Subject: [PATCH 227/247] fix/stm32c5-macro-naming: fix flash bank size typos for STM32C59x --- src/target/stm32h5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index a80664a549a..6d7f14355d9 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -408,9 +408,9 @@ bool stm32c5_probe(target_s *const target) * Flash: 1 MiB as two equal banks (64 sectors of 8 KiB each) */ stm32h5_add_flash(target, STM32C59x_FLASH_BANK1_BASE, STM32C59x_FLASH_BANK_SIZE, - STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + STM32C59x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); stm32h5_add_flash(target, STM32C59x_FLASH_BANK2_BASE, STM32C59x_FLASH_BANK_SIZE, - STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + STM32C59x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); break; default: return false; From 6c73d2e4c01abce1f2fac61b1de6b577b937b4d5 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Thu, 30 Apr 2026 14:36:45 -0700 Subject: [PATCH 228/247] stm32u3: initial checkin of stm32u3 support - probes, attaches, debugs, reads but erase, write fail --- src/target/stm32l4.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 4a7d6db2b02..982c7407c2b 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -5,6 +5,7 @@ * Copyright (C) 2022-2025 1BitSquared * Written by Uwe Bonnes * Modified by Rachel Mant + * Modified by Eric Brombaugh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,6 +47,8 @@ * RM0434 - Multiprotocol wireless 32-bit MCU Arm®-based Cortex®-M4 with * FPU, Bluetooth® Low-Energy and 802.15.4 radio solution Rev 10 * - https://www.st.com/resource/en/reference_manual/rm0434-multiprotocol-wireless-32bit-mcu-armbased-cortexm4-with-fpu-bluetooth-lowenergy-and-802154-radio-solution-stmicroelectronics.pdf + * RM0487 - STM32U3 Series Arm®-based 32-bit MCUs - Reference manual Rev 3 + * - https://www.st.com/resource/en/reference_manual/rm0487-stm32u3-series-armbased-32bit-mcus-stmicroelectronics.pdf */ #include "general.h" @@ -198,6 +201,10 @@ * - RM0478, Rev.8 §31.4.8 DP_TARGETID pg 980 * - RM0478, Rev.8 §31.8.1 DBGMCU_IDCODE pg 1011 (at address 0xe0042000) * - RM0478, Rev.8 §31.13.3 CPU1 ROM table PIDR pg 1057 + * References for the U3 parts: + * - RM0487, Rev.3 §57.3.3 DP_TARGETID pg 2745 + * - RM0487, Rev.3 §57.5.1 MCU ROM table PIDR pg 2754 + * - RM0487, Rev.3 §57.12.4 DBGMCU_IDCODE pg 2848 (at address 0xe0044000) * * NB: For WL5x parts, core 2's AP requires using DBGMCU_IDCODE for identification. * The outer ROM table for this core carries the ARM core ID, not the part ID. @@ -213,6 +220,9 @@ #define ID_STM32WLxx 0x497U #define ID_STM32WB35 0x495U /* STM32WB35/55 */ #define ID_STM32WB1x 0x494U +#define ID_STM32U3B5 0x42AU /* STM32U3B5/3C5 */ +#define ID_STM32U356 0x42BU /* STM32U356/366 */ +#define ID_STM32U375 0x454U /* STM32U375/385 */ static bool stm32l4_cmd_erase_bank1(target_s *target, int argc, const char **argv); static bool stm32l4_cmd_erase_bank2(target_s *target, int argc, const char **argv); @@ -241,6 +251,7 @@ typedef enum stm32l4_family { STM32L4_FAMILY_L55x, STM32L4_FAMILY_U5xx, STM32L4_FAMILY_WLxx, + STM32L4_FAMILY_U3xx, } stm32l4_family_e; /* @@ -483,6 +494,33 @@ static stm32l4_device_info_s const stm32l4_device_info[] = { .flash_regs_map = stm32wb_flash_regs_map, .flash_size_reg = STM32L4_FLASH_SIZE_REG, }, + { + .device_id = ID_STM32U3B5, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U3B5/3C5", + .sram1 = 192U + 64U + 320U, /* SRAM1+2+3 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, + { + .device_id = ID_STM32U356, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U356/366", + .sram1 = 128U + 64U, /* SRAM1+2 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, + { + .device_id = ID_STM32U375, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U375/385", + .sram1 = 192U + 64U, /* SRAM1+2 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, { /* Sentinel entry */ .device_id = 0, @@ -1112,6 +1150,18 @@ static bool stm32l4_cmd_option(target_s *const target, const int argc, const cha tc_printf(target, "%s options not implemented!\n", "STM32WLxx"); return false; } + if (target->part_id == ID_STM32U3B5) { + tc_printf(target, "%s options not implemented!\n", "STM32U3B5/3C5"); + return false; + } + if (target->part_id == ID_STM32U356) { + tc_printf(target, "%s options not implemented!\n", "STM32U356/366"); + return false; + } + if (target->part_id == ID_STM32U375) { + tc_printf(target, "%s options not implemented!\n", "STM32U375/385"); + return false; + } const stm32l4_option_bytes_info_s info = stm32l4_get_opt_bytes_info(target->part_id); const uint32_t fpec_base = stm32l4_fpec_base_addr(target); From a38cc9ca3bcf86ce0fb08b642241cd321ce48dde Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Fri, 1 May 2026 19:36:07 -0700 Subject: [PATCH 229/247] stm32u3: Erase works - needed to set VOS1. Write / load hangs gdb --- src/target/stm32l4.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 982c7407c2b..4d094d2d744 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -142,6 +142,13 @@ #define STM32L5_PWR_CR1 0x50007000U #define STM32L5_PWR_CR1_VOS (3U << 9U) +#define STM32U3_RCC_AHB1ENR2 0x40030C94U +#define STM32U3_RCC_AHB1ENR2_PWREN (1U << 2U) +#define STM32U3_PWR_VOSR 0x4003080CU +#define STM32U3_PWR_VOSR_R1EN (1 << 0U) +#define STM32U3_PWR_VOSR_R2EN (1 << 1U) +#define STM32U3_PWR_VOSR_R1RDY (1 << 16U) + #define STM32L4_FLAG_DUAL_BANK 0x80U /* TODO: add block size constants for other MCUs */ @@ -680,6 +687,36 @@ static void stm32l5_flash_enable(target_s *const target) target_mem32_write32(target, STM32L5_PWR_CR1, pwr_ctrl1); } +/* stm32u3 needs to be put in VOS 1 after reset in order to flash */ +static bool stm32u3_enter_flash_mode(target_s *target) +{ + DEBUG_INFO("in %s\n", __func__); + if (target->flash_mode) + return true; + + bool result = true; + + /* Reset target on flash command */ + /* This saves us if we're interrupted in IRQ context */ + target_reset(target); + + /* enable PWR */ + target_mem32_write32(target, STM32U3_RCC_AHB1ENR2, STM32U3_RCC_AHB1ENR2_PWREN); + + /* switch to VOS 1 */ + target_mem32_write32(target, STM32U3_PWR_VOSR, STM32U3_PWR_VOSR_R1EN); + + /* wait for R1RDY */ + uint32_t vosr = 0; + while((vosr & STM32U3_PWR_VOSR_R1RDY) == 0U) { + vosr = target_mem32_read32(target, STM32U3_PWR_VOSR); + } + + if (result == true) + target->flash_mode = true; + return result; +} + static uint32_t stm32l4_main_sram_length(const target_s *const target) { const stm32l4_priv_s *const priv = (const stm32l4_priv_s *)target->target_storage; @@ -809,6 +846,12 @@ bool stm32l4_probe(target_s *const target) case ID_STM32U575: target->target_options |= TOPT_NON_HALTING_MEM_IO; break; + case ID_STM32U3B5: + case ID_STM32U356: + case ID_STM32U375: + target->enter_flash_mode = stm32u3_enter_flash_mode; + target->target_options |= TOPT_NON_HALTING_MEM_IO; + break; default: break; } From cca765737326ee4cbd66ce3a6cc96c2892845040 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Sat, 2 May 2026 14:48:43 -0700 Subject: [PATCH 230/247] stm32u3: removed 0-length SRAM region - programming works --- src/target/stm32l4.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 4d094d2d744..a4ce6f9d214 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -690,31 +690,19 @@ static void stm32l5_flash_enable(target_s *const target) /* stm32u3 needs to be put in VOS 1 after reset in order to flash */ static bool stm32u3_enter_flash_mode(target_s *target) { - DEBUG_INFO("in %s\n", __func__); - if (target->flash_mode) - return true; - - bool result = true; - /* Reset target on flash command */ /* This saves us if we're interrupted in IRQ context */ target_reset(target); - /* enable PWR */ target_mem32_write32(target, STM32U3_RCC_AHB1ENR2, STM32U3_RCC_AHB1ENR2_PWREN); - /* switch to VOS 1 */ target_mem32_write32(target, STM32U3_PWR_VOSR, STM32U3_PWR_VOSR_R1EN); - /* wait for R1RDY */ uint32_t vosr = 0; while((vosr & STM32U3_PWR_VOSR_R1RDY) == 0U) { vosr = target_mem32_read32(target, STM32U3_PWR_VOSR); } - - if (result == true) - target->flash_mode = true; - return result; + return true; } static uint32_t stm32l4_main_sram_length(const target_s *const target) @@ -880,7 +868,7 @@ static bool stm32l4_attach(target_s *const target) /* And rebuild the RAM map */ if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx) target_add_ram32(target, 0x0a000000, (device->sram1 + device->sram2) * 1024U); - else + else if(device->sram2 != 0U) target_add_ram32(target, 0x10000000, device->sram2 * 1024U); target_add_ram32(target, 0x20000000, stm32l4_main_sram_length(target)); /* Every STM32U5xx has 16 KiB of SRAM4 in SmartRun domain */ From ca5c3a731d136248e321ab94d388b5826865eb37 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Sat, 2 May 2026 15:01:56 -0700 Subject: [PATCH 231/247] stm32u3: fix sram2 region logic per ALTracer --- src/target/stm32l4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index a4ce6f9d214..4086017a152 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -866,9 +866,9 @@ static bool stm32l4_attach(target_s *const target) /* Free any previously built memory map */ target_mem_map_free(target); /* And rebuild the RAM map */ - if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx) + if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx || device->family == STM32L4_FAMILY_U3xx) target_add_ram32(target, 0x0a000000, (device->sram1 + device->sram2) * 1024U); - else if(device->sram2 != 0U) + else target_add_ram32(target, 0x10000000, device->sram2 * 1024U); target_add_ram32(target, 0x20000000, stm32l4_main_sram_length(target)); /* Every STM32U5xx has 16 KiB of SRAM4 in SmartRun domain */ From 55549ec712cbbdc962d01ea2aa42720df62683f5 Mon Sep 17 00:00:00 2001 From: Eric Brombaugh Date: Sat, 2 May 2026 18:30:05 -0700 Subject: [PATCH 232/247] stm32u3: correct copyright and formatting issues found during review --- src/target/stm32l4.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 4086017a152..c05d559975b 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -2,7 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015, 2017-2022 Uwe Bonnes - * Copyright (C) 2022-2025 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Written by Uwe Bonnes * Modified by Rachel Mant * Modified by Eric Brombaugh @@ -142,9 +142,9 @@ #define STM32L5_PWR_CR1 0x50007000U #define STM32L5_PWR_CR1_VOS (3U << 9U) -#define STM32U3_RCC_AHB1ENR2 0x40030C94U +#define STM32U3_RCC_AHB1ENR2 0x40030c94U #define STM32U3_RCC_AHB1ENR2_PWREN (1U << 2U) -#define STM32U3_PWR_VOSR 0x4003080CU +#define STM32U3_PWR_VOSR 0x4003080cU #define STM32U3_PWR_VOSR_R1EN (1 << 0U) #define STM32U3_PWR_VOSR_R2EN (1 << 1U) #define STM32U3_PWR_VOSR_R1RDY (1 << 16U) @@ -227,8 +227,8 @@ #define ID_STM32WLxx 0x497U #define ID_STM32WB35 0x495U /* STM32WB35/55 */ #define ID_STM32WB1x 0x494U -#define ID_STM32U3B5 0x42AU /* STM32U3B5/3C5 */ -#define ID_STM32U356 0x42BU /* STM32U356/366 */ +#define ID_STM32U3B5 0x42aU /* STM32U3B5/3C5 */ +#define ID_STM32U356 0x42bU /* STM32U356/366 */ #define ID_STM32U375 0x454U /* STM32U375/385 */ static bool stm32l4_cmd_erase_bank1(target_s *target, int argc, const char **argv); @@ -699,9 +699,8 @@ static bool stm32u3_enter_flash_mode(target_s *target) target_mem32_write32(target, STM32U3_PWR_VOSR, STM32U3_PWR_VOSR_R1EN); /* wait for R1RDY */ uint32_t vosr = 0; - while((vosr & STM32U3_PWR_VOSR_R1RDY) == 0U) { + while ((vosr & STM32U3_PWR_VOSR_R1RDY) == 0U) vosr = target_mem32_read32(target, STM32U3_PWR_VOSR); - } return true; } @@ -866,7 +865,8 @@ static bool stm32l4_attach(target_s *const target) /* Free any previously built memory map */ target_mem_map_free(target); /* And rebuild the RAM map */ - if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx || device->family == STM32L4_FAMILY_U3xx) + if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx || + device->family == STM32L4_FAMILY_U3xx) target_add_ram32(target, 0x0a000000, (device->sram1 + device->sram2) * 1024U); else target_add_ram32(target, 0x10000000, device->sram2 * 1024U); From 85fcb0ef1d14ad652f9e7b30a410efd23360ba0f Mon Sep 17 00:00:00 2001 From: zhangjiance Date: Sun, 26 Apr 2026 18:48:59 +0800 Subject: [PATCH 233/247] riscv_debug: Fix stack buffer overflow in 128-bit CSR access --- src/target/riscv_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index d797e8052ba..0047b91f1c1 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -790,7 +790,7 @@ static bool riscv_csr_progbuf_read(riscv_hart_s *const hart, const uint16_t reg, /* Wait for both the register read and progbuf execution to complete */ result &= riscv_command_wait_complete(hart); /* Extract the data read out by the GPR read */ - uint32_t a0_value[3]; + uint32_t a0_value[4]; result &= riscv_csr_read_data(hart, a0_value, hart->access_width); /* Now try to read out the requested data from a0 at the requested size */ result &= riscv_csr_read(hart, RV_GPR_A0 | (reg & RV_CSR_FORCE_MASK), data); @@ -845,7 +845,7 @@ static bool riscv_csr_write_data(riscv_hart_s *const hart, const void *const dat static bool riscv_csr_progbuf_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) { /* Read out a0 to keep it safe as the actions below clobber it */ - uint32_t a0_value[3]; + uint32_t a0_value[4]; if (!riscv_csr_read(hart, RV_GPR_A0, a0_value)) return false; From de3341ecc10bf54775ddcea7447e4336dd047e23 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 3 May 2026 06:24:01 +0100 Subject: [PATCH 234/247] adiv5_jtag: Fixed adiv5_jtag_ensure_idle() so it doesn't mis-step the state machine when in idle already --- src/target/adiv5_jtag.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/target/adiv5_jtag.c b/src/target/adiv5_jtag.c index d91671569af..4da8920be4d 100644 --- a/src/target/adiv5_jtag.c +++ b/src/target/adiv5_jtag.c @@ -1,8 +1,10 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin + * Copyright (C) 2022-2026 1BitSquared + * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -184,6 +186,6 @@ void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp) * from DPACC/APACC to IDCODE. We want BYPASS in case of daisy-chaining. */ jtag_devs[dp->dev_index].current_ir = 0xffU; - /* Go from TLR to RTI. */ - jtagtap_return_idle(1); + /* Ensure the TAP state machine is back in Run/Test Idle */ + jtagtap_soft_reset(); } From 73159874b693c680e0b951b8ea48add18ab57710 Mon Sep 17 00:00:00 2001 From: Kat Date: Sun, 3 May 2026 18:50:15 -0600 Subject: [PATCH 235/247] bmp-v2: disambiguate `native` platform by renaming to `bmp-v2` --- .codeberg/ci/build-firmware.yml | 10 +++++----- .github/workflows/build-and-upload.yml | 12 ++++++------ .github/workflows/build-pr.yml | 10 +++++----- CONTRIBUTING.md | 2 +- Makefile | 6 +++--- cross-file/{native-remote.ini => bmp-v2-remote.ini} | 4 ++-- cross-file/{native-riscv.ini => bmp-v2-riscv.ini} | 4 ++-- .../{native-st-clones.ini => bmp-v2-st-clones.ini} | 4 ++-- .../{native-uncommon.ini => bmp-v2-uncommon.ini} | 4 ++-- cross-file/{native.ini => bmp-v2.ini} | 4 ++-- meson_options.txt | 2 +- scripts/run-clang-tidy.py | 2 +- src/platforms/README.md | 2 +- src/platforms/{native => bmp-v2}/meson.build | 3 ++- src/platforms/{native => bmp-v2}/native.ld | 0 src/platforms/{native => bmp-v2}/platform.c | 0 src/platforms/{native => bmp-v2}/platform.h | 2 +- src/platforms/{native => bmp-v2}/usbdfu.c | 0 src/platforms/common/blackpill-f4/README.md | 2 +- src/platforms/hosted/bmp_libusb.c | 4 ++-- src/target/cortexm.c | 2 +- src/target/meson.build | 2 +- 22 files changed, 41 insertions(+), 40 deletions(-) rename cross-file/{native-remote.ini => bmp-v2-remote.ini} (88%) rename cross-file/{native-riscv.ini => bmp-v2-riscv.ini} (87%) rename cross-file/{native-st-clones.ini => bmp-v2-st-clones.ini} (89%) rename cross-file/{native-uncommon.ini => bmp-v2-uncommon.ini} (89%) rename cross-file/{native.ini => bmp-v2.ini} (90%) rename src/platforms/{native => bmp-v2}/meson.build (97%) rename src/platforms/{native => bmp-v2}/native.ld (100%) rename src/platforms/{native => bmp-v2}/platform.c (100%) rename src/platforms/{native => bmp-v2}/platform.h (99%) rename src/platforms/{native => bmp-v2}/usbdfu.c (100%) diff --git a/.codeberg/ci/build-firmware.yml b/.codeberg/ci/build-firmware.yml index 17c4196ef4f..1a4f08e9a09 100644 --- a/.codeberg/ci/build-firmware.yml +++ b/.codeberg/ci/build-firmware.yml @@ -27,11 +27,11 @@ matrix: - 'f4discovery' - 'hydrabus' - 'launchpad-icdi' - - 'native' - - 'native-uncommon' - - 'native-st-clones' - - 'native-riscv' - - 'native-remote' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-remote' - 'stlink' - 'stlinkv3' - 'swlink' diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index f6ca637e520..246448e3b33 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -46,11 +46,11 @@ jobs: - 'f4discovery' - 'hydrabus' - 'launchpad-icdi' - - 'native' - - 'native-uncommon' - - 'native-st-clones' - - 'native-riscv' - - 'native-remote' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-remote' - 'stlink' - 'stlinkv3' - 'swlink' @@ -104,7 +104,7 @@ jobs: meson compile -C build mkdir src/artefacts case "${{ matrix.probe }}" in - native-*) + bmp-v2-*) mv build/blackmagic_native_firmware.elf build/blackmagic_${{ matrix.probe }}_firmware.elf mv build/blackmagic_native_firmware.bin build/blackmagic_${{ matrix.probe }}_firmware.bin ;; diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 1a403a84518..f37c4d983a4 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -49,11 +49,11 @@ jobs: - 'f4discovery' - 'hydrabus' - 'launchpad-icdi' - - 'native' - - 'native-uncommon' - - 'native-st-clones' - - 'native-riscv' - - 'native-remote' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-remote' - 'stlink' - 'stlinkv3' - 'swlink' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29a1d253fc5..a276bcfae5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,7 @@ When writing commit messages, please prefix the component being modified using t * If the commit modifies target support, prefix with the path under src/target including the name of the file minus its extension - for example, "adiv5:", "stm32f1:" or "flashstub/lmi:" * If the commit modifies a platform, prefix with the name of that platform followed by the file - for example, - "hosted/cli:" or "native/platform:" + "hosted/cli:" or "bmp-v2/platform:" * If the commit modifies a significant number of files, us the overarching theme - for example if it's a platform API change then use "platform:" * If the commit modifies files such as the build system, the main project readme, or any other files about the project diff --git a/Makefile b/Makefile index 55103d8d979..22e0b41eaa3 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,9 @@ all: @echo "> meson compile -C build" @echo "" @echo "You can find example firmware configuration files in the \`cross-file\` subdirectory." - @echo "For example, to build a firmware for the native hardware run the following commands:" - @echo "> meson setup build-native --cross-file cross-file/native.ini --werror" - @echo "> meson compile -C build-native" + @echo "For example, to build a firmware for the bmp-v2 hardware run the following commands:" + @echo "> meson setup build-bmp-v2 --cross-file cross-file/bmp-v2.ini --werror" + @echo "> meson compile -C build-bmp-v2" @echo "" @echo "For further instructions please refer to the README.md in the root directory of this repository." @echo "" diff --git a/cross-file/native-remote.ini b/cross-file/bmp-v2-remote.ini similarity index 88% rename from cross-file/native-remote.ini rename to cross-file/bmp-v2-remote.ini index 6f42e0562d4..2da75954505 100644 --- a/cross-file/native-remote.ini +++ b/cross-file/bmp-v2-remote.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides only architecture support intended for probes that will be used soley via the # Black Magic Debug remote protocol with BMDA. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,riscv32,riscv64' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-riscv.ini b/cross-file/bmp-v2-riscv.ini similarity index 87% rename from cross-file/native-riscv.ini rename to cross-file/bmp-v2-riscv.ini index 17ebf4c6482..10154187792 100644 --- a/cross-file/native-riscv.ini +++ b/cross-file/bmp-v2-riscv.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides only support for both RISC-V architectures and support for RISC-V targets. [binaries] @@ -19,7 +19,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'riscv32,riscv64,gd32,rp' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-st-clones.ini b/cross-file/bmp-v2-st-clones.ini similarity index 89% rename from cross-file/native-st-clones.ini rename to cross-file/bmp-v2-st-clones.ini index 780028a3423..29ee05d3605 100644 --- a/cross-file/native-st-clones.ini +++ b/cross-file/bmp-v2-st-clones.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for all ARM Cortex architectures and target support for ST's parts, # and their clones by Artery Tek, GigaDevice, WinChipHead, MindMotion, Puya and HDSC. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,stm,at32f4,gd32,ch32,ch579,mm32,puya,hc32' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-uncommon.ini b/cross-file/bmp-v2-uncommon.ini similarity index 89% rename from cross-file/native-uncommon.ini rename to cross-file/bmp-v2-uncommon.ini index 293d1093c37..97cedbf70b0 100644 --- a/cross-file/native-uncommon.ini +++ b/cross-file/bmp-v2-uncommon.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for all ARM Cortex architectures and target support for less commonly # used targets such as the Energy Micro parts, Renesas parts, Xilinx Zynq and Ambiq Apollo3. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,apollo3,efm,hc32,renesas,xilinx' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native.ini b/cross-file/bmp-v2.ini similarity index 90% rename from cross-file/native.ini rename to cross-file/bmp-v2.ini index 44cabd1d5fc..f849e804e17 100644 --- a/cross-file/native.ini +++ b/cross-file/bmp-v2.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for only the ARM Cortex-M architecture and target support for the NXP LPC # families, nRF series', NXP's Kinetis and i.MXRT parts, RPi Foundation's MCUs (ARM part only), # Atmel's ATSAM parts, ST's parts, and TI's Stellaris/Tiva-C parts, @@ -21,7 +21,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = true diff --git a/meson_options.txt b/meson_options.txt index 460d7b707d4..0eff649b2b5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -16,7 +16,7 @@ option( 'f4discovery', 'hydrabus', 'launchpad-icdi', - 'native', + 'bmp-v2', 'stlink', 'stlinkv3', 'swlink' diff --git a/scripts/run-clang-tidy.py b/scripts/run-clang-tidy.py index e4639e0f0d1..1e354abd7f4 100755 --- a/scripts/run-clang-tidy.py +++ b/scripts/run-clang-tidy.py @@ -34,7 +34,7 @@ def gatherFiles(): extraArgs = [ '-Isrc/target', '-Isrc', '-Isrc/include', '-Isrc/platforms/common', - '-Isrc/platforms/native', '-Ideps/libopencm3/include', '-Isrc/platforms/stm32' + '-Isrc/platforms/bmp-v2', '-Ideps/libopencm3/include', '-Isrc/platforms/stm32' ] + args.includePaths for i, arg in enumerate(extraArgs): diff --git a/src/platforms/README.md b/src/platforms/README.md index 3408021b024..3c524e648ed 100644 --- a/src/platforms/README.md +++ b/src/platforms/README.md @@ -5,7 +5,7 @@ used by (multiple) platforms. ## Implementation directories -* native: Firmware for [Black Magic Probe](https://1bitsquared.com/products/black-magic-probe) +* bmp-v2: Firmware for [Black Magic Probe v2](https://1bitsquared.com/products/black-magic-probe) * stlink: Firmware for ST-Link v2 and ST-Link v2.1 * swlink: Firmware for ST-Link v1 and Bluepill * blackpill-f401cc: Firmware for the WeAct Studio [Black Pill F401CC](https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x1) diff --git a/src/platforms/native/meson.build b/src/platforms/bmp-v2/meson.build similarity index 97% rename from src/platforms/native/meson.build rename to src/platforms/bmp-v2/meson.build index 473ba65d4dd..11c968a7eb2 100644 --- a/src/platforms/native/meson.build +++ b/src/platforms/bmp-v2/meson.build @@ -2,6 +2,7 @@ # # Copyright (C) 2023 1BitSquared # Written by Rafael Silva +# Modified by Kat Mitchell # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -76,7 +77,7 @@ probe_bootloader = declare_dependency( summary( { - 'Name': 'Black Magic Probe (native)', + 'Name': 'Black Magic Probe (bmp-v2)', 'Platform': 'STM32F1', 'Bootloader': 'Black Magic Debug Bootloader', 'Load Address': '0x8002000', diff --git a/src/platforms/native/native.ld b/src/platforms/bmp-v2/native.ld similarity index 100% rename from src/platforms/native/native.ld rename to src/platforms/bmp-v2/native.ld diff --git a/src/platforms/native/platform.c b/src/platforms/bmp-v2/platform.c similarity index 100% rename from src/platforms/native/platform.c rename to src/platforms/bmp-v2/platform.c diff --git a/src/platforms/native/platform.h b/src/platforms/bmp-v2/platform.h similarity index 99% rename from src/platforms/native/platform.h rename to src/platforms/bmp-v2/platform.h index 579bc149b90..2d55030d11d 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/bmp-v2/platform.h @@ -21,7 +21,7 @@ * along with this program. If not, see . */ -/* This file provides the platform specific declarations for the native implementation. */ +/* This file provides the platform specific declarations for the bmp-v2 implementation. */ #ifndef PLATFORMS_NATIVE_PLATFORM_H #define PLATFORMS_NATIVE_PLATFORM_H diff --git a/src/platforms/native/usbdfu.c b/src/platforms/bmp-v2/usbdfu.c similarity index 100% rename from src/platforms/native/usbdfu.c rename to src/platforms/bmp-v2/usbdfu.c diff --git a/src/platforms/common/blackpill-f4/README.md b/src/platforms/common/blackpill-f4/README.md index d6c09300b8b..0ec2221d716 100644 --- a/src/platforms/common/blackpill-f4/README.md +++ b/src/platforms/common/blackpill-f4/README.md @@ -131,4 +131,4 @@ SPI ports are set to Pclk/8 each (use with `bmpflash`). As SPI1 pins may conflic a) has a fixed per-board PLL config, no autodetection; b) understands buttons, drives LED, does not touch other GPIOs, talks USB DfuSe, ~~has MS OS descriptors for automatic driver installation on Windows~~, uses same libopencm3 code so you can verify hardware config via a smaller binary; c) erases and writes to internal Flash ~2.4x faster than MaskROM; - d) all of that in first 8-9 KiB of first page of 16 KiB (of F2/F4/F7 flash), just like on `native`/`stlink`/`swlink` etc. + d) all of that in first 8-9 KiB of first page of 16 KiB (of F2/F4/F7 flash), just like on `bmp-v2`/`stlink`/`swlink` etc. diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index 01d6f8feae5..6e29ccb26fc 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -177,7 +177,7 @@ void bmp_read_product_version(libusb_device_descriptor_s *device_descriptor, lib /* Let's start out easy - check to see if the string contains an opening paren (alternate platform) */ const char *const opening_paren = strchr(description + BMP_PRODUCT_STRING_LENGTH, '('); - /* If there isn't one, we're dealing with nominally a native probe */ + /* If there isn't one, we're dealing with nominally a BMPv1 or v2 probe */ if (!opening_paren) { /* Knowing this, let's see if there are enough bytes for a version string, and if there are.. extract it */ if (description_len > BMP_PRODUCT_STRING_LENGTH) { @@ -192,7 +192,7 @@ void bmp_read_product_version(libusb_device_descriptor_s *device_descriptor, lib *product = strdup(description); } } else { - /* Otherwise, we've got a non-native probe, so find the closing paren for the probe type */ + /* Otherwise, we've got a non-bmp probe, so find the closing paren for the probe type */ const char *const closing_paren = strchr(opening_paren, ')'); /* If we didn't find one, we've got a problem */ if (!closing_paren) { diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 95780497f89..527fe59f59d 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -791,7 +791,7 @@ static void cortexm_reset(target_s *const target) if (!(target->target_options & TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); platform_nrst_set_val(false); - /* Some NRF52840 users saw invalid SWD transaction with native/firmware without this delay.*/ + /* Some NRF52840 users saw invalid SWD transaction with bmp-v2/firmware without this delay.*/ platform_delay(10); if (dp->ensure_idle) dp->ensure_idle(dp); diff --git a/src/target/meson.build b/src/target/meson.build index c6ead651359..53fca2389a2 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -100,7 +100,7 @@ if is_firmware_build # Check to see if the set of enabled targets is all of them and the # firmware is to be built for a STM32F1 based probe (requires a restriction) - if probe in ['bluepill', 'native', 'stlink', 'swlink'] and enabled_targets.length() == target_names.keys().length() + if probe in ['bluepill', 'bmp-v2', 'stlink', 'swlink'] and enabled_targets.length() == target_names.keys().length() warning('Disabling some targets as your build will not fit the target probe otherwise') enabled_targets = ['cortexm', 'lpc', 'nrf', 'nxp', 'renesas', 'rp', 'sam', 'stm', 'ti'] elif probe in ['f072'] and enabled_targets.length() == target_names.keys().length() From bd2969ce5197d461a1dfabd7873951bf24d9dea8 Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 6 May 2026 20:27:15 -0600 Subject: [PATCH 236/247] bmp-v1: add `bmp-v1` cross-files files referencing `bmp-v2` to make backwards compatibility less ambiguous --- README.md | 8 ++++---- cross-file/bmp-v1-remote.ini | 26 ++++++++++++++++++++++++++ cross-file/bmp-v1-riscv.ini | 25 +++++++++++++++++++++++++ cross-file/bmp-v1-st-clones.ini | 26 ++++++++++++++++++++++++++ cross-file/bmp-v1-uncommon.ini | 26 ++++++++++++++++++++++++++ cross-file/bmp-v1.ini | 27 +++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 cross-file/bmp-v1-remote.ini create mode 100644 cross-file/bmp-v1-riscv.ini create mode 100644 cross-file/bmp-v1-st-clones.ini create mode 100644 cross-file/bmp-v1-uncommon.ini create mode 100644 cross-file/bmp-v1.ini diff --git a/README.md b/README.md index 837bf00984b..2b7b1dbfe21 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ configuration for it. The build configuration command for the native probe may look like: ```sh -meson setup build --cross-file cross-file/native.ini +meson setup build --cross-file cross-file/bmp-v2.ini ``` Note that even if you are using the pre-configured cross-file, you may still override it's defaults with @@ -208,7 +208,7 @@ Alternatively (for advanced users), if you wish to configure manually, for insta for a new probe, or a different toolchain, you can run something similar to this: ```sh -meson setup build --cross-file cross-file/arm-none-eabi.ini -Dprobe=native -Dtargets=cortexm,stm +meson setup build --cross-file cross-file/arm-none-eabi.ini -Dprobe=bmp-v2 -Dtargets=cortexm,stm ``` After following one of these two paths, you now should have a `build` directory from where you can build @@ -223,8 +223,8 @@ meson compile -C build You should now see the resulting binaries in `build`, in this case: -* `blackmagic_native_firmware.bin` -* `blackmagic_native_firmware.elf` +* `blackmagic_bmp-v2_firmware.bin` +* `blackmagic_bmp-v2_firmware.elf` These are the binary files you will use to flash to your probe. diff --git a/cross-file/bmp-v1-remote.ini b/cross-file/bmp-v1-remote.ini new file mode 100644 index 00000000000..4994d1e390a --- /dev/null +++ b/cross-file/bmp-v1-remote.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides only architecture support intended for probes that will be used soley via the +# Black Magic Debug remote protocol with BMDA. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,riscv32,riscv64' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-riscv.ini b/cross-file/bmp-v1-riscv.ini new file mode 100644 index 00000000000..92d45f4864e --- /dev/null +++ b/cross-file/bmp-v1-riscv.ini @@ -0,0 +1,25 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides only support for both RISC-V architectures and support for RISC-V targets. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'riscv32,riscv64,gd32,rp' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-st-clones.ini b/cross-file/bmp-v1-st-clones.ini new file mode 100644 index 00000000000..a4927be775f --- /dev/null +++ b/cross-file/bmp-v1-st-clones.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for all ARM Cortex architectures and target support for ST's parts, +# and their clones by Artery Tek, GigaDevice, WinChipHead, MindMotion, Puya and HDSC. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,stm,at32f4,gd32,ch32,ch579,mm32,puya,hc32' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-uncommon.ini b/cross-file/bmp-v1-uncommon.ini new file mode 100644 index 00000000000..8061ae50f59 --- /dev/null +++ b/cross-file/bmp-v1-uncommon.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for all ARM Cortex architectures and target support for less commonly +# used targets such as the Energy Micro parts, Renesas parts, Xilinx Zynq and Ambiq Apollo3. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,apollo3,efm,hc32,renesas,xilinx' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1.ini b/cross-file/bmp-v1.ini new file mode 100644 index 00000000000..34ba0ca581e --- /dev/null +++ b/cross-file/bmp-v1.ini @@ -0,0 +1,27 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for only the ARM Cortex-M architecture and target support for the NXP LPC +# families, nRF series', NXP's Kinetis and i.MXRT parts, RPi Foundation's MCUs (ARM part only), +# Atmel's ATSAM parts, ST's parts, and TI's Stellaris/Tiva-C parts, + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' +rtt_support = false +bmd_bootloader = true From 19e3e698005bd6f30f6a8b4d5ca487b461bedf5b Mon Sep 17 00:00:00 2001 From: ALTracer Date: Sun, 10 May 2026 20:00:00 +0300 Subject: [PATCH 237/247] cortexm, cortexar: Add a minimum reset pulse duration --- src/target/cortexar.c | 1 + src/target/cortexm.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/target/cortexar.c b/src/target/cortexar.c index 9d2664de2c5..c9c0cd4555c 100644 --- a/src/target/cortexar.c +++ b/src/target/cortexar.c @@ -1354,6 +1354,7 @@ static void cortexar_reset(target_s *const target) /* If the physical reset pin is not inhibited, use it */ if (!(target->target_options & TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); + platform_delay(1); platform_nrst_set_val(false); /* Precautionary delay as with the Cortex-M code for targets that take a hot minute to come back */ platform_delay(10); diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 527fe59f59d..bf822491f4e 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -790,6 +790,7 @@ static void cortexm_reset(target_s *const target) /* If the physical reset pin is not inhibited, use it */ if (!(target->target_options & TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); + platform_delay(1); platform_nrst_set_val(false); /* Some NRF52840 users saw invalid SWD transaction with bmp-v2/firmware without this delay.*/ platform_delay(10); From c3fb02ef8910ddabeef07c6b549fcc82d8c0c359 Mon Sep 17 00:00:00 2001 From: ALTracer Date: Tue, 12 May 2026 20:19:18 +0300 Subject: [PATCH 238/247] swlink: Do not wait for RST pin level to change in nrst_set_val() --- src/platforms/swlink/platform.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/platforms/swlink/platform.c b/src/platforms/swlink/platform.c index bff34040943..3e71b974ef0 100644 --- a/src/platforms/swlink/platform.c +++ b/src/platforms/swlink/platform.c @@ -113,14 +113,10 @@ void platform_nrst_set_val(bool assert) /* We reuse nTRST as nRST. */ if (assert) { gpio_set_mode(TRST_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TRST_PIN); - /* Wait until requested value is active. */ - while (gpio_get(TRST_PORT, TRST_PIN)) - gpio_clear(TRST_PORT, TRST_PIN); + gpio_clear(TRST_PORT, TRST_PIN); } else { gpio_set_mode(TRST_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, TRST_PIN); - /* Wait until requested value is active .*/ - while (!gpio_get(TRST_PORT, TRST_PIN)) - gpio_set(TRST_PORT, TRST_PIN); + gpio_set(TRST_PORT, TRST_PIN); } } From 64958ec29dd1e2a66a13bbd938dec6e27dfe479f Mon Sep 17 00:00:00 2001 From: ALTracer Date: Tue, 12 May 2026 20:21:22 +0300 Subject: [PATCH 239/247] platforms: Delete the "for-volatile-counter" delay from nrst_set_val() * Any delays necessary will exist at callsites * Most platforms do not have this delay * Duration of 10000 iterations depends on Hclk and architecture (stlinkv3 CM7 may complete faster) --- src/platforms/bmp-v2/platform.c | 5 ----- src/platforms/bmp-v3/platform.c | 5 ----- src/platforms/launchpad-icdi/platform.c | 2 -- src/platforms/stlinkv3/platform.c | 4 ---- 4 files changed, 16 deletions(-) diff --git a/src/platforms/bmp-v2/platform.c b/src/platforms/bmp-v2/platform.c index 7babd42ce0a..63b4a13be55 100644 --- a/src/platforms/bmp-v2/platform.c +++ b/src/platforms/bmp-v2/platform.c @@ -294,11 +294,6 @@ void platform_nrst_set_val(bool assert) gpio_set_val(NRST_PORT, NRST_PIN, assert); else gpio_set_val(NRST_PORT, NRST_PIN, !assert); - - if (assert) { - for (volatile size_t i = 0; i < 10000U; ++i) - continue; - } } bool platform_nrst_get_val(void) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c index b0658629859..44183e1d379 100644 --- a/src/platforms/bmp-v3/platform.c +++ b/src/platforms/bmp-v3/platform.c @@ -254,11 +254,6 @@ void platform_nrst_set_val(bool assert) { gpio_set(TMS_PORT, TMS_PIN); gpio_set_val(NRST_PORT, NRST_PIN, assert); - - if (assert) { - for (volatile size_t i = 0; i < 10000U; ++i) - continue; - } } bool platform_nrst_get_val(void) diff --git a/src/platforms/launchpad-icdi/platform.c b/src/platforms/launchpad-icdi/platform.c index 4d496bdec7b..9f535be6d7c 100644 --- a/src/platforms/launchpad-icdi/platform.c +++ b/src/platforms/launchpad-icdi/platform.c @@ -99,8 +99,6 @@ void platform_nrst_set_val(bool assert) { if (assert) { gpio_clear(NRST_PORT, NRST_PIN); - for (volatile size_t i = 0; i < 10000U; ++i) - continue; } else gpio_set(NRST_PORT, NRST_PIN); } diff --git a/src/platforms/stlinkv3/platform.c b/src/platforms/stlinkv3/platform.c index 18403bda17c..5ec119e34a6 100644 --- a/src/platforms/stlinkv3/platform.c +++ b/src/platforms/stlinkv3/platform.c @@ -115,10 +115,6 @@ int platform_hwversion(void) void platform_nrst_set_val(bool assert) { gpio_set_val(NRST_PORT, NRST_PIN, !assert); - if (assert) { - for (volatile size_t i = 0; i < 10000; i++) - continue; - } } bool platform_nrst_get_val() From 0fe4d819f11d40bfef525ce5921f267de75f6e96 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Sat, 25 Apr 2026 19:31:50 -0700 Subject: [PATCH 240/247] lattice_ecp5: Fix 2 small typos The first was that we forgot a `!` on a `calloc` check, the second is we were doing a `CMD_LSC_READ_CRC` vs a `CMD_LSC_RESET_CRC` --- src/target/lattice_ecp5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 7c613789878..4cdb1a35d61 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -278,7 +278,7 @@ void lattice_ecp5_handler(const uint8_t dev_index) ctx->data_buffer = calloc(1, ctx->buffer_len); ctx->cmd_buffer = calloc(1, ctx->buffer_len); - if (!ctx->data_buffer || ctx->cmd_buffer) + if (!ctx->data_buffer || !ctx->cmd_buffer) DEBUG_ERROR("calloc: failed in %s\n", __func__); } @@ -370,7 +370,7 @@ static bool ecp5_enter_flash(target_s *const target) jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); jtag_proc.jtagtap_cycle(false, false, 50U); // Reset the CRC - jtag_dev_write_ir(dev_index, CMD_LSC_READ_CRC); + jtag_dev_write_ir(dev_index, CMD_LSC_RESET_CRC); jtag_proc.jtagtap_cycle(false, false, 50U); // Wait for the configuration to be erased From 26f187e14fe9d26b64d37703b4b1e99f77d5cc5d Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Tue, 12 May 2026 17:41:57 -0700 Subject: [PATCH 241/247] lattice_ecp5: Fixed IR value for JTAG devices It seems to have inexplicably changed? It was 0x5U, but now it's 0x1U? I have no idea what happened. --- src/target/jtag_devs.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 5993c65c01a..220b89dd450 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -445,7 +445,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -458,7 +458,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -471,7 +471,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -484,7 +484,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -497,7 +497,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -510,7 +510,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -523,7 +523,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -536,7 +536,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -549,7 +549,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, { @@ -562,7 +562,7 @@ const jtag_dev_descr_s dev_descr[] = { .ir_quirks = { .ir_length = 8U, - .ir_value = 0x5U, + .ir_value = 0x1U, }, }, #endif From 8d13ae55702662c5c9ec314d6471163d919e3143 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Tue, 12 May 2026 17:43:09 -0700 Subject: [PATCH 242/247] ftdi: Fixed a bug in the `ftdi_jtag_tdi_tdo_seq` that wasn't taking the `final_tms` adjustment for `bits` into account --- src/platforms/hosted/ftdi_bmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index f2b78fa8f90..e4d9becc341 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -835,7 +835,7 @@ void ftdi_jtag_tdi_tdo_seq(uint8_t *data_out, const bool final_tms, const uint8_ if (bytes) ftdi_buffer_read(data_out, bytes); /* Read the residual bits */ - if (bits) { + if (bits - (final_tms ? 1U : 0U)) { ftdi_buffer_read_val(data_out[bytes]); /* Because of a quirk in how the FTDI device works, the bits will be MSb aligned, so shift them down */ const size_t shift = bits - (final_tms ? 1U : 0U); From e6a5d8ef7168ae9a98fac2f70606607ac54174a5 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Tue, 12 May 2026 17:45:01 -0700 Subject: [PATCH 243/247] lattice_ecp5: Fix transparent SPI mode when there are multiple devices in the JTAG chain --- src/target/lattice_ecp5.c | 43 +++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 4cdb1a35d61..3c4c6f26368 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -489,21 +489,52 @@ static void ecp5_spi_xfr_jtag( /* Switch into Shift-DR */ jtagtap_shift_dr(); - /* Now we're in Shift-DR, clock out 1's till we hit the right device in the chain */ - jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); uint8_t tap_out; for (size_t idx = 0U; idx < length; ++idx) { const uint8_t tap_in = reverse_bits8(data_in[idx]); - jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); + jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_prescan, &tap_in, 8U); if (data_out) data_out[idx] = reverse_bits8(tap_out); } - DEBUG_PROTO("%s: %" PRIu32 " cycles\n", __func__, (uint32_t)length); + if (data_out) { + /* Fixup the TDO to TDI skew caused by extra devices on the JTAG chain in bypass mode */ + for (size_t scan = 0U; scan < device->dr_prescan; ++scan) { + uint8_t trailing_bit = 0U; + for (size_t offset = 0U; offset < length; ++offset) { + const size_t index = length - 1U - offset; + const uint8_t carry_bit = data_out[index] & 0x80U; + data_out[index] <<= 1U; + data_out[index] |= trailing_bit >> 7U; + trailing_bit = carry_bit; + } + } + } + + DEBUG_PROTO("%s: %" PRIu32 " cycles\n", __func__, (uint32_t)length * 8U); + + if (device->dr_prescan) { + /* Squeeze the trailing bits from the SPI transaction out of the chain */ + uint8_t trailing_bits[4]; + jtag_proc.jtagtap_tdi_tdo_seq(trailing_bits, true, NULL, device->dr_prescan); + + if (data_out) { + /* forcibly re-align the data buffer's chakra */ + size_t bit_len = length * 8U; + for (size_t offset = bit_len - device->dr_prescan, idx = 0; offset < bit_len; ++offset, ++idx) { + const size_t output_byte = offset >> 3U; /* Divide by 8 */ + const size_t output_bit = 7U - (offset & 7U); + + const size_t input_byte = idx >> 3U; + const size_t input_bit = idx & 7U; + + const uint8_t trailing_bit = trailing_bits[input_byte] >> input_bit; + data_out[output_byte] |= trailing_bit << output_bit; + } + } + } - /* Make sure we're in Exit1-DR having clocked out 1's for any more devices on the chain */ - jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); /* Now go through Update-DR and back to Idle */ jtagtap_return_idle(1U); } From c49024f3d94aa73836057ff13940a45b11deea43 Mon Sep 17 00:00:00 2001 From: Aki Van Ness Date: Tue, 12 May 2026 17:53:05 -0700 Subject: [PATCH 244/247] lattice_ecp5: Don't try to scan for attached SPI flash if we're not the first device in the chain --- src/target/lattice_ecp5.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c index 3c4c6f26368..8b510aee253 100644 --- a/src/target/lattice_ecp5.c +++ b/src/target/lattice_ecp5.c @@ -303,6 +303,7 @@ static bool ecp5_attach(target_s *const target) { const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; const uint32_t status = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + const jtag_dev_s *const device = &jtag_devs[ctx->device_index]; if (ECP5_STATUS_ENCRYPTED_ONLY(status)) DEBUG_WARN("This FPGA only accepts encrypted bitstreams!\n"); @@ -310,6 +311,11 @@ static bool ecp5_attach(target_s *const target) if (ECP5_STATUS_DONE(status)) DEBUG_INFO("FPGA is configured\n"); + if (device->dr_postscan) { + DEBUG_WARN("Transparent SPI Flash not possible, not first device in the chain\n"); + return true; + } + ecp5_enter_flash(target); // Create a synthetic flash object so we can shell out to `spi_flash_prepare` to enter transparent SPI mode From 24faa4964b8ed11c2118726450c238d7bca48902 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:13:36 +0300 Subject: [PATCH 245/247] hosted/ftdi_bmp: Register generic FT2232H on Icebreaker for JTAG on MPSSE interface A --- src/platforms/hosted/ftdi_bmp.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index e4d9becc341..a9e16f8727c 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -354,6 +354,19 @@ const cable_desc_s cable_desc[] = { .bb_swdio_in_pin = MPSSE_CS, .name = "hifive1", }, + { + /* + * 1bitSquared Icebreaker FT2232H + * Direct connection on Interface A, JTAG only + */ + .vendor = 0x0403U, + .product = 0x6010U, + .interface = INTERFACE_A, + .init.data[0] = MPSSE_CS | MPSSE_DO | MPSSE_DI, + .init.dirs[0] = MPSSE_CS | MPSSE_DO | MPSSE_SK, + .description = "iCEBreaker V1.0e", + .name = "icebreaker", + }, { /* * https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/ From 9a6dd6169105f7246b25ff65a8daf3d2b0a62f20 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:14:25 +0300 Subject: [PATCH 246/247] jtag_devs: Register Hazard3/ice40 TAP as a RISC-V DTM --- src/target/jtag_devs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 220b89dd450..d0d534b70a8 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -420,6 +420,14 @@ const jtag_dev_descr_s dev_descr[] = { .idmask = 0x0fffffffU, #if ENABLE_DEBUG == 1 .descr = "Nuclei Systems RISC-V DTM.", +#endif + .handler = riscv_jtag_dtm_handler, + }, + { + .idcode = 0xdeadbeefU, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "Hazard3 RISC-V DTM.", #endif .handler = riscv_jtag_dtm_handler, }, From 7eed088be0b97285b2e10b98e3b16d257df48cd7 Mon Sep 17 00:00:00 2001 From: ALTracer <11005378+ALTracer@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:25:50 +0300 Subject: [PATCH 247/247] riscv32: Add very basic support for Hazard3 SoC on Icebreaker SPRAM --- src/target/riscv32.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index a4dca1bef86..0f2edb4434a 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -92,6 +92,13 @@ static void riscv32_regs_write(target_s *target, const void *data); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); +bool hazard3_probe(target_s *const target) +{ + target->driver = "Hazard3"; + target_add_ram32(target, 0x0, 131072); + return true; +} + bool riscv32_probe(target_s *const target) { /* 'E' base ISA has 16 GPRs + PC, 'I' base ISA has 32 GPRs + PC */ @@ -123,6 +130,9 @@ bool riscv32_probe(target_s *const target) PROBE(ch32v003x_probe); PROBE(ch32vx_probe); break; + case 0xe77: + PROBE(hazard3_probe); + break; default: break; }