From da9e44a5e3e430aeaa5cadfe03336c41087a4016 Mon Sep 17 00:00:00 2001 From: gojimmypi Date: Sun, 8 Mar 2026 09:20:52 -0700 Subject: [PATCH 001/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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/115] 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);