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 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' 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/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/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/include/platform_support.h b/src/include/platform_support.h index ed49fb5166e..c417fce5e2c 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -84,4 +84,19 @@ uint8_t platform_spi_xfer(spi_bus_e bus, uint8_t value); const char *platform_ident(void); #endif +#ifdef PLATFORM_MULTI_UART +typedef enum uart_state { + UART_STATE_UNKNOWN, + UART_STATE_IDLE, + UART_STATE_LOST, +} uart_state_e; + +void platform_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/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 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 new file mode 100644 index 00000000000..9e144b2e727 --- /dev/null +++ b/src/platforms/bmp-v3/meson.build @@ -0,0 +1,86 @@ +# 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('platform.c') + +probe_bmp_dfu_sources = files('bootloader.c') + +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 = [ + # Reserve two pages for the bootloader + '-Wl,-Ttext=0x8004000', +] + +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], +) + +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', + 'Platform': 'STM32U5', + 'Bootloader': 'Black Magic Debug Bootloader', + 'Load Address': '0x8002000', + }, + section: 'Probe', +) diff --git a/src/platforms/bmp-v3/platform.c b/src/platforms/bmp-v3/platform.c new file mode 100644 index 00000000000..2a9ccd92e12 --- /dev/null +++ b/src/platforms/bmp-v3/platform.c @@ -0,0 +1,489 @@ +/* + * 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 "usb.h" +#include "rcc_clocking.h" +#include "aux_serial.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + +int hwversion = -1; + +static uart_state_e uart2_state = UART_STATE_UNKNOWN; + +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; + + /* 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); + 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); + /* 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); + + /* 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_set_output_options(TCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, 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); + + /* 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); + 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); + + /* 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 | 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_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); + 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 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 */ +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) +{ + /* + * 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); + + 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); +} + +int platform_hwversion(void) +{ + return hwversion; +} + +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); +} + +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) +{ + /* + * 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_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); + + /* 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) +{ + /* 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); +} + +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) +{ + /* 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); +} + +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); +} + +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/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h new file mode 100644 index 00000000000..2641d4ea419 --- /dev/null +++ b/src/platforms/bmp-v3/platform.h @@ -0,0 +1,303 @@ +/* + * 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" +#include "timing.h" +#include "timing_stm32.h" + +#define PLATFORM_HAS_TRACESWO +#define PLATFORM_HAS_POWER_SWITCH +#define PLATFORM_MULTI_UART + +#define PLATFORM_IDENT "v3 " + +extern int hwversion; + +/* + * 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... */ +#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 BNT_BOOT_REQ_PORT GPIOA +#define BTN_BOOT_REQ_PIN GPIO15 + +#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 + +#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); \ + 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 UART_PIN_SETUP() + +#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_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 +#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 +#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 +#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 +#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 +#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 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) +#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/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 */ diff --git a/src/platforms/common/aux_serial.c b/src/platforms/common/aux_serial.c index 5d44dfb37d1..deb2a135c84 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-2026 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -18,11 +18,19 @@ * along with this program. If not, see . */ -#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) +#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 #include #include +#ifdef PLATFORM_MULTI_UART +#include +#endif #elif defined(LM4F) #include #include @@ -31,18 +39,13 @@ #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; /* 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; @@ -59,6 +62,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_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 +#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 @@ -87,10 +96,27 @@ 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 + +#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 */ -#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,14 +124,14 @@ 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 */ 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); @@ -120,24 +146,69 @@ 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_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; +#ifdef STM32U5 + USART_CR3(uart_base) |= USART_CR3_EIE; +#endif +} + +#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 */ +#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); + rcc_periph_clock_enable(AUX_UART_DMA_CLK); +#endif /* 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 +219,14 @@ 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 +#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); @@ -163,10 +240,29 @@ 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 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); @@ -184,9 +280,61 @@ void aux_serial_init(void) #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_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 + +#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)) { + aux_serial_activate_uart(AUX_UART1); + 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 */ +#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,11 +349,30 @@ 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 */ + /* 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) @@ -238,7 +405,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 +413,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) +#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 +440,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 +490,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,25 +503,33 @@ 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 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; +#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) @@ -366,7 +550,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); @@ -411,28 +599,56 @@ 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(); } +#ifdef PLATFORM_MULTI_UART + if (uart == AUX_UART2) + platform_uart2_state_change(status); +#endif +#ifndef STM32U5 nvic_enable_irq(dma_irq); +#else + dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); +#endif } 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); @@ -450,20 +666,29 @@ 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) { +#ifndef STM32U5 nvic_disable_irq(usart_irq); +#else + (void)usart_irq; +#endif - /* 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(); +#ifndef STM32U5 nvic_enable_irq(usart_irq); +#endif } +#ifndef PLATFORM_MULTI_UART #if defined(USBUSART_ISR) void USBUSART_ISR(void) { @@ -496,50 +721,80 @@ void USBUSART2_ISR(void) #endif } #endif +#else +void AUX_UART1_ISR(void) +{ + aux_serial_receive_isr(AUX_UART1, AUX_UART_DMA_RX_IRQ); +} -#if defined(USBUSART_DMA_TX_ISR) +void AUX_UART2_ISR(void) +{ + aux_serial_receive_isr(AUX_UART2, AUX_UART_DMA_RX_IRQ); +} +#endif + +#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)) @@ -548,6 +803,43 @@ 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); +} + +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); + 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) 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..3fe2cdefe89 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 */ @@ -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/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/dfucore.c b/src/platforms/common/stm32/dfucore.c index 1a697d499e7..bdd052d40b9 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 @@ -21,6 +22,7 @@ #include "platform.h" #include "version.h" #include "serialno.h" +#include "buffer_utils.h" #include #include @@ -42,6 +44,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 @@ -145,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: @@ -180,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) { @@ -236,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; @@ -302,7 +301,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 +317,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 +326,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 +345,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(); } diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 516ffda36fa..2edfe2cf1a3 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -31,40 +31,43 @@ #include "usb_serial.h" #include "gdb_if.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]; -#if defined(STM32F4) || defined(STM32F7) -static volatile uint32_t count_new; -static char double_buffer_out[CDCACM_PACKET_SIZE]; +#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]; +#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. */ @@ -73,17 +76,17 @@ void gdb_if_flush(const bool force) } /* Reset the buffer */ - count_in = 0U; + gdb_send_amount_queued = 0U; } -#if defined(STM32F4) || defined(STM32F7) -void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) +#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 @@ -91,34 +94,33 @@ static void gdb_if_update_buf(void) { while (usb_get_config() != 1) continue; -#if !defined(STM32F4) && !defined(STM32F7) - count_out = usbd_ep_read_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_out, CDCACM_PACKET_SIZE); - out_ptr = 0; +#if !defined(STM32F4) && !defined(STM32F7) && !defined(STM32U5) + 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 (!gdb_receive_amount_available) + __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; - out_ptr = 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"); - /* 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) { - while (out_ptr >= count_out) { + while (gdb_receive_index >= gdb_receive_amount_available) { /* * Detach if port closed * @@ -133,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) @@ -142,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 * @@ -156,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/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/stm32/meson.build b/src/platforms/common/stm32/meson.build index cb31141ce27..4e5a386ab63 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,34 @@ platform_stm32f7_dfu = declare_dependency( link_args: platform_stm32f7_link_args, dependencies: [platform_stm32_dfu_common, dependency('opencm3_stm32f7')], ) + +## STM32U5 Platform +## ________________ + +platform_stm32u5_dfu_sources = files('dfu_u5.c') + +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_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/stm32/swo_manchester.c b/src/platforms/common/stm32/swo_manchester.c index 61c71f8a1f8..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 @@ -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); @@ -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 @@ -122,7 +127,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..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 */ @@ -67,7 +79,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); @@ -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); @@ -139,7 +178,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 6f291380486..702bb210a49 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) { @@ -61,9 +64,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(); @@ -84,7 +96,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; @@ -134,6 +150,34 @@ 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 { + /* + * 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 } uint32_t platform_time_ms(void) @@ -178,7 +222,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; @@ -206,7 +254,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/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; } 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.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 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[] = { 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 diff --git a/src/platforms/common/usb_serial.c b/src/platforms/common/usb_serial.c index bd634f7f3e3..cf132dfd7cf 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,11 +56,17 @@ #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 +#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); @@ -68,7 +74,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 @@ -102,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; } @@ -146,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; } @@ -159,46 +169,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) - usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, gdb_usb_out_cb); +#if defined(STM32F4) || defined(LM4F) || defined(STM32F7) || defined(STM32U5) + 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 @@ -256,7 +263,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 +311,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 +338,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); @@ -348,7 +355,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(); 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 */ diff --git a/src/platforms/ctxlink/ctxlink_gdb_if.c b/src/platforms/ctxlink/ctxlink_gdb_if.c index 2205920c070..4a1f19ebda8 100644 --- a/src/platforms/ctxlink/ctxlink_gdb_if.c +++ b/src/platforms/ctxlink/ctxlink_gdb_if.c @@ -34,30 +34,33 @@ #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 volatile uint32_t count_new; -static char double_buffer_out[CDCACM_PACKET_SIZE]; +#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]; +/* 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) { /* 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,47 +70,45 @@ 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); } -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); - 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(buffer_out, double_buffer_out, count_new); - count_out = count_new; - count_new = 0; - out_ptr = 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 (!count_out) + } else __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 +124,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 +133,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 +147,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; } @@ -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) 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 */ diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index bd8250c4ef0..7babd42ce0a 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 @@ -27,7 +30,6 @@ #include "morse.h" #include -#include #include #include #include @@ -391,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); @@ -400,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) @@ -411,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) 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 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; 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 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) {