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/.codeberg/ci/build-firmware.yml b/.codeberg/ci/build-firmware.yml new file mode 100644 index 00000000000..17c4196ef4f --- /dev/null +++ b/.codeberg/ci/build-firmware.yml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +matrix: + # Define a matrix of all the probe platforms to get full build coverage for all the firmware possibilities + BMD_PROBE: + - '96b_carbon' + - 'blackpill-f401cc' + - 'blackpill-f401ce' + - 'blackpill-f411ce' + - 'bluepill' + - 'bmp-v3' + - 'ctxlink' + - 'f072' + - 'f3' + - 'f4discovery' + - 'hydrabus' + - 'launchpad-icdi' + - 'native' + - 'native-uncommon' + - 'native-st-clones' + - 'native-riscv' + - 'native-remote' + - 'stlink' + - 'stlinkv3' + - 'swlink' + +steps: + # Build the firmware for a given probe platform w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + export PATH="$$HOME/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi/bin:$$PATH" + apt update + apt install -y libftdi1-dev libhidapi-dev libusb-1.0-0-dev + + cc --version + arm-none-eabi-gcc --version + meson --version + ninja --version + + meson setup build --cross-file cross-file/${BMD_PROBE}.ini --werror + meson compile -C build diff --git a/.codeberg/ci/build-linux.yml b/.codeberg/ci/build-linux.yml new file mode 100644 index 00000000000..356e17b0610 --- /dev/null +++ b/.codeberg/ci/build-linux.yml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +matrix: + # Define a matrix of compilers to build BMDA with + C_COMPILER: + - gcc + - clang + +steps: + # Build BMDA with a given compiler from the matrix w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + export CC="${C_COMPILER}" + + $$CC --version + meson --version + ninja --version + + meson setup build -Db_lto=true + meson compile -C build diff --git a/.codeberg/ci/build-windows.yml b/.codeberg/ci/build-windows.yml new file mode 100644 index 00000000000..c6cdf413402 --- /dev/null +++ b/.codeberg/ci/build-windows.yml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Triggers the workflow on push events, but only for the main branch + - event: push + branch: main + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +steps: + # Build BMDA with a Meson config for Windows cross-builds w/ `-Werror` so warnings are errors + - name: build + image: codeberg.org/blackmagic-debug/blackmagic-ci-images/blackmagic-c + pull: true + commands: | + x86_64-w64-mingw32ucrt-gcc --version + meson --version + ninja --version + + meson setup build --cross-file .codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini -Db_lto=true + meson compile -C build diff --git a/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini b/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini new file mode 100644 index 00000000000..70a4427ff00 --- /dev/null +++ b/.codeberg/ci/x86_64-pc-windows-mingw-ucrt64.ini @@ -0,0 +1,34 @@ +[constants] +compileFlags = ['--sysroot', '/usr/x86_64-w64-mingw32ucrt'] +linkFlags = ['--sysroot', '/usr/x86_64-w64-mingw32ucrt'] + +[binaries] +c = 'x86_64-w64-mingw32ucrt-gcc' +cpp = 'x86_64-w64-mingw32ucrt-g++' +# c_ld = 'x86_64-w64-mingw32ucrt-ld.bdf' +# cpp_ld = 'x86_64-w64-mingw32ucrt-ld.bdf' +ar = 'x86_64-w64-mingw32ucrt-ar' +as = 'x86_64-w64-mingw32ucrt-as' +rc = 'x86_64-w64-mingw32ucrt-rc' +windres = 'x86_64-w64-mingw32ucrt-windres' +strip = 'x86_64-w64-mingw32ucrt-strip' +objcopy = 'x86_64-w64-mingw32ucrt-objcopy' +objdump = 'x86_64-w64-mingw32ucrt-objdump' +size = 'x86_64-w64-mingw32ucrt-size' +cmake = 'false' +exe_wrapper = 'wine' + +[properties] +needs_exe_wrapper = true + +[built-in options] +c_args = compileFlags +cpp_args = compileFlags +c_link_args = linkFlags +cpp_link_args = linkFlags + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'amd64' +endian = 'little' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 633996ff88f..7180a49cbbb 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,10 +13,10 @@ Information embedded in the description part of the commits doesn't count. ## Your checklist for this pull request -* [ ] I've read the [Code of Conduct](https://github.com/blackmagic-debug/blackmagic/blob/main/CODE_OF_CONDUCT.md) -* [ ] I've read the [guidelines for contributing](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md) to this repository -* [ ] It builds for hardware native (see [Building the firmware](https://github.com/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-firmware)) -* [ ] It builds as BMDA (see [Building the BMDA](https://github.com/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-app)) +* [ ] I've read the [Code of Conduct](https://codeberg.org/blackmagic-debug/blackmagic/src/CODE_OF_CONDUCT.md) +* [ ] I've read the [guidelines for contributing](https://codeberg.org/blackmagic-debug/blackmagic/src/CONTRIBUTING.md) to this repository +* [ ] It builds for hardware native (see [Building the firmware](https://codeberg.org/blackmagic-debug/blackmagic?tab=readme-ov-file#building-the-firmware)) +* [ ] It builds as BMDA (see [Building the BMDA](https://codeberg.org/blackmagic-debug/blackmagic?tab=readme-ov-file#building-black-magic-debug-app)) * [ ] I've tested it to the best of my ability * [ ] My commit messages provide a useful short description of what the commits do diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 486d2e90b6b..f6ca637e520 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -32,13 +32,14 @@ jobs: os: - {id: ubuntu-24.04, name: noble} compiler: - - '14.3.Rel1' + - '15.2.Rel1' probe: - '96b_carbon' - 'blackpill-f401cc' - 'blackpill-f401ce' - 'blackpill-f411ce' - 'bluepill' + - 'bmp-v3' - 'ctxlink' - 'f072' - 'f3' @@ -90,7 +91,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -112,7 +113,7 @@ jobs: # Package up the artefacts and upload them - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_firmware-${{ matrix.probe }} path: src/artefacts/* @@ -121,7 +122,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-firmware-${{ matrix.probe }} path: ${{ github.workspace }}/build/meson-logs/* @@ -132,7 +133,7 @@ jobs: needs: build-firmware steps: - name: Merge firmware artefacts - uses: actions/upload-artifact/merge@v4 + uses: actions/upload-artifact/merge@v6 with: name: blackmagic_firmware pattern: blackmagic_firmware-* @@ -150,9 +151,9 @@ jobs: - {id: ubuntu-24.04, name: noble} compiler: - 'clang-17' # Native Clang compiler for the CI image - - 'clang-20' # Latest Clang compiler from the apt mirror + - 'clang-21' # Latest Clang compiler from the apt mirror - 'gcc-12' # Native GCC compiler for the CI image - - 'gcc-13' # Latest GCC compiler from the toolchain PPA + - 'gcc-14' # Latest GCC compiler from the toolchain PPA fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -172,7 +173,7 @@ jobs: shell: bash run: | CXX=${CC/#gcc/g++} - sudo apt-add-repository ppa:ubuntu-toolchain-r/test + sudo apt-add-repository ppa:ubuntu-toolchain-r/ppa sudo apt-get update sudo apt-get install $CC $CXX echo "CC=$CC" >> $GITHUB_ENV @@ -219,7 +220,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -235,7 +236,7 @@ jobs: # Package up the artefact and upload it - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_linux-${{ matrix.os.id }}-${{ matrix.compiler }} path: src/artefacts/* @@ -244,7 +245,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-linux-${{ matrix.os.id }}-${{ matrix.compiler }} path: ${{ github.workspace }}/build/meson-logs/* @@ -266,7 +267,7 @@ jobs: strategy: matrix: os: - - windows-2022 + - windows-2025 sys: - {abi: ucrt64, env: ucrt-x86_64, compiler: gcc} fail-fast: false @@ -319,7 +320,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -336,7 +337,7 @@ jobs: # Package up all the artefacts and upload them - name: Archive firmware build artefacts as a zip - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_${{ matrix.os }}-${{ matrix.sys.abi }}-${{ matrix.sys.compiler }} path: src/artefacts/* @@ -345,7 +346,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-${{ matrix.os }}-${{ matrix.sys.abi }}-${{ matrix.sys.compiler }} path: ${{ github.workspace }}/build/meson-logs/* @@ -368,8 +369,9 @@ jobs: strategy: matrix: os: - - {id: macos-13, name: '13'} - {id: macos-14, name: '14'} + - {id: macos-15, name: '15'} + - {id: macos-26, name: '26'} fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -397,7 +399,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 fetch-tags: true @@ -413,7 +415,7 @@ jobs: # Package up the artefact and upload it - name: Archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: blackmagic_macos-${{ matrix.os.name }} path: src/artefacts/* @@ -422,7 +424,7 @@ jobs: # Package and upload logs if the build fails so we can dissect why - name: Upload failure logs if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: logs-macos-${{ matrix.os.name }} path: ${{ github.workspace }}/build/meson-logs/* diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index cf2ac9aed31..1a403a84518 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -35,13 +35,14 @@ jobs: os: - {id: ubuntu-24.04, name: noble} arm-compiler: - - '14.3.Rel1' + - '15.2.Rel1' probe: - '96b_carbon' - 'blackpill-f401cc' - 'blackpill-f401ce' - 'blackpill-f411ce' - 'bluepill' + - 'bmp-v3' - 'ctxlink' - 'f072' - 'f3' @@ -98,7 +99,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true @@ -125,7 +126,8 @@ jobs: strategy: matrix: os: - - windows-2025 + - windows-2025 # Currently latest + - windows-2025-vs2026 fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -159,7 +161,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -239,7 +241,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Install the dependencies needed for BMDA build - name: Install extra BMDA dependencies @@ -269,10 +271,9 @@ jobs: strategy: matrix: os: - - macos-13 - macos-14 - - macos-15 - - macos-latest + - macos-15 # Currently latest + - macos-26 fail-fast: false # Steps represent a sequence of tasks that will be executed as part of the job @@ -300,7 +301,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -377,7 +378,7 @@ jobs: # Checkout the repository and branch to build under the default location - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Build the default BMDA configuration - name: Build full BMDA @@ -402,7 +403,7 @@ jobs: - name: Setup ARM GCC uses: carlosperate/arm-none-eabi-gcc-action@v1 with: - release: '14.3.Rel1' + release: '15.2.Rel1' # Install and setup a suitable Meson + Ninja - name: Setup Meson + Ninja @@ -411,12 +412,12 @@ jobs: sudo python3 -m pip install meson ninja working-directory: ${{ runner.temp }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.base_ref }} path: base - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.sha }} path: head @@ -424,14 +425,14 @@ jobs: # Build the base ref firmware for the largest Flash target available - name: Build base run: | - meson setup build --cross-file=cross-file/blackpill-f411ce.ini + meson setup build --cross-file=cross-file/bmp-v3.ini meson compile -C build > build.log working-directory: base # Build the PR `HEAD` ref firmware for the largest Flash target available - name: Build head run: | - meson setup build --cross-file=cross-file/blackpill-f411ce.ini + meson setup build --cross-file=cross-file/bmp-v3.ini meson compile -C build > build.log working-directory: head diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7661661067a..29a1d253fc5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,17 +36,17 @@ When reporting issues, be as specific as possible! ### If contributing for the first time - 1. [Fork](https://github.com/blackmagic-debug/blackmagic/fork) and clone the repository + 1. [Fork](https://codeberg.org/blackmagic-debug/blackmagic/fork) and clone the repository 2. Create a new branch: `git switch -c type/branch-name` (`git checkout -b type/branch-name` in the old syntax) 3. Make your change - 4. Push to your fork and submit a [pull request](https://github.com/blackmagic-debug/blackmagic/compare) + 4. Push to your fork and submit a [pull request](https://codeberg.org/blackmagic-debug/blackmagic/compare) If you wish to fix a bug, `type` in the new branch name should be `fix`, otherwise if you wish to implement a new feature, `type` should be `feature`. ### If you are working from an existing clone of the repository -1. Ensure you have our repo as a remote (`git remote add upstream https://github.com/blackmagic-debug/blackmagic`) +1. Ensure you have our repo as a remote (`git remote add upstream https://codeberg.org/blackmagic-debug/blackmagic`) 2. Switch back to `main` (`git switch main`/`git checkout main`) 3. Pull to ensure you're up to date (`git pull upstream`) 4. Push to your fork (`git push`) diff --git a/README.md b/README.md index 6151cca001d..4422306385f 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ a debugger-in-a-dongle that provides multi-voltage debug with no other external or as Black Magic Debug App (BMDA) which is the project built for the host machine, more details below. The project allows debugging of devices connected over JTAG or SWD, and via the companion tool -[bmpflash](https://github.com/blackmagic-debug/bmpflash) the programming of SPI Flash devices. +[bmpflash](https://codeberg.org/blackmagic-debug/bmpflash) the programming of SPI Flash devices. This includes support for ARM and RISC-V devices, the complete list can be found on the website. [![Discord](https://img.shields.io/discord/613131135903596547?logo=discord)](https://discord.gg/P7FYThy) -[![Current release](https://img.shields.io/github/v/release/blackmagic-debug/blackmagic.svg?logo=github)](https://github.com/blackmagic-debug/blackmagic/releases) +[![Current release](https://codeberg.org/blackmagic-debug/blackmagic/badges/release.svg)](https://codeberg.org/blackmagic-debug/blackmagic/releases) [![CI flow status](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml/badge.svg)](https://github.com/blackmagic-debug/blackmagic/actions/workflows/build-and-upload.yml) [![AI free project](https://badges.ws/badge/NO-AI-ff0000)](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md#contributing) diff --git a/UsingRTT.md b/UsingRTT.md index 0e59323b813..387a4992a26 100644 --- a/UsingRTT.md +++ b/UsingRTT.md @@ -206,7 +206,7 @@ to the RTT input of the target. ### Linux On Linux, install udev rules as described in the [driver -documentation](https://github.com/blackmagic-debug/blackmagic/blob/main/driver/README.md). +documentation](https://codeberg.org/blackmagic-debug/blackmagic/src/driver/README.md). Disconnect and re-connect the BMP. Check the device shows up in `/dev/`: ```sh diff --git a/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/deps/hidapi.wrap b/deps/hidapi.wrap index eb8cc2e83b3..f7c57bcc276 100644 --- a/deps/hidapi.wrap +++ b/deps/hidapi.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/hidapi +url = https://codeberg.org/blackmagic-debug/hidapi revision = hidapi-0.14.0-meson clone-recursive = false diff --git a/deps/libftdi.wrap b/deps/libftdi.wrap index 88cd4319a9c..cfbbe3a7e8d 100644 --- a/deps/libftdi.wrap +++ b/deps/libftdi.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libftdi +url = https://codeberg.org/blackmagic-debug/libftdi revision = v1.5-meson clone-recursive = false diff --git a/deps/libopencm3.wrap b/deps/libopencm3.wrap index 1abb643ddc1..42573bbdb78 100644 --- a/deps/libopencm3.wrap +++ b/deps/libopencm3.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libopencm3 +url = https://codeberg.org/blackmagic-debug/libopencm3 revision = head depth = 1 diff --git a/deps/libusb.wrap b/deps/libusb.wrap index 8d691f0dce4..6850c43bb4c 100644 --- a/deps/libusb.wrap +++ b/deps/libusb.wrap @@ -1,5 +1,5 @@ [wrap-git] -url = https://github.com/blackmagic-debug/libusb +url = https://codeberg.org/blackmagic-debug/libusb revision = v1.0.27-meson clone-recursive = false diff --git a/meson.build b/meson.build index d7d5f49bbf8..8dff9419a47 100644 --- a/meson.build +++ b/meson.build @@ -320,7 +320,7 @@ elif bmda_platform.found() bmda = executable( 'blackmagic', dependencies: [libbmd_core, bmda_platform], - native: is_cross_build, + native: is_cross_build and is_firmware_build, ) alias_target('bmda', bmda) elif not is_firmware_build diff --git a/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/gdb_main.c b/src/gdb_main.c index 218b93a9c27..72d48720aed 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -498,16 +498,30 @@ static void exec_q_memory_map(const char *packet, const size_t length) (void)length; target_s *target = cur_target; - /* Read target XML memory map */ + /* Figure out which target to read the map for, if there is a valid one */ if (!target) target = last_target; if (!target) { gdb_put_packet_error(1U); return; } - char buf[1024]; - target_mem_map(target, buf, sizeof(buf)); /* Fixme: Check size!*/ - handle_q_string_reply(buf, packet); + + /* Decode the offset into the map being requested */ + uint32_t offset = 0; + if (!read_hex32(packet, NULL, &offset, ',')) { + gdb_put_packet_error(1U); + return; + } + + /* Grab not more than a GDB packet buffer's worth of data */ + char buffer[GDB_PACKET_BUFFER_SIZE]; + const size_t chunk_length = target_mem_map_chunk(target, buffer, ARRAY_LENGTH(buffer), offset); + + /* Determine if this was the last chunk so we generate the right kind of packet */ + const bool end = target->map_transfer_offset == 0U; + + /* And now send the chunk back to the host */ + gdb_put_packet(end ? "l" : "m", 1U, buffer, chunk_length, false); } static void exec_q_feature_read(const char *packet, const size_t length) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 420ed3f3227..b0c95613b8a 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -119,36 +119,35 @@ static uint8_t gdb_packet_checksum(const gdb_packet_s *const packet) return checksum; } -packet_state_e consume_remote_packet(char *const packet, const size_t size) +packet_state_e consume_remote_packet(gdb_packet_s *const packet) { #if CONFIG_BMDA == 0 /* We got what looks like probably a remote control packet */ - size_t offset = 0; + packet->size = 0; while (true) { /* Consume bytes until we either have a complete remote control packet or have to leave this mode */ const char rx_char = gdb_if_getchar(); switch (rx_char) { case '\x04': - packet[0] = rx_char; + packet->data[0] = rx_char; /* EOT (end of transmission) - connection was closed */ return PACKET_IDLE; case REMOTE_SOM: /* Oh dear, restart remote packet capture */ - offset = 0; + packet->size = 0; break; case REMOTE_EOM: /* Complete packet for processing */ /* Null terminate packet */ - packet[offset] = '\0'; + packet->data[packet->size] = '\0'; /* Handle packet */ - remote_packet_process(packet, offset); + remote_packet_process(packet); /* Restart packet capture */ - packet[0] = '\0'; return PACKET_IDLE; case GDB_PACKET_START: @@ -156,18 +155,15 @@ packet_state_e consume_remote_packet(char *const packet, const size_t size) return PACKET_GDB_CAPTURE; default: - if (offset < size) - packet[offset++] = rx_char; - else { - packet[0] = '\0'; + if (packet->size < GDB_PACKET_BUFFER_SIZE) + packet->data[packet->size++] = rx_char; + else /* Buffer overflow, restart packet capture */ return PACKET_IDLE; - } } } #else (void)packet; - (void)size; /* Hosted builds ignore remote control packets */ return PACKET_IDLE; @@ -199,7 +195,7 @@ gdb_packet_s *gdb_packet_receive(void) * Let consume_remote_packet handle this * returns PACKET_IDLE or PACKET_GDB_CAPTURE if it detects the start of a GDB packet */ - state = consume_remote_packet(packet->data, GDB_PACKET_BUFFER_SIZE); + state = consume_remote_packet(packet); packet->size = 0; } #endif @@ -236,7 +232,7 @@ gdb_packet_s *gdb_packet_receive(void) case PACKET_GDB_ESCAPE: /* Resolve escaped char */ - packet->data[packet->size++] = rx_char ^ GDB_PACKET_ESCAPE_XOR; + packet->data[packet->size++] = (char)((uint8_t)rx_char ^ GDB_PACKET_ESCAPE_XOR); /* Return to normal packet capture */ state = PACKET_GDB_CAPTURE; @@ -446,10 +442,10 @@ void gdb_out(const char *const str) /** * Program console output packet * See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html#Stop-Reply-Packets - * + * * Format: ‘O XX…’ * ‘XX…’ is hex encoding of ASCII data, to be written as the program’s console output. - * + * * Can happen at any time while the program is running and the debugger should continue to wait for ‘W’, ‘T’, etc. * This reply is not permitted in non-stop mode. */ @@ -462,7 +458,7 @@ void gdb_voutf(const char *const fmt, va_list ap) * We could technically do the formatting and transformation in a single buffer reducing stack usage * But it is a bit more complex and likely slower, we would need to spread the characters out such * that each occupies two bytes, and then we could hex them in place - * + * * If this stack usage proves to be a problem, we can revisit this */ char str_scratch[GDB_OUT_PACKET_MAX_SIZE + 1U]; 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/include/target.h b/src/include/target.h index 6f9151d3503..ce0be5d1b34 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -69,7 +69,6 @@ target_s *target_attach_n(size_t n, target_controller_s *controller); void target_detach(target_s *target); /* Memory access functions */ -bool target_mem_map(target_s *target, char *buf, size_t len); bool target_mem32_read(target_s *target, void *dest, target_addr_t src, size_t len); bool target_mem64_read(target_s *target, void *dest, target_addr64_t src, size_t len); bool target_mem32_write(target_s *target, target_addr_t dest, const void *src, size_t len); @@ -82,13 +81,16 @@ bool target_flash_complete(target_s *target); bool target_flash_mass_erase(target_s *target); /* Register access functions */ -size_t target_regs_size(target_s *target); -const char *target_regs_description(target_s *target); void target_regs_read(target_s *target, void *data); void target_regs_write(target_s *target, const void *data); size_t target_reg_read(target_s *target, uint32_t reg, void *data, size_t max); size_t target_reg_write(target_s *target, uint32_t reg, const void *data, size_t size); +/* Target metadata functions */ +size_t target_mem_map_chunk(target_s *target, char *buffer, size_t length, uint32_t start_offset); +size_t target_regs_size(target_s *target); +const char *target_regs_description(target_s *target); + /* Halt/resume functions */ typedef enum target_halt_reason { TARGET_HALT_RUNNING = 0, /* Target not halted */ diff --git a/src/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/blackpill-f4/README.md b/src/platforms/common/blackpill-f4/README.md index fdddb63303c..d6c09300b8b 100644 --- a/src/platforms/common/blackpill-f4/README.md +++ b/src/platforms/common/blackpill-f4/README.md @@ -38,7 +38,7 @@ In the example command lines for building and flashing the firmware to the Black 0. Clone the repo and libopencm3 submodule, install toolchains, meson, etc. ```sh -git clone https://github.com/blackmagic-debug/blackmagic.git +git clone https://codeberg.org/blackmagic-debug/blackmagic.git cd blackmagic ``` @@ -53,7 +53,7 @@ meson setup build --cross-file=cross-file/blackpill-xxxxxx.ini -Dbmd_bootloader= Also Note: If the bootloader and firmware are going to be built for a Blackpill connected to a "Blackpill Carrier", the above setup MUST have "-Don_carrier_board=true" added to it. This is required to ensure the LEDs are correctly mapped to the Blackpill Carrier Board. - + 2. Compile the firmware and bootloader ```sh @@ -99,7 +99,7 @@ If you flashed the bootloader using the above instructions, it may be invoked us - Wait a moment - Release KEY -Once activated the BMD bootloader may be used to flash the device using 'bmputil,' available [here](https://github.com/blackmagic-debug/bmputil). +Once activated the BMD bootloader may be used to flash the device using 'bmputil,' available [here](https://codeberg.org/blackmagic-debug/bmputil). ## SWD/JTAG frequency setting diff --git a/src/platforms/common/jtagtap.c b/src/platforms/common/jtagtap.c index 2a6565752f1..ad76f5c0426 100644 --- a/src/platforms/common/jtagtap.c +++ b/src/platforms/common/jtagtap.c @@ -340,6 +340,10 @@ static void jtagtap_cycle_no_delay(const size_t clock_cycles) static void jtagtap_cycle(const bool tms, const bool tdi, const size_t clock_cycles) { + /* If `clock_cycles` is 0, do nothing so we don't wind up doing UINT32_MAX things */ + if (clock_cycles == 0U) + return; + jtagtap_next(tms, tdi); if (target_clk_divider != UINT32_MAX) jtagtap_cycle_clk_delay(clock_cycles - 1U); 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..b0ebb88347b 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,27 +22,31 @@ #include "platform.h" #include "version.h" #include "serialno.h" +#include "buffer_utils.h" #include #include #if defined(STM32F1HD) -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/4*002Ka,000*002Kg" -#define DFU_IFACE_STRING_OFFSET 38 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/4*002Ka,000*002Kg" +#define DFU_IFACE_STRING_OFFSET 35 #define DFU_IFACE_PAGESIZE 2 #elif defined(STM32F1) -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,000*001Kg" -#define DFU_IFACE_STRING_OFFSET 38 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/8*001Ka,000*001Kg" +#define DFU_IFACE_STRING_OFFSET 35 #define DFU_IFACE_PAGESIZE 1 #elif defined(STM32F4) || defined(STM32F7) #define DFU_IFACE_PAGESIZE 128 #if APP_START == 0x08020000 -#define DFU_IFACE_STRING_OFFSET 62 -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Ka,1*064Ka,1*128Kg,002*128Kg" +#define DFU_IFACE_STRING_OFFSET 59 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/1*016Ka,3*016Ka,1*064Ka,1*128Kg,002*128Kg" #elif APP_START == 0x08004000 -#define DFU_IFACE_STRING_OFFSET 54 -#define DFU_IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,000*128Kg" +#define DFU_IFACE_STRING_OFFSET 51 +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/1*016Ka,3*016Kg,1*064Kg,000*128Kg" #endif +#elif defined(STM32U5) +#define DFU_IFACE_STRING "@Internal Flash/0x08000000/2*8Ka,000*8Kg" +#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/README.md b/src/platforms/ctxlink/README.md index 6d5a215365f..86ea3966523 100644 --- a/src/platforms/ctxlink/README.md +++ b/src/platforms/ctxlink/README.md @@ -36,7 +36,7 @@ ctxLink uses the common 2 x 5 0.05" pin-header. 1. Clone the Blackmagic Debug Repository ```sh -git clone https://github.com/blackmagic-debug/blackmagic.git +git clone https://codeberg.org/blackmagic-debug/blackmagic.git cd blackmagic ``` diff --git a/src/platforms/ctxlink/WiFi_Server.c b/src/platforms/ctxlink/WiFi_Server.c index 1e2b69e5353..3fad1f4d492 100644 --- a/src/platforms/ctxlink/WiFi_Server.c +++ b/src/platforms/ctxlink/WiFi_Server.c @@ -1629,7 +1629,7 @@ void send_swo_trace_data(uint8_t *buffer, uint8_t length) void wifi_gdb_putchar(const uint8_t ch, const bool flush) { send_buffer[send_count++] = ch; - if (flush || send_count >= sizeof(send_buffer)) + if (flush || send_count == sizeof(send_buffer)) wifi_gdb_flush(flush); } @@ -1641,13 +1641,9 @@ void wifi_gdb_flush(const bool force) if (send_count == 0U) return; - // TODO is this check required now, looks like a debug test left in place? - if (send_count <= 0U) - DEBUG_WARN("WiFi_putchar bad count\r\n"); - DEBUG_WARN("Wifi_putchar %c\r\n", send_buffer[0]); - send(gdb_client_socket, &send_buffer[0], send_count, 0); + DEBUG_WARN("Wifi_putchar %c\n", send_buffer[0]); + send(gdb_client_socket, send_buffer, send_count, 0); /* Reset the buffer */ send_count = 0U; - memset(&send_buffer[0], 0x00, sizeof(send_buffer)); } 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/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index fb5ad14d925..01d6f8feae5 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -537,10 +537,18 @@ static bool process_vid_pid_table_probe( if (version == NULL) version = strdup("---"); - *probe_list = probe_info_add_by_id(*probe_list, debugger_device->type, device, device_descriptor->idVendor, - device_descriptor->idProduct, manufacturer, product, serial, version); + probe_info_s *probe_info = probe_info_add_by_id(*probe_list, debugger_device->type, device, + device_descriptor->idVendor, device_descriptor->idProduct, manufacturer, product, serial, version); + if (probe_info) + *probe_list = probe_info; + else { + free(product); + free(manufacturer); + free(serial); + free(version); + } libusb_close(handle); - return true; + return probe_info != NULL; } static const probe_info_s *scan_for_devices(bmda_probe_s *info) diff --git a/src/platforms/hosted/bmp_serial.c b/src/platforms/hosted/bmp_serial.c index 79a4d15a8c3..e200cce4ef2 100644 --- a/src/platforms/hosted/bmp_serial.c +++ b/src/platforms/hosted/bmp_serial.c @@ -109,7 +109,7 @@ static const char *read_value_str_from_path(HKEY path_handle, const char *const { DWORD value_len = 0U; /* Start by trying to discover how long the string held by the key is */ - const LSTATUS result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); + LSTATUS result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); /* If that didn't work, we have no hoope, so bail */ if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA) { display_error(result, "retrieving registry value", value_name); @@ -126,7 +126,8 @@ static const char *read_value_str_from_path(HKEY path_handle, const char *const } /* Finally, try reading the value and return it to the user if this didn't explode */ - assert(RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, value, &value_len) == ERROR_SUCCESS); + result = RegGetValue(path_handle, NULL, value_name, RRF_RT_REG_SZ, NULL, value, &value_len); + assert(result == ERROR_SUCCESS); return value; } diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index b9915866df4..07181dc3224 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -354,6 +354,19 @@ const cable_desc_s cable_desc[] = { .bb_swdio_in_pin = MPSSE_CS, .name = "hifive1", }, + { + /* + * 1bitSquared Icebreaker FT2232H + * Direct connection on Interface A, JTAG only + */ + .vendor = 0x0403U, + .product = 0x6010U, + .interface = INTERFACE_A, + .init.data[0] = MPSSE_CS | MPSSE_DO | MPSSE_DI, + .init.dirs[0] = MPSSE_CS | MPSSE_DO | MPSSE_SK, + .description = "iCEBreaker V1.0e", + .name = "icebreaker", + }, { /* * https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/ diff --git a/src/platforms/hosted/meson.build b/src/platforms/hosted/meson.build index 13c0d511113..229ef4a94d3 100644 --- a/src/platforms/hosted/meson.build +++ b/src/platforms/hosted/meson.build @@ -70,7 +70,10 @@ bmda_args = [ bmda_link_args = [] bmda_deps = [] -cc = is_cross_build ? cc_native : cc_host +bmda_native_build = is_cross_build and is_firmware_build +bmda_machine = bmda_native_build ? build_machine : host_machine + +cc = bmda_native_build ? cc_native : cc_host # Ensure that MSVC is switched to standards compliant mode if cc.get_define('_MSC_VER') != '' @@ -84,7 +87,7 @@ endif # Determine if we're on a MSYS2 environment of some kind # If the compiler is MSYS2 GCC or Clang (but not Clang-cl) -if build_machine.system() == 'windows' and cc.get_define('__MINGW32__') == '1' +if bmda_machine.system() == 'windows' and cc.get_define('__MINGW32__') == '1' # It needs custom location for some MinGWs # See https://github.com/msys2/MINGW-packages/issues/10275 ucrt_check = ''' @@ -117,18 +120,18 @@ libusb = dependency( version: '>=1.0.13', method: 'pkg-config', fallback: 'libusb', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'default_library=static', 'install_targets=false', 'docs=disabled', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) -if build_machine.system() in ['windows', 'cygwin'] +if bmda_machine.system() in ['windows', 'cygwin'] subdir('windows') # Make sure we build for Windows Vista and above, where the @@ -150,7 +153,7 @@ else 'libftdi1', method: 'pkg-config', fallback: 'libftdi', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'default_library=static', 'install_targets=false', @@ -159,29 +162,29 @@ else 'examples=disabled', 'ftdi_eeprom=disabled', #'python_bindings=disabled', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) bmda_sources += files('serial_unix.c') endif # Pick the appropriate HIDAPI depending on platform -if build_machine.system() == 'linux' +if bmda_machine.system() == 'linux' hidapi = dependency( 'hidapi-hidraw', method: 'pkg-config', fallback: ['hidapi', 'hidapi_hidraw_dep'], - native: is_cross_build, + native: bmda_native_build, default_options: [ 'c_std=gnu99', 'default_library=static', 'install_targets=false', 'with_libusb=false', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) else @@ -189,24 +192,24 @@ else 'hidapi', method: 'pkg-config', fallback: 'hidapi', - native: is_cross_build, + native: bmda_native_build, default_options: [ 'c_std=c99', 'default_library=static', 'install_targets=false', - 'build_native=true', + 'build_native=@0@'.format(bmda_native_build), ], - required: not is_cross_build, + required: not bmda_native_build, disabler: true, ) endif -if build_machine.system() == 'linux' +if bmda_machine.system() == 'linux' libgpiod = dependency( 'libgpiod', version: ['>=1.0.0', '<2.0.0'], required: get_option('enable_gpiod'), - native: is_cross_build, + native: bmda_native_build, ) if libgpiod.found() diff --git a/src/platforms/hosted/probe_info.c b/src/platforms/hosted/probe_info.c index b068b8dfbd6..1c69d8c221d 100644 --- a/src/platforms/hosted/probe_info.c +++ b/src/platforms/hosted/probe_info.c @@ -50,7 +50,7 @@ probe_info_s *probe_info_add_by_id(probe_info_s *const list, const probe_type_e #endif probe_info_s *probe_info = malloc(sizeof(*probe_info)); if (!probe_info) { - DEBUG_INFO("Fatal: Failed to allocate memory for a probe info structure\n"); + DEBUG_ERROR("Fatal: Failed to allocate memory for a probe info structure\n"); return NULL; } diff --git a/src/platforms/hosted/remote/protocol_v2.c b/src/platforms/hosted/remote/protocol_v2.c index b5b70f9e1b2..dc4eee06930 100644 --- a/src/platforms/hosted/remote/protocol_v2.c +++ b/src/platforms/hosted/remote/protocol_v2.c @@ -121,6 +121,10 @@ static inline uint8_t bool_to_int(const bool value) static void remote_v2_jtag_cycle(const bool tms, const bool tdi, const size_t clock_cycles) { + /* If `clock_cycles` is 0, avoid issuing the command to the probe as there's nothing to do */ + if (clock_cycles == 0U) + return; + char buffer[REMOTE_MAX_MSG_SIZE]; int length = snprintf(buffer, REMOTE_MAX_MSG_SIZE, REMOTE_JTAG_CYCLE_STR, bool_to_int(tms), bool_to_int(tdi), clock_cycles); diff --git a/src/platforms/hosted/serial_win.c b/src/platforms/hosted/serial_win.c index 0b16f485756..5bf0a9155e9 100644 --- a/src/platforms/hosted/serial_win.c +++ b/src/platforms/hosted/serial_win.c @@ -175,7 +175,7 @@ static char *read_key_from_path(const char *const subpath, const char *const key return NULL; DWORD value_len = 0; - const LSTATUS result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); + LSTATUS result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, NULL, &value_len); if (result != ERROR_SUCCESS && result != ERROR_MORE_DATA) { display_error(result, "retrieving value for key", key_name); RegCloseKey(key_path_handle); @@ -188,7 +188,8 @@ static char *read_key_from_path(const char *const subpath, const char *const key RegCloseKey(key_path_handle); return NULL; } - assert(RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, value, &value_len) == ERROR_SUCCESS); + result = RegGetValue(key_path_handle, NULL, key_name, RRF_RT_REG_SZ, NULL, value, &value_len); + assert(result == ERROR_SUCCESS); RegCloseKey(key_path_handle); return value; } 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/stlink/README.md b/src/platforms/stlink/README.md index 436576b52f2..85004c4251a 100644 --- a/src/platforms/stlink/README.md +++ b/src/platforms/stlink/README.md @@ -52,7 +52,7 @@ NB: SWDIO/TMS is on P**B**14, not P**A**14. * Keep the original ST Bootloader. * Compile firmware with the option `-Dbmd_bootloader=false` -* Upload firmware with stlink-tool from [stlink-tool](https://github.com/blackmagic-debug/stlink-tool)(*3). +* Upload firmware with stlink-tool from [stlink-tool](https://codeberg.org/blackmagic-debug/stlink-tool)(*3). * For ST-Link v2, as on older disco boards, un- and replug USB to enter the bootloader. * Upload BMP firmware with `stlink-tool blackmagic_stlink_firmware.bin` * For ST-Link v2, after each stlink replug, call either `blackmacic -t` or `stlink-tool` without arguments or on Linux use some udev rule like the one shown below to enter the BMP firmware diff --git a/src/platforms/stlinkv3/README.md b/src/platforms/stlinkv3/README.md index a43077dcb84..d9fbb3b2f65 100644 --- a/src/platforms/stlinkv3/README.md +++ b/src/platforms/stlinkv3/README.md @@ -77,7 +77,7 @@ use soldered connections to CN3. For [STLINK-V3MINI](https://www.st.com/resource It is a good idea to keep a full image of the original flash content as backup! If you want to keep the original bootloader or access via SWD is disabled, clone -https://github.com/blackmagic-debug/stlink-tool +https://codeberg.org/blackmagic-debug/stlink-tool make and use like `stlink-tool blackmagic_stlinkv3_firmware.bin` Revert to original ST firmware with 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/remote.c b/src/remote.c index d5a8c03fc04..2c80c749786 100644 --- a/src/remote.c +++ b/src/remote.c @@ -43,7 +43,7 @@ #include "hex_utils.h" #if CONFIG_BMDA == 0 -static void remote_packet_process_adiv6(const char *packet, size_t packet_len); +static void remote_packet_process_adiv6(gdb_packet_s *packet); /* hex-ify and send a buffer of data */ static void remote_send_buf(const void *const buffer, const size_t len) @@ -128,11 +128,11 @@ static adiv5_debug_port_s remote_dp = { .mem_write = adiv5_mem_write_bytes, }; -static void remote_packet_process_swd(const char *const packet, const size_t packet_len) +static void remote_packet_process_swd(gdb_packet_s *const packet) { - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_INIT: /* SS = initialise =============================== */ - if (packet_len == 2) { + if (packet->size == 2) { remote_dp.write_no_check = adiv5_swd_write_no_check; remote_dp.read_no_check = adiv5_swd_read_no_check; remote_dp.dp_read = adiv5_swd_read; @@ -146,7 +146,7 @@ static void remote_packet_process_swd(const char *const packet, const size_t pac break; case REMOTE_IN_PAR: { /* SI = In parity ============================= */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); uint32_t result = 0; const bool parity_ok = swd_proc.seq_in_parity(&result, clock_cycles); remote_respond(parity_ok ? REMOTE_RESP_OK : REMOTE_RESP_PARERR, result); @@ -154,23 +154,23 @@ static void remote_packet_process_swd(const char *const packet, const size_t pac } case REMOTE_IN: { /* Si = In ======================================= */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); const uint32_t result = swd_proc.seq_in(clock_cycles); remote_respond(REMOTE_RESP_OK, result); break; } case REMOTE_OUT: { /* So = Out ====================================== */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t data = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t data = hex_string_to_num(-1, packet->data + 4); swd_proc.seq_out(data, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; } case REMOTE_OUT_PAR: { /* SO = Out parity ========================== */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t data = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t data = hex_string_to_num(-1, packet->data + 4); swd_proc.seq_out_parity(data, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; @@ -182,9 +182,9 @@ static void remote_packet_process_swd(const char *const packet, const size_t pac } } -static void remote_packet_process_jtag(const char *const packet, const size_t packet_len) +static void remote_packet_process_jtag(gdb_packet_s *const packet) { - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_INIT: /* JS = initialise ============================= */ remote_dp.write_no_check = NULL; remote_dp.read_no_check = NULL; @@ -202,10 +202,10 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa break; case REMOTE_TMS: { /* JT = TMS Sequence ============================ */ - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint32_t tms_states = hex_string_to_num(2, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint32_t tms_states = hex_string_to_num(2, packet->data + 4); - if (packet_len < 4U) + if (packet->size < 4U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { jtag_proc.jtagtap_tms_seq(tms_states, clock_cycles); @@ -214,10 +214,10 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa break; } - case REMOTE_CYCLE: { /* JC = clock cycle ============================ */ - const size_t clock_cycles = hex_string_to_num(8, packet + 4); - const bool tms = packet[2] != '0'; - const bool tdi = packet[3] != '0'; + case REMOTE_CYCLE: { /* Jc = clock cycle ============================ */ + const size_t clock_cycles = hex_string_to_num(8, packet->data + 4); + const bool tms = packet->data[2] != '0'; + const bool tdi = packet->data[3] != '0'; jtag_proc.jtagtap_cycle(tms, tdi, clock_cycles); remote_respond(REMOTE_RESP_OK, 0); break; @@ -225,14 +225,14 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa case REMOTE_TDITDO_TMS: /* JD = TDI/TDO ========================================= */ case REMOTE_TDITDO_NOTMS: { - if (packet_len < 5U) + if (packet->size < 5U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { - const size_t clock_cycles = hex_string_to_num(2, packet + 2); - const uint64_t data_in = hex_string_to_num(-1, packet + 4); + const size_t clock_cycles = hex_string_to_num(2, packet->data + 2); + const uint64_t data_in = hex_string_to_num(-1, packet->data + 4); uint64_t data_out = 0; jtag_proc.jtagtap_tdi_tdo_seq( - (uint8_t *)&data_out, packet[1] == REMOTE_TDITDO_TMS, (const uint8_t *)&data_in, clock_cycles); + (uint8_t *)&data_out, packet->data[1] == REMOTE_TDITDO_TMS, (const uint8_t *)&data_in, clock_cycles); remote_respond(REMOTE_RESP_OK, data_out); } @@ -240,10 +240,10 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa } case REMOTE_NEXT: { /* JN = NEXT ======================================== */ - if (packet_len != 4U) + if (packet->size != 4U) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); else { - const bool tdo = jtag_proc.jtagtap_next(packet[2] == '1', packet[3] == '1'); + const bool tdo = jtag_proc.jtagtap_next(packet->data[2] == '1', packet->data[3] == '1'); remote_respond(REMOTE_RESP_OK, tdo ? 1U : 0U); } break; @@ -255,22 +255,21 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa } } -static void remote_packet_process_general(char *packet, const size_t packet_len) +static void remote_packet_process_general(gdb_packet_s *const packet) { - (void)packet_len; - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_VOLTAGE: remote_respond_string(REMOTE_RESP_OK, platform_target_voltage()); break; case REMOTE_NRST_SET: - platform_nrst_set_val(packet[2] == '1'); + platform_nrst_set_val(packet->data[2] == '1'); remote_respond(REMOTE_RESP_OK, 0); break; case REMOTE_NRST_GET: remote_respond(REMOTE_RESP_OK, platform_nrst_get_val()); break; case REMOTE_FREQ_SET: - platform_max_frequency_set(hex_string_to_num(8U, packet + 2U)); + platform_max_frequency_set(hex_string_to_num(8U, packet->data + 2U)); remote_respond(REMOTE_RESP_OK, 0); break; case REMOTE_FREQ_GET: { @@ -280,14 +279,14 @@ static void remote_packet_process_general(char *packet, const size_t packet_len) } case REMOTE_PWR_SET: #ifdef PLATFORM_HAS_POWER_SWITCH - if (packet[2] == '1' && !platform_target_get_power() && + if (packet->data[2] == '1' && !platform_target_get_power() && platform_target_voltage_sense() > POWER_CONFLICT_THRESHOLD) { /* want to enable target power, but voltage > 0.5V sensed * on the pin -> cancel */ remote_respond(REMOTE_RESP_ERR, 0); } else { - const bool result = platform_target_set_power(packet[2] == '1'); + const bool result = platform_target_set_power(packet->data[2] == '1'); remote_respond(result ? REMOTE_RESP_OK : REMOTE_RESP_ERR, 0U); } #else @@ -308,12 +307,12 @@ static void remote_packet_process_general(char *packet, const size_t packet_len) #ifndef PLATFORM_IDENT_DYNAMIC remote_respond_string(REMOTE_RESP_OK, BOARD_IDENT); #else - snprintf(packet, GDB_PACKET_BUFFER_SIZE, BOARD_IDENT, platform_ident()); - remote_respond_string(REMOTE_RESP_OK, packet); + snprintf(packet->data, GDB_PACKET_BUFFER_SIZE, BOARD_IDENT, platform_ident()); + remote_respond_string(REMOTE_RESP_OK, packet->data); #endif break; case REMOTE_TARGET_CLK_OE: - platform_target_clk_output_enable(packet[2] != '0'); + platform_target_clk_output_enable(packet->data[2] != '0'); remote_respond(REMOTE_RESP_OK, 0); break; default: @@ -322,29 +321,29 @@ static void remote_packet_process_general(char *packet, const size_t packet_len) } } -static void remote_packet_process_high_level(const char *packet, const size_t packet_len) +static void remote_packet_process_high_level(gdb_packet_s *const packet) { SET_IDLE_STATE(0); - switch (packet[1]) { + switch (packet->data[1]) { case REMOTE_HL_CHECK: /* HC = check the version of the protocol */ remote_respond(REMOTE_RESP_OK, REMOTE_HL_VERSION); break; case REMOTE_HL_ADD_JTAG_DEV: { /* HJ = fill firmware jtag_devs */ /* Check the packet is an appropriate length */ - if (packet_len < 22U) { + if (packet->size < 22U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); break; } jtag_dev_s jtag_dev = {0}; - const uint8_t index = hex_string_to_num(2, packet + 2); - jtag_dev.dr_prescan = hex_string_to_num(2, packet + 4); - jtag_dev.dr_postscan = hex_string_to_num(2, packet + 6); - jtag_dev.ir_len = hex_string_to_num(2, packet + 8); - jtag_dev.ir_prescan = hex_string_to_num(2, packet + 10); - jtag_dev.ir_postscan = hex_string_to_num(2, packet + 12); - jtag_dev.current_ir = hex_string_to_num(8, packet + 14); + const uint8_t index = hex_string_to_num(2, packet->data + 2); + jtag_dev.dr_prescan = hex_string_to_num(2, packet->data + 4); + jtag_dev.dr_postscan = hex_string_to_num(2, packet->data + 6); + jtag_dev.ir_len = hex_string_to_num(2, packet->data + 8); + jtag_dev.ir_prescan = hex_string_to_num(2, packet->data + 10); + jtag_dev.ir_postscan = hex_string_to_num(2, packet->data + 12); + jtag_dev.current_ir = hex_string_to_num(8, packet->data + 14); jtag_add_device(index, &jtag_dev); remote_respond(REMOTE_RESP_OK, 0); break; @@ -459,20 +458,20 @@ static void remote_adiv5_respond(const void *const data, const size_t length) } } -static void remote_packet_process_adiv5(const char *const packet, const size_t packet_len) +static void remote_packet_process_adiv5(gdb_packet_s *const packet) { /* Check there's at least an ADI command byte */ - if (packet_len < 2U) { + if (packet->size < 2U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); return; } /* Check if this is a DP version packet and handle it if it is */ - if (packet[1] == REMOTE_DP_VERSION) { + if (packet->data[1] == REMOTE_DP_VERSION) { /* Check there are enough bytes for the request */ - if (packet_len == 4U) { + if (packet->size == 4U) { /* Extract the new version information into the DP */ - remote_dp.version = hex_string_to_num(2U, packet + 2U); + remote_dp.version = hex_string_to_num(2U, packet->data + 2U); remote_respond(REMOTE_RESP_OK, 0); } else /* There weren't enough bytes, so tell the host and get out of here */ @@ -480,11 +479,11 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p return; } /* Check if this is a DP targetsel packet and handle it if it is */ - if (packet[1] == REMOTE_DP_TARGETSEL) { + if (packet->data[1] == REMOTE_DP_TARGETSEL) { /* Check if there are enough bytes for the request */ - if (packet_len == 10U) { + if (packet->size == 10U) { /* Extract the new targetsel information into the DP */ - remote_dp.targetsel = hex_string_to_num(8U, packet + 2U); + remote_dp.targetsel = hex_string_to_num(8U, packet->data + 2U); remote_respond(REMOTE_RESP_OK, 0); } else /* There weren't enough bytes, so tell the host and get out of here */ @@ -493,30 +492,30 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } /* Our shortest ADIv5 packet is 8 bytes long, check that we have at least that */ - if (packet_len < 8U) { + if (packet->size < 8U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Check if this is actually an ADIv6 acceleration packet and dispatch */ - if (packet[1] == REMOTE_ADIV6_PACKET) { - remote_packet_process_adiv6(packet, packet_len); + if (packet->data[1] == REMOTE_ADIV6_PACKET) { + remote_packet_process_adiv6(packet); return; } /* Set up the DP and a fake AP structure to perform the access with */ - remote_dp.dev_index = hex_string_to_num(2, packet + 2); + remote_dp.dev_index = hex_string_to_num(2, packet->data + 2); remote_dp.fault = 0U; adiv5_access_port_s remote_ap; - remote_ap.apsel = hex_string_to_num(2, packet + 4); + remote_ap.apsel = hex_string_to_num(2, packet->data + 4); remote_ap.dp = &remote_dp; SET_IDLE_STATE(0); - switch (packet[1]) { + switch (packet->data[1]) { /* DP access commands */ case REMOTE_DP_READ: { /* Ad = Read from DP register */ /* Grab the address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); const uint32_t data = adiv5_dp_read(&remote_dp, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU)); remote_adiv5_respond(&data, 4U); @@ -525,8 +524,8 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Raw access comands */ case REMOTE_ADIV5_RAW_ACCESS: { /* AR = Perform a raw ADIv5 access */ /* Grab the address to perform an access against and the value to work with */ - const uint16_t addr = hex_string_to_num(4, packet + 6); - const uint32_t value = hex_string_to_num(8, packet + 10); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); + const uint32_t value = hex_string_to_num(8, packet->data + 10); /* Try to perform the access using the AP selection value as R/!W */ const uint32_t data = adiv5_dp_low_access( &remote_dp, remote_ap.apsel, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU), value); @@ -536,7 +535,7 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* AP access commands */ case REMOTE_AP_READ: { /* Aa = Read from AP register */ /* Grab the AP address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); const uint32_t data = adiv5_ap_read(&remote_ap, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU)); remote_adiv5_respond(&data, 4U); @@ -544,8 +543,8 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } case REMOTE_AP_WRITE: { /* AA = Write to AP register */ /* Grab the AP address to write to and the data to write then try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 6); - const uint32_t value = hex_string_to_num(8, packet + 10); + const uint16_t addr = hex_string_to_num(4, packet->data + 6); + const uint32_t value = hex_string_to_num(8, packet->data + 10); adiv5_ap_write(&remote_ap, (addr & REMOTE_ADIV5_APnDP ? ADIV5_APnDP : 0U) | (addr & 0x00ffU), value); remote_adiv5_respond(NULL, 0U); break; @@ -553,11 +552,11 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Memory access commands */ case REMOTE_MEM_READ: { /* Am = Read from memory */ /* Grab the CSW value to use in the access */ - remote_ap.csw = hex_string_to_num(8, packet + 6); + remote_ap.csw = hex_string_to_num(8, packet->data + 6); /* Grab the start address for the read */ - const target_addr64_t address = hex_string_to_num(16, packet + 14U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 14U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 30U); + const uint32_t length = hex_string_to_num(8, packet->data + 30U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_READ_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -572,13 +571,13 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p } case REMOTE_MEM_WRITE: { /* AM = Write to memory */ /* Grab the CSW value to use in the access */ - remote_ap.csw = hex_string_to_num(8, packet + 6); + remote_ap.csw = hex_string_to_num(8, packet->data + 6); /* Grab the alignment for the access */ - const align_e align = hex_string_to_num(2, packet + 14U); + const align_e align = hex_string_to_num(2, packet->data + 14U); /* Grab the start address for the write */ - const target_addr64_t address = hex_string_to_num(16, packet + 16U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 16U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 32U); + const uint32_t length = hex_string_to_num(8, packet->data + 32U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_WRITE_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -592,7 +591,7 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 40U, length); + unhexify(data, packet->data + 40U, length); /* Perform the write and report success/failures */ adiv5_mem_write_aligned(&remote_ap, address, data, length, align); remote_adiv5_respond(NULL, 0); @@ -606,10 +605,10 @@ static void remote_packet_process_adiv5(const char *const packet, const size_t p SET_IDLE_STATE(1); } -static void remote_packet_process_adiv6(const char *const packet, const size_t packet_len) +static void remote_packet_process_adiv6(gdb_packet_s *const packet) { /* Our shortest ADIv5 packet is 15 bytes long, check that we have at least that */ - if (packet_len < 15U) { + if (packet->size < 15U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } @@ -624,26 +623,26 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p dp.ap_write = adiv6_ap_reg_write; /* Set up the DP and a fake AP structure to perform the access with */ - remote_dp.dev_index = hex_string_to_num(2, packet + 3); + remote_dp.dev_index = hex_string_to_num(2, packet->data + 3); remote_dp.fault = 0U; adiv6_access_port_s remote_ap; - remote_ap.ap_address = hex_string_to_num(16, packet + 5); + remote_ap.ap_address = hex_string_to_num(16, packet->data + 5); remote_ap.base.dp = &dp; SET_IDLE_STATE(0); - switch (packet[2]) { + switch (packet->data[2]) { /* AP access commands */ case REMOTE_AP_READ: { /* A6a = Read from APv2 register */ /* Grab the AP address to read from and try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 21); + const uint16_t addr = hex_string_to_num(4, packet->data + 21); const uint32_t data = adiv5_ap_read(&remote_ap.base, addr); remote_adiv5_respond(&data, 4U); break; } case REMOTE_AP_WRITE: { /* A6A = Write to APv2 register */ /* Grab the AP address to write to and the data to write then try to perform the access */ - const uint16_t addr = hex_string_to_num(4, packet + 21); - const uint32_t value = hex_string_to_num(8, packet + 25); + const uint16_t addr = hex_string_to_num(4, packet->data + 21); + const uint32_t value = hex_string_to_num(8, packet->data + 25); adiv5_ap_write(&remote_ap.base, addr, value); remote_adiv5_respond(NULL, 0U); break; @@ -651,11 +650,11 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p /* Memory access commands */ case REMOTE_MEM_READ: { /* Am = Read from memory */ /* Grab the CSW value to use in the access */ - remote_ap.base.csw = hex_string_to_num(8, packet + 21); + remote_ap.base.csw = hex_string_to_num(8, packet->data + 21); /* Grab the start address for the read */ - const target_addr64_t address = hex_string_to_num(16, packet + 29U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 29U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 45U); + const uint32_t length = hex_string_to_num(8, packet->data + 45U); /* NB: Hex encoding on the response data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV6_MEM_READ_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -670,13 +669,13 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p } case REMOTE_MEM_WRITE: { /* AM = Write to memory */ /* Grab the CSW value to use in the access */ - remote_ap.base.csw = hex_string_to_num(8, packet + 21); + remote_ap.base.csw = hex_string_to_num(8, packet->data + 21); /* Grab the alignment for the access */ - const align_e align = hex_string_to_num(2, packet + 29U); + const align_e align = hex_string_to_num(2, packet->data + 29U); /* Grab the start address for the write */ - const target_addr64_t address = hex_string_to_num(16, packet + 31U); + const target_addr64_t address = hex_string_to_num(16, packet->data + 31U); /* And how many bytes to read, validating it for buffer overflows */ - const uint32_t length = hex_string_to_num(8, packet + 47U); + const uint32_t length = hex_string_to_num(8, packet->data + 47U); /* NB: Hex encoding on the request data halfs the available buffer capacity */ if (length > (GDB_PACKET_BUFFER_SIZE - REMOTE_ADIV5_MEM_WRITE_LENGTH) >> 1U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -690,7 +689,7 @@ static void remote_packet_process_adiv6(const char *const packet, const size_t p /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 55U, length); + unhexify(data, packet->data + 55U, length); /* Perform the write and report success/failures */ adiv5_mem_write_aligned(&remote_ap.base, address, data, length, align); remote_adiv5_respond(NULL, 0); @@ -718,33 +717,33 @@ static riscv_dmi_s remote_dmi = { .write = NULL, }; -void remote_packet_process_riscv(const char *const packet, const size_t packet_len) +void remote_packet_process_riscv(gdb_packet_s *const packet) { /* Our shortest RISC-V Debug protocol packet is 2 bytes long, check that we have at least that */ - if (packet_len < 2U) { + if (packet->size < 2U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Check for and handle the protocols packet */ - if (packet[1U] == REMOTE_RISCV_PROTOCOLS) { + if (packet->data[1U] == REMOTE_RISCV_PROTOCOLS) { /* Validate the length of the packet, then handle it if that checks out */ - if (packet_len != 2U) + if (packet->size != 2U) remote_respond(REMOTE_RESP_PARERR, 0); else remote_respond(REMOTE_RESP_OK, REMOTE_RISCV_PROTOCOL_JTAG); return; } /* Check for and handle the initialisation packet */ - else if (packet[1U] == REMOTE_INIT) { + else if (packet->data[1U] == REMOTE_INIT) { /* Check the length of the packet */ - if (packet_len != 3U) { + if (packet->size != 3U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* We got a good packet, so handle initialisation accordingly */ - switch (packet[2U]) { + switch (packet->data[2U]) { case REMOTE_RISCV_JTAG: remote_dmi.read = riscv_jtag_dmi_read; remote_dmi.write = riscv_jtag_dmi_write; @@ -758,21 +757,21 @@ void remote_packet_process_riscv(const char *const packet, const size_t packet_l return; } /* Our shortest RISC-V protocol packet is 16 bytes long, check that we have at least that */ - else if (packet_len < 16U) { + else if (packet->size < 16U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } /* Having dealt with the other requests, set up the fake DMI structure to perform the access with */ - remote_dmi.dev_index = hex_string_to_num(2, packet + 2); - remote_dmi.idle_cycles = hex_string_to_num(2, packet + 4); - remote_dmi.address_width = hex_string_to_num(2, packet + 6); + remote_dmi.dev_index = hex_string_to_num(2, packet->data + 2); + remote_dmi.idle_cycles = hex_string_to_num(2, packet->data + 4); + remote_dmi.address_width = hex_string_to_num(2, packet->data + 6); remote_dmi.fault = 0U; - switch (packet[1U]) { + switch (packet->data[1U]) { case REMOTE_RISCV_DMI_READ: { /* Grab the DMI address to read from and try to perform the access */ - const uint32_t addr = hex_string_to_num(8, packet + 8); + const uint32_t addr = hex_string_to_num(8, packet->data + 8); uint32_t value = 0; if (!remote_dmi.read(&remote_dmi, addr, &value)) /* If the request didn't work, and caused a fault, tell the host */ @@ -784,13 +783,13 @@ void remote_packet_process_riscv(const char *const packet, const size_t packet_l } case REMOTE_RISCV_DMI_WRITE: { /* Write packets are 24 bytes long, verify we have enough bytes */ - if (packet_len != 24U) { + if (packet->size != 24U) { remote_respond(REMOTE_RESP_PARERR, 0); break; } /* Grab the DMI address to write to and the data to write then try to perform the access */ - const uint32_t addr = hex_string_to_num(8, packet + 8); - const uint32_t value = hex_string_to_num(8, packet + 16); + const uint32_t addr = hex_string_to_num(8, packet->data + 8); + const uint32_t value = hex_string_to_num(8, packet->data + 16); if (!remote_dmi.write(&remote_dmi, addr, value)) /* If the request didn't work, and caused a fault, tell the host */ remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_FAULT | ((uint16_t)remote_dmi.fault << 8U)); @@ -816,17 +815,17 @@ static void remote_spi_respond(const bool result) remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_FAULT); } -void remote_packet_process_spi(const char *const packet, const size_t packet_len) +void remote_packet_process_spi(gdb_packet_s *const packet) { /* Our shortest SPI packet is 4 bytes long, check that we have at least that */ - if (packet_len < 4U) { + if (packet->size < 4U) { remote_respond(REMOTE_RESP_PARERR, 0); return; } - const uint8_t spi_bus = hex_string_to_num(2, packet + 2); + const uint8_t spi_bus = hex_string_to_num(2, packet->data + 2); - switch (packet[1]) { + switch (packet->data[1]) { /* Bus initialisation/deinitialisation commands */ case REMOTE_SPI_BEGIN: remote_spi_respond(platform_spi_init(spi_bus)); @@ -841,7 +840,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len break; /* Performs a single byte SPI transfer */ case REMOTE_SPI_TRANSFER: { - const uint8_t data_in = hex_string_to_num(2, packet + 4); + const uint8_t data_in = hex_string_to_num(2, packet->data + 4); const uint8_t data_out = platform_spi_xfer(spi_bus, data_in); remote_respond(REMOTE_RESP_OK, data_out); break; @@ -852,10 +851,10 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len * Decode the device to talk to, what command to send, and the addressing * and length information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); - const size_t length = hex_string_to_num(4, packet + 16); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); + const size_t length = hex_string_to_num(4, packet->data + 16); /* Validate the data length isn't overly long */ if (length > 256U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -874,10 +873,10 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len * Decode the device to talk to, what command to send, and the addressing * and length information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); - const size_t length = hex_string_to_num(4, packet + 16); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); + const size_t length = hex_string_to_num(4, packet->data + 16); /* Validate the data length isn't overly long */ if (length > 256U) { remote_respond(REMOTE_RESP_PARERR, 0); @@ -886,7 +885,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Get the aligned packet buffer to reuse for the data to write */ void *data = gdb_packet_buffer(); /* And decode the data from the packet into it */ - unhexify(data, packet + 20U, length); + unhexify(data, packet->data + 20U, length); /* Perform the write cycle */ bmp_spi_write(spi_bus, spi_device, command, address, data, length); remote_respond(REMOTE_RESP_OK, 0); @@ -895,7 +894,7 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Get the JEDEC device ID for a Flash device */ case REMOTE_SPI_CHIP_ID: { /* Decoder the device to talk to */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); /* Set up a suitable buffer for and read the JEDEC ID */ spi_flash_id_s flash_id; bmp_spi_read(spi_bus, spi_device, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); @@ -906,9 +905,9 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len /* Run a command against a SPI Flash device */ case REMOTE_SPI_RUN_COMMAND: { /* Decode the device to talk to, what command to send, and the addressing information for that command */ - const uint8_t spi_device = hex_string_to_num(2, packet + 4); - const uint16_t command = hex_string_to_num(4, packet + 6); - const target_addr_t address = hex_string_to_num(6, packet + 10); + const uint8_t spi_device = hex_string_to_num(2, packet->data + 4); + const uint16_t command = hex_string_to_num(4, packet->data + 6); + const target_addr_t address = hex_string_to_num(6, packet->data + 10); /* Execute the command and signal success */ bmp_spi_run_command(spi_bus, spi_device, command, address); remote_respond(REMOTE_RESP_OK, 0); @@ -920,34 +919,34 @@ void remote_packet_process_spi(const char *const packet, const size_t packet_len } } -void remote_packet_process(char *const packet, const size_t packet_length) +void remote_packet_process(gdb_packet_s *const packet) { /* Check there's at least a request byte */ - if (packet_length < 1U) { + if (packet->size < 1U) { remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_WRONGLEN); return; } - switch (packet[0]) { + switch (packet->data[0]) { case REMOTE_SWDP_PACKET: - remote_packet_process_swd(packet, packet_length); + remote_packet_process_swd(packet); break; case REMOTE_JTAG_PACKET: - remote_packet_process_jtag(packet, packet_length); + remote_packet_process_jtag(packet); break; case REMOTE_GEN_PACKET: - remote_packet_process_general(packet, packet_length); + remote_packet_process_general(packet); break; case REMOTE_HL_PACKET: - remote_packet_process_high_level(packet, packet_length); + remote_packet_process_high_level(packet); break; case REMOTE_ADIV5_PACKET: { /* Setup an exception frame to try the ADIv5 operation in */ TRY (EXCEPTION_ALL) { - remote_packet_process_adiv5(packet, packet_length); + remote_packet_process_adiv5(packet); } CATCH () { /* Handle any exception we've caught by translating it into a remote protocol response */ @@ -959,12 +958,12 @@ void remote_packet_process(char *const packet, const size_t packet_length) #if defined(CONFIG_RISCV_ACCEL) && CONFIG_RISCV_ACCEL == 1 case REMOTE_RISCV_PACKET: - remote_packet_process_riscv(packet, packet_length); + remote_packet_process_riscv(packet); break; #endif case REMOTE_SPI_PACKET: - remote_packet_process_spi(packet, packet_length); + remote_packet_process_spi(packet); break; default: /* Oh dear, unrecognised, return an error */ diff --git a/src/remote.h b/src/remote.h index 66094206885..520cb8a3a64 100644 --- a/src/remote.h +++ b/src/remote.h @@ -25,6 +25,7 @@ #include #include "general.h" +#include "gdb_packet.h" #define REMOTE_HL_VERSION 4 @@ -512,6 +513,6 @@ REMOTE_UINT24, REMOTE_EOM, 0 \ } -void remote_packet_process(char *packet, size_t packet_length); +void remote_packet_process(gdb_packet_s *packet); #endif /* REMOTE_H */ diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 8712e2beeb2..963af409199 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -440,10 +440,26 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) adiv5_dp_clear_sticky_errors(dp); if (dp->version >= 2) { - /* TARGETID is on bank 2 */ - adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK2); - const uint32_t targetid = adiv5_dp_read(dp, ADIV5_DP_TARGETID); - adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK0); + uint32_t targetid = 0U; + uint8_t read_attempts = 0U; + /* + * Retry reading TARGETID until partno is non zero + * On some Nordic devices the TARGETID register isn't fully read on the first attempt + * resulting in the designer code being set while the part no. is still 0x0 + */ + while (targetid == 0U || dp->target_partno == 0U) { + /* TARGETID is on bank 2 */ + adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK2); + targetid = adiv5_dp_read(dp, ADIV5_DP_TARGETID); + adiv5_dp_write(dp, ADIV5_DP_SELECT, ADIV5_DP_BANK0); + + dp->target_partno = (targetid & ADIV5_DP_TARGETID_TPARTNO_MASK) >> ADIV5_DP_TARGETID_TPARTNO_OFFSET; + if (++read_attempts >= 128U && dp->target_partno == 0U) { + DEBUG_WARN("Failed to read TARGETID partno after 128 attempts\n"); + break; + } + + }; /* * Use TARGETID register to identify target and convert it @@ -451,9 +467,6 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp) */ dp->target_designer_code = adi_decode_designer((targetid & ADIV5_DP_TARGETID_TDESIGNER_MASK) >> ADIV5_DP_TARGETID_TDESIGNER_OFFSET); - - dp->target_partno = (targetid & ADIV5_DP_TARGETID_TPARTNO_MASK) >> ADIV5_DP_TARGETID_TPARTNO_OFFSET; - DEBUG_INFO("TARGETID 0x%08" PRIx32 " designer 0x%x partno 0x%x\n", targetid, dp->target_designer_code, dp->target_partno); 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) { diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 280163f71a2..ce59bf5c7ba 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -524,9 +524,13 @@ bool cortexm_attach(target_s *target) /* Try to halt the core, and then check that it worked (which also resets the halt reason) */ target_halt_request(target); - const target_halt_reason_e halt_result = target_halt_poll(target, NULL); - /* If we failed to halt the target somehow, bail */ - if (halt_result == TARGET_HALT_ERROR || halt_result == TARGET_HALT_RUNNING) + platform_timeout_s timeout; + platform_timeout_set(&timeout, 250); + target_halt_reason_e reason = TARGET_HALT_RUNNING; + while (!platform_timeout_is_expired(&timeout) && reason == TARGET_HALT_RUNNING) + reason = target_halt_poll(target, NULL); + /* If we did not succeed, we must abort at this point. */ + if (reason == TARGET_HALT_FAULT || reason == TARGET_HALT_ERROR) return false; /* Request halt on reset */ @@ -570,7 +574,6 @@ bool cortexm_attach(target_s *target) (void)target_mem32_read32(target, CORTEXM_DHCSR); if (target_mem32_read32(target, CORTEXM_DHCSR) & CORTEXM_DHCSR_S_RESET_ST) { platform_nrst_set_val(false); - platform_timeout_s timeout; platform_timeout_set(&timeout, 1000); while (true) { const uint32_t reset_status = target_mem32_read32(target, CORTEXM_DHCSR); @@ -864,21 +867,21 @@ static target_halt_reason_e cortexm_halt_poll(target_s *target, target_addr64_t priv->dcache_enabled = ccr & CORTEXM_CCR_DCACHE_ENABLE; priv->icache_enabled = ccr & CORTEXM_CCR_ICACHE_ENABLE; - bool fault_state = false; - // the V8 may stop before actually executing the instruction - // so reading dfsr might not work. - // Instead, we check if there are pending faults on ICSR - // meaning we stopped while trying to execute a fault - // but maybe did not execute it - if ((target->target_options & CORTEXM_TOPT_FLAVOUR_V8M)) { + bool fault = false; + /* + * On ARMv8-M, execution may stop before actually retiring the instruction related to a fault, + * so reading DFSR might not work - instead we check if there are pending faults in ICSR, + * meaning we stopped while trying to execute a faulting instruction but maybe that didn't retire + */ + if (target->target_options & CORTEXM_TOPT_FLAVOUR_V8M) { const uint32_t icsr = target_mem32_read32(target, CORTEXM_ICSR); const uint32_t pending = CORTEXM_ICSR_VEC_PENDING(icsr); - // catch all pending faults - if (pending > 0U && pending < 8U) - fault_state = true; + /* Catch all pending exceptions, but not IRQs */ + fault = pending > 0U && pending < 8U; } else - fault_state = (dfsr & CORTEXM_DFSR_VCATCH) != 0U; - if (fault_state && cortexm_fault_unwind(target)) + fault = (dfsr & CORTEXM_DFSR_VCATCH) != 0U; + /* If there was a fault of some kind, unwind and report */ + if (fault && cortexm_fault_unwind(target)) return TARGET_HALT_FAULT; /* Remember if we stopped on a breakpoint */ diff --git a/src/target/flashstub/meson.build b/src/target/flashstub/meson.build index d56822d403d..8b9ef16058c 100644 --- a/src/target/flashstub/meson.build +++ b/src/target/flashstub/meson.build @@ -32,6 +32,7 @@ lmi_stub = [] efm32_stub = [] rp2040_stub = [] +mspm0_stub = [] # If we're doing a firmware build, type to find hexdump and objcopy if is_firmware_build @@ -189,3 +190,35 @@ rp2040_stub = custom_target( output: 'rp.stub', capture: true, ) + +# Flash stub for MSPM0 parts +mspm0_stub_elf = executable( + 'mspm0_stub.elf', + 'mspm0.c', + c_args: [ + '-mcpu=cortex-m0plus', + stub_build_args + ], + link_args: [ + '-mcpu=cortex-m0plus', + stub_build_args, + '-T', '@0@/mspm0.ld'.format(meson.current_source_dir()), + ], + link_depends: files('mspm0.ld'), + pie: false, + install: false, +) + +mspm0_stub_bin = custom_target( + command: [ objcopy, '-O', 'binary', '@INPUT@', '@OUTPUT@' ], + input: mspm0_stub_elf, + output: 'mspm0_stub.bin' +) + +mspm0_stub = custom_target( + 'mspm0_stub-hex', + command: [ hexdump, '-v', '-e', '/2 "0x%04X, "' , '@INPUT@' ], + input: mspm0_stub_bin, + output: 'mspm0.stub', + capture: true, +) diff --git a/src/target/flashstub/mspm0.c b/src/target/flashstub/mspm0.c new file mode 100644 index 00000000000..10544b3f43c --- /dev/null +++ b/src/target/flashstub/mspm0.c @@ -0,0 +1,81 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by hardesk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "stub.h" + +#define MSPM0_FLASH_MAIN 0x00000000U +#define MSPM0_FLASH_SECTOR_SZ 1024U + +#define MSPM0_FLASHCTL_BASE 0x400cd000U +#define MSPM0_FLASHCTL_CMDEXEC *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1100U)) +#define MSPM0_FLASHCTL_CMDTYPE *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1104U)) +#define MSPM0_FLASHCTL_CMDCTL *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1108U)) +#define MSPM0_FLASHCTL_CMDADDR *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1120U)) +#define MSPM0_FLASHCTL_BYTEN *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1124U)) +#define MSPM0_FLASHCTL_STATCMD *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x13d0U)) +#define MSPM0_FLASHCTL_CMDDATA0 *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1130U)) +#define MSPM0_FLASHCTL_CMDDATA1 *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x1134U)) +#define MSPM0_FLASHCTL_CMDWEPROTA *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d0U)) +#define MSPM0_FLASHCTL_CMDWEPROTB *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d4U)) +#define MSPM0_FLASHCTL_CMDWEPROTC *((volatile uint32_t *)(MSPM0_FLASHCTL_BASE + 0x11d8U)) +#define MSPM0_FLASHCTL_CMDTYPE_PROG 1U +#define MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD (0U << 4U) +#define MSPM0_FLASHCTL_CMDEXEC_EXEC 1U +#define MSPM0_FLASHCTL_STATCMD_DONE 0x01U +#define MSPM0_FLASHCTL_STATCMD_CMDPASS 0x02U + +char __attribute__((aligned(0x04))) stub_stack[0x20]; + +void mspm0_flash_writer(const uint32_t *const dest, const uint32_t *const src, const uint32_t size) +{ + for (uint32_t i = 0U; i < size / 4U; i += 2U) { + uint32_t addr = (uint32_t)(dest + i); + uint32_t sector = (addr - MSPM0_FLASH_MAIN) / MSPM0_FLASH_SECTOR_SZ; + + if (sector < 32U) + MSPM0_FLASHCTL_CMDWEPROTA = ~(1U << sector); + else if (sector < 256U) + MSPM0_FLASHCTL_CMDWEPROTB = 0U; + else + MSPM0_FLASHCTL_CMDWEPROTC = 0U; + + MSPM0_FLASHCTL_CMDCTL = 0U; + MSPM0_FLASHCTL_BYTEN = 0xffffffffU; + MSPM0_FLASHCTL_CMDTYPE = MSPM0_FLASHCTL_CMDTYPE_PROG | MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD; + + MSPM0_FLASHCTL_CMDADDR = addr; + MSPM0_FLASHCTL_CMDDATA0 = src[i]; + MSPM0_FLASHCTL_CMDDATA1 = src[i + 1U]; + MSPM0_FLASHCTL_CMDEXEC = MSPM0_FLASHCTL_CMDEXEC_EXEC; + while (!(MSPM0_FLASHCTL_STATCMD & MSPM0_FLASHCTL_STATCMD_DONE)) + continue; + if (!(MSPM0_FLASHCTL_STATCMD & MSPM0_FLASHCTL_STATCMD_CMDPASS)) + stub_exit(0); + } + + stub_exit(1); +} + +void __attribute__((naked, used, section(".entry"))) mspm0_flash_write_entry( + const uint32_t *const dest, const uint32_t *const src, const uint32_t size) +{ + __asm volatile("msr msp, %0" : : "r"(stub_stack + sizeof(stub_stack)) :); + mspm0_flash_writer(dest, src, size); +} \ No newline at end of file diff --git a/src/target/flashstub/mspm0.ld b/src/target/flashstub/mspm0.ld new file mode 100644 index 00000000000..622de40f83d --- /dev/null +++ b/src/target/flashstub/mspm0.ld @@ -0,0 +1,13 @@ +MEMORY { sram (rwx): ORIGIN = 0x20000000, LENGTH = 0x00000400 } + +SECTIONS +{ + .text : + { + KEEP(*(.entry)) + *(.text.*, .text) + /* include data so we store stack area in objcopy's output */ + *(.data) + *(.bss) + } > sram +} diff --git a/src/target/flashstub/mspm0.stub b/src/target/flashstub/mspm0.stub new file mode 100644 index 00000000000..35dbaa08eaf --- /dev/null +++ b/src/target/flashstub/mspm0.stub @@ -0,0 +1 @@ +0x4B02, 0xF383, 0x8808, 0xF000, 0xF803, 0x46C0, 0x00D8, 0x2000, 0xB5F7, 0x0893, 0x9301, 0x2380, 0x2580, 0x021B, 0x2601, 0x469C, 0x2200, 0x2301, 0x02ED, 0x4276, 0x9C01, 0x4294, 0xD801, 0xBE01, 0xBDF7, 0x4560, 0xD222, 0x001F, 0x0A84, 0x40A7, 0x43FC, 0x4F13, 0x603C, 0x2700, 0x4C12, 0x6027, 0x4C12, 0x6026, 0x4C12, 0x6023, 0x4C12, 0x6020, 0x680F, 0x4C12, 0x6027, 0x684F, 0x4C11, 0x6027, 0x4C11, 0x6023, 0x4C11, 0x6827, 0x421F, 0xD0FC, 0x6824, 0x07A4, 0xD400, 0xBE00, 0x3202, 0x3008, 0x3108, 0xE7D5, 0x2400, 0x42A8, 0xD201, 0x4F0B, 0xE7DC, 0x4F0B, 0xE7DA, 0x46C0, 0xE1D0, 0x400C, 0xE108, 0x400C, 0xE124, 0x400C, 0xE104, 0x400C, 0xE120, 0x400C, 0xE130, 0x400C, 0xE134, 0x400C, 0xE100, 0x400C, 0xE3D0, 0x400C, 0xE1D4, 0x400C, 0xE1D8, 0x400C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index 1830de43e86..01c5769e727 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -419,6 +419,14 @@ const jtag_dev_descr_s dev_descr[] = { .idmask = 0x0fffffffU, #if ENABLE_DEBUG == 1 .descr = "Nuclei Systems RISC-V DTM.", +#endif + .handler = riscv_jtag_dtm_handler, + }, + { + .idcode = 0xdeadbeefU, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "Hazard3 RISC-V DTM.", #endif .handler = riscv_jtag_dtm_handler, }, diff --git a/src/target/meson.build b/src/target/meson.build index be10d0d317a..48c5e74bda0 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -370,7 +370,7 @@ target_ti_cortexm = declare_dependency( 'msp432e4.c', 'msp432p4.c', 'mspm0.c' - ) + lmi_stub, + ) + lmi_stub + mspm0_stub, compile_args: ['-DCONFIG_TI=1'], dependencies: target_cortexm, ) diff --git a/src/target/mspm0.c b/src/target/mspm0.c index 922ab044a6a..6c300d59f23 100644 --- a/src/target/mspm0.c +++ b/src/target/mspm0.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2024 hardesk + * Copyright (C) 2024-2026 hardesk * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,21 +22,15 @@ #include "target_internal.h" #include "buffer_utils.h" #include "jep106.h" -#include "cortex.h" +#include "cortexm.h" -#define MSPM0_CONFIG_FLASH_DUMP_SUPPORT (CONFIG_BMDA == 1 || ENABLE_DEBUG == 1) - -#define TI_DEVID_MSPM0C 0xbba1U /* MSPM0C110[34] */ -#define TI_DEVID_MSPM0L 0xbb82U /* MSPM0L110[56], MSPM0L13[04][456] */ -#define TI_DEVID_MSPM0L_1227_2228 0xbb9fU /* MSPM0L[12]22[78]*/ -#define TI_DEVID_MSPM0G 0xbb88U /* MSPM0G310[567], MSPM0G150[567], MSPM0G350[567] */ - -#define MSPM0_SRAM_BASE 0x20000000U -#define MSPM0_FLASH_MAIN 0x00000000U -#define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ -#define MSPM0_FLASH_FACTORY 0x41c40000U /* One Sector, BANK0. Non modifiable */ -#define MSPM0_FLASH_DATA 0x41d00000U -#define MSPM0_FLASH_SECTOR_SZ 1024U +#define MSPM0_SRAM_BASE 0x20000000U +#define MSPM0_FLASH_MAIN 0x00000000U +#define MSPM0_FLASH_NONMAIN 0x41c00000U /* One Sector, BANK0. Device boot configuration (BCR, BSL) */ +#define MSPM0_FLASH_FACTORY 0x41c40000U /* One Sector, BANK0. Non modifiable */ +#define MSPM0_FLASH_DATA 0x41d00000U +#define MSPM0_FLASH_SECTOR_SZ 1024U +#define MSPM0_FLASH_WRITE_CHUNK_SZ MSPM0_FLASH_SECTOR_SZ #define MSPM0_FACTORYREGION_DEVICEID (MSPM0_FLASH_FACTORY + 0x4U) #define MSPM0_FACTORYREGION_SRAMFLASH (MSPM0_FLASH_FACTORY + 0x18U) @@ -91,101 +85,33 @@ typedef struct mspm0_flash { uint32_t banks; } mspm0_flash_s; -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT -static bool mspm0_dump_factory_config(target_s *target, int argc, const char **argv); -static bool mspm0_dump_bcr_config(target_s *target, int argc, const char **argv); +static uint16_t mspm0_partnums[] = { + 0xbba1U, /* MSPM0C: 1103 1104 1103-Q1 1104-Q1 */ + 0x0bbbU, /* MSPM0C: 1105-Q1 1106-Q1 */ + 0xbbbaU, /* MSPM0C: 1105 1106 */ + 0xbb82U, /* MSPM0L: 1105 1106 1304 1305 1305 1344 1345 1346 1345-Q1 1346-Q1 */ + 0xbb9fU, /* MSPM0L: 1227 1228 2227 2228 1227-Q1 1228-Q1 2227-Q1 2228-Q1 */ + 0xbbb4U, /* MSPM0L: 1116 1117 1116-Q1 1117-Q1 */ + 0xbbc7U, /* MSPM0L: 1126 1127 2116 2117 */ + 0x0bbaU, /* MSPM0H: 3215 3216 */ + 0xbb88U, /* MSPM0G: 1105 1106 1107 1505 1506 1507 3105 3106 3107 3505 3506 3507 3105-Q1 3106-Q1 3107-Q1 */ + /* 3505-Q1 3506-Q1 3507-Q1 */ + 0xbba9U, /* MSPM0G: 1518 1519 3518 3519 3518-Q1 3519-Q1 3529-Q1 */ + 0xbbbcU, /* MSPM0G: 5187 */ + 0xbbceU, /* MSPM0G: 1207 1218 3207 3218 */ +}; -static command_s mspm0_cmds_list[] = { - {"dump_factory", mspm0_dump_factory_config, "Display FACTORY registers"}, - {"dump_bcr", mspm0_dump_bcr_config, "Display NONMAIN (BCR/BSL) registers"}, - {NULL, NULL, NULL}, +static const uint16_t mspm0_flash_write_stub[] = { +#include "flashstub/mspm0.stub" }; -#endif +#define STUB_BUFFER_BASE ALIGN(MSPM0_SRAM_BASE + sizeof(mspm0_flash_write_stub), 4) static bool mspm0_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length); static bool mspm0_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length); static bool mspm0_mass_erase(target_s *target, platform_timeout_s *print_progess); -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT -typedef struct conf_register { - uint16_t reg_offset; - uint16_t size_words; - const char *id; -} conf_register_s; - -static conf_register_s mspm0_factory_regs[] = { - {0x00U, 1U, "TRACEID"}, - {0x04U, 1U, "DEVICEID"}, - {0x08U, 1U, "USERID"}, - {0x0cU, 1U, "BSLPIN_UART"}, - {0x10U, 1U, "BSLPIN_I2C"}, - {0x14U, 1U, "BSLPIN_INVOKE"}, - {0x18U, 1U, "SRAMFLASH"}, - {0x3cU, 1U, "TEMP_SENSE0"}, - {0x7cU, 1U, "BOOTCRC"}, - {0U, 0U, NULL}, -}; - -static conf_register_s mspm0_bcr_regs[] = { - {0x00U, 1U, "BCRCONFIGID"}, - {0x04U, 1U, "BOOTCFG0"}, - {0x08U, 1U, "BOOTCFG1"}, - {0x0cU, 4U, "PWDDEBUGLOCK"}, - {0x1cU, 4U, "BOOTCFG2"}, - {0x20U, 1U, "BOOTCFG3"}, - {0x24U, 4U, "PWDMASSERASE"}, - {0x34U, 4U, "PWDFACTORYRESET"}, - {0x44U, 1U, "FLASHSWP0"}, - {0x48U, 1U, "FLASHSWP1"}, - {0x4cU, 1U, "BOOTCFG4"}, - {0x50U, 1U, "APPCRCSTART"}, - {0x54U, 1U, "APPCRCLENGTH"}, - {0x58U, 1U, "APPCRC"}, - {0x5cU, 1U, "BOOTCRC"}, - {0x100U, 1U, "BSLCONFIGID"}, - {0x104U, 1U, "BSLPINCFG0"}, - {0x108U, 1U, "BSLPINCFG1"}, - {0x10cU, 1U, "BSLCONFIG0"}, - {0x110U, 8U, "BSLPW"}, - {0x130U, 1U, "BSLPLUGINCFG"}, - {0x134U, 4U, "BSLPLUGINHOOK"}, - {0x144U, 1U, "PATCHHOOKID"}, - {0x148U, 1U, "SBLADDRESS"}, - {0x14cU, 1U, "BSLAPPVER"}, - {0x150U, 1U, "BSLCONFIG1"}, - {0x154U, 1U, "BSLCRC"}, - {0U, 0U, NULL}, -}; - -static void mspm0_dump_regs(target_s *const target, const conf_register_s *const regs, const uint32_t base) -{ - for (const conf_register_s *reg = regs; reg->id; ++reg) { - tc_printf(target, "%15s: ", reg->id); - for (size_t i = 0; i < reg->size_words; ++i) { - uint32_t value = target_mem32_read32(target, base + reg->reg_offset + (uint32_t)(i * 4U)); - tc_printf(target, "0x%08" PRIx32 "%s", value, i == reg->size_words - 1U ? "\n" : " "); - } - } -} - -static bool mspm0_dump_factory_config(target_s *const target, const int argc, const char **const argv) -{ - (void)argc; - (void)argv; - mspm0_dump_regs(target, mspm0_factory_regs, MSPM0_FLASH_FACTORY); - return true; -} - -static bool mspm0_dump_bcr_config(target_s *const target, const int argc, const char **const argv) -{ - (void)argc; - (void)argv; - mspm0_dump_regs(target, mspm0_bcr_regs, MSPM0_FLASH_NONMAIN); - return true; -} -#endif - -static void mspm0_add_flash(target_s *const target, const uint32_t base, const size_t length, const uint32_t banks) +static void mspm0_add_flash( + target_s *const target, const uint32_t base, const size_t length, const uint32_t banks, uint32_t write_size) { mspm0_flash_s *const flash = calloc(1, sizeof(*flash)); if (flash == NULL) { @@ -198,7 +124,7 @@ static void mspm0_add_flash(target_s *const target, const uint32_t base, const s target_flash->start = base; target_flash->length = length; target_flash->blocksize = MSPM0_FLASH_SECTOR_SZ; - target_flash->writesize = 8U; + target_flash->writesize = write_size; target_flash->erase = mspm0_flash_erase; target_flash->write = mspm0_flash_write; target_flash->erased = 0xffU; @@ -208,14 +134,17 @@ static void mspm0_add_flash(target_s *const target, const uint32_t base, const s bool mspm0_probe(target_s *const target) { const uint32_t deviceid = target_mem32_read32(target, MSPM0_FACTORYREGION_DEVICEID); - const uint32_t manufacturer = (deviceid & MSPM0_DEVICEID_MANUFACTURER_MASK) >> MSPM0_DEVICEID_MANUFACTURER_SHIFT; if (manufacturer != JEP106_MANUFACTURER_TEXAS) return false; const uint32_t partnum = (deviceid & MSPM0_DEVICEID_PARTNUM_MASK) >> MSPM0_DEVICEID_PARTNUM_SHIFT; - if (partnum != TI_DEVID_MSPM0C && partnum != TI_DEVID_MSPM0L && partnum != TI_DEVID_MSPM0L_1227_2228 && - partnum != TI_DEVID_MSPM0G) + size_t partnum_idx = 0; + for (; partnum_idx < ARRAY_LENGTH(mspm0_partnums); ++partnum_idx) { + if (partnum == mspm0_partnums[partnum_idx]) + break; + } + if (partnum_idx >= ARRAY_LENGTH(mspm0_partnums)) return false; target->driver = "MSPM0"; @@ -235,18 +164,21 @@ bool mspm0_probe(target_s *const target) MSPM0_FACTORYREGION_SRAMFLASH_DATAFLASH_SZ_SHIFT); target_add_ram32(target, MSPM0_SRAM_BASE, sram_size); - mspm0_add_flash(target, MSPM0_FLASH_MAIN, mainflash_size, main_num_banks); - if (dataflash_size != 0) - mspm0_add_flash(target, MSPM0_FLASH_DATA, dataflash_size, 1U); -#if MSPM0_CONFIG_FLASH_DUMP_SUPPORT - target_add_commands(target, mspm0_cmds_list, "MSPM0"); -#endif + /* Decrease flash write size until it fits within available RAM */ + uint32_t write_size = MSPM0_FLASH_WRITE_CHUNK_SZ; + uint32_t avail_ram = sram_size - (STUB_BUFFER_BASE - MSPM0_SRAM_BASE); + while (write_size > avail_ram) + write_size >>= 1U; + + mspm0_add_flash(target, MSPM0_FLASH_MAIN, mainflash_size, main_num_banks, write_size); + if (dataflash_size != 0) + mspm0_add_flash(target, MSPM0_FLASH_DATA, dataflash_size, 1U, write_size); return true; } -/* Wait for flash command to finish and return the status word or UINT32_MAX if timout */ +/* Wait for a flash command to finish and return the status word or 0 on timeout */ static uint32_t mspm0_flash_wait_done(target_s *const target) { platform_timeout_s timeout; @@ -257,7 +189,7 @@ static uint32_t mspm0_flash_wait_done(target_s *const target) status = target_mem32_read32(target, MSPM0_FLASHCTL_STATCMD); if (platform_timeout_is_expired(&timeout)) return 0U; - }; + } return status; } @@ -278,8 +210,12 @@ static void mspm0_flash_unprotect_sector(target_flash_s *const target_flash, con uint32_t mask = ~(1U << sector); target_mem32_write32(target_flash->t, MSPM0_FLASHCTL_CMDWEPROTA, mask); } else if (sector < 256U) { /* 8 sectors per bit */ - /* When main flash is single bank, PROTB covers sectors starting after PROTA which is 32k. In multibank case - * PROTB bits overlap PROTA and starts at sector 0. */ + /* + * Sectors affected by PROTB depend on the flash configuration. In single-bank + * main flash, PROTB applies to sectors after those affected by PROTA + * (that is, starting at sector 32). In multi-bank configurations, PROTA overlaps + * PROTB, so PROTB applies starting at sector 0. + */ uint32_t start_protb_sector = mspm0_flash->banks > 1U ? 0U : 32U; uint32_t mask = ~(1U << ((sector - start_protb_sector) >> 3U)); target_mem32_write32(target_flash->t, MSPM0_FLASHCTL_CMDWEPROTB, mask); @@ -316,27 +252,14 @@ static bool mspm0_flash_erase(target_flash_s *const target_flash, const target_a static bool mspm0_flash_write( target_flash_s *const target_flash, target_addr_t dest, const void *const src, const size_t length) { -#ifdef DEBUG_TARGET_IS_NOOP - (void)length; -#endif - target_s *const target = target_flash->t; + DEBUG_TARGET( + "%s: Writing flash addr %08" PRIx32 " length %08" PRIx32 "\n", __func__, (uint32_t)dest, (uint32_t)length); - mspm0_flash_unprotect_sector(target_flash, dest); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDTYPE, MSPM0_FLASHCTL_CMDTYPE_PROG | MSPM0_FLASHCTL_CMDTYPE_SZ_1WORD); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDCTL, 0U); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDADDR, dest); - target_mem32_write32(target, MSPM0_FLASHCTL_BYTEN, 0xffffffffU); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDDATA0, read_le4((const uint8_t *)src, 0U)); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDDATA1, read_le4((const uint8_t *)src, 4U)); - target_mem32_write32(target, MSPM0_FLASHCTL_CMDEXEC, MSPM0_FLASHCTL_CMDEXEC_EXEC); - - const uint32_t status = mspm0_flash_wait_done(target); - if (!(status & MSPM0_FLASHCTL_STAT_CMDPASS)) - DEBUG_TARGET("%s: Failed to write to flash, status %08" PRIx32 " addr %08" PRIx32 " length %08" PRIx32 "\n", - __func__, status, dest, (uint32_t)length); + target_mem32_write(target, MSPM0_SRAM_BASE, mspm0_flash_write_stub, sizeof(mspm0_flash_write_stub)); + target_mem32_write(target, STUB_BUFFER_BASE, src, length); - return status & MSPM0_FLASHCTL_STAT_CMDPASS; + return cortexm_run_stub(target, MSPM0_SRAM_BASE, dest, STUB_BUFFER_BASE, length, 0); } static bool mspm0_mass_erase(target_s *const target, platform_timeout_s *const print_progess) @@ -360,7 +283,7 @@ static bool mspm0_mass_erase(target_s *const target, platform_timeout_s *const p target_mem32_write32(target, MSPM0_FLASHCTL_CMDEXEC, MSPM0_FLASHCTL_CMDEXEC_EXEC); uint32_t status = 0U; - while (status & MSPM0_FLASHCTL_STAT_DONE) { + while (!(status & MSPM0_FLASHCTL_STAT_DONE)) { status = target_mem32_read32(target, MSPM0_FLASHCTL_STATCMD); if (print_progess) target_print_progress(print_progess); diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f77cf410a43..d46e91fe9c2 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -71,6 +71,13 @@ static void riscv32_regs_write(target_s *target, const void *data); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); +bool hazard3_probe(target_s *const target) +{ + target->driver = "Hazard3"; + target_add_ram32(target, 0x0, 131072); + return true; +} + bool riscv32_probe(target_s *const target) { /* 'E' base ISA has 16 GPRs + PC, 'I' base ISA has 32 GPRs + PC */ @@ -102,6 +109,9 @@ bool riscv32_probe(target_s *const target) PROBE(ch32v003x_probe); PROBE(ch32vx_probe); break; + case 0xe77: + PROBE(hazard3_probe); + break; default: break; } diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 0dd02dc76e2..a51c73daee8 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -293,7 +293,7 @@ void riscv_dmi_init(riscv_dmi_s *const dmi) /* * The DMI version does not actually matter here, the implementation details have already been * abstracted away at this point and we have a generic DMI to work with - * + * * But the dminfo register (at 0x11) of v0.11 DM is incompatible with dmstatus (also at 0x11) of * later versions, meaning we can't easily/reliably determine the version of the DM. * We ignore all v0.11 DMI's in the hope we don't encounter a v0.11 DM with a later version DMI. @@ -968,6 +968,13 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) /* Now info's bottom 16 bits contain the supported trigger modes, so write this info to the slot in the hart */ hart->trigger_uses[trigger] = info; DEBUG_TARGET("Hart trigger slot %" PRIu32 " modes: %04" PRIx32 "\n", trigger, info); +#ifndef DEBUG_TARGET_IS_NOOP + /* Display whatever contents of tdata2 (normally address to match for stale trigger) */ + target_addr64_t tdata2 = 0; + riscv_csr_read(hart, RV_TRIG_DATA_2, &tdata2); + DEBUG_TARGET("Hart trigger slot %" PRIu32 " tdata2 = %08" PRIx32 "%08" PRIx32 "\n", trigger, + (uint32_t)(tdata2 >> 32U), (uint32_t)tdata2); +#endif } } @@ -1045,6 +1052,12 @@ bool riscv_attach(target_s *const target) /* We then also need to select the Hart again so we're poking with the right one on the target */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel)) return false; + /* Clear any stale triggers */ + for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { + const uint32_t tdata1 = 0U; + const uint32_t tdata2 = 0U; + riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); + } /* We then need to halt the hart so the attach process can function */ riscv_halt_request(target); return true; @@ -1053,6 +1066,12 @@ bool riscv_attach(target_s *const target) void riscv_detach(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); + /* Clear any stale triggers */ + for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { + const uint32_t tdata1 = 0U; + const uint32_t tdata2 = 0U; + riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); + } /* Once we get done and the user's asked us to detach, we need to resume the hart */ riscv_halt_resume(target, false); /* If the DMI needs steps done to quiesce it, finsh up with that */ @@ -1319,14 +1338,14 @@ static size_t riscv_build_target_description( const char *const name = riscv_gpr_names[i]; const gdb_reg_type_e type = riscv_gpr_types[i]; - offset += (size_t)snprintf(buffer + offset, print_size, "", name, - address_width, gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); + offset += snprintf(buffer + offset, print_size, "", name, address_width, + gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); } /* Then build the program counter register description, which has the same bitsize as the GPRs. */ if (max_length != 0) print_size = max_length - offset; - offset += (size_t)snprintf(buffer + offset, print_size, "", address_width, + offset += snprintf(buffer + offset, print_size, "", address_width, gdb_reg_type_strings[GDB_TYPE_CODE_PTR]); /* If the target has basic single precision support, generate a block for that */ @@ -1341,13 +1360,13 @@ static size_t riscv_build_target_description( /* Add main CSR registers*/ if (max_length != 0) print_size = max_length - offset; - offset += (size_t)snprintf(buffer + offset, print_size, ""); + offset += snprintf(buffer + offset, print_size, ""); for (size_t i = 0; i < ARRAY_LENGTH(riscv_csrs); i++) { if (max_length != 0) print_size = max_length - offset; - offset += (size_t)snprintf(buffer + offset, print_size, - " ", riscv_csrs[i].name, address_width, - riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); + offset += snprintf(buffer + offset, print_size, " ", + riscv_csrs[i].name, address_width, riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, + gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); } /* Add the closing tags required */ if (max_length != 0) diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index b51f910ce2b..7501b3e02ac 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -398,10 +398,10 @@ static void gd32vf1_detach(target_s *const target) bool gd32vw5_probe(target_s *const target) { const uint16_t device_id = target_mem32_read32(target, GD32E5_DBGMCU_BASE) & 0xfffU; - const uint32_t signature = target_mem32_read32(target, GD32Fx_FLASHSIZE); - const uint16_t flash_size = signature & 0xffffU; - const uint16_t ram_size = signature >> 16U; - DEBUG_WARN("Stub for detection of GD32VW553. DBG_ID=0x%x, RAM=%u, flash=%u\n", device_id, ram_size, flash_size); + /* Either 2 or 4 MiB of main SiP Flash */ + const uint16_t flash_size = 4096U; + /* SRAM0/1/2 each 64 KiB, SRAM3 128 KiB (96+32 shared) */ + const uint16_t ram_size = 320U; target->driver = "GD32VW5"; target->part_id = device_id; target_add_ram32(target, STM32F1_SRAM_BASE, ram_size * 1024U); diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 0bb0a4e55f8..f74efc9088b 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -493,6 +493,8 @@ static const uint8_t stm32l4_opt_reg_offsets[9] = {0x20, 0x24, 0x28, 0x2c, 0x30, static const uint8_t stm32g4_opt_reg_offsets[11] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x70, 0x44, 0x48, 0x4c, 0x50, 0x74}; static const uint8_t stm32wl_opt_reg_offsets[7] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38}; static const uint8_t stm32wb_opt_reg_offsets[10] = {0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c, 0x80, 0x84}; +static const uint8_t stm32u5_opt_reg_offsets[16] = { + 0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c}; static const uint32_t stm32l4_default_options_values[9] = { 0xffeff8aaU, @@ -548,6 +550,25 @@ static const uint32_t stm32wb_default_options_values[10] = { 0x00000000U, // Secure SRAM2 start address and CPU2 reset vector option bytes }; +static const uint32_t stm32u575_default_options_values[16] = { + 0x1feff8aaU, + 0x0800007fU, + 0x0bf9007fU, + 0x0c00007cU, + 0xffffff80U, + 0x7f807f80U, + 0xff80ffffU, + 0xff80ffffU, + 0xffffff80U, + 0x7f807f80U, + 0xff80ffffU, + 0xff80ffffU, + 0x00000000U, + 0x00000000U, + 0x00000000U, + 0x00000000U, +}; + static_assert(ARRAY_LENGTH(stm32l4_opt_reg_offsets) == ARRAY_LENGTH(stm32l4_default_options_values), "Number of stm32l4 option registers must match number of default values"); static_assert(ARRAY_LENGTH(stm32g4_opt_reg_offsets) == ARRAY_LENGTH(stm32g4_default_options_values), @@ -556,6 +577,8 @@ static_assert(ARRAY_LENGTH(stm32wl_opt_reg_offsets) == ARRAY_LENGTH(stm32wl_defa "Number of stm32wl option registers must match number of default values"); static_assert(ARRAY_LENGTH(stm32wb_opt_reg_offsets) == ARRAY_LENGTH(stm32wb_default_options_values), "Number of stm32wb option registers must match number of default values"); +static_assert(ARRAY_LENGTH(stm32u5_opt_reg_offsets) == ARRAY_LENGTH(stm32u575_default_options_values), + "Number of stm32u5 option registers must match number of default values"); /* Retrieve device basic information, just add to the vector to extend */ static const stm32l4_device_info_s *stm32l4_get_device_info(const uint16_t device_id) @@ -742,6 +765,12 @@ bool stm32l4_probe(target_s *const target) target->core = "M33+TZ"; } break; + case ID_STM32U535: + case ID_STM32U5Fx: + case ID_STM32U59x: + case ID_STM32U575: + target->target_options |= TOPT_NON_HALTING_MEM_IO; + break; default: break; } @@ -886,6 +915,7 @@ static bool stm32l4_flash_busy_wait(target_s *const target, platform_timeout_s * static bool stm32l4_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t len) { + (void)len; target_s *target = flash->t; const stm32l4_flash_s *const sf = (stm32l4_flash_s *)flash; @@ -897,21 +927,17 @@ static bool stm32l4_flash_erase(target_flash_s *const flash, const target_addr_t if (!stm32l4_flash_busy_wait(target, NULL)) return false; - /* Erase the requested chunk of flash, one page at a time. */ - for (size_t offset = 0; offset < len; offset += flash->blocksize) { - const uint32_t page = (addr + offset - STM32L4_FLASH_BANK1_BASE) / flash->blocksize; - const uint32_t bank_flags = addr + offset >= sf->bank1_start ? STM32L4_FPEC_CTRL_BANK_ERASE : 0; - const uint32_t ctrl = STM32L4_FPEC_CTRL_PAGE_ERASE | (page << STM32L4_FPEC_CTRL_PAGE_SHIFT) | bank_flags; - /* Flash page erase instruction */ - stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl); - /* write address to FMA */ - stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl | STM32L4_FPEC_CTRL_START); - - /* Wait for completion or an error */ - if (!stm32l4_flash_busy_wait(target, NULL)) - return false; - } - return true; + /* Erase the requested chunk of flash */ + const uint32_t page = (addr - STM32L4_FLASH_BANK1_BASE) / flash->blocksize; + const uint32_t bank_flags = addr >= sf->bank1_start ? STM32L4_FPEC_CTRL_BANK_ERASE : 0; + const uint32_t ctrl = STM32L4_FPEC_CTRL_PAGE_ERASE | (page << STM32L4_FPEC_CTRL_PAGE_SHIFT) | bank_flags; + /* Flash page erase instruction */ + stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl); + /* write address to FMA */ + stm32l4_flash_write32(target, STM32L4_FPEC_CTRL, ctrl | STM32L4_FPEC_CTRL_START); + + /* Wait for completion or an error */ + return stm32l4_flash_busy_wait(target, NULL); } static bool stm32l4_flash_write( @@ -1036,6 +1062,12 @@ static stm32l4_option_bytes_info_s stm32l4_get_opt_bytes_info(const uint16_t par .offsets = stm32wb_opt_reg_offsets, .default_values = stm32wb_default_options_values, }; + case ID_STM32U575: + return (stm32l4_option_bytes_info_s){ + .word_count = ARRAY_LENGTH(stm32u575_default_options_values), + .offsets = stm32u5_opt_reg_offsets, + .default_values = stm32u575_default_options_values, + }; default: return (stm32l4_option_bytes_info_s){ .word_count = ARRAY_LENGTH(stm32l4_default_options_values), diff --git a/src/target/target.c b/src/target/target.c index b358f8d7090..226266ade81 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -54,6 +54,9 @@ const command_s target_cmd_list[] = { {NULL, NULL, NULL}, }; +static const char map_begin[] = ""; +static const char map_end[] = ""; + target_s *target_new(void) { target_s *target = calloc(1, sizeof(*target)); @@ -253,35 +256,65 @@ bool target_enter_flash_mode_stub(target_s *target) return true; } -static ssize_t map_ram(char *buf, size_t len, target_ram_s *ram) +static ssize_t mem_map_ram(char *const buffer, const size_t length, const target_ram_s *const ram) { - return snprintf(buf, len, "", ram->start, - (uint32_t)ram->length); + return snprintf(buffer, length, "", + ram->start, (uint32_t)ram->length); } -static ssize_t map_flash(char *buf, size_t len, target_flash_s *flash) +static ssize_t mem_map_flash(char *const buffer, const size_t length, const target_flash_s *const flash) { - ssize_t offset = 0; - offset += snprintf(&buf[offset], len - offset, - "", flash->start, - (uint32_t)flash->length); - offset += snprintf(buf + offset, len - offset, "0x%" PRIx32 "", - (uint32_t)flash->blocksize); - return offset; + return snprintf(buffer, length, + "0x%" PRIx32 "", + flash->start, (uint32_t)flash->length, (uint32_t)flash->blocksize); } -bool target_mem_map(target_s *target, char *tmp, size_t len) +size_t target_mem_map_chunk( + target_s *const target, char *const buffer, const size_t length, const uint32_t start_offset) { - size_t offset = 0; - offset = snprintf(tmp + offset, len - offset, ""); - /* Map each defined RAM */ - for (target_ram_s *ram = target->ram; ram; ram = ram->next) - offset += map_ram(tmp + offset, len - offset, ram); - /* Map each defined Flash */ - for (target_flash_s *flash = target->flash; flash; flash = flash->next) - offset += map_flash(tmp + offset, len - offset, flash); - offset += snprintf(tmp + offset, len - offset, ""); - return offset < len - 1U; + /* Simple case - the offset for the next block directly follows on from the last */ + if (start_offset == target->map_transfer_offset) { + /* Figure out where the next chunk is - RAM, Flash or top-and-tail */ + if (start_offset == 0U) { + memcpy(buffer, map_begin, ARRAY_LENGTH(map_begin)); + target->map_transfer_offset = ARRAY_LENGTH(map_begin) - 1U; + return ARRAY_LENGTH(map_begin) - 1U; + } + /* It wasn't the top of the map, so let's find an object that ends past the end of the offset */ + size_t offset = ARRAY_LENGTH(map_begin) - 1U; + /* Start with the RAM for the target */ + for (target_ram_s *ram = target->ram; ram; ram = ram->next) { + /* If this is the entry we're at, format it out and return */ + if (offset == target->map_transfer_offset) { + size_t entry_length = mem_map_ram(buffer, length, ram); + target->map_transfer_offset += entry_length; + return entry_length; + } + /* Otherwise see how long it is and skip past it */ + offset += mem_map_ram(NULL, 0U, ram); + } + /* Now the Flash */ + for (target_flash_s *flash = target->flash; flash; flash = flash->next) { + /* If this is the entry we're at, format it out and return */ + if (offset == target->map_transfer_offset) { + size_t entry_length = mem_map_flash(buffer, length, flash); + target->map_transfer_offset += entry_length; + return entry_length; + } + /* Otherwise see how long it is and skip past it */ + offset += mem_map_flash(NULL, 0U, flash); + } + /* If we've processed all that, then it's an end of map request */ + memcpy(buffer, map_end, ARRAY_LENGTH(map_end)); + target->map_transfer_offset = 0U; + return ARRAY_LENGTH(map_end) - 1U; + } + /* For now don't bother handling the complex case - GDB itself will never invoke this */ + DEBUG_WARN("qXfer memory map request asking for data at offset %" PRIu32 ", but last request ended at %" PRIu32 + " - unsupported request", + start_offset, target->map_transfer_offset); + return 0U; } void target_print_progress(platform_timeout_s *const timeout) diff --git a/src/target/target_internal.h b/src/target/target_internal.h index f93238e2a75..4b56cdaa245 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -176,6 +176,8 @@ struct target { target_ram_s *ram; target_flash_s *flash; + uint32_t map_transfer_offset; + /* Other stuff */ const char *driver; uint32_t cpuid;