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..1a4f08e9a09 --- /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' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-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/lint.yml b/.codeberg/ci/lint.yml new file mode 100644 index 00000000000..75ebbff5e1b --- /dev/null +++ b/.codeberg/ci/lint.yml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT OR Apache-2.0 +# SPDX-FileCopyrightText: 2026 1BitSquared +# SPDX-FileContributor: Written by Rachel Mant + +# Controls when the workflow will run +when: + # Triggers the workflow on pull request events + - event: pull_request + # Allows us to trigger the workflow manually from in Woodpecker for any branch + - event: manual + +steps: + - name: lint + image: debian:trixie + pull: true + commands: | + # Get Python installed + apt update + apt install -y python3 python3-venv git + # Create a Python venv for pre-commit to be run from and activate it + python3 -m venv --upgrade-deps env + . env/bin/activate + # Now install pre-commit and run it for BMD + pip install pre-commit + pre-commit run --show-diff-on-failure --color=always --all-files 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..246448e3b33 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -32,24 +32,25 @@ 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' - 'f4discovery' - 'hydrabus' - 'launchpad-icdi' - - 'native' - - 'native-uncommon' - - 'native-st-clones' - - 'native-riscv' - - 'native-remote' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-remote' - 'stlink' - 'stlinkv3' - 'swlink' @@ -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 @@ -103,7 +104,7 @@ jobs: meson compile -C build mkdir src/artefacts case "${{ matrix.probe }}" in - native-*) + bmp-v2-*) mv build/blackmagic_native_firmware.elf build/blackmagic_${{ matrix.probe }}_firmware.elf mv build/blackmagic_native_firmware.bin build/blackmagic_${{ matrix.probe }}_firmware.bin ;; @@ -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..f37c4d983a4 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -35,24 +35,25 @@ 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' - 'f4discovery' - 'hydrabus' - 'launchpad-icdi' - - 'native' - - 'native-uncommon' - - 'native-st-clones' - - 'native-riscv' - - 'native-remote' + - 'bmp-v2' + - 'bmp-v2-uncommon' + - 'bmp-v2-st-clones' + - 'bmp-v2-riscv' + - 'bmp-v2-remote' - 'stlink' - 'stlinkv3' - 'swlink' @@ -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..a276bcfae5c 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`) @@ -64,7 +64,7 @@ When writing commit messages, please prefix the component being modified using t * If the commit modifies target support, prefix with the path under src/target including the name of the file minus its extension - for example, "adiv5:", "stm32f1:" or "flashstub/lmi:" * If the commit modifies a platform, prefix with the name of that platform followed by the file - for example, - "hosted/cli:" or "native/platform:" + "hosted/cli:" or "bmp-v2/platform:" * If the commit modifies a significant number of files, us the overarching theme - for example if it's a platform API change then use "platform:" * If the commit modifies files such as the build system, the main project readme, or any other files about the project diff --git a/Makefile b/Makefile index 55103d8d979..22e0b41eaa3 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,9 @@ all: @echo "> meson compile -C build" @echo "" @echo "You can find example firmware configuration files in the \`cross-file\` subdirectory." - @echo "For example, to build a firmware for the native hardware run the following commands:" - @echo "> meson setup build-native --cross-file cross-file/native.ini --werror" - @echo "> meson compile -C build-native" + @echo "For example, to build a firmware for the bmp-v2 hardware run the following commands:" + @echo "> meson setup build-bmp-v2 --cross-file cross-file/bmp-v2.ini --werror" + @echo "> meson compile -C build-bmp-v2" @echo "" @echo "For further instructions please refer to the README.md in the root directory of this repository." @echo "" diff --git a/README.md b/README.md index 6151cca001d..2b7b1dbfe21 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ 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) -[![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) +[![Current release](https://codeberg.org/blackmagic-debug/blackmagic/badges/release.svg)](https://codeberg.org/blackmagic-debug/blackmagic/releases) +[![CI status](https://ci.codeberg.org/api/badges/16587/status.svg)](https://ci.codeberg.org/repos/16587) [![AI free project](https://badges.ws/badge/NO-AI-ff0000)](https://github.com/blackmagic-debug/blackmagic/blob/main/CONTRIBUTING.md#contributing) Table of contents: @@ -197,7 +197,7 @@ configuration for it. The build configuration command for the native probe may look like: ```sh -meson setup build --cross-file cross-file/native.ini +meson setup build --cross-file cross-file/bmp-v2.ini ``` Note that even if you are using the pre-configured cross-file, you may still override it's defaults with @@ -208,7 +208,7 @@ Alternatively (for advanced users), if you wish to configure manually, for insta for a new probe, or a different toolchain, you can run something similar to this: ```sh -meson setup build --cross-file cross-file/arm-none-eabi.ini -Dprobe=native -Dtargets=cortexm,stm +meson setup build --cross-file cross-file/arm-none-eabi.ini -Dprobe=bmp-v2 -Dtargets=cortexm,stm ``` After following one of these two paths, you now should have a `build` directory from where you can build @@ -223,8 +223,8 @@ meson compile -C build You should now see the resulting binaries in `build`, in this case: -* `blackmagic_native_firmware.bin` -* `blackmagic_native_firmware.elf` +* `blackmagic_bmp-v2_firmware.bin` +* `blackmagic_bmp-v2_firmware.elf` These are the binary files you will use to flash to your probe. diff --git a/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/blackpill-f401cd.ini b/cross-file/blackpill-f401cd.ini new file mode 100644 index 00000000000..bc286c80245 --- /dev/null +++ b/cross-file/blackpill-f401cd.ini @@ -0,0 +1,24 @@ +# This a cross-file for the blackpill-f401cd probe, providing sane default options for it. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'blackpill-f401cd' +targets = 'cortexar,cortexm,riscv32,riscv64,apollo3,at32f4,ch32,ch579,efm,gd32,hc32,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' +rtt_support = true +bmd_bootloader = true diff --git a/cross-file/bluepill.ini b/cross-file/bluepill.ini index 50028721002..2f09284e334 100644 --- a/cross-file/bluepill.ini +++ b/cross-file/bluepill.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'bluepill' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = true diff --git a/cross-file/bmp-v1-remote.ini b/cross-file/bmp-v1-remote.ini new file mode 100644 index 00000000000..4994d1e390a --- /dev/null +++ b/cross-file/bmp-v1-remote.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides only architecture support intended for probes that will be used soley via the +# Black Magic Debug remote protocol with BMDA. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,riscv32,riscv64' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-riscv.ini b/cross-file/bmp-v1-riscv.ini new file mode 100644 index 00000000000..92d45f4864e --- /dev/null +++ b/cross-file/bmp-v1-riscv.ini @@ -0,0 +1,25 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides only support for both RISC-V architectures and support for RISC-V targets. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'riscv32,riscv64,gd32,rp' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-st-clones.ini b/cross-file/bmp-v1-st-clones.ini new file mode 100644 index 00000000000..a4927be775f --- /dev/null +++ b/cross-file/bmp-v1-st-clones.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for all ARM Cortex architectures and target support for ST's parts, +# and their clones by Artery Tek, GigaDevice, WinChipHead, MindMotion, Puya and HDSC. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,stm,at32f4,gd32,ch32,ch579,mm32,puya,hc32' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1-uncommon.ini b/cross-file/bmp-v1-uncommon.ini new file mode 100644 index 00000000000..8061ae50f59 --- /dev/null +++ b/cross-file/bmp-v1-uncommon.ini @@ -0,0 +1,26 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for all ARM Cortex architectures and target support for less commonly +# used targets such as the Energy Micro parts, Renesas parts, Xilinx Zynq and Ambiq Apollo3. + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexar,cortexm,apollo3,efm,hc32,renesas,xilinx' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/bmp-v1.ini b/cross-file/bmp-v1.ini new file mode 100644 index 00000000000..34ba0ca581e --- /dev/null +++ b/cross-file/bmp-v1.ini @@ -0,0 +1,27 @@ +# This a cross-file for the bmp-v1 probe. It defines a default configuration profile that +# provides support for only the ARM Cortex-M architecture and target support for the NXP LPC +# families, nRF series', NXP's Kinetis and i.MXRT parts, RPi Foundation's MCUs (ARM part only), +# Atmel's ATSAM parts, ST's parts, and TI's Stellaris/Tiva-C parts, + +[binaries] +c = 'arm-none-eabi-gcc' +cpp = 'arm-none-eabi-g++' +ld = 'arm-none-eabi-gcc' +ar = 'arm-none-eabi-ar' +nm = 'arm-none-eabi-nm' +strip = 'arm-none-eabi-strip' +objcopy = 'arm-none-eabi-objcopy' +objdump = 'arm-none-eabi-objdump' +size = 'arm-none-eabi-size' + +[host_machine] +system = 'bare-metal' +cpu_family = 'arm' +cpu = 'arm' +endian = 'little' + +[project options] +probe = 'bmp-v2' # v2 is backwards compatible with v1 +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' +rtt_support = false +bmd_bootloader = true diff --git a/cross-file/native-remote.ini b/cross-file/bmp-v2-remote.ini similarity index 88% rename from cross-file/native-remote.ini rename to cross-file/bmp-v2-remote.ini index 6f42e0562d4..2da75954505 100644 --- a/cross-file/native-remote.ini +++ b/cross-file/bmp-v2-remote.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides only architecture support intended for probes that will be used soley via the # Black Magic Debug remote protocol with BMDA. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,riscv32,riscv64' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-riscv.ini b/cross-file/bmp-v2-riscv.ini similarity index 87% rename from cross-file/native-riscv.ini rename to cross-file/bmp-v2-riscv.ini index 17ebf4c6482..10154187792 100644 --- a/cross-file/native-riscv.ini +++ b/cross-file/bmp-v2-riscv.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides only support for both RISC-V architectures and support for RISC-V targets. [binaries] @@ -19,7 +19,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'riscv32,riscv64,gd32,rp' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-st-clones.ini b/cross-file/bmp-v2-st-clones.ini similarity index 89% rename from cross-file/native-st-clones.ini rename to cross-file/bmp-v2-st-clones.ini index 780028a3423..29ee05d3605 100644 --- a/cross-file/native-st-clones.ini +++ b/cross-file/bmp-v2-st-clones.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for all ARM Cortex architectures and target support for ST's parts, # and their clones by Artery Tek, GigaDevice, WinChipHead, MindMotion, Puya and HDSC. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,stm,at32f4,gd32,ch32,ch579,mm32,puya,hc32' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native-uncommon.ini b/cross-file/bmp-v2-uncommon.ini similarity index 89% rename from cross-file/native-uncommon.ini rename to cross-file/bmp-v2-uncommon.ini index 293d1093c37..97cedbf70b0 100644 --- a/cross-file/native-uncommon.ini +++ b/cross-file/bmp-v2-uncommon.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for all ARM Cortex architectures and target support for less commonly # used targets such as the Energy Micro parts, Renesas parts, Xilinx Zynq and Ambiq Apollo3. @@ -20,7 +20,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' +probe = 'bmp-v2' targets = 'cortexar,cortexm,apollo3,efm,hc32,renesas,xilinx' rtt_support = false bmd_bootloader = true diff --git a/cross-file/native.ini b/cross-file/bmp-v2.ini similarity index 83% rename from cross-file/native.ini rename to cross-file/bmp-v2.ini index 2aaae354ad4..f849e804e17 100644 --- a/cross-file/native.ini +++ b/cross-file/bmp-v2.ini @@ -1,4 +1,4 @@ -# This a cross-file for the native probe. It defines a default configuration profile that +# This a cross-file for the bmp-v2 probe. It defines a default configuration profile that # provides support for only the ARM Cortex-M architecture and target support for the NXP LPC # families, nRF series', NXP's Kinetis and i.MXRT parts, RPi Foundation's MCUs (ARM part only), # Atmel's ATSAM parts, ST's parts, and TI's Stellaris/Tiva-C parts, @@ -21,7 +21,7 @@ cpu = 'arm' endian = 'little' [project options] -probe = 'native' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +probe = 'bmp-v2' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = true diff --git a/cross-file/bmp-v3.ini b/cross-file/bmp-v3.ini new file mode 100644 index 00000000000..584ddb86306 --- /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,lattice,lpc,mm32,nrf,nxp,puya,renesas,rp,sam,stm,ti,xilinx' +rtt_support = true +bmd_bootloader = true +enable_riscv_accel = true diff --git a/cross-file/f072.ini b/cross-file/f072.ini index 2382d205205..d09b7adc114 100644 --- a/cross-file/f072.ini +++ b/cross-file/f072.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'f072' -targets = 'cortexm,riscv32,riscv64,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,riscv32,riscv64,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = false diff --git a/cross-file/swlink.ini b/cross-file/swlink.ini index 3a744b7675f..f40d75bb151 100644 --- a/cross-file/swlink.ini +++ b/cross-file/swlink.ini @@ -19,6 +19,6 @@ endian = 'little' [project options] probe = 'swlink' -targets = 'cortexm,lpc,nrf,nxp,renesas,rp,sam,stm,ti' +targets = 'cortexm,lpc,nrf,nxp,rp,sam,stm,ti' rtt_support = false bmd_bootloader = false 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..0eff649b2b5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,16 +5,18 @@ option( '', '96b_carbon', 'blackpill-f401cc', + 'blackpill-f401cd', 'blackpill-f401ce', 'blackpill-f411ce', 'bluepill', + 'bmp-v3', 'ctxlink', 'f072', 'f3', 'f4discovery', 'hydrabus', 'launchpad-icdi', - 'native', + 'bmp-v2', 'stlink', 'stlinkv3', 'swlink' @@ -38,6 +40,7 @@ option( 'efm', 'gd32', 'hc32', + 'lattice', 'lpc', 'mm32', 'nrf', @@ -154,3 +157,9 @@ option( type: 'boolean', value: false ) +option( + 'ecp5_decode_registers', + type: 'boolean', + value: true, + description: 'Enable decoding of Lattice ECP5 registers where applicable, rather than printing them raw' +) diff --git a/scripts/run-clang-tidy.py b/scripts/run-clang-tidy.py index e4639e0f0d1..1e354abd7f4 100755 --- a/scripts/run-clang-tidy.py +++ b/scripts/run-clang-tidy.py @@ -34,7 +34,7 @@ def gatherFiles(): extraArgs = [ '-Isrc/target', '-Isrc', '-Isrc/include', '-Isrc/platforms/common', - '-Isrc/platforms/native', '-Ideps/libopencm3/include', '-Isrc/platforms/stm32' + '-Isrc/platforms/bmp-v2', '-Ideps/libopencm3/include', '-Isrc/platforms/stm32' ] + args.includePaths for i, arg in enumerate(extraArgs): diff --git a/src/command.c b/src/command.c index c86dedb34f7..d280ddd4a44 100644 --- a/src/command.c +++ b/src/command.c @@ -4,7 +4,7 @@ * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin * Copyright (C) 2021 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de) - * Copyright (C) 2023-2025 1BitSquared + * Copyright (C) 2023-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -100,7 +100,7 @@ static bool cmd_shutdown_bmda(target_s *target, int argc, const char **argv); #define strtok_r strtok_s #endif -const command_s cmd_list[] = { +static const command_s cmd_list[] = { {"version", cmd_version, "Display firmware version info"}, {"help", cmd_help, "Display help for monitor commands"}, {"jtag_scan", cmd_jtag_scan, "Scan JTAG chain for devices"}, @@ -111,7 +111,7 @@ const command_s cmd_list[] = { #endif {"spi_scan", cmd_onboard_flash_scan, "Scan for on-board SPI Flash devices"}, {"auto_scan", cmd_auto_scan, "Automatically scan all chain types for devices"}, - {"frequency", cmd_frequency, "set minimum high and low times: [FREQ]"}, + {"frequency", cmd_frequency, "Set minimum high and low times: [FREQ]"}, {"targets", cmd_targets, "Display list of available targets"}, {"morse", cmd_morse, "Display morse error message"}, {"halt_timeout", cmd_halt_timeout, "Timeout to wait until Cortex-M is halted: [TIMEOUT, default 2000ms]"}, @@ -153,10 +153,6 @@ const command_s cmd_list[] = { {NULL, NULL, NULL}, }; -#ifdef PLATFORM_HAS_CUSTOM_COMMANDS -extern const command_s platform_cmd_list[]; -#endif - bool connect_assert_nrst; #if defined(PLATFORM_HAS_DEBUG) && CONFIG_BMDA == 0 bool debug_bmp; diff --git a/src/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..25e713dc673 100644 --- a/src/include/buffer_utils.h +++ b/src/include/buffer_utils.h @@ -36,6 +36,9 @@ #include #include +#include + +#include static inline void write_le2(uint8_t *const buffer, const size_t offset, const uint16_t value) { @@ -73,6 +76,15 @@ static inline uint32_t read_le4(const uint8_t *const buffer, const size_t offset return data[0U] | ((uint32_t)data[1U] << 8U) | ((uint32_t)data[2U] << 16U) | ((uint32_t)data[3U] << 24U); } +static inline uint64_t read_le8(const uint8_t *const buffer, const size_t offset) +{ + uint8_t data[8U]; + memcpy(data, buffer + offset, 8U); + return data[0U] | ((uint64_t)data[1U] << 8U) | ((uint64_t)data[2U] << 16U) | ((uint64_t)data[3U] << 24U) | + ((uint64_t)data[4U] << 32U) | ((uint64_t)data[5U] << 40U) | ((uint64_t)data[6U] << 48U) | + ((uint64_t)data[7U] << 56U); +} + static inline uint32_t read_be4(const uint8_t *const buffer, const size_t offset) { uint8_t data[4U]; @@ -96,4 +108,78 @@ static inline size_t write_char(char *const buffer, const size_t buffer_size, co return offset + 1U; } +static inline uint8_t reverse_bits8(const uint8_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xff000000U) >> 24U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x01U) << 7U) | ((data & 0x02U) << 5U) | ((data & 0x04U) << 3U) | ((data & 0x08U) << 1U) | + ((data & 0x10U) >> 1U) | ((data & 0x20U) >> 3U) | ((data & 0x40U) >> 5U) | ((data & 0x80U) >> 7U); +} + +static inline uint16_t reverse_bits16(const uint16_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xffff0000U) >> 16U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x0001U) << 15U) | ((data & 0x0002U) << 13U) | ((data & 0x0004U) << 11U) | + ((data & 0x0008U) << 9U) | ((data & 0x0010U) << 7U) | ((data & 0x0020U) << 5U) | ((data & 0x0040U) << 3U) | + ((data & 0x0080U) << 1U) | ((data & 0x0100U) >> 1U) | ((data & 0x0200U) >> 3U) | ((data & 0x0400U) >> 5U) | + ((data & 0x0800U) >> 7U) | ((data & 0x1000U) >> 9U) | ((data & 0x2000U) >> 11U) | ((data & 0x4000U) >> 13U) | + ((data & 0x8000U) >> 15U); +} + +static inline uint32_t reverse_bits24(const uint32_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return (result & 0xffffff00U) >> 8U; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x00000001U) << 23U) | ((data & 0x00000002U) << 21U) | ((data & 0x00000004U) << 19U) | + ((data & 0x00000008U) << 17U) | ((data & 0x00000010U) << 15U) | ((data & 0x00000020U) << 13U) | + ((data & 0x00000040U) << 11U) | ((data & 0x00000080U) << 9U) | ((data & 0x00000100U) << 7U) | + ((data & 0x00000200U) << 5U) | ((data & 0x00000400U) << 3U) | ((data & 0x00000800U) << 1U) | + ((data & 0x00001000U) >> 1U) | ((data & 0x00002000U) >> 3U) | ((data & 0x00004000U) >> 5U) | + ((data & 0x00008000U) >> 7U) | ((data & 0x00010000U) >> 9U) | ((data & 0x00020000U) >> 11U) | + ((data & 0x00040000U) >> 13U) | ((data & 0x00080000U) >> 15U) | ((data & 0x00100000U) >> 17U) | + ((data & 0x00200000U) >> 19U) | ((data & 0x00400000U) >> 21U) | ((data & 0x00800000U) >> 23U); +} + +static inline uint32_t reverse_bits32(const uint32_t data) +{ + if (!BMD_CONSTANT_P(data)) { +#if (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB >= 2) || defined(__aarch64__) + uint32_t result; + __asm__("rbit %0, %1" : "=r"(result) : "r"(data)); + return result; +#endif + } + // We don't have an optimized routine for this, so just use the fallback + return ((data & 0x00000001U) << 31U) | ((data & 0x00000002U) << 29U) | ((data & 0x00000004U) << 27U) | + ((data & 0x00000008U) << 25U) | ((data & 0x00000010U) << 23U) | ((data & 0x00000020U) << 21U) | + ((data & 0x00000040U) << 19U) | ((data & 0x00000080U) << 17U) | ((data & 0x00000100U) << 15U) | + ((data & 0x00000200U) << 13U) | ((data & 0x00000400U) << 11U) | ((data & 0x00000800U) << 9U) | + ((data & 0x00001000U) << 7U) | ((data & 0x00002000U) << 5U) | ((data & 0x00004000U) << 3U) | + ((data & 0x00008000U) << 1U) | ((data & 0x00010000U) >> 1U) | ((data & 0x00020000U) >> 3U) | + ((data & 0x00040000U) >> 5U) | ((data & 0x00080000U) >> 7U) | ((data & 0x00100000U) >> 9U) | + ((data & 0x00200000U) >> 11U) | ((data & 0x00400000U) >> 13U) | ((data & 0x00800000U) >> 15U) | + ((data & 0x01000000U) >> 17U) | ((data & 0x02000000U) >> 19U) | ((data & 0x04000000U) >> 21U) | + ((data & 0x08000000U) >> 23U) | ((data & 0x10000000U) >> 25U) | ((data & 0x20000000U) >> 27U) | + ((data & 0x40000000U) >> 29U) | ((data & 0x80000000U) >> 31U); +} + #endif /*INCLUDE_BUFFER_UTILS_H*/ diff --git a/src/include/command.h b/src/include/command.h index 311324ed7fd..fab346e828c 100644 --- a/src/include/command.h +++ b/src/include/command.h @@ -25,7 +25,7 @@ #include "target.h" -int command_process(target_s *t, char *cmd_buffer); +int command_process(target_s *target, char *cmd_buffer); /* * Attempts to parse a string as either being "enable" or "disable". @@ -33,7 +33,7 @@ int command_process(target_s *t, char *cmd_buffer); * indicate what was parsed. If not successful, emits a warning to the * gdb port, returns false and leaves out untouched. */ -bool parse_enable_or_disable(const char *s, bool *out); +bool parse_enable_or_disable(const char *value, bool *out); #if CONFIG_BMDA == 1 extern bool shutdown_bmda; 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/general.h b/src/include/general.h index 34278e949c5..27a334318bf 100644 --- a/src/include/general.h +++ b/src/include/general.h @@ -163,6 +163,16 @@ void debug_serial_send_stdout(const uint8_t *data, size_t len); #define BMD_UNUSED __attribute__((unused)) #endif +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_constant_p) +#define BMD_CONSTANT_P __builtin_constant_p +#else +#define BMD_CONSTANT_P(x) 0 +#endif + #ifdef _MSC_VER #define strcasecmp _stricmp #define strncasecmp _strnicmp diff --git a/src/include/platform_support.h b/src/include/platform_support.h index ed49fb5166e..a4b4ff6eb7c 100644 --- a/src/include/platform_support.h +++ b/src/include/platform_support.h @@ -2,7 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015 Gareth McMullin - * Copyright (C) 2022-2025 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify 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/README.md b/src/platforms/README.md index 3408021b024..3c524e648ed 100644 --- a/src/platforms/README.md +++ b/src/platforms/README.md @@ -5,7 +5,7 @@ used by (multiple) platforms. ## Implementation directories -* native: Firmware for [Black Magic Probe](https://1bitsquared.com/products/black-magic-probe) +* bmp-v2: Firmware for [Black Magic Probe v2](https://1bitsquared.com/products/black-magic-probe) * stlink: Firmware for ST-Link v2 and ST-Link v2.1 * swlink: Firmware for ST-Link v1 and Bluepill * blackpill-f401cc: Firmware for the WeAct Studio [Black Pill F401CC](https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x1) diff --git a/src/platforms/blackpill-f401cd/README.md b/src/platforms/blackpill-f401cd/README.md new file mode 100644 index 00000000000..fa4e2106eb8 --- /dev/null +++ b/src/platforms/blackpill-f401cd/README.md @@ -0,0 +1,4 @@ +This platform supports the Black Pill F401CD, with STM32F401CD. + +Detailed information about this platform can be found found in the [readme of the common blackpill-f4 code](./../common/blackpill-f4/README.md). + diff --git a/src/platforms/blackpill-f401cd/platform.h b/src/platforms/blackpill-f401cd/platform.h new file mode 100644 index 00000000000..34c767c3dbc --- /dev/null +++ b/src/platforms/blackpill-f401cd/platform.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file provides the platform specific declarations for the blackpill-f401cd implementation. */ + +#ifndef PLATFORMS_BLACKPILL_F401CD_PLATFORM_H +#define PLATFORMS_BLACKPILL_F401CD_PLATFORM_H + +#define PLATFORM_IDENT "(BlackPill-F401CD) " +#define PLATFORM_CLOCK_FREQ RCC_CLOCK_3V3_84MHZ + +#include "blackpill-f4.h" + +#endif /* PLATFORMS_BLACKPILL_F401CD_PLATFORM_H */ diff --git a/src/platforms/native/meson.build b/src/platforms/bmp-v2/meson.build similarity index 97% rename from src/platforms/native/meson.build rename to src/platforms/bmp-v2/meson.build index 473ba65d4dd..11c968a7eb2 100644 --- a/src/platforms/native/meson.build +++ b/src/platforms/bmp-v2/meson.build @@ -2,6 +2,7 @@ # # Copyright (C) 2023 1BitSquared # Written by Rafael Silva +# Modified by Kat Mitchell # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -76,7 +77,7 @@ probe_bootloader = declare_dependency( summary( { - 'Name': 'Black Magic Probe (native)', + 'Name': 'Black Magic Probe (bmp-v2)', 'Platform': 'STM32F1', 'Bootloader': 'Black Magic Debug Bootloader', 'Load Address': '0x8002000', diff --git a/src/platforms/native/native.ld b/src/platforms/bmp-v2/native.ld similarity index 100% rename from src/platforms/native/native.ld rename to src/platforms/bmp-v2/native.ld diff --git a/src/platforms/native/platform.c b/src/platforms/bmp-v2/platform.c similarity index 97% rename from src/platforms/native/platform.c rename to src/platforms/bmp-v2/platform.c index bd8250c4ef0..63b4a13be55 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/bmp-v2/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 @@ -292,11 +294,6 @@ void platform_nrst_set_val(bool assert) gpio_set_val(NRST_PORT, NRST_PIN, assert); else gpio_set_val(NRST_PORT, NRST_PIN, !assert); - - if (assert) { - for (volatile size_t i = 0; i < 10000U; ++i) - continue; - } } bool platform_nrst_get_val(void) @@ -391,7 +388,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 +397,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 +408,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/bmp-v2/platform.h similarity index 98% rename from src/platforms/native/platform.h rename to src/platforms/bmp-v2/platform.h index 45ada7551a9..2d55030d11d 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/bmp-v2/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 @@ -18,7 +21,7 @@ * along with this program. If not, see . */ -/* This file provides the platform specific declarations for the native implementation. */ +/* This file provides the platform specific declarations for the bmp-v2 implementation. */ #ifndef PLATFORMS_NATIVE_PLATFORM_H #define PLATFORMS_NATIVE_PLATFORM_H diff --git a/src/platforms/native/usbdfu.c b/src/platforms/bmp-v2/usbdfu.c similarity index 100% rename from src/platforms/native/usbdfu.c rename to src/platforms/bmp-v2/usbdfu.c diff --git a/src/platforms/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..44183e1d379 --- /dev/null +++ b/src/platforms/bmp-v3/platform.c @@ -0,0 +1,472 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2025-2026 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 "command.h" +#include "gdb_packet.h" +#include "target_internal.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 bool cmd_switched_txrx(target_s *target, int argc, const char **argv); + +const command_s platform_cmd_list[] = { + {"switched_txrx", cmd_switched_txrx, "Switched TX/RX on BMDU connector: [enable|disable]"}, + {NULL, NULL, NULL}, +}; + +void platform_init(void) +{ + hwversion = 0; + 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_AF, GPIO_PUPD_NONE, AUX_UART2_TX_PIN | AUX_UART2_RX_PIN); + /* Start with the pins configured the "correct" way around (unswapped) */ + gpio_set(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN); + gpio_set_output_options(AUX_UART2_DIR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, AUX_UART2_DIR_PIN); + 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); +} + +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); +} + +static bool cmd_switched_txrx(target_s *target, int argc, const char **argv) +{ + (void)target; + /* If invoked without arguments, display the state of the switching */ + if (argc == 1) { + gdb_outf("BMDU connector UART pin swapping %s\n", + gpio_get(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN) ? "disabled" : "enabled"); + return true; + } + if (argc == 2) { + bool swap_pins = false; + /* Try and parse the state to put the pins into, failing if we can't */ + if (!parse_enable_or_disable(argv[1], &swap_pins)) + return false; + /* Disable the UART so we can alter the pin swapping settings */ + usart_disable(AUX_UART2); + /* Set the pin swapping up */ + gpio_set_val(AUX_UART2_DIR_PORT, AUX_UART2_DIR_PIN, !swap_pins); + usart_set_swap_tx_rx(AUX_UART2, swap_pins); + /* Re-enable it now that configuration is updated */ + usart_enable(AUX_UART2); + return true; + } + gdb_out("Unrecognized command format\n"); + return false; +} diff --git a/src/platforms/bmp-v3/platform.h b/src/platforms/bmp-v3/platform.h new file mode 100644 index 00000000000..9e4fc58ffc1 --- /dev/null +++ b/src/platforms/bmp-v3/platform.h @@ -0,0 +1,304 @@ +/* + * 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_HAS_CUSTOM_COMMANDS + +#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..33f4af21325 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,35 @@ 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 + +/* UART state tracking so we can properly handle when a UART goes into error and might need disengaging from */ +static uart_state_e uart_state = UART_STATE_UNKNOWN; +#endif + +#ifdef STM32U5 +/* NOLINTBEGIN(clang-diagnostic-error, clang-diagnostic-pointer-to-int-cast) */ +/* Defines a linked list of things to be done at the completion of RX DMA */ +static const uintptr_t aux_serial_dma_receive_ll[] = { + /* + * Make sure we reset the number of bytes we can write so that if we've been doing UART swapping we don't + * wind up screwed and unable to take more data + */ + (uintptr_t)AUX_UART_BUFFER_SIZE, + /* This controls the next RX destination address to use */ + (uintptr_t)aux_serial_receive_buffer, +}; +/* 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 +132,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 +154,80 @@ 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 +static void aux_serial_activate_uart(const uintptr_t uart_base) +{ + dma_set_destination_address(AUX_UART_DMA_BUS, AUX_UART_DMA_TX_CHAN, (uintptr_t)&USART_TDR(uart_base)); + if (uart_base == AUX_UART1) + 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; +} + +void aux_serial_deactivate_uart(void) +{ + active_uart = 0U; + uart_state = UART_STATE_UNKNOWN; +} + +uart_state_e aux_serial_uart_state(void) +{ + return uart_state; +} +#endif + void aux_serial_init(void) { /* 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 +238,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 +259,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 +299,62 @@ 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_CxLLR_UB1); + dma_set_source_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); + dma_set_destination_width(AUX_UART_DMA_BUS, AUX_UART_DMA_RX_CHAN, DMA_CxTR1_DW_BYTE); + + 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 +369,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); + usart_enable(AUX_UART2); + 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 +425,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 +433,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 +460,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 +510,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 +523,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 +570,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); @@ -389,6 +597,7 @@ void aux_serial_send(const size_t len) void aux_serial_update_receive_buffer_fullness(void) { + /* Extract from the DMA controller where it is in the FIFO buffer and turn that back into a write index */ aux_serial_receive_write_index = AUX_UART_BUFFER_SIZE - dma_get_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); aux_serial_receive_write_index %= AUX_UART_BUFFER_SIZE; @@ -411,28 +620,62 @@ 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 this is a state change on the active UART */ + if (active_uart == uart) { + /* Make a note of whether either Idle or Framing Error have occured */ + if (status & USART_ISR_IDLE) + uart_state = UART_STATE_IDLE; + else if (status & USART_ISR_FE) + uart_state = UART_STATE_LOST; + } +#endif +#ifndef STM32U5 nvic_enable_irq(dma_irq); +#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 +693,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 +748,80 @@ void USBUSART2_ISR(void) #endif } #endif +#else +void AUX_UART1_ISR(void) +{ + aux_serial_receive_isr(AUX_UART1, AUX_UART_DMA_RX_IRQ); +} + +void AUX_UART2_ISR(void) +{ + aux_serial_receive_isr(AUX_UART2, AUX_UART_DMA_RX_IRQ); +} +#endif -#if defined(USBUSART_DMA_TX_ISR) +#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 +830,42 @@ 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 + */ + 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..3827030bab9 100644 --- a/src/platforms/common/aux_serial.h +++ b/src/platforms/common/aux_serial.h @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2022 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -20,14 +20,15 @@ #ifndef PLATFORMS_COMMON_AUX_SERIAL_H #define PLATFORMS_COMMON_AUX_SERIAL_H +#include "general.h" #include #include #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 +50,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 +69,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); @@ -78,4 +79,15 @@ void aux_serial_stage_debug_buffer(void); void aux_serial_stage_receive_buffer(void); #endif +#ifdef PLATFORM_MULTI_UART +typedef enum uart_state { + UART_STATE_UNKNOWN, + UART_STATE_IDLE, + UART_STATE_LOST, +} uart_state_e; + +uart_state_e aux_serial_uart_state(void); +void aux_serial_deactivate_uart(void); +#endif + #endif /* PLATFORMS_COMMON_AUX_SERIAL_H */ diff --git a/src/platforms/common/blackpill-f4/README.md b/src/platforms/common/blackpill-f4/README.md index fdddb63303c..0ec2221d716 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 @@ -131,4 +131,4 @@ SPI ports are set to Pclk/8 each (use with `bmpflash`). As SPI1 pins may conflic a) has a fixed per-board PLL config, no autodetection; b) understands buttons, drives LED, does not touch other GPIOs, talks USB DfuSe, ~~has MS OS descriptors for automatic driver installation on Windows~~, uses same libopencm3 code so you can verify hardware config via a smaller binary; c) erases and writes to internal Flash ~2.4x faster than MaskROM; - d) all of that in first 8-9 KiB of first page of 16 KiB (of F2/F4/F7 flash), just like on `native`/`stlink`/`swlink` etc. + d) all of that in first 8-9 KiB of first page of 16 KiB (of F2/F4/F7 flash), just like on `bmp-v2`/`stlink`/`swlink` etc. diff --git a/src/platforms/common/blackpill-f4/blackpill-f401cd.ld b/src/platforms/common/blackpill-f4/blackpill-f401cd.ld new file mode 100644 index 00000000000..ea032a38af9 --- /dev/null +++ b/src/platforms/common/blackpill-f4/blackpill-f401cd.ld @@ -0,0 +1,30 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * Copyright (C) 2024 1BitSquared + * Modified by Rachel Mant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 384K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K +} + +/* Include the platform common linker script. */ +INCLUDE ../blackmagic.ld diff --git a/src/platforms/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..3a4e70377e7 100644 --- a/src/platforms/common/stm32/timing_stm32.c +++ b/src/platforms/common/stm32/timing_stm32.c @@ -2,7 +2,7 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015 Gareth McMullin - * Copyright (C) 2023 1BitSquared + * Copyright (C) 2023-2026 1BitSquared * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "platform.h" #include "morse.h" #include "usb.h" +#include "aux_serial.h" #include #include @@ -29,13 +30,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 +46,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 +65,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 +97,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 +151,23 @@ void sys_tick_handler(void) } else monitor_ticks = 0; #endif + +#ifdef PLATFORM_MULTI_UART + /* + * If a UART goes into framing error and that persists for more than a milisecond or two, then + * it's probably safe to assume that the wires became disconnected and the UART is no longer active + * in which case we then want to disable that UART and go back to waiting for a UART to become active. + */ + const uart_state_e state = aux_serial_uart_state(); + if (state == UART_STATE_LOST) { + if (++uart_ticks == 2U) { + aux_serial_deactivate_uart(); + uart_ticks = 0U; + } + } else + /* Otherwise if the UART state is either not known or the UART is idle, reset the tick counter */ + uart_ticks = 0U; +#endif } uint32_t platform_time_ms(void) @@ -178,7 +212,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 +244,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..64ee94ac6fe 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 @@ -64,6 +66,13 @@ void swdptap_init(void) swd_proc.seq_in_parity = swdptap_seq_in_parity; swd_proc.seq_out = swdptap_seq_out; swd_proc.seq_out_parity = swdptap_seq_out_parity; + /* + * IEEE 1149.1-2013, 4.4 Test Data Input (TDI) + * b) The design of the circuitry fed from TDI shall be such that + * an undriven input produces a logical response + * identical to the application of a logic 1. + */ + gpio_set(TDI_PORT, TDI_PIN); } static void swdptap_turnaround(const swdio_status_t dir) @@ -107,9 +116,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 +130,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 +147,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 +160,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..b00e494834b 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,8 +74,8 @@ 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) -static bool debug_serial_send_complete = true; +#if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) || defined(STM32F7) || defined(STM32U5) +static _Atomic bool debug_serial_send_complete = true; #endif #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) @@ -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. @@ -267,36 +274,36 @@ static void debug_serial_send_data(void) aux_serial_update_receive_buffer_fullness(); /* Forcibly empty fifo if no USB endpoint. If fifo empty, nothing further to do. */ - if (usb_get_config() != 1 || + if (usb_get_config() != 1U || (aux_serial_receive_buffer_empty() #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) && debug_serial_fifo_buffer_empty() #endif )) { + /* Mark the FIFOs as empty */ #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) debug_serial_debug_read_index = debug_serial_debug_write_index; #endif aux_serial_drain_receive_buffer(); debug_serial_send_complete = true; } else { + /* We have a good USB link and data to send, so queue up anything that's in the debug buffer */ #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) debug_serial_debug_read_index = debug_serial_fifo_send( debug_serial_debug_buffer, debug_serial_debug_read_index, debug_serial_debug_write_index); #endif + /* And anything that's in the AUX serial buffer */ aux_serial_stage_receive_buffer(); } } void debug_serial_run(void) { - nvic_disable_irq(USB_IRQ); aux_serial_set_led(AUX_SERIAL_LED_RX); /* Try to send a packet if usb is idle */ if (debug_serial_send_complete) debug_serial_send_data(); - - nvic_enable_irq(USB_IRQ); } #endif @@ -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); @@ -339,21 +346,40 @@ static void debug_serial_receive_callback(usbd_device *dev, uint8_t ep) } #if ENABLE_DEBUG == 1 && defined(PLATFORM_HAS_DEBUG) -static void debug_serial_append_char(const char c) +static void debug_serial_append_char(const char ch) { - debug_serial_debug_buffer[debug_serial_debug_write_index] = c; + /* Write the character into the buffer and increment the write index */ + debug_serial_debug_buffer[debug_serial_debug_write_index] = ch; ++debug_serial_debug_write_index; + /* Bound the index on the size of the buffer so it remains a FIFO */ debug_serial_debug_write_index %= AUX_UART_BUFFER_SIZE; } 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)) + /* + * Gate actually writing the byte if there are pending USB or UART interrupts + * XXX: Why? What's the point of these checks - that's not to say they're wrong, just + * this needs documenting why this is okay and what its purpose is. + */ + if (nvic_get_active_irq(USB_IRQ) || +#ifndef PLATFORM_MULTI_UART + nvic_get_active_irq(USBUSART_IRQ) || nvic_get_active_irq(USBUSART_DMA_RX_IRQ) +#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; + /* Do something to make this atomic? */ CM_ATOMIC_CONTEXT(); size_t offset = 0; + /* + * Loop through all the bytes in the buffer to send, and if there would be a new line, insert the carridge return + * too so that terminal emulation on the receiving end works correctly. + */ for (; offset < len && (debug_serial_debug_write_index + 1U) % AUX_UART_BUFFER_SIZE != debug_serial_debug_read_index; ++offset) { @@ -366,6 +392,7 @@ size_t debug_serial_debug_write(const char *buf, const size_t len) debug_serial_append_char(buf[offset]); } + /* Try to send out the resulting buffer of data now that as much as we've got space for has been queued */ debug_serial_run(); return offset; } 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..6e29ccb26fc 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -177,7 +177,7 @@ void bmp_read_product_version(libusb_device_descriptor_s *device_descriptor, lib /* Let's start out easy - check to see if the string contains an opening paren (alternate platform) */ const char *const opening_paren = strchr(description + BMP_PRODUCT_STRING_LENGTH, '('); - /* If there isn't one, we're dealing with nominally a native probe */ + /* If there isn't one, we're dealing with nominally a BMPv1 or v2 probe */ if (!opening_paren) { /* Knowing this, let's see if there are enough bytes for a version string, and if there are.. extract it */ if (description_len > BMP_PRODUCT_STRING_LENGTH) { @@ -192,7 +192,7 @@ void bmp_read_product_version(libusb_device_descriptor_s *device_descriptor, lib *product = strdup(description); } } else { - /* Otherwise, we've got a non-native probe, so find the closing paren for the probe type */ + /* Otherwise, we've got a non-bmp probe, so find the closing paren for the probe type */ const char *const closing_paren = strchr(opening_paren, ')'); /* If we didn't find one, we've got a problem */ if (!closing_paren) { @@ -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..a9e16f8727c 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -354,6 +354,19 @@ const cable_desc_s cable_desc[] = { .bb_swdio_in_pin = MPSSE_CS, .name = "hifive1", }, + { + /* + * 1bitSquared Icebreaker FT2232H + * Direct connection on Interface A, JTAG only + */ + .vendor = 0x0403U, + .product = 0x6010U, + .interface = INTERFACE_A, + .init.data[0] = MPSSE_CS | MPSSE_DO | MPSSE_DI, + .init.dirs[0] = MPSSE_CS | MPSSE_DO | MPSSE_SK, + .description = "iCEBreaker V1.0e", + .name = "icebreaker", + }, { /* * https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/ @@ -395,10 +408,29 @@ const cable_desc_s cable_desc[] = { .vendor = 0x15baU, .product = 0x0003U, .interface = INTERFACE_A, + .init.data[0] = 0U, .init.dirs[0] = PIN4, .description = "Olimex OpenOCD JTAG", .name = "arm-usb-ocd", }, + { + /* + * https://www.latticesemi.com/view_document?document_id=51396 + * This interface is JTAG-only. + * DBUS 4 is an activity indicator + * MPSSE_SK (DB0) ----------- TCK + * MPSSE-DO (DB1) ----------- TDI + * MPSSE-DI (DB2) ----------- TDO + * MPSSE-CS (DB3) ----------- TMS + */ + .vendor = 0x0403U, + .product = 0x6010U, + .interface = INTERFACE_A, + .init.data[0] = 0U, + .init.dirs[0] = PIN4, + .description = "Lattice ECP5_5G VERSA Board", + .name = "ecp5-versa", + }, {0}, }; @@ -816,7 +848,7 @@ void ftdi_jtag_tdi_tdo_seq(uint8_t *data_out, const bool final_tms, const uint8_ if (bytes) ftdi_buffer_read(data_out, bytes); /* Read the residual bits */ - if (bits) { + if (bits - (final_tms ? 1U : 0U)) { ftdi_buffer_read_val(data_out[bytes]); /* Because of a quirk in how the FTDI device works, the bits will be MSb aligned, so shift them down */ const size_t shift = bits - (final_tms ? 1U : 0U); 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/remote/protocol_v4.c b/src/platforms/hosted/remote/protocol_v4.c index a57ce0d9f34..b4005e6c48e 100644 --- a/src/platforms/hosted/remote/protocol_v4.c +++ b/src/platforms/hosted/remote/protocol_v4.c @@ -108,6 +108,7 @@ bool remote_v4_adiv5_init(adiv5_debug_port_s *const dp) dp->ap_write = remote_v4_adiv5_ap_write; dp->mem_read = remote_v4_adiv5_mem_read_bytes; dp->mem_write = remote_v4_adiv5_mem_write_bytes; + dp->ensure_idle = remote_v4_jtag_ensure_idle; return true; } @@ -172,3 +173,17 @@ uint64_t remote_v4_supported_families(void) return remote_decode_response(buffer + 1U, 8U); return 0U; } + +void remote_v4_jtag_ensure_idle(adiv5_debug_port_s *dp) +{ + /* Ask remote_dp !JI# to set IR cache to BYPASS (because a reset happened) */ + platform_buffer_write(REMOTE_JTAG_ENSURE_IDLE_STR, sizeof(REMOTE_JTAG_ENSURE_IDLE_STR)); + char buffer[REMOTE_MAX_MSG_SIZE]; + /* Read back the answer and check for errors */ + const int length = platform_buffer_read(buffer, REMOTE_MAX_MSG_SIZE); + if (length < 1 || buffer[0U] != REMOTE_RESP_OK) { + DEBUG_ERROR("%s failed, error %s\n", __func__, length ? buffer + 1 : "with communication"); + return; + } + jtag_devs[dp->dev_index].current_ir = 0xffU; +} diff --git a/src/platforms/hosted/remote/protocol_v4.h b/src/platforms/hosted/remote/protocol_v4.h index e55fd078a1c..c42d8f5ee50 100644 --- a/src/platforms/hosted/remote/protocol_v4.h +++ b/src/platforms/hosted/remote/protocol_v4.h @@ -43,6 +43,7 @@ bool remote_v4_init(void); bool remote_v4_adiv5_init(adiv5_debug_port_s *dp); bool remote_v4_adiv6_init(adiv5_debug_port_s *dp); bool remote_v4_riscv_jtag_init(riscv_dmi_s *dmi); +void remote_v4_jtag_ensure_idle(adiv5_debug_port_s *dp); uint64_t remote_v4_supported_architectures(void); uint64_t remote_v4_supported_families(void); diff --git a/src/platforms/hosted/remote/protocol_v4_defs.h b/src/platforms/hosted/remote/protocol_v4_defs.h index 1fac0569254..ac80d2ce84b 100644 --- a/src/platforms/hosted/remote/protocol_v4_defs.h +++ b/src/platforms/hosted/remote/protocol_v4_defs.h @@ -154,6 +154,13 @@ REMOTE_SOM, REMOTE_ADIV5_PACKET, REMOTE_DP_TARGETSEL, REMOTE_ADIV5_DATA, REMOTE_EOM, 0 \ } +#define REMOTE_JTAG_ENSURE_IDLE 'I' +#define REMOTE_JTAG_ENSURE_IDLE_STR \ + (char[]) \ + { \ + REMOTE_SOM, REMOTE_JTAG_PACKET, REMOTE_JTAG_ENSURE_IDLE, REMOTE_EOM, 0 \ + } + /* ADIv6 acceleration protocol elements */ #define REMOTE_ADIV6_PACKET '6' diff --git a/src/platforms/hosted/remote/protocol_v4_riscv.c b/src/platforms/hosted/remote/protocol_v4_riscv.c index 17642d4f148..c061318a698 100644 --- a/src/platforms/hosted/remote/protocol_v4_riscv.c +++ b/src/platforms/hosted/remote/protocol_v4_riscv.c @@ -51,8 +51,15 @@ bool remote_v4_riscv_check_error( const uint64_t response_code = remote_decode_response(buffer + 1, (size_t)length - 1U); const uint8_t error = response_code & 0xffU; /* If the error part of the response code indicates a fault, store the fault value */ - if (error == REMOTE_ERROR_FAULT) + if (error == REMOTE_ERROR_FAULT) { dmi->fault = response_code >> 8U; + /* + * If we got RV_DMI_TOO_SOON and we're under 8 idle cycles, increase the number + * of idle cycles used to compensate and have the outer code re-run the transfers + */ + if (dmi->fault == RV_DMI_TOO_SOON && dmi->idle_cycles < 8U) + ++dmi->idle_cycles; + } /* If the error part indicates an exception had occurred, make that happen here too */ else if (error == REMOTE_ERROR_EXCEPTION) raise_exception(response_code >> 8U, "Remote protocol exception"); @@ -65,6 +72,8 @@ bool remote_v4_riscv_check_error( /* Check if the firmware is reporting some other kind of error */ else if (buffer[0U] != REMOTE_RESP_OK) DEBUG_ERROR("%s: Firmware reported unexpected error: %c\n", func, buffer[0]); + else + dmi->fault = RV_DMI_SUCCESS; /* Return whether the remote indicated the request was successful */ return buffer[0U] == REMOTE_RESP_OK; } 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/launchpad-icdi/platform.c b/src/platforms/launchpad-icdi/platform.c index 4d496bdec7b..9f535be6d7c 100644 --- a/src/platforms/launchpad-icdi/platform.c +++ b/src/platforms/launchpad-icdi/platform.c @@ -99,8 +99,6 @@ void platform_nrst_set_val(bool assert) { if (assert) { gpio_clear(NRST_PORT, NRST_PIN); - for (volatile size_t i = 0; i < 10000U; ++i) - continue; } else gpio_set(NRST_PORT, NRST_PIN); } diff --git a/src/platforms/meson.build b/src/platforms/meson.build index 5232c208ca8..3e4e6210127 100644 --- a/src/platforms/meson.build +++ b/src/platforms/meson.build @@ -44,6 +44,7 @@ subdir('common/tm4c') # This is used to allow multiple probes share the same source code probe_alias_map = { 'blackpill-f401cc': 'common/blackpill-f4', + 'blackpill-f401cd': 'common/blackpill-f4', 'blackpill-f401ce': 'common/blackpill-f4', 'blackpill-f411ce': 'common/blackpill-f4', 'bluepill': 'stlink', diff --git a/src/platforms/stlink/README.md b/src/platforms/stlink/README.md index 436576b52f2..4c444c2e045 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 @@ -62,6 +62,7 @@ NB: SWDIO/TMS is on P**B**14, not P**A**14. SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", ACTION=="add", RUN+="/stlink-tool" EOF ``` +* For ST-Link v2-1 instead run `stlink-tool -m blackmagic_stlink_firmware.bin` to make the bootloader recognize the firmware as valid thus allowing it to boot on replug ## Reverting to original ST Firmware with running BMP firmware @@ -145,3 +146,15 @@ UART RX/TX On ST-Link v2/2-1 boards with the original bootloader, you can force bootloader entry with asserting [NRST](https://www.carminenoviello.com/2016/02/26/restore-st-link-interface-bad-update-2-26-15-firmware/) of the STM32F103CB of the USB powered board. Several attempts may be needed. + +## ST-Link V2-1 Bootloader Magic + +On boot, the ST-Link V2-1 bootloader checks two conditions to decide whether to jump directly to the application: + +1. Whether the device was reset via Power-on Reset (PoR). If not, it enters DFU mode. +2. Whether the top word of ROM contains the magic number 0xA50027D3. If not, it enters DFU mode. + +For more details, see [this article](https://github.com/GMMan/st-link-hack/blob/master/paper/paper.md#main-function). + +You can either patch this check or include the required value directly in your application. [stlink-tool](https://codeberg.org/blackmagic-debug/stlink-tool) provides the ```-m``` flag to patch the value during firmware upload. + 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..5ec119e34a6 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; @@ -116,10 +115,6 @@ int platform_hwversion(void) void platform_nrst_set_val(bool assert) { gpio_set_val(NRST_PORT, NRST_PIN, !assert); - if (assert) { - for (volatile size_t i = 0; i < 10000; i++) - continue; - } } bool platform_nrst_get_val() diff --git a/src/platforms/swlink/platform.c b/src/platforms/swlink/platform.c index bff34040943..3e71b974ef0 100644 --- a/src/platforms/swlink/platform.c +++ b/src/platforms/swlink/platform.c @@ -113,14 +113,10 @@ void platform_nrst_set_val(bool assert) /* We reuse nTRST as nRST. */ if (assert) { gpio_set_mode(TRST_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TRST_PIN); - /* Wait until requested value is active. */ - while (gpio_get(TRST_PORT, TRST_PIN)) - gpio_clear(TRST_PORT, TRST_PIN); + gpio_clear(TRST_PORT, TRST_PIN); } else { gpio_set_mode(TRST_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, TRST_PIN); - /* Wait until requested value is active .*/ - while (!gpio_get(TRST_PORT, TRST_PIN)) - gpio_set(TRST_PORT, TRST_PIN); + gpio_set(TRST_PORT, TRST_PIN); } } diff --git a/src/remote.c b/src/remote.c index d5a8c03fc04..77837b0a324 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; @@ -192,6 +192,7 @@ static void remote_packet_process_jtag(const char *const packet, const size_t pa remote_dp.error = adiv5_jtag_clear_error; remote_dp.low_access = adiv5_jtag_raw_access; remote_dp.abort = adiv5_jtag_abort; + remote_dp.ensure_idle = adiv5_jtag_ensure_idle; jtagtap_init(); remote_respond(REMOTE_RESP_OK, 0); break; @@ -202,10 +203,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 +215,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 +226,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,37 +241,41 @@ 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; } + case REMOTE_JTAG_ENSURE_IDLE: /* JI = re-cycle IR after indirect resets */ + remote_dp.ensure_idle(&remote_dp); + remote_respond(REMOTE_RESP_OK, 0); + break; + default: remote_respond(REMOTE_RESP_ERR, REMOTE_ERROR_UNRECOGNISED); break; } } -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 +285,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 +313,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 +327,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 +464,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 +485,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 +498,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 +530,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 +541,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 +549,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 +558,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 +577,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 +597,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 +611,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 +629,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 +656,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 +675,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 +695,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 +723,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 +763,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 +789,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 +821,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 +846,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 +857,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 +879,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 +891,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 +900,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 +911,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 +925,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 +964,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..e1109eff123 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 @@ -302,6 +303,7 @@ * need new bits as they are already covered). We will co-ordinate with you in making sure the bit * is unique when going through the PR'ing process. */ +#define REMOTE_JTAG_ENSURE_IDLE 'I' /* ADIv5 accleration protocol elements */ #define REMOTE_ADIV5_PACKET 'A' @@ -512,6 +514,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..7b254df24f4 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -440,10 +440,25 @@ 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 +466,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.h b/src/target/adiv5.h index 719fd8eec17..ab8f24ff373 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -294,5 +294,6 @@ uint32_t adiv5_jtag_read(adiv5_debug_port_s *dp, uint16_t addr); uint32_t adiv5_jtag_raw_access(adiv5_debug_port_s *dp, uint8_t rnw, uint16_t addr, uint32_t value); uint32_t adiv5_jtag_clear_error(adiv5_debug_port_s *dp, bool protocol_recovery); void adiv5_jtag_abort(adiv5_debug_port_s *dp, uint32_t abort); +void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp); #endif /* TARGET_ADIV5_H */ diff --git a/src/target/adiv5_internal.h b/src/target/adiv5_internal.h index b69c0ac51c4..4d529e1062c 100644 --- a/src/target/adiv5_internal.h +++ b/src/target/adiv5_internal.h @@ -203,6 +203,7 @@ struct adiv5_debug_port { uint32_t (*error)(adiv5_debug_port_s *dp, bool protocol_recovery); uint32_t (*low_access)(adiv5_debug_port_s *dp, uint8_t RnW, uint16_t addr, uint32_t value); void (*abort)(adiv5_debug_port_s *dp, uint32_t abort); + void (*ensure_idle)(adiv5_debug_port_s *dp); #if CONFIG_BMDA == 1 void (*ap_regs_read)(adiv5_access_port_s *ap, void *data); diff --git a/src/target/adiv5_jtag.c b/src/target/adiv5_jtag.c index 6667a658866..4da8920be4d 100644 --- a/src/target/adiv5_jtag.c +++ b/src/target/adiv5_jtag.c @@ -1,8 +1,10 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin + * Copyright (C) 2022-2026 1BitSquared + * Modified by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,6 +57,7 @@ void adiv5_jtag_dp_handler(const uint8_t dev_index) dp->low_access = adiv5_jtag_raw_access; dp->error = adiv5_jtag_clear_error; dp->abort = adiv5_jtag_abort; + dp->ensure_idle = adiv5_jtag_ensure_idle; #if CONFIG_BMDA == 1 bmda_jtag_dp_init(dp); #endif @@ -175,3 +178,14 @@ void adiv5_jtag_abort(adiv5_debug_port_s *dp, uint32_t abort) jtag_dev_write_ir(dp->dev_index, IR_ABORT); jtag_dev_shift_dr(dp->dev_index, NULL, (const uint8_t *)&request, 35); } + +void adiv5_jtag_ensure_idle(adiv5_debug_port_s *dp) +{ + /* + * On devices where nRST pulls TRST, the JTAG-DP's IR is reset + * from DPACC/APACC to IDCODE. We want BYPASS in case of daisy-chaining. + */ + jtag_devs[dp->dev_index].current_ir = 0xffU; + /* Ensure the TAP state machine is back in Run/Test Idle */ + jtagtap_soft_reset(); +} 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/at32f43x.c b/src/target/at32f43x.c index 1b7c052b661..42cfcb98e28 100644 --- a/src/target/at32f43x.c +++ b/src/target/at32f43x.c @@ -300,6 +300,9 @@ static bool at32f43_detect(target_s *const target, const uint16_t part_id) target->attach = at32f43_attach; target->detach = at32f43_detach; + /* On AT32F435/F437 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + at32f43_configure_dbgmcu(target); return true; } 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/cortexar.c b/src/target/cortexar.c index 4ec6ad10806..c9c0cd4555c 100644 --- a/src/target/cortexar.c +++ b/src/target/cortexar.c @@ -384,8 +384,6 @@ static_assert(ARRAY_LENGTH(cortexr_spr_types) == ARRAY_LENGTH(cortexr_spr_names) /* clang-format on */ static bool cortexar_check_error(target_s *target); -static void cortexar_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); -static void cortexar_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); static void cortexar_regs_read(target_s *target, void *data); static void cortexar_regs_write(target_s *target, const void *data); @@ -402,9 +400,6 @@ static int cortexar_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int cortexar_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); static void cortexar_config_breakpoint(target_s *target, size_t slot, uint32_t mode, target_addr_t addr); -bool cortexar_attach(target_s *target); -void cortexar_detach(target_s *target); - static const char *cortexar_target_description(target_s *target); static void cortexar_banked_dcc_mode(target_s *const target) @@ -1101,7 +1096,7 @@ static void cortexar_mem_handle_fault(target_s *const target, const char *const * If core is not halted, temporarily halts target and resumes at the end * of the function. */ -static void cortexar_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) +void cortexar_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { /* If system is not halted, halt temporarily within this function. */ const bool halted_in_function = cortexar_halt_and_wait(target); @@ -1223,8 +1218,7 @@ static bool cortexar_mem_write_slow( * If core is not halted, temporarily halts target and resumes at the end * of the function. */ -static void cortexar_mem_write( - target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) +void cortexar_mem_write(target_s *const target, const target_addr64_t dest, const void *const src, const size_t len) { /* If system is not halted, halt temporarily within this function. */ const bool halted_in_function = cortexar_halt_and_wait(target); @@ -1360,6 +1354,7 @@ static void cortexar_reset(target_s *const target) /* If the physical reset pin is not inhibited, use it */ if (!(target->target_options & TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); + platform_delay(1); platform_nrst_set_val(false); /* Precautionary delay as with the Cortex-M code for targets that take a hot minute to come back */ platform_delay(10); diff --git a/src/target/cortexar.h b/src/target/cortexar.h index 25551ce9b16..59c952f903c 100644 --- a/src/target/cortexar.h +++ b/src/target/cortexar.h @@ -36,6 +36,10 @@ #include "general.h" +bool cortexar_attach(target_s *target); +void cortexar_detach(target_s *target); +void cortexar_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); +void cortexar_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len); void cortexar_invalidate_all_caches(target_s *target); #endif /* TARGET_CORTEXAR_H */ diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 280163f71a2..bf822491f4e 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -395,12 +395,14 @@ bool cortexm_probe(adiv5_access_port_s *ap) PROBE(stm32f4_probe); PROBE(stm32h5_probe); PROBE(stm32h7_probe); + PROBE(stm32h7rs_probe); PROBE(stm32mp15_cm4_probe); PROBE(stm32l0_probe); PROBE(stm32l1_probe); PROBE(stm32l4_probe); PROBE(stm32g0_probe); PROBE(stm32wb0_probe); + PROBE(stm32c5_probe); break; case JEP106_MANUFACTURER_CYPRESS: DEBUG_WARN("Unhandled Cypress device\n"); @@ -524,9 +526,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 +576,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); @@ -777,14 +782,20 @@ static void cortexm_pc_write(target_s *target, const uint32_t val) */ static void cortexm_reset(target_s *const target) { + adiv5_access_port_s *ap = cortex_ap(target); + adiv5_debug_port_s *dp = ap->dp; + /* Read DHCSR here to clear S_RESET_ST bit before reset */ target_mem32_read32(target, CORTEXM_DHCSR); /* If the physical reset pin is not inhibited, use it */ if (!(target->target_options & TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); + platform_delay(1); platform_nrst_set_val(false); - /* Some NRF52840 users saw invalid SWD transaction with native/firmware without this delay.*/ + /* Some NRF52840 users saw invalid SWD transaction with bmp-v2/firmware without this delay.*/ platform_delay(10); + if (dp->ensure_idle) + dp->ensure_idle(dp); } /* Check if the reset succeeded */ @@ -795,6 +806,9 @@ static void cortexm_reset(target_s *const target) * Trigger reset by AIRCR. */ target_mem32_write32(target, CORTEXM_AIRCR, CORTEXM_AIRCR_VECTKEY | CORTEXM_AIRCR_SYSRESETREQ); + platform_delay(10); + if (dp->ensure_idle) + dp->ensure_idle(dp); } /* If target needs to do something extra (see Atmel SAM4L for example) */ @@ -864,21 +878,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..d0d534b70a8 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -25,6 +25,7 @@ #include "adiv5.h" #include "riscv_debug.h" #include "icepick.h" +#include "lattice_ecp5.h" #include "jtag_devs.h" const jtag_dev_descr_s dev_descr[] = { @@ -419,6 +420,14 @@ const jtag_dev_descr_s dev_descr[] = { .idmask = 0x0fffffffU, #if ENABLE_DEBUG == 1 .descr = "Nuclei Systems RISC-V DTM.", +#endif + .handler = riscv_jtag_dtm_handler, + }, + { + .idcode = 0xdeadbeefU, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "Hazard3 RISC-V DTM.", #endif .handler = riscv_jtag_dtm_handler, }, @@ -433,6 +442,138 @@ const jtag_dev_descr_s dev_descr[] = { .handler = icepick_router_handler, }, #endif +#ifdef CONFIG_LATTICE + { + .idcode = 0x21111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-12", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x41111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x41112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x41113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5U-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x01111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x01112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x01113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x81111043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-25", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x81112043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-45", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, + { + .idcode = 0x81113043U, + .idmask = 0xffffffffU, +#if ENABLE_DEBUG == 1 + .descr = "LFE5UM5G-85", +#endif + .handler = lattice_ecp5_handler, + .ir_quirks = + { + .ir_length = 8U, + .ir_value = 0x1U, + }, + }, +#endif #if ENABLE_DEBUG == 1 { .idcode = 0x000007a3U, diff --git a/src/target/lattice_common.h b/src/target/lattice_common.h new file mode 100644 index 00000000000..919a1b9f895 --- /dev/null +++ b/src/target/lattice_common.h @@ -0,0 +1,121 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLEs + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_LATTICE_COMMON_H +#define TARGET_LATTICE_COMMON_H + +#include + +// Read out the 32-bit IDCODE of the device +#define CMD_READ_ID 0xe0U +// Read 32-bit user code +#define CMD_USERCODE 0xc0U +// Read out internal status +#define CMD_LSC_READ_STATUS 0x3cU +// Read 1 bit busy flag to check the command execution status +#define CMD_LSC_CHECK_BSY 0xf0U +// Equivalent to toggle PROGRAMN pin +#define CMD_LSC_REFRESH 0x79U +// Enable the Offline configuration mode +#define CMD_ISC_ENABLE 0xc6U +// Enable the Transparent configuration mode +#define CMD_ISC_ENABLE_X 0x74U +// Disable the configuration operation +#define CMD_ISC_DISABLE 0x26U +// Write the 32-bit new USERCODE data to USERCODE register +#define CMD_ISC_PROGRAM_USERCODE 0xc2U +// Bulk erase the memory array base on the access mode and array selection +#define CMD_ISC_ERASE 0x0eU +// Program the DONE bit if the device is in Configuration state. +#define CMD_ISC_PROGRAM_DONE 0x5eU +// Program the Security bit if the device is in Configuration state. +#define CMD_ISC_PROGRAM_SECURITY 0xceU +// Initialize the Address Shift Register +#define CMD_LSC_INIT_ADDRESS 0x46U +// Write the 16 bit Address Register to move the address quickly +#define CMD_LSC_WRITE_ADDRESS 0xb4U +// Program the device the whole bitstream sent in as the command operand +#define CMD_LSC_BITSTREAM_BURST 0x7aU +/* + * Write configuration data to the configuration memory frame at current address + * and post increment the address. + * + * Byte 2~0 of the opcode indicate number of the frames included in the operand field. + */ +#define CMD_LSC_PROG_INCR_RTI 0x82U +// Encrypt the configuration data then write +#define CMD_LSC_PROG_INCR_ENC 0xb6U +// Decompress the configuration data, then write +#define CMD_LSC_PROG_INCR_CMP 0xb8U +// Decompress and Encrypt the configuration data, then write +#define CMD_LSC_PROG_INCR_CNE 0xbaU +// Read back the configuration memory frame selected by the address register and post increment the address +#define CMD_LSC_VERIFY_INCR_RTI 0x6aU +// Modify the Control Register 0 +#define CMD_LSC_PROG_CTRL0 0x22U +// Read the Control Register 0 +#define CMD_LSC_READ_CTRL0 0x20U +// Reset 16-bit frame CRC register to 0x0000 +#define CMD_LSC_RESET_CRC 0x3bU +// Read 16-bit frame CRC register content +#define CMD_LSC_READ_CRC 0x60U +// Program the calculated 32-bit CRC based on configuration bit values only into overall CRC register +#define CMD_LSC_PROG_SED_CRC 0xa2U +// Read the 32-bit SED CRC +#define CMD_LSC_READ_SED_CRC 0xa4U +// Program 64-bit password into the non-volatile memory (Efuse) +#define CMD_LSC_PROG_PASSWORD 0xf1U +// Read out the 64-bit password before activated for verification +#define CMD_LSC_READ_PASSWORD 0xf2U +// Shift in the password to unlock for re-configuration (necessary when password protection feature is active). +#define CMD_LSC_SHIFT_PASSWORD 0xbcU +// Program the 128-bit cipher key into Efuse +#define CMD_LSC_PROG_CIPHER_KEY 0xf3U +// Read out the 128-bit cipher key before activated for verification +#define CMD_LSC_READ_CIPHER_KEY 0xf4U +// Program User Feature, such as Customer ID, I2C Slave Address, Unique ID Header +#define CMD_LSC_PROG_FEATURE 0xe4U +// Read User Feature, such as Customer ID, I2C Slave Address, Unique ID Header +#define CMD_LSC_READ_FEATURE 0xe7U +// Program User Feature Bits, such as CFG port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. +#define CMD_LSC_PROG_FEABITS 0xf8U +// Read User Feature Bits, such as CFH port and pin persistence, PWD_EN, PWD_ALL, DEC_ONLY, Feature Row Lock etc. +#define CMD_LSC_READ_FEABITS 0xfbU +// Program OTP bits, to set Memory Sectors One Time Programmable +#define CMD_LSC_PROG_OTP 0xf9U +// Read OTP bits setting +#define CMD_LSC_READ_OTP 0xfaU +// Enable SPI Programming via JTAG +#define CMD_LSC_BACKGROUND_SPI 0x3aU + +#endif /* TARGET_LATTICE_COMMON_H */ diff --git a/src/target/lattice_ecp5.c b/src/target/lattice_ecp5.c new file mode 100644 index 00000000000..8b510aee253 --- /dev/null +++ b/src/target/lattice_ecp5.c @@ -0,0 +1,747 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLEs + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "buffer_utils.h" +#include "jtag_scan.h" +#include "jtagtap.h" +#include "spi.h" +#include "sfdp.h" +#include "gdb_packet.h" +#include "lattice_common.h" +#include "lattice_ecp5.h" + +#define REGISTER_FIELD(reg, mask, shift) (((reg) >> (shift)) & (mask)) + +#define ECP5_STATUS_TRANSPARENT_MASK 1U +#define ECP5_STATUS_TRANSPARENT_SHIFT 0U +#define ECP5_STATUS_TRANSPARENT(reg) REGISTER_FIELD(reg, ECP5_STATUS_TRANSPARENT_MASK, ECP5_STATUS_TRANSPARENT_SHIFT) +#define ECP5_STATUS_TARGET_MASK 0x7U +#define ECP5_STATUS_TARGET_SHIFT 1U +#define ECP5_STATUS_TARGET(reg) REGISTER_FIELD(reg, ECP5_STATUS_TARGET_MASK, ECP5_STATUS_TARGET_SHIFT) +#define ECP5_STATUS_JTAG_ACTIVE_MASK 1U +#define ECP5_STATUS_JTAG_ACTIVE_SHIFT 4U +#define ECP5_STATUS_JTAG_ACTIVE(reg) REGISTER_FIELD(reg, ECP5_STATUS_JTAG_ACTIVE_MASK, ECP5_STATUS_JTAG_ACTIVE_SHIFT) +#define ECP5_STATUS_PASSWORD_PROTECTED_MASK 1U +#define ECP5_STATUS_PASSWORD_PROTECTED_SHIFT 5U +#define ECP5_STATUS_PASSWORD_PROTECTED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PASSWORD_PROTECTED_MASK, ECP5_STATUS_PASSWORD_PROTECTED_SHIFT) +#define ECP5_STATUS_INTERNAL0_MASK 1U +#define ECP5_STATUS_INTERNAL0_SHIFT 6U +#define ECP5_STATUS_INTERNAL0(reg) REGISTER_FIELD(reg, ECP5_STATUS_INTERNAL0_MASK, ECP5_STATUS_INTERNAL0_SHIFT) +#define ECP5_STATUS_DECRYPT_ENABLED_MASK 1U +#define ECP5_STATUS_DECRYPT_ENABLED_SHIFT 7U +#define ECP5_STATUS_DECRYPT_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_DECRYPT_ENABLED_MASK, ECP5_STATUS_DECRYPT_ENABLED_SHIFT) +#define ECP5_STATUS_DONE_MASK 1U +#define ECP5_STATUS_DONE_SHIFT 8U +#define ECP5_STATUS_DONE(reg) REGISTER_FIELD(reg, ECP5_STATUS_DONE_MASK, ECP5_STATUS_DONE_SHIFT) +#define ECP5_STATUS_ISC_ENABLED_MASK 1U +#define ECP5_STATUS_ISC_ENABLED_SHIFT 9U +#define ECP5_STATUS_ISC_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_ISC_ENABLED_MASK, ECP5_STATUS_ISC_ENABLED_SHIFT) +#define ECP5_STATUS_WRITE_ENABLED_MASK 1U +#define ECP5_STATUS_WRITE_ENABLED_SHIFT 10U +#define ECP5_STATUS_WRITE_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_WRITE_ENABLED_MASK, ECP5_STATUS_WRITE_ENABLED_SHIFT) +#define ECP5_STATUS_READ_ENABLED_MASK 1U +#define ECP5_STATUS_READ_ENABLED_SHIFT 11U +#define ECP5_STATUS_READ_ENABLED(reg) REGISTER_FIELD(reg, ECP5_STATUS_READ_ENABLED_MASK, ECP5_STATUS_READ_ENABLED_SHIFT) +#define ECP5_STATUS_BUSY_MASK 1U +#define ECP5_STATUS_BUSY_SHIFT 12U +#define ECP5_STATUS_BUSY(reg) REGISTER_FIELD(reg, ECP5_STATUS_BUSY_MASK, ECP5_STATUS_BUSY_SHIFT) +#define ECP5_STATUS_FAILURE_MASK 1U +#define ECP5_STATUS_FAILURE_SHIFT 13U +#define ECP5_STATUS_FAILURE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FAILURE_MASK, ECP5_STATUS_FAILURE_SHIFT) +#define ECP5_STATUS_FEATURES_OTP_MASK 1U +#define ECP5_STATUS_FEATURES_OTP_SHIFT 14U +#define ECP5_STATUS_FEATURES_OTP(reg) REGISTER_FIELD(reg, ECP5_STATUS_FEATURES_OTP_MASK, ECP5_STATUS_FEATURES_OTP_SHIFT) +#define ECP5_STATUS_ENCRYPTED_ONLY_MASK 1U +#define ECP5_STATUS_ENCRYPTED_ONLY_SHIFT 15U +#define ECP5_STATUS_ENCRYPTED_ONLY(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_ENCRYPTED_ONLY_MASK, ECP5_STATUS_ENCRYPTED_ONLY_SHIFT) +#define ECP5_STATUS_PASSWORD_ENABLED_MASK 1U +#define ECP5_STATUS_PASSWORD_ENABLED_SHIFT 16U +#define ECP5_STATUS_PASSWORD_ENABLED(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PASSWORD_ENABLED_MASK, ECP5_STATUS_PASSWORD_ENABLED_SHIFT) +#define ECP5_STATUS_INTERNAL1_MASK 0x7U +#define ECP5_STATUS_INTERNAL1_SHIFT 17U +#define ECP5_STATUS_INTERNAL1(reg) REGISTER_FIELD(reg, ECP5_STATUS_INTERNAL1_MASK, ECP5_STATUS_INTERNAL1_SHIFT) +#define ECP5_STATUS_ENCRYPT_PREAMBLE_MASK 1U +#define ECP5_STATUS_ENCRYPT_PREAMBLE_SHIFT 20U +#define ECP5_STATUS_ENCRYPT_PREAMBLE(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_ENCRYPT_PREAMBLE_MASK, ECP5_STATUS_ENCRYPT_PREAMBLE_SHIFT) +#define ECP5_STATUS_STANDARD_PREAMBLE_MASK 1U +#define ECP5_STATUS_STANDARD_PREAMBLE_SHIFT 21U +#define ECP5_STATUS_STANDARD_PREAMBLE(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_STANDARD_PREAMBLE_MASK, ECP5_STATUS_STANDARD_PREAMBLE_SHIFT) +#define ECP5_STATUS_PRIMARY_CFG_FAIL_MASK 1U +#define ECP5_STATUS_PRIMARY_CFG_FAIL_SHIFT 22U +#define ECP5_STATUS_PRIMARY_CFG_FAIL(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_PRIMARY_CFG_FAIL_MASK, ECP5_STATUS_PRIMARY_CFG_FAIL_SHIFT) +#define ECP5_STATUS_BSE_ERROR_MASK 0x7U +#define ECP5_STATUS_BSE_ERROR_SHIFT 23U +#define ECP5_STATUS_BSE_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_BSE_ERROR_MASK, ECP5_STATUS_BSE_ERROR_SHIFT) +#define ECP5_STATUS_EXEC_ERROR_MASK 1U +#define ECP5_STATUS_EXEC_ERROR_SHIFT 26U +#define ECP5_STATUS_EXEC_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_EXEC_ERROR_MASK, ECP5_STATUS_EXEC_ERROR_SHIFT) +#define ECP5_STATUS_ID_ERROR_MASK 1U +#define ECP5_STATUS_ID_ERROR_SHIFT 27U +#define ECP5_STATUS_ID_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_ID_ERROR_MASK, ECP5_STATUS_ID_ERROR_SHIFT) +#define ECP5_STATUS_INVALID_COMMAND_MASK 1U +#define ECP5_STATUS_INVALID_COMMAND_SHIFT 28U +#define ECP5_STATUS_INVALID_COMMAND(reg) \ + REGISTER_FIELD(reg, ECP5_STATUS_INVALID_COMMAND_MASK, ECP5_STATUS_INVALID_COMMAND_SHIFT) +#define ECP5_STATUS_SED_ERROR_MASK 1U +#define ECP5_STATUS_SED_ERROR_SHIFT 29U +#define ECP5_STATUS_SED_ERROR(reg) REGISTER_FIELD(reg, ECP5_STATUS_SED_ERROR_MASK, ECP5_STATUS_SED_ERROR_SHIFT) +#define ECP5_STATUS_BYPASS_MODE_MASK 1U +#define ECP5_STATUS_BYPASS_MODE_SHIFT 30U +#define ECP5_STATUS_BYPASS_MODE(reg) REGISTER_FIELD(reg, ECP5_STATUS_BYPASS_MODE_MASK, ECP5_STATUS_BYPASS_MODE_SHIFT) +#define ECP5_STATUS_FLOW_MODE_MASK 1U +#define ECP5_STATUS_FLOW_MODE_SHIFT 31U +#define ECP5_STATUS_FLOW_MODE(reg) REGISTER_FIELD(reg, ECP5_STATUS_FLOW_MODE_MASK, ECP5_STATUS_FLOW_MODE_SHIFT) + +#define ECP5_CTRL0_MSPI_CLK_MASK 0x1fU +#define ECP5_CTRL0_MSPI_CLK_SHIFT 0U +#define ECP5_CTRL0_MSPI_CLK(reg) REGISTER_FIELD(reg, ECP5_CTRL0_MSPI_CLK_MASK, ECP5_CTRL0_MSPI_CLK_SHIFT) +#define ECP5_CTRL0_SLEW_MASK 0x3U +#define ECP5_CTRL0_SLEW_SHIFT 6U +#define ECP5_CTRL0_SLEW(reg) REGISTER_FIELD(reg, ECP5_CTRL0_SLEW_MASK, ECP5_CTRL0_SLEW_SHIFT) +#define ECP5_CTRL0_RSVD0_MASK 0x1ffU +#define ECP5_CTRL0_RSVD0_SHIFT 8U +#define ECP5_CTRL0_RSVD0(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD0_MASK, ECP5_CTRL0_RSVD0_SHIFT) +#define ECP5_CTRL0_PDONE_MASK 0x3U +#define ECP5_CTRL0_PDONE_SHIFT 18U +#define ECP5_CTRL0_PDONE(reg) REGISTER_FIELD(reg, ECP5_CTRL0_PDONE_MASK, ECP5_CTRL0_PDONE_SHIFT) +#define ECP5_CTRL0_RSVD1_MASK 0x80U +#define ECP5_CTRL0_RSVD1_SHIFT 20U +#define ECP5_CTRL0_RSVD1(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD1_MASK, ECP5_CTRL0_RSVD1_SHIFT) +#define ECP5_CTRL0_NDR_MASK 1U +#define ECP5_CTRL0_NDR_SHIFT 28U +#define ECP5_CTRL0_NDR(reg) REGISTER_FIELD(reg, ECP5_CTRL0_NDR_MASK, ECP5_CTRL0_NDR_SHIFT) +#define ECP5_CTRL0_WAKEUP_TRANS_MASK 1U +#define ECP5_CTRL0_WAKEUP_TRANS_SHIFT 29U +#define ECP5_CTRL0_WAKEUP_TRANS(reg) REGISTER_FIELD(reg, ECP5_CTRL0_WAKEUP_TRANS_MASK, ECP5_CTRL0_WAKEUP_TRANS_SHIFT) +#define ECP5_CTRL0_RSVD2_MASK 0x3U +#define ECP5_CTRL0_RSVD2_SHIFT 30U +#define ECP5_CTRL0_RSVD2(reg) REGISTER_FIELD(reg, ECP5_CTRL0_RSVD2_MASK, ECP5_CTRL0_RSVD2_SHIFT) + +#define ECP5_SRAM_BASE 0x00000000U +#define ECP5_FLASH_BASE 0x04000000U + +static const uint8_t ecp5_spi_unlock[2U] = {0xfeU, 0x68U}; + +typedef struct ecp5_ctx { + uint8_t device_index; + uint8_t *cmd_buffer; + uint8_t *data_buffer; + uint16_t buffer_len; +} ecp5_ctx_s; + +typedef struct ecp5_device { + uint32_t idcode; + uint32_t bitstream_len; + uint8_t frame_len; +} ecp5_device_s; + +static const ecp5_device_s devices[] = { + // LEF5-12 + {.idcode = 0x21111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5-25 + {.idcode = 0x41111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5UM-25 + {.idcode = 0x01111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5UM5G-25 + {.idcode = 0x81111043U, .bitstream_len = 677500U, .frame_len = 74U}, + // LEF5-45 + {.idcode = 0x41112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5UM-45 + {.idcode = 0x01112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5UM5G-45 + {.idcode = 0x81112043U, .bitstream_len = 1217500U, .frame_len = 106U}, + // LEF5-85 + {.idcode = 0x41113043U, .bitstream_len = 2293750U, .frame_len = 142U}, + // LEF5UM-85 + {.idcode = 0x01113043U, .bitstream_len = 2293750U, .frame_len = 142U}, + // LEF5UM5G-85 + {.idcode = 0x81113043U, .bitstream_len = 2293750U, .frame_len = 142U}, +}; + +static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv); +static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv); +static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv); + +static const command_s ecp5_cmd_list[] = { + {"status", ecp5_read_reg_status, "Read FPGA status register"}, + {"control", ecp5_read_reg_control, "Read FPGA control register"}, + {"usercode", ecp5_read_reg_usercode, "Read FPGA USERCODE register"}, + {NULL, NULL, NULL}, +}; + +static void ecp5_free_ctx(void *priv); +static uint32_t ecp5_read32(uint8_t dev_index, uint8_t cmd); + +static bool ecp5_attach(target_s *target); +static bool ecp5_check_error(target_s *target); +static void ecp5_reset(target_s *target); +static bool ecp5_enter_flash(target_s *target); +static bool ecp5_exit_flash(target_s *target); + +static bool ecp5_spi_flash_prepare(target_flash_s *flash); +static bool ecp5_spi_flash_done(target_flash_s *flash); +static void ecp5_spi_read(target_s *target, uint16_t command, target_addr_t address, void *buffer, size_t length); +static void ecp5_spi_write( + target_s *target, uint16_t command, target_addr_t address, const void *buffer, size_t length); +static void ecp5_spi_run_command(target_s *target, uint16_t command, target_addr_t address); +static void ecp5_spi_xfr_jtag(target_s *target, uint8_t *data_out, const uint8_t *data_in, size_t length); + +static bool ecp5_sram_done(target_flash_s *flash); +static bool ecp5_sram_erase(target_flash_s *flash, target_addr_t addr, size_t length); +static bool ecp5_sram_write(target_flash_s *flash, target_addr_t dest, const void *buffer, size_t length); + +void lattice_ecp5_handler(const uint8_t dev_index) +{ + target_s *target = target_new(); + target->driver = "Lattice"; + target->core = "ECP5"; + target->priv = calloc(1U, sizeof(ecp5_ctx_s)); + + if (!target->priv) { + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + target->priv_free = ecp5_free_ctx; + target->attach = ecp5_attach; + target->check_error = ecp5_check_error; + target->reset = ecp5_reset; + target->enter_flash_mode = ecp5_enter_flash; + target->exit_flash_mode = ecp5_exit_flash; + target_add_commands(target, ecp5_cmd_list, target->driver); + + for (size_t dev = 0U; dev < ARRAY_LENGTH(devices); ++dev) { + if (devices[dev].idcode == jtag_devs[dev_index].jd_idcode) { + target_flash_s *flash = calloc(1U, sizeof(*flash)); + + if (!flash) { + DEBUG_ERROR("calloc: %s: failed to allocate flash\n", __func__); + return; + } + + flash->length = devices[dev].bitstream_len; + flash->start = ECP5_SRAM_BASE; + flash->blocksize = flash->length; + flash->writesize = flash->length; // devices[dev].frame_len; + flash->done = ecp5_sram_done; + flash->erase = ecp5_sram_erase; + flash->write = ecp5_sram_write; + + target_add_flash(target, flash); + break; + } + } + + ecp5_ctx_s *ctx = target->priv; + ctx->device_index = dev_index; + // Setup the command/data buffers + ctx->buffer_len = 4100U; + ctx->data_buffer = calloc(1, ctx->buffer_len); + ctx->cmd_buffer = calloc(1, ctx->buffer_len); + + if (!ctx->data_buffer || !ctx->cmd_buffer) + DEBUG_ERROR("calloc: failed in %s\n", __func__); +} + +static void ecp5_free_ctx(void *const priv) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)priv; + + free(ctx->cmd_buffer); + free(ctx->data_buffer); + free(priv); +} + +static uint32_t ecp5_read32(const uint8_t dev_index, const uint8_t cmd) +{ + uint8_t data[4U]; + jtag_dev_write_ir(dev_index, cmd); + jtag_dev_shift_dr(dev_index, data, NULL, 32U); + return read_le4(data, 0U); +} + +static bool ecp5_attach(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + const jtag_dev_s *const device = &jtag_devs[ctx->device_index]; + + if (ECP5_STATUS_ENCRYPTED_ONLY(status)) + DEBUG_WARN("This FPGA only accepts encrypted bitstreams!\n"); + + if (ECP5_STATUS_DONE(status)) + DEBUG_INFO("FPGA is configured\n"); + + if (device->dr_postscan) { + DEBUG_WARN("Transparent SPI Flash not possible, not first device in the chain\n"); + return true; + } + + ecp5_enter_flash(target); + + // Create a synthetic flash object so we can shell out to `spi_flash_prepare` to enter transparent SPI mode + target_flash_s flash; + flash.t = target; + ecp5_spi_flash_prepare(&flash); + + spi_flash_id_s flash_id; + ecp5_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0U, &flash_id, sizeof(flash_id)); + + /* If we read out valid Flash information, set up a region for it */ + if (flash_id.manufacturer != 0xffU && flash_id.type != 0xffU && flash_id.capacity != 0xffU) { + const uint32_t capacity = 1U << flash_id.capacity; + DEBUG_INFO("SPI Flash: mfr = %02" PRIx8 ", type = %02" PRIx8 ", capacity = %08" PRIx32 "\n", + flash_id.manufacturer, flash_id.type, capacity); + spi_flash_s *const spi_flash = + bmp_spi_add_flash(target, ECP5_FLASH_BASE, capacity, ecp5_spi_read, ecp5_spi_write, ecp5_spi_run_command); + target_flash_s *const target_flash = &spi_flash->flash; + target_flash->prepare = ecp5_spi_flash_prepare; + target_flash->done = ecp5_spi_flash_done; + } else + DEBUG_INFO("Flash identification failed\n"); + + // Make sure to reset the FPGA so we don't leave it in a coma + ecp5_spi_flash_done(&flash); + ecp5_exit_flash(target); + + return true; +} + +static bool ecp5_check_error(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + + return !(ECP5_STATUS_BSE_ERROR(status) || ECP5_STATUS_ID_ERROR(status) || ECP5_STATUS_EXEC_ERROR(status) || + ECP5_STATUS_PRIMARY_CFG_FAIL(status) || ECP5_STATUS_FAILURE(status) || ECP5_STATUS_INVALID_COMMAND(status)); +} + +static void ecp5_reset(target_s *const target) +{ + // NOTE: BMDA doesn't handle flash finalization properly when in CLI mode, so we need to do so manually + for (target_flash_s *flash = target->flash; flash != NULL; flash = flash->next) { + if (flash->operation != FLASH_OPERATION_NONE) + flash->done(flash); + } +} + +static bool ecp5_enter_flash(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + // Enter Offline configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_ENABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + // Erase configuration SRAM + jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); + jtag_proc.jtagtap_cycle(false, false, 50U); + // Reset the CRC + jtag_dev_write_ir(dev_index, CMD_LSC_RESET_CRC); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Wait for the configuration to be erased + while (ECP5_STATUS_BUSY(ecp5_read32(dev_index, CMD_LSC_READ_STATUS))) + platform_delay(100U); + + return true; +} + +static bool ecp5_exit_flash(target_s *const target) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + const uint32_t status = ecp5_read32(dev_index, CMD_LSC_READ_STATUS); + + const bool result = + (ECP5_STATUS_BSE_ERROR(status) || ECP5_STATUS_ID_ERROR(status) || ECP5_STATUS_EXEC_ERROR(status) || + ECP5_STATUS_PRIMARY_CFG_FAIL(status) || ECP5_STATUS_FAILURE(status) || ECP5_STATUS_INVALID_COMMAND(status)); + + if (result) + DEBUG_ERROR("Bitstream programming failed: %" PRIu32 "\n", status); + + return !result; +} + +static bool ecp5_spi_flash_prepare(target_flash_s *flash) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)flash->t->priv; + const uint8_t dev_index = ctx->device_index; + + // Exit Offline configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Enter background SPI programming mode + jtag_dev_write_ir(dev_index, CMD_LSC_BACKGROUND_SPI); + jtag_dev_shift_dr(dev_index, NULL, ecp5_spi_unlock, 16U); + jtag_proc.jtagtap_cycle(false, false, 50U); + + return true; +} + +static bool ecp5_spi_flash_done(target_flash_s *flash) +{ + target_s *const target = flash->t; + ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + /* + * The ECP5 doesn't have any way to exit SPI background mode, so we need to reset the whole + * device. + */ + jtag_dev_write_ir(dev_index, CMD_LSC_REFRESH); + jtag_proc.jtagtap_cycle(false, false, 50U); + + return ecp5_check_error(target); +} + +static void ecp5_spi_read(target_s *const target, const uint16_t command, const target_addr_t address, + void *const buffer, const size_t length) +{ + const ecp5_ctx_s *ctx = (ecp5_ctx_s *)target->priv; + size_t offset = 0U; + ctx->cmd_buffer[offset++] = SPI_FLASH_OPCODE(command); + if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { + ctx->cmd_buffer[offset++] = (address & 0xff0000U) >> 16U; + ctx->cmd_buffer[offset++] = (address & 0x00ff00U) >> 8U; + ctx->cmd_buffer[offset++] = address & 0x0000ffU; + } + + const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; + for (size_t dummy = 0U; dummy < dummy_len; ++dummy) + ctx->cmd_buffer[offset++] = 0U; + + memset(ctx->cmd_buffer + offset, 0, length); + + ecp5_spi_xfr_jtag(target, ctx->data_buffer, ctx->cmd_buffer, length + offset); + memcpy(buffer, ctx->data_buffer + offset, length); +} + +static void ecp5_spi_write(target_s *const target, const uint16_t command, const target_addr_t address, + const void *const buffer, const size_t length) +{ + const ecp5_ctx_s *ctx = (ecp5_ctx_s *)target->priv; + size_t offset = 0U; + ctx->cmd_buffer[offset++] = SPI_FLASH_OPCODE(command); + if ((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) { + ctx->cmd_buffer[offset++] = (address & 0xff0000U) >> 16U; + ctx->cmd_buffer[offset++] = (address & 0x00ff00U) >> 8U; + ctx->cmd_buffer[offset++] = address & 0x0000ffU; + } + + const size_t dummy_len = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT; + for (size_t dummy = 0U; dummy < dummy_len; ++dummy) + ctx->cmd_buffer[offset++] = 0U; + + // Guard in the case buffer is `NULL` + if (buffer) + memcpy(ctx->cmd_buffer + offset, buffer, length); + + ecp5_spi_xfr_jtag(target, NULL, ctx->cmd_buffer, length + offset); +} + +static void ecp5_spi_run_command(target_s *const target, const uint16_t command, const target_addr_t address) +{ + ecp5_spi_write(target, command, address, NULL, 0UL); +} + +static void ecp5_spi_xfr_jtag( + target_s *const target, uint8_t *const data_out, const uint8_t *const data_in, const size_t length) +{ + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + const jtag_dev_s *const device = &jtag_devs[dev_index]; + + /* Switch into Shift-DR */ + jtagtap_shift_dr(); + + uint8_t tap_out; + for (size_t idx = 0U; idx < length; ++idx) { + const uint8_t tap_in = reverse_bits8(data_in[idx]); + jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_prescan, &tap_in, 8U); + if (data_out) + data_out[idx] = reverse_bits8(tap_out); + } + + if (data_out) { + /* Fixup the TDO to TDI skew caused by extra devices on the JTAG chain in bypass mode */ + for (size_t scan = 0U; scan < device->dr_prescan; ++scan) { + uint8_t trailing_bit = 0U; + for (size_t offset = 0U; offset < length; ++offset) { + const size_t index = length - 1U - offset; + const uint8_t carry_bit = data_out[index] & 0x80U; + data_out[index] <<= 1U; + data_out[index] |= trailing_bit >> 7U; + trailing_bit = carry_bit; + } + } + } + + DEBUG_PROTO("%s: %" PRIu32 " cycles\n", __func__, (uint32_t)length * 8U); + + if (device->dr_prescan) { + /* Squeeze the trailing bits from the SPI transaction out of the chain */ + uint8_t trailing_bits[4]; + jtag_proc.jtagtap_tdi_tdo_seq(trailing_bits, true, NULL, device->dr_prescan); + + if (data_out) { + /* forcibly re-align the data buffer's chakra */ + size_t bit_len = length * 8U; + for (size_t offset = bit_len - device->dr_prescan, idx = 0; offset < bit_len; ++offset, ++idx) { + const size_t output_byte = offset >> 3U; /* Divide by 8 */ + const size_t output_bit = 7U - (offset & 7U); + + const size_t input_byte = idx >> 3U; + const size_t input_bit = idx & 7U; + + const uint8_t trailing_bit = trailing_bits[input_byte] >> input_bit; + data_out[output_byte] |= trailing_bit << output_bit; + } + } + } + + /* Now go through Update-DR and back to Idle */ + jtagtap_return_idle(1U); +} + +static bool ecp5_sram_done(target_flash_s *const flash) +{ + target_s *const target = flash->t; + ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + if (flash->operation == FLASH_OPERATION_WRITE) { + // Exit configuration mode + jtag_dev_write_ir(dev_index, CMD_ISC_DISABLE); + jtag_proc.jtagtap_cycle(false, false, 50U); + } + + return ecp5_check_error(target); +} + +static bool ecp5_sram_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length) +{ + (void)addr; + (void)length; + + const target_s *const target = flash->t; + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + + // Erase configuration SRAM + jtag_dev_write_ir(dev_index, CMD_ISC_ERASE); + jtag_proc.jtagtap_cycle(false, false, 50U); + + // Wait for the configuration to be erased + while (ECP5_STATUS_BUSY(ecp5_read32(dev_index, CMD_LSC_READ_STATUS))) + platform_delay(100U); + + // Reset the configuration CRC SRAM + jtag_dev_write_ir(dev_index, CMD_LSC_RESET_CRC); + jtag_proc.jtagtap_cycle(false, false, 50U); + + return true; +} + +static bool ecp5_sram_write( + target_flash_s *const flash, const target_addr_t dest, const void *const buffer, const size_t length) +{ + (void)dest; + + const target_s *const target = flash->t; + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint8_t dev_index = ctx->device_index; + const jtag_dev_s *const device = &jtag_devs[dev_index]; + + // Write bitstream to SRAM + jtag_dev_write_ir(dev_index, CMD_LSC_BITSTREAM_BURST); + jtag_proc.jtagtap_cycle(false, false, 50U); + + /* Switch into Shift-DR */ + jtagtap_shift_dr(); + /* Now we're in Shift-DR, clock out 1's till we hit the right device in the chain */ + jtag_proc.jtagtap_tdi_seq(false, ones, device->dr_prescan); + + uint8_t tap_out; + const uint8_t *const data_in = buffer; + for (size_t idx = 0U; idx < length; ++idx) { + const uint8_t tap_in = reverse_bits8(data_in[idx]); + jtag_proc.jtagtap_tdi_tdo_seq(&tap_out, (idx + 1U) == length && !device->dr_postscan, &tap_in, 8U); + + if (idx % 8192U) + DEBUG_TARGET("%s: %" PRIu32 "/%" PRIu32 " bytes written\n", __func__, (uint32_t)idx, (uint32_t)length); + } + + /* Make sure we're in Exit1-DR having clocked out 1's for any more devices on the chain */ + jtag_proc.jtagtap_tdi_seq(true, ones, device->dr_postscan); + /* Now go through Update-DR and back to Idle */ + jtagtap_return_idle(1U); + + return true; +} + +static bool ecp5_read_reg_status(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t status_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_STATUS); + +#if CONFIG_LATTICE_ECP5_DECODE + gdb_outf("Transparent: %" PRIu32 "\n", ECP5_STATUS_TRANSPARENT(status_register)); + gdb_outf("Configuration Target: %s\n", ECP5_STATUS_TARGET(status_register) ? "eFUSE" : "SRAM"); + gdb_outf("JTAG Active: %" PRIu32 "\n", ECP5_STATUS_JTAG_ACTIVE(status_register)); + gdb_outf("Password Protected: %" PRIu32 "\n", ECP5_STATUS_PASSWORD_PROTECTED(status_register)); + gdb_outf("Internal: %" PRIu32 "\n", ECP5_STATUS_INTERNAL0(status_register)); + gdb_outf("Encryption Enabled: %" PRIu32 "\n", ECP5_STATUS_DECRYPT_ENABLED(status_register)); + gdb_outf("Configuration Success: %" PRIu32 "\n", ECP5_STATUS_DONE(status_register)); + gdb_outf("ISC Enabled: %" PRIu32 "\n", ECP5_STATUS_ISC_ENABLED(status_register)); + gdb_outf("Configuration Writable: %" PRIu32 "\n", ECP5_STATUS_WRITE_ENABLED(status_register)); + gdb_outf("Configuration Readable: %" PRIu32 "\n", ECP5_STATUS_READ_ENABLED(status_register)); + gdb_outf("Configuration Busy: %" PRIu32 "\n", ECP5_STATUS_BUSY(status_register)); + gdb_outf("Last Command Failed: %" PRIu32 "\n", ECP5_STATUS_FAILURE(status_register)); + gdb_outf("Features are OTP: %" PRIu32 "\n", ECP5_STATUS_FEATURES_OTP(status_register)); + gdb_outf("Encrypted Bitstream Only: %" PRIu32 "\n", ECP5_STATUS_ENCRYPTED_ONLY(status_register)); + gdb_outf("Password Protection Enabled: %" PRIu32 "\n", ECP5_STATUS_PASSWORD_ENABLED(status_register)); + gdb_outf("Internal: %" PRIu32 "\n", ECP5_STATUS_INTERNAL1(status_register)); + gdb_outf("Encrypted Preamble: %" PRIu32 "\n", ECP5_STATUS_ENCRYPT_PREAMBLE(status_register)); + gdb_outf("Standard Preamble: %" PRIu32 "\n", ECP5_STATUS_STANDARD_PREAMBLE(status_register)); + gdb_outf("Primary Bitstream Failure: %" PRIu32 "\n", ECP5_STATUS_PRIMARY_CFG_FAIL(status_register)); + gdb_outf("BSE Status:\n"); + switch (ECP5_STATUS_BSE_ERROR(status_register)) { + case 0x0U: + gdb_outf("\tNo Errors\n"); + break; + case 0x1U: + gdb_outf("\tID Error\n"); + break; + case 0x2U: + gdb_outf("\tIllegal Command\n"); + break; + case 0x3U: + gdb_outf("\tCRC Error\n"); + break; + case 0x4U: + gdb_outf("\tPreamble Error\n"); + break; + case 0x5U: + gdb_outf("\tConfiguration Aborted By User\n"); + break; + case 0x6U: + gdb_outf("\tData Overflow\n"); + break; + case 0x7U: + gdb_outf("\tConfiguration too big for device SRAM\n"); + break; + } + gdb_outf("Execution Error: %" PRIu32 "\n", ECP5_STATUS_EXEC_ERROR(status_register)); + gdb_outf("ID Error: %" PRIu32 "\n", ECP5_STATUS_ID_ERROR(status_register)); + gdb_outf("Invalid Command: %" PRIu32 "\n", ECP5_STATUS_INVALID_COMMAND(status_register)); + gdb_outf("SED Error: %" PRIu32 "\n", ECP5_STATUS_SED_ERROR(status_register)); + gdb_outf("Bypass Mode: %" PRIu32 "\n", ECP5_STATUS_BYPASS_MODE(status_register)); + gdb_outf("Flow Through Mode: %" PRIu32 "\n", ECP5_STATUS_FLOW_MODE(status_register)); +#else /* CONFIG_LATTICE_ECP5_DECODE */ + gdb_outf("Status: %08" PRIx32 "\n", status_register); +#endif + + return true; +} + +static bool ecp5_read_reg_control(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t control_register = ecp5_read32(ctx->device_index, CMD_LSC_READ_CTRL0); + +#if CONFIG_LATTICE_ECP5_DECODE + gdb_outf("MSPI Clock Divider: %" PRIu32 "\n", ECP5_CTRL0_MSPI_CLK(control_register)); + gdb_outf("\tSlew Rate: "); + switch (ECP5_CTRL0_SLEW(control_register)) { + case 0x0U: + gdb_outf("Slow\n"); + break; + case 0x1U: + gdb_outf("Medium\n"); + break; + default: + gdb_outf("Fast\n"); + break; + } + gdb_outf("\tPROGRAM_DONE: "); + switch (ECP5_CTRL0_PDONE(control_register)) { + case 0x2U: + gdb_outf("Overload with BYPASS\n"); + break; + case 0x3U: + gdb_outf("Overload with FLOW_THROUGH\n"); + break; + default: + gdb_outf("No Overload\n"); + break; + } + gdb_outf("\tNDR/TransFR: %" PRIu32 "\n", ECP5_CTRL0_NDR(control_register)); + gdb_outf("Wakeup Transparent: %" PRIu32 "\n", ECP5_CTRL0_WAKEUP_TRANS(control_register)); + +#else /* CONFIG_LATTICE_ECP5_DECODE */ + gdb_outf("Control: %08" PRIx32 "\n", control_register); +#endif + + return true; +} + +static bool ecp5_read_reg_usercode(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + + const ecp5_ctx_s *const ctx = (ecp5_ctx_s *)target->priv; + const uint32_t usercode = ecp5_read32(ctx->device_index, CMD_USERCODE); + + gdb_outf("USERCODE: %08" PRIx32 "\n", usercode); + + return true; +} diff --git a/src/target/lattice_ecp5.h b/src/target/lattice_ecp5.h new file mode 100644 index 00000000000..9cc884f04e5 --- /dev/null +++ b/src/target/lattice_ecp5.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2026 1BitSquared + * Written by Aki Van Ness + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLEs + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_LATTICE_ECP5_H +#define TARGET_LATTICE_ECP5_H + +#include + +void lattice_ecp5_handler(uint8_t dev_index); + +#endif /* TARGET_LATTICE_ECP5_H */ diff --git a/src/target/meson.build b/src/target/meson.build index be10d0d317a..53fca2389a2 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -95,11 +95,12 @@ if is_firmware_build 'stm': 'STM32 (and clones) parts', 'ti': 'Texas Instruments parts', 'xilinx': 'Xilinx parts', + 'lattice': 'Lattice parts', } # Check to see if the set of enabled targets is all of them and the # firmware is to be built for a STM32F1 based probe (requires a restriction) - if probe in ['bluepill', 'native', 'stlink', 'swlink'] and enabled_targets.length() == target_names.keys().length() + if probe in ['bluepill', 'bmp-v2', 'stlink', 'swlink'] and enabled_targets.length() == target_names.keys().length() warning('Disabling some targets as your build will not fit the target probe otherwise') enabled_targets = ['cortexm', 'lpc', 'nrf', 'nxp', 'renesas', 'rp', 'sam', 'stm', 'ti'] elif probe in ['f072'] and enabled_targets.length() == target_names.keys().length() @@ -200,6 +201,18 @@ target_hc32 = declare_dependency( dependencies: target_cortexm, ) +target_lattice_defs = ['-DCONFIG_LATTICE=1'] +if get_option('ecp5_decode_registers') + target_lattice_defs += '-DCONFIG_LATTICE_ECP5_DECODE=1' +endif + +target_lattice = declare_dependency( + sources: files( + 'lattice_ecp5.c', + ), + compile_args: [target_lattice_defs], +) + target_lpc = declare_dependency( sources: files( 'lpc11xx.c', @@ -330,6 +343,7 @@ target_stm = declare_dependency( 'stm32g0.c', 'stm32h5.c', 'stm32h7.c', + 'stm32h7rs.c', 'stm32l0.c', 'stm32l4.c', 'stm32mp15.c', @@ -370,7 +384,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, ) @@ -439,6 +453,7 @@ libbmd_target_deps = [ target_efm, target_gd32, target_hc32, + target_lattice, target_lpc, target_mm32, target_nrf, 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/nrf51.c b/src/target/nrf51.c index 6e8c1862139..da9252294fe 100644 --- a/src/target/nrf51.c +++ b/src/target/nrf51.c @@ -157,6 +157,8 @@ bool nrf51_probe(target_s *const target) uint32_t ram_size = target_mem32_read32(target, NRF52_INFO_RAM); target->driver = "nRF52"; target->target_options |= TOPT_INHIBIT_NRST; + /* On nRF52 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target_add_ram32(target, 0x20000000U, ram_size * 1024U); nrf51_add_flash(target, 0, page_size * code_size, page_size); nrf51_add_flash(target, NRF51_UICR, page_size, page_size); diff --git a/src/target/nrf54l.c b/src/target/nrf54l.c index 7d9e16411db..617962d2222 100644 --- a/src/target/nrf54l.c +++ b/src/target/nrf54l.c @@ -118,6 +118,8 @@ bool nrf54l_probe(target_s *const target) case ID_NRF54L: target->driver = "nRF54L"; target->target_options |= TOPT_INHIBIT_NRST; + /* On nRF54L SoC, Cortex-M33 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; default: return false; diff --git a/src/target/onboard_flash.c b/src/target/onboard_flash.c index 8c7ffceb244..9f15b98493e 100644 --- a/src/target/onboard_flash.c +++ b/src/target/onboard_flash.c @@ -121,16 +121,26 @@ static bool onboard_flash_add(target_s *const target) spi_flash_id_s flash_id; onboard_spi_read(target, SPI_FLASH_CMD_READ_JEDEC_ID, 0, &flash_id, sizeof(flash_id)); +#if defined(BLACKMAGIC) || defined(BLACKMAGICPROBE_V3) /* If it doesn't match up to being the expected device (a Winbond Flash), bail */ if (flash_id.manufacturer != 0xefU) { DEBUG_ERROR( "%s: Expecting Winbond SPI Flash device, manufacturer ID is %02x\n", __func__, flash_id.manufacturer); return false; + } else { + target->core = "Winbond"; } +#else + if (flash_id.manufacturer == 0xffU || flash_id.type == 0xffU || flash_id.capacity == 0xffU) { + DEBUG_ERROR("Flash identification failed\n"); + return false; + } else { + target->core = "25-series"; + } +#endif DEBUG_INFO( "Found Flash chip w/ ID: 0x%02x 0x%02x 0x%02x\n", flash_id.manufacturer, flash_id.type, flash_id.capacity); - target->core = "Windbond"; /* Otherwise add it to the providied target */ bmp_spi_add_flash( target, 0U, 1U << flash_id.capacity, onboard_spi_read, onboard_spi_write, onboard_spi_run_command); diff --git a/src/target/riscv32.c b/src/target/riscv32.c index f77cf410a43..0f2edb4434a 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -63,6 +63,27 @@ #define RV32_MATCH_BEFORE 0x00000000U #define RV32_MATCH_AFTER 0x00040000U +/* Uncompressed instruction encodings for progbuf-based memory access */ +#define RV_LOAD_BYTE 0x00000003U +#define RV_LOAD_HALF 0x00001003U +#define RV_LOAD_WORD 0x00002003U +#define RV_STORE_BYTE 0x00000023U +#define RV_STORE_HALF 0x00001023U +#define RV_STORE_WORD 0x00002023U +/* Only GPR A0 & A1 are used for source/destination (and saved-restored) */ +#define RV_RS1_A0 0x00050000U +#define RV_RS2_A1 0x00b00000U +#define RV_RD_A0 0x00000500U +#define RV_RD_A1 0x00000580U +/* RV32I opcodes: load word/half/byte from address pointed-by a0 into a1 */ +#define RV_LW_A1_A0 (RV_LOAD_WORD | RV_RD_A1 | RV_RS1_A0) // 0x00052583U // lw a1, 0(a0) +#define RV_LH_A1_A0 (RV_LOAD_HALF | RV_RD_A1 | RV_RS1_A0) // 0x00051583U // lh a1, 0(a0) +#define RV_LB_A1_A0 (RV_LOAD_BYTE | RV_RD_A1 | RV_RS1_A0) // 0x00050583U // lb a1, 0(a0) +/* RV32I opcodes: store word/half/byte from a1 into address pointed-by a0 */ +#define RV_SW_A1_A0 (RV_STORE_WORD | RV_RS2_A1 | RV_RS1_A0) // 0x00b52023U // sw a1, 0(a0) +#define RV_SH_A1_A0 (RV_STORE_HALF | RV_RS2_A1 | RV_RS1_A0) // 0x00b51023U // sh a1, 0(a0) +#define RV_SB_A1_A0 (RV_STORE_BYTE | RV_RS2_A1 | RV_RS1_A0) // 0x00b50023U // sb a1, 0(a0) + static size_t riscv32_reg_read(target_s *target, uint32_t reg, void *data, size_t max); static size_t riscv32_reg_write(target_s *target, uint32_t reg, const void *data, size_t max); static void riscv32_regs_read(target_s *target, void *data); @@ -71,6 +92,13 @@ static void riscv32_regs_write(target_s *target, const void *data); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); +bool hazard3_probe(target_s *const target) +{ + target->driver = "Hazard3"; + target_add_ram32(target, 0x0, 131072); + return true; +} + bool riscv32_probe(target_s *const target) { /* 'E' base ISA has 16 GPRs + PC, 'I' base ISA has 32 GPRs + PC */ @@ -102,6 +130,9 @@ bool riscv32_probe(target_s *const target) PROBE(ch32v003x_probe); PROBE(ch32vx_probe); break; + case 0xe77: + PROBE(hazard3_probe); + break; default: break; } @@ -162,6 +193,13 @@ static size_t riscv32_reg_read(target_s *target, const uint32_t reg, void *data, return riscv32_bool_to_4(riscv_csr_read(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, reg - RV_CSR_GDB_OFFSET, data)); + /* GDB register numbers between 69 and 127 don't map to anything */ + if (reg >= RV_FPU_GDB_CSR_OFFSET + 3U) + return 0; + /* GDB registers 66..68 map to FPU CSR 0x001..0x003 */ + if (reg >= RV_FPU_GDB_CSR_OFFSET) + return riscv32_bool_to_4(riscv_csr_read(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); + /* GDB registers 33..65 map to FPU GPR 0x1020..0x1033 */ if (reg >= RV_FPU_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_read(hart, RV_FP_BASE + reg - RV_FPU_GDB_OFFSET, data)); return 0; @@ -179,6 +217,10 @@ static size_t riscv32_reg_write(target_s *const target, const uint32_t reg, cons return riscv32_bool_to_4(riscv_csr_write(hart, RV_DPC, data)); if (reg >= RV_CSR_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, reg - RV_CSR_GDB_OFFSET, data)); + if (reg >= RV_FPU_GDB_CSR_OFFSET + 3U) + return 0; + if (reg >= RV_FPU_GDB_CSR_OFFSET) + return riscv32_bool_to_4(riscv_csr_write(hart, RV_FP_CTRL_BASE + reg - RV_FPU_GDB_CSR_OFFSET, data)); if (reg >= RV_FPU_GDB_OFFSET) return riscv32_bool_to_4(riscv_csr_write(hart, RV_FP_BASE + reg - RV_FPU_GDB_OFFSET, data)); return 0; @@ -567,6 +609,124 @@ static void riscv32_sysbus_mem_write( riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); } +static void riscv32_progbuf_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) +{ + /* Figure out the maximal width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + const uint8_t access_length = 1U << access_width; + + uint32_t progbuf_read = RV_LOAD_BYTE; + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: + progbuf_read = RV_LB_A1_A0; + break; + case RV_MEM_ACCESS_16_BIT: + progbuf_read = RV_LH_A1_A0; + break; + case RV_MEM_ACCESS_32_BIT: + progbuf_read = RV_LW_A1_A0; + break; + default: + return; + } + /* Fill the program buffer */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_read)) + return; + /* Append literal ebreak (if impebreak is not reached) */ + if (hart->progbuf_size > 1U) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK)) + return; + } + + uint32_t a0_save = 0; + uint32_t a1_save = 0; + riscv_csr_read(hart, RV_GPR_A0, &a0_save); + riscv_csr_read(hart, RV_GPR_A1, &a1_save); + + uint8_t *const data = (uint8_t *)dest; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Write the source address to DATA0 */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, src + offset)) + break; + /* Copy the source address from DATA0 to GPR A0 and launch the progbuf postexec */ + bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | + RV_GPR_A0); + /* Wait for both the register write and progbuf execution to complete */ + result &= riscv_command_wait_complete(hart); + if (!result) + break; + uint32_t value = 0; + result = riscv_csr_read(hart, RV_GPR_A1, &value); + if (!result) + break; + riscv32_unpack_data(data + offset, value, access_width); + } + + riscv_csr_write(hart, RV_GPR_A0, &a0_save); + riscv_csr_write(hart, RV_GPR_A1, &a1_save); +} + +static void riscv32_progbuf_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + const uint8_t access_length = 1U << access_width; + + uint32_t progbuf_write = RV_STORE_BYTE; + switch (access_width) { + case RV_MEM_ACCESS_8_BIT: + progbuf_write = RV_SB_A1_A0; + break; + case RV_MEM_ACCESS_16_BIT: + progbuf_write = RV_SH_A1_A0; + break; + case RV_MEM_ACCESS_32_BIT: + progbuf_write = RV_SW_A1_A0; + break; + default: + return; + } + /* Fill the program buffer */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE, progbuf_write)) + return; + /* Append literal ebreak (if impebreak is not reached) */ + if (hart->progbuf_size > 1U) { + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK)) + return; + } + + uint32_t a0_save = 0; + uint32_t a1_save = 0; + riscv_csr_read(hart, RV_GPR_A0, &a0_save); + riscv_csr_read(hart, RV_GPR_A1, &a1_save); + + const uint8_t *const data = (const uint8_t *)src; + for (size_t offset = 0; offset < len; offset += access_length) { + /* Prepare the destination address in GPR A0 */ + const uint32_t dest_a0 = dest + offset; + if (!riscv_csr_write(hart, RV_GPR_A0, &dest_a0)) + break; + + /* Pack the data to write into GPR A1 */ + uint32_t value = riscv32_pack_data(data + offset, access_width); + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) + break; + /* Copy the write value from DATA0 to GPR A1 and launch the progbuf postexec */ + bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, + RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC | RV_REG_ACCESS_32_BIT | + RV_GPR_A1); + result &= riscv_command_wait_complete(hart); + if (!result) + break; + } + + riscv_csr_write(hart, RV_GPR_A0, &a0_save); + riscv_csr_write(hart, RV_GPR_A1, &a1_save); +} + void riscv32_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { /* If we're asked to do a 0-byte read, do nothing */ @@ -578,6 +738,8 @@ void riscv32_mem_read(target_s *const target, void *const dest, const target_add riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_read(hart, dest, src, len); + else if (hart->flags & RV_HART_FLAG_MEMORY_PROGBUF) + riscv32_progbuf_mem_read(hart, dest, src, len); else riscv32_abstract_mem_read(hart, dest, src, len); @@ -620,6 +782,8 @@ void riscv32_mem_write(target_s *const target, const target_addr64_t dest, const riscv_hart_s *const hart = riscv_hart_struct(target); if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) riscv32_sysbus_mem_write(hart, dest, src, len); + else if (hart->flags & RV_HART_FLAG_MEMORY_PROGBUF) + riscv32_progbuf_mem_write(hart, dest, src, len); else riscv32_abstract_mem_write(hart, dest, src, len); } diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 0dd02dc76e2..0047b91f1c1 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -37,6 +37,7 @@ #include "gdb_reg.h" #include "riscv_debug.h" #include "buffer_utils.h" +#include "semihosting.h" #include @@ -50,10 +51,9 @@ * https://github.com/riscv/riscv-debug-spec/blob/master/riscv-debug-stable.pdf */ -#define RV_DM_CONTROL 0x10U -#define RV_DM_STATUS 0x11U -#define RV_DM_NEXT_DM 0x1dU -#define RV_DM_PROGBUF_BASE 0x20U +#define RV_DM_CONTROL 0x10U +#define RV_DM_STATUS 0x11U +#define RV_DM_NEXT_DM 0x1dU #define RV_DM_CTRL_ACTIVE (1U << 0U) #define RV_DM_CTRL_SYSTEM_RESET (1U << 1U) @@ -110,9 +110,6 @@ /* tdata2 -> selected trigger configuration register 2 */ #define RV_TRIG_DATA_2 0x7a2U -/* GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O */ -#define RV_GPR_A0 0x100aU - /* * Instructions for reading and writing CSRs through a0 * CSRR -> CSR Read, abuses the CSRRS atomic read and set bits instruction @@ -122,7 +119,15 @@ */ #define RV_CSRR_A0 0x00002573U #define RV_CSRW_A0 0x00051073U -#define RV_EBREAK 0x00100073U + +/* + * A semihosting call is three consecutive uncompressed instructions: + * 0x01f01013 slli zero, zero 0x1f; + * 0x00100073 ebreak; + * 0x40705013 srai zero, zero, 7. + */ +#define RV_ENTRY_NOP 0x01f01013U +#define RV_EXIT_NOP 0x40705013U #define RV_VENDOR_JEP106_CONT_MASK 0x7fffff80U #define RV_VENDOR_JEP106_CODE_MASK 0x7fU @@ -293,7 +298,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. @@ -674,6 +679,9 @@ static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart) /* Now use the data count bits to divine an initial guess on the platform width */ data_registers &= RV_DM_ABST_STATUS_DATA_COUNT; DEBUG_INFO("Hart has %" PRIu32 " data registers and %u progbuf registers\n", data_registers, hart->progbuf_size); + /* Memory access using just 1 progbuf slot is possible */ + if (hart->progbuf_size >= 1U) + hart->flags |= RV_HART_FLAG_MEMORY_PROGBUF; /* Check we have at least enough data registers for arg0 */ if (data_registers >= 4) hart->access_width = 128U; @@ -782,7 +790,7 @@ static bool riscv_csr_progbuf_read(riscv_hart_s *const hart, const uint16_t reg, /* Wait for both the register read and progbuf execution to complete */ result &= riscv_command_wait_complete(hart); /* Extract the data read out by the GPR read */ - uint32_t a0_value[3]; + uint32_t a0_value[4]; result &= riscv_csr_read_data(hart, a0_value, hart->access_width); /* Now try to read out the requested data from a0 at the requested size */ result &= riscv_csr_read(hart, RV_GPR_A0 | (reg & RV_CSR_FORCE_MASK), data); @@ -837,7 +845,7 @@ static bool riscv_csr_write_data(riscv_hart_s *const hart, const void *const dat static bool riscv_csr_progbuf_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data) { /* Read out a0 to keep it safe as the actions below clobber it */ - uint32_t a0_value[3]; + uint32_t a0_value[4]; if (!riscv_csr_read(hart, RV_GPR_A0, a0_value)) return false; @@ -894,6 +902,31 @@ bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *c return true; } +static target_addr64_t riscv_pc_read(riscv_hart_s *const hart) +{ + target_addr64_t data = 0U; + riscv_csr_read(hart, RV_DPC, &data); + return data; +} + +static bool riscv_hostio_request(target_s *const target) +{ + /* Read out syscall number from a0/x10 and first argument from a1/x11 */ + riscv_hart_s *const hart = riscv_hart_struct(target); + uint64_t syscall = 0U; + riscv_csr_read(hart, RV_GPR_A0, &syscall); + uint64_t a1 = 0U; + riscv_csr_read(hart, RV_GPR_A1, &a1); + + /* Hand off to the main semihosting implementation */ + const int32_t result = semihosting_request(target, syscall, a1); + + /* Write the result back to the target */ + riscv_csr_write(hart, RV_GPR_A0, &result); + /* Return if the request was in any way interrupted */ + return target->tc->interrupted; +} + uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length) { /* Grab the Hart's most maxmimally aligned possible write width */ @@ -968,6 +1001,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 } } @@ -984,7 +1024,7 @@ static void riscv_hart_memory_access_type(target_s *const target) !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) return; /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ - hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + hart->flags |= RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); /* System Bus also means the target can have memory read without halting */ target->target_options |= TOPT_NON_HALTING_MEM_IO; /* Make sure the system bus is not in any kind of error state */ @@ -1047,12 +1087,24 @@ bool riscv_attach(target_s *const target) return false; /* We then need to halt the hart so the attach process can function */ riscv_halt_request(target); + /* Clear any stale triggers (after halting) */ + for (size_t trigger = 0U; trigger < hart->triggers; trigger++) { + const uint32_t tdata1 = 0U; + const uint32_t tdata2 = 0U; + riscv_config_trigger(hart, trigger, RISCV_TRIGGER_MODE_UNUSED, &tdata1, &tdata2); + } return true; } 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 */ @@ -1106,6 +1158,19 @@ static void riscv_halt_resume(target_s *target, const bool step) } if (!riscv_csr_write(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &stepping_config)) return; + /* Step over coded breakpoints */ + uint32_t dcsr_cause = 0U; + riscv_csr_read(hart, RV_DCSR | RV_CSR_FORCE_32_BIT, &dcsr_cause); + dcsr_cause &= RV_DCSR_CAUSE_MASK; + if (dcsr_cause == RV_HALT_CAUSE_EBREAK) { + /* Read the instruction to resume on */ + target_addr64_t program_counter = riscv_pc_read(hart); + /* If it actually is a breakpoint instruction, update the program counter one past it. */ + if (target_mem32_read32(target, program_counter) == RV_EBREAK) { + program_counter += 4U; + riscv_csr_write(hart, RV_DPC, &program_counter); + } + } /* Request the hart to resume */ if (!riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel | RV_DM_CTRL_RESUME_REQ)) return; @@ -1133,6 +1198,21 @@ static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr6 status &= RV_DCSR_CAUSE_MASK; /* Dispatch on the cause code */ switch (status) { + case RV_HALT_CAUSE_EBREAK: { + /* If we've hit a programmed breakpoint, check for semihosting call. */ + const target_addr64_t program_counter = riscv_pc_read(hart); + uint32_t instructions[3] = {0}; + target_mem64_read(target, &instructions, program_counter - 4U, sizeof(instructions)); + /* A semihosting call is three consecutive uncompressed instructions: slli zero, zero 0x1f; ebreak, srai zero, zero, 7. */ + if (instructions[0] == RV_ENTRY_NOP && instructions[1] == RV_EBREAK && instructions[2] == RV_EXIT_NOP) { + if (riscv_hostio_request(target)) + return TARGET_HALT_REQUEST; + + riscv_halt_resume(target, false); + return TARGET_HALT_RUNNING; + } + return TARGET_HALT_BREAKPOINT; + } case RV_HALT_CAUSE_TRIGGER: /* XXX: Need to read out the triggers to find the one causing this, and grab the watch value */ return TARGET_HALT_BREAKPOINT; @@ -1319,14 +1399,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 +1421,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/riscv_debug.h b/src/target/riscv_debug.h index 0c89137b06d..85b2ce52cce 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -101,6 +101,7 @@ typedef enum riscv_match_size { #define RV_HART_FLAG_MEMORY_ABSTRACT (0U << 4U) #define RV_HART_FLAG_MEMORY_SYSBUS (1U << 4U) #define RV_HART_FLAG_DATA_GPR_ONLY (1U << 5U) /* Hart supports Abstract Data commands for GPRs only */ +#define RV_HART_FLAG_MEMORY_PROGBUF (1U << 6U) typedef struct riscv_dmi riscv_dmi_s; @@ -180,6 +181,7 @@ typedef struct riscv_hart { #define RV_DM_DATA3 0x07U #define RV_DM_ABST_CTRLSTATUS 0x16U #define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_PROGBUF_BASE 0x20U #define RV_DM_SYSBUS_CTRLSTATUS 0x38U #define RV_DM_SYSBUS_ADDR0 0x39U #define RV_DM_SYSBUS_ADDR1 0x3aU @@ -218,6 +220,17 @@ typedef struct riscv_hart { #define RV_GPR_BASE 0x1000U /* The FP base defines the starting register space address for the floating point registers */ #define RV_FP_BASE 0x1020U +/* The FP control base defines the starting register space address for the floating point modes and flags */ +#define RV_FP_CTRL_BASE 0x001U +/* + * GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O, + * as semihosting syscall number and result register per ABI + */ +#define RV_GPR_A0 0x100aU +/* GPR a1, aka x11, is used as semihosting argument */ +#define RV_GPR_A1 0x100bU + +#define RV_EBREAK 0x00100073U /** * The MXL (Machine XLEN) field encodes the native base integer ISA width diff --git a/src/target/semihosting.c b/src/target/semihosting.c index b469dfb48a9..c9bac52721d 100644 --- a/src/target/semihosting.c +++ b/src/target/semihosting.c @@ -337,6 +337,10 @@ int32_t semihosting_open(target_s *const target, const semihosting_s *const requ { const target_addr_t file_name_taddr = request->params[0]; const uint32_t file_name_length = request->params[2]; + /* Explicit bounds check for open_mode_flags */ + const uint32_t open_mode_libc = request->params[1]; + if (open_mode_libc > 11) + return -1; /* * Translation table of fopen() modes to GDB-compatible open flags diff --git a/src/target/stm32f1.c b/src/target/stm32f1.c index b51f910ce2b..99f991cfd2e 100644 --- a/src/target/stm32f1.c +++ b/src/target/stm32f1.c @@ -287,8 +287,10 @@ bool gd32f1_probe(target_s *const target) switch (device_id) { case 0x414U: /* GD32F30x_HD, High density */ case 0x430U: /* GD32F30x_XD, XL-density */ - target->driver = "GD32F3"; + target->driver = "GD32F3 HD/XD"; block_size = 0x800; + /* On this SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; case 0x418U: /* Connectivity Line */ target->driver = "GD32F2"; @@ -298,14 +300,19 @@ bool gd32f1_probe(target_s *const target) if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M23) target->driver = "GD32E230"; /* GD32E230, 64 KiB max in 1 KiB pages */ else if ((target->cpuid & CORTEX_CPUID_PARTNO_MASK) == CORTEX_M4) { - target->driver = "GD32F3"; + target->driver = "GD32F3 MD"; block_size = 0x800; - } else + } else { target->driver = "GD32F1"; /* GD32F103, 1 KiB pages */ + /* On this SoC, Cortex-M3 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + } break; case 0x444U: /* GD32E50x_CL, 512 KiB max in 8 KiB pages */ target->driver = "GD32E5"; block_size = 0x2000; + /* On this SoC, Cortex-M33 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; break; default: return false; @@ -398,10 +405,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); @@ -511,6 +518,8 @@ static bool at32f403a_407_detect(target_s *const target, const uint16_t part_id) } // All parts have 96 KiB SRAM target_add_ram32(target, STM32F1_SRAM_BASE, 96U * 1024U); + /* On AT32F403A/F407 SoC, Cortex-M4F allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target->driver = "AT32F403A/407"; target->part_id = part_id; target->target_options |= STM32F1_TOPT_32BIT_WRITES; @@ -694,6 +703,8 @@ static bool at32f425_detect(target_s *const target, const uint16_t part_id) #endif // All parts have 20 KiB SRAM target_add_ram32(target, 0x20000000, 20U * 1024U); + /* On AT32F425 SoC, Cortex-M4 allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target->driver = "AT32F425"; target->part_id = part_id; target->target_options |= STM32F1_TOPT_32BIT_WRITES; @@ -1026,6 +1037,9 @@ bool stm32f1_probe(target_s *const target) stm32f1_add_flash(target, STM32F1_FLASH_BANK1_BASE, flash_size, block_size); target_add_commands(target, stm32f1_cmd_list, target->driver); + /* On STM32F1 (F3, F0) SoC, Cortex-M3 (M4F, M0) allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */ return stm32f1_configure_dbgmcu(target, dbgmcu_config_taddr); } diff --git a/src/target/stm32f4.c b/src/target/stm32f4.c index b8e20f6ad4d..704a413b169 100644 --- a/src/target/stm32f4.c +++ b/src/target/stm32f4.c @@ -486,6 +486,10 @@ static bool stm32f4_attach(target_s *const target) } } + /* On STM32F4 SoC, Cortex-M4F allows SRAM access without halting */ + if (!is_f7 && target->part_id != ID_STM32F20X) + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Now we have a base RAM map, rebuild the Flash map */ uint8_t split = 0; uint32_t bank_length; diff --git a/src/target/stm32g0.c b/src/target/stm32g0.c index 340da6e14a9..a2d13c5ca27 100644 --- a/src/target/stm32g0.c +++ b/src/target/stm32g0.c @@ -404,6 +404,8 @@ bool stm32g0_probe(target_s *const target) target->attach = stm32g0_attach; target->detach = stm32g0_detach; + /* On this SoC, Cortex-M0+ allows SRAM access without halting */ + target->target_options |= TOPT_NON_HALTING_MEM_IO; target_add_ram32(target, STM32G0_SRAM_BASE, ram_size); /* Even dual Flash bank devices have a contiguous Flash memory space */ stm32g0_add_flash(target, STM32G0_FLASH_BASE, flash_size, STM32G0_FLASH_PAGE_SIZE); diff --git a/src/target/stm32h5.c b/src/target/stm32h5.c index f3d592e8437..6d7f14355d9 100644 --- a/src/target/stm32h5.c +++ b/src/target/stm32h5.c @@ -32,7 +32,7 @@ */ /* - * This file implements support for STM32H5xx series devices, providing + * This file implements support for STM32H5xx and STM32C5xx series devices, providing * memory maps and Flash programming routines. * * References: @@ -40,6 +40,8 @@ * https://www.st.com/resource/en/reference_manual/rm0481-stm32h563h573-and-stm32h562-armbased-32bit-mcus-stmicroelectronics.pdf * RM0492 - STM32H503 Arm®-based 32-bit MCUs, Rev. 2 * https://www.st.com/resource/en/reference_manual/rm0492-stm32h503-line-armbased-32bit-mcus-stmicroelectronics.pdf + * RM0522 - STM32C5xx Arm®-based 32-bit MCUs, Rev. 1 + * https://www.st.com/resource/en/reference_manual/rm0522-stm32c5-series-armbased-32bit-mcus-stmicroelectronics.pdf */ #include "general.h" @@ -82,6 +84,40 @@ #define STM32H523_SRAM3_SIZE 0x00010000U #define STM32H523_SRAM123_SIZE (STM32H523_SRAM1_SIZE + STM32H523_SRAM2_SIZE + STM32H523_SRAM3_SIZE) +/* Memory map constants for STM32C53x/C54x */ +#define STM32C53x_FLASH_BANK1_BASE 0x08000000U +#define STM32C53x_FLASH_BANK2_BASE 0x08020000U +#define STM32C53x_FLASH_BANK_SIZE 0x00020000U +#define STM32C53x_SECTORS_PER_BANK 0x00000010U +#define STM32C53x_SRAM1_BASE 0x0a000000U +#define STM32C53x_SRAM1_SIZE 0x00008000U +#define STM32C53x_SRAM2_BASE 0x0a008000U +#define STM32C53x_SRAM2_SIZE 0x00008000U +#define STM32C53x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C53x_SRAM12_SIZE (STM32C53x_SRAM1_SIZE + STM32C53x_SRAM2_SIZE) +/* Memory map constants for STM32C55x/C56x */ +#define STM32C55x_FLASH_BANK1_BASE 0x08000000U +#define STM32C55x_FLASH_BANK2_BASE 0x08040000U +#define STM32C55x_FLASH_BANK_SIZE 0x00040000U +#define STM32C55x_SECTORS_PER_BANK 0x00000020U +#define STM32C55x_SRAM1_BASE 0x0a000000U +#define STM32C55x_SRAM1_SIZE 0x00010000U +#define STM32C55x_SRAM2_BASE 0x0a010000U +#define STM32C55x_SRAM2_SIZE 0x00010000U +#define STM32C55x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C55x_SRAM12_SIZE (STM32C55x_SRAM1_SIZE + STM32C55x_SRAM2_SIZE) +/* Memory map constants for STM32C59x/C5Ax */ +#define STM32C59x_FLASH_BANK1_BASE 0x08000000U +#define STM32C59x_FLASH_BANK2_BASE 0x08080000U +#define STM32C59x_FLASH_BANK_SIZE 0x00080000U +#define STM32C59x_SECTORS_PER_BANK 0x00000040U +#define STM32C59x_SRAM1_BASE 0x0a000000U +#define STM32C59x_SRAM1_SIZE 0x00020000U +#define STM32C59x_SRAM2_BASE 0x0a020000U +#define STM32C59x_SRAM2_SIZE 0x00020000U +#define STM32C59x_SRAM_ALIAS_BASE 0x20000000U +#define STM32C59x_SRAM12_SIZE (STM32C59x_SRAM1_SIZE + STM32C59x_SRAM2_SIZE) + /* Flash Program and Erase Controller (FPEC) Register Map */ #define STM32H5_FPEC_BASE 0x40022000 #define STM32H5_FPEC_ACCESS_CTRL (STM32H5_FPEC_BASE + 0x000U) @@ -140,6 +176,10 @@ #define ID_STM32H503 0x474U /* Taken from DBGMCU_IDCODE in §59.12.4 of RM0481 rev 2, pg3116 */ #define ID_STM32H523 0x478U +/* Taken from DBGMCU_IDCODE in §49.12.4 of RM0522 rev 1, pg2542 */ +#define ID_STM32C53x 0x44fU +#define ID_STM32C55x 0x44eU +#define ID_STM32C59x 0x45aU typedef struct stm32h5_flash { target_flash_s target_flash; @@ -297,6 +337,88 @@ bool stm32h5_probe(target_s *const target) return true; } +bool stm32c5_probe(target_s *const target) +{ + const adiv5_access_port_s *const ap = cortex_ap(target); + /* Use the partno from the AP always to handle the difference between JTAG and SWD */ + if (ap->partno != ID_STM32C55x && ap->partno != ID_STM32C53x && ap->partno != ID_STM32C59x) + return false; + target->part_id = ap->partno; + + /* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */ + if (!stm32h5_configure_dbgmcu(target)) + return false; + + target->driver = "STM32C5"; + target->attach = stm32h5_attach; + target->detach = stm32h5_detach; + target->mass_erase = stm32h5_mass_erase; + target->enter_flash_mode = stm32h5_enter_flash_mode; + target->exit_flash_mode = stm32h5_exit_flash_mode; + target_add_commands(target, stm32h5_cmd_list, target->driver); + + switch (target->part_id) { + case ID_STM32C53x: + /* + * Build the RAM map. 32+32=64. + * This uses the addresses and sizes found in §2.2.2, Figure 2, pg91 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C53x_SRAM1_BASE, STM32C53x_SRAM1_SIZE); + target_add_ram32(target, STM32C53x_SRAM2_BASE, STM32C53x_SRAM2_SIZE); + target_add_ram32(target, STM32C53x_SRAM_ALIAS_BASE, STM32C53x_SRAM12_SIZE); + + /* + * Build the Flash map. C53X/C54X has one of: + * Flash: 256 KiB as two equal banks (16 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C53x_FLASH_BANK1_BASE, STM32C53x_FLASH_BANK_SIZE, + STM32C53x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C53x_FLASH_BANK2_BASE, STM32C53x_FLASH_BANK_SIZE, + STM32C53x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + case ID_STM32C55x: + /* + * Build the RAM map. 64+64=128. + * This uses the addresses and sizes found in §2.2.2, Figure 3, pg92 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C55x_SRAM1_BASE, STM32C55x_SRAM1_SIZE); + target_add_ram32(target, STM32C55x_SRAM2_BASE, STM32C55x_SRAM2_SIZE); + target_add_ram32(target, STM32C55x_SRAM_ALIAS_BASE, STM32C55x_SRAM12_SIZE); + + /* + * Build the Flash map. C55X/C56X has one of: + * Flash: 512 KiB as two equal banks (32 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C55x_FLASH_BANK1_BASE, STM32C55x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C55x_FLASH_BANK2_BASE, STM32C55x_FLASH_BANK_SIZE, + STM32C55x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + case ID_STM32C59x: + /* + * Build the RAM map. 128+128=256. + * This uses the addresses and sizes found in §2.2.2, Figure 4, pg93 of RM0522 Rev. 1 + */ + target_add_ram32(target, STM32C59x_SRAM1_BASE, STM32C59x_SRAM1_SIZE); + target_add_ram32(target, STM32C59x_SRAM2_BASE, STM32C59x_SRAM2_SIZE); + target_add_ram32(target, STM32C59x_SRAM_ALIAS_BASE, STM32C59x_SRAM12_SIZE); + + /* + * Build the Flash map. C59X has + * Flash: 1 MiB as two equal banks (64 sectors of 8 KiB each) + */ + stm32h5_add_flash(target, STM32C59x_FLASH_BANK1_BASE, STM32C59x_FLASH_BANK_SIZE, + STM32C59x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK1); + stm32h5_add_flash(target, STM32C59x_FLASH_BANK2_BASE, STM32C59x_FLASH_BANK_SIZE, + STM32C59x_SECTORS_PER_BANK | STM32H5_FPEC_CTRL_BANK2); + break; + default: + return false; + } + + return true; +} + static bool stm32h5_attach(target_s *const target) { /* @@ -424,6 +546,15 @@ static const struct { {0x1007U, 'X'}, }; +static const struct { + uint16_t rev_id; + char revision; +} stm32c5_revisions[] = { + {0x1000U, 'A'}, + {0x1001U, 'Z'}, + {0x1003U, 'Y'}, +}; + static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) { (void)argc; @@ -432,6 +563,7 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) const uint32_t idcode = target_mem32_read32(target, STM32H5_DBGMCU_IDCODE); const uint16_t rev_id = (idcode & STM32H5_DBGMCU_IDCODE_REV_MASK) >> STM32H5_DBGMCU_IDCODE_REV_SHIFT; const uint16_t dev_id = idcode & STM32H5_DBGMCU_IDCODE_DEV_MASK; + bool isc5 = false; /* Display the device ID */ switch (dev_id) { @@ -444,13 +576,32 @@ static bool stm32h5_cmd_rev(target_s *target, int argc, const char **argv) case ID_STM32H523: tc_printf(target, "STM32H523/533\n"); break; + case ID_STM32C53x: + tc_printf(target, "STM32C53x/54x\n"); + isc5 = true; + break; + case ID_STM32C55x: + tc_printf(target, "STM32C55x/56x\n"); + isc5 = true; + break; + case ID_STM32C59x: + tc_printf(target, "STM32C59x/5Ax\n"); + isc5 = true; + break; default: tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); } char revision = '?'; - for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { - if (stm32h5_revisions[i].rev_id == rev_id) - revision = stm32h5_revisions[i].revision; + if (!isc5) { + for (size_t i = 0; i < ARRAY_LENGTH(stm32h5_revisions); ++i) { + if (stm32h5_revisions[i].rev_id == rev_id) + revision = stm32h5_revisions[i].revision; + } + } else { + for (size_t i = 0; i < ARRAY_LENGTH(stm32c5_revisions); ++i) { + if (stm32c5_revisions[i].rev_id == rev_id) + revision = stm32c5_revisions[i].revision; + } } tc_printf(target, "Revision %c\n", revision); return true; diff --git a/src/target/stm32h7rs.c b/src/target/stm32h7rs.c new file mode 100644 index 00000000000..ae292720dbd --- /dev/null +++ b/src/target/stm32h7rs.c @@ -0,0 +1,545 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017-2020 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de + * Copyright (C) 2022-2023 1BitSquared + * Copyright (C) 2025-2026 Eric Brombaugh + * Written by Eric Brombaugh + * Based on initial work done by Rachel Mant and zyp. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file implements STM32H7R/S target specific functions for detecting + * the device, providing the XML memory map and Flash memory programming. + * + * References: + * RM0477 - STM32H7Rx/7Sx Arm®-based 32-bit MCUs, Rev. 6 + * https://www.st.com/resource/en/reference_manual/rm0477-stm32h7rx7sx-armbased-32bit-mcus-stmicroelectronics.pdf + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" +#include "stm32_common.h" + +/* Flash Program and Erase Controller Register Map */ +#define FPEC1_BASE 0x52002000U +#define FLASH_ACR 0x000U +#define FLASH_KEYR 0x004U +#define FLASH_CR 0x010U +#define FLASH_SR 0x014U +#define FLASH_IER 0x020U +#define FLASH_ISR 0x024U +#define FLASH_ICR 0x028U +#define FLASH_CRCCR 0x030U +#define FLASH_CRCDATA 0x03cU +#define FLASH_SR_BSY (1U << 0U) +#define FLASH_SR_WBNE (1U << 1U) +#define FLASH_SR_QW (1U << 2U) +#define FLASH_SR_CRC_BUSY (1U << 3U) +#define FLASH_ISR_EOP (1U << 16U) +#define FLASH_ISR_WRPERR (1U << 17U) +#define FLASH_ISR_PGSERR (1U << 18U) +#define FLASH_ISR_STRBERR (1U << 19U) +#define FLASH_ISR_INCERR (1U << 21U) +#define FLASH_ISR_RDSERR (1U << 24U) +#define FLASH_ISR_SNECCERR (1U << 25U) +#define FLASH_ISR_DBECCERR (1U << 26U) +#define FLASH_ISR_CRCEND (1U << 27U) +#define FLASH_ISR_CRCRDERR (1U << 28U) +#define FLASH_ISR_ERROR_READ (FLASH_ISR_RDSERR | FLASH_ISR_SNECCERR | FLASH_ISR_DBECCERR) +#define FLASH_ISR_ERROR_MASK \ + (FLASH_ISR_WRPERR | FLASH_ISR_PGSERR | FLASH_ISR_STRBERR | FLASH_ISR_INCERR | FLASH_ISR_ERROR_READ) +#define FLASH_CR_LOCK (1U << 0U) +#define FLASH_CR_PG (1U << 1U) +#define FLASH_CR_SER (1U << 2U) +#define FLASH_CR_BER (1U << 3U) +#define FLASH_CR_FW (1U << 4U) +#define FLASH_CR_START (1U << 5U) +#define FLASH_CR_SSN_SHIFT 6U +#define FLASH_CR_CRC_EN (1U << 17U) +#define FLASH_CRCCR_ALL_BANK (1U << 7U) +#define FLASH_CRCCR_START_CRC (1U << 16U) +#define FLASH_CRCCR_CLEAN_CRC (1U << 17U) +#define FLASH_CRCCR_CRC_BURST_3 (3U << 20U) + +#define STM32H7RS_FLASH_KEY1 0x45670123U +#define STM32H7RS_FLASH_KEY2 0xcdef89abU + +#define STM32H7RS_OPT_KEY1 0x08192a3bU +#define STM32H7RS_OPT_KEY2 0x4c5d6e7fU + +#define STM32H7RS_FLASH_SIZE 0x1ff1e880U +#define STM32H7RS_FLASH_BANK1_BASE 0x08000000U +#define STM32H7RS_FLASH_BANK_SIZE 0x00010000U +#define NUM_SECTOR_PER_BANK 8U +#define FLASH_SECTOR_SIZE 0x2000U + +/* WWDG base address and register map */ +#define STM32H7RS_WWDG_BASE 0x40002c00U +#define STM32H7RS_WWDG_CR (STM32H7RS_WWDG_BASE + 0x00) +#define STM32H7RS_WWDG_CR_RESET 0x0000007fU + +/* IWDG base address and register map */ +#define STM32H7RS_IWDG_BASE 0x58004800U +#define STM32H7RS_IWDG_KEY (STM32H7RS_IWDG_BASE + 0x00U) +#define STM32H7RS_IWDG_KEY_RESET 0x0000aaaaU + +/* + * Access from processor address space. + * Access via the APB-D is at 0xe00e1000 + */ +#define DBGMCU_IDCODE 0x5c001000U +#define DBGMCU_IDC (DBGMCU_IDCODE + 0U) +#define DBGMCU_CR (DBGMCU_IDCODE + 4U) +#define DBGMCU_APB1FREEZE (DBGMCU_IDCODE + 0x03cU) +#define DBGMCU_APB4FREEZE (DBGMCU_IDCODE + 0x054U) +#define DBGSLEEP_D1 (1U << 0U) +#define DBGSTOP_D1 (1U << 1U) +#define DBGSTBY_D1 (1U << 2U) +#define DBGSTOP_D3 (1U << 7U) +#define DBGSTBY_D3 (1U << 8U) +#define D1DBGCKEN (1U << 21U) +#define D3DBGCKEN (1U << 22U) +#define DBGMCU_APB1FREEZE_WWDG1 (1U << 11U) +#define DBGMCU_APB4FREEZE_IWDG1 (1U << 18U) + +#define STM32H7RS_DBGMCU_IDCODE_DEV_MASK 0x00000fffU +#define STM32H7RS_DBGMCU_IDCODE_REV_SHIFT 16U + +#define ID_STM32H7RS 0x485U /* RM0477 */ + +/* + * Uncomment this to enable DBGMCU setup in attach() and detach() + * This seem to cause problems with reconnecting to the target and is + * also somewhat redundant with similar setup that happens in probe() + */ +//#define CONFIG_EXPERIMENTAL_DBGMCU + +typedef struct stm32h7rs_flash { + target_flash_s target_flash; + uint32_t regbase; +} stm32h7rs_flash_s; + +typedef struct stm32h7rs_priv { + uint32_t dbg_cr; + align_e psize; +} stm32h7rs_priv_s; + +static bool stm32h7rs_uid(target_s *target, int argc, const char **argv); +static bool stm32h7rs_crc(target_s *target, int argc, const char **argv); +static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv); +static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv); + +static const command_s stm32h7rs_cmd_list[] = { + {"psize", stm32h7rs_cmd_psize, "Configure flash write parallelism: (x8|x16|x32|x64(default))"}, + {"uid", stm32h7rs_uid, "Print unique device ID"}, + {"crc", stm32h7rs_crc, "Print CRC of bank 1"}, + {"revision", stm32h7rs_cmd_rev, "Returns the Device ID and Revision"}, + {NULL, NULL, NULL}, +}; + +static bool stm32h7rs_attach(target_s *target); +static void stm32h7rs_detach(target_s *target); +static bool stm32h7rs_flash_erase(target_flash_s *target_flash, target_addr_t addr, size_t len); +static bool stm32h7rs_flash_write(target_flash_s *target_flash, target_addr_t dest, const void *src, size_t len); +static bool stm32h7rs_flash_prepare(target_flash_s *target_flash); +static bool stm32h7rs_flash_done(target_flash_s *target_flash); +static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *print_progess); + +static void stm32h7rs_add_flash(target_s *target, uint32_t addr, size_t length, size_t blocksize) +{ + stm32h7rs_flash_s *flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + + target_flash_s *target_flash = &flash->target_flash; + target_flash->start = addr; + target_flash->length = length; + target_flash->blocksize = blocksize; + target_flash->erase = stm32h7rs_flash_erase; + target_flash->write = stm32h7rs_flash_write; + target_flash->prepare = stm32h7rs_flash_prepare; + target_flash->done = stm32h7rs_flash_done; + target_flash->writesize = 2048; + target_flash->erased = 0xffU; + flash->regbase = FPEC1_BASE; + target_add_flash(target, target_flash); +} + +bool stm32h7rs_probe(target_s *target) +{ + const adiv5_access_port_s *const ap = cortex_ap(target); + if (ap->partno != ID_STM32H7RS) + return false; + + target->part_id = ap->partno; + + /* Save private storage */ + stm32h7rs_priv_s *priv = calloc(1, sizeof(*priv)); + if (!priv) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return false; + } + target->target_storage = priv; + /* Get the current value of the debug control register (and store it for later) */ + priv->dbg_cr = target_mem32_read32(target, DBGMCU_CR); + /* Set up the Flash write/erase parallelism to 64-bit default */ + priv->psize = ALIGN_64BIT; + + target->driver = "STM32H7R/S"; + target->attach = stm32h7rs_attach; + target->detach = stm32h7rs_detach; + target->mass_erase = stm32h7rs_mass_erase; + target_add_commands(target, stm32h7rs_cmd_list, target->driver); + + /* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */ + target_mem32_write32(target, DBGMCU_APB1FREEZE, DBGMCU_APB1FREEZE_WWDG1); + target_mem32_write32(target, DBGMCU_APB4FREEZE, DBGMCU_APB4FREEZE_IWDG1); + /* + * Make sure that both domain D1 and D3 debugging are enabled and that we can keep + * debugging through sleep, stop and standby states for domain D1 + */ + target_mem32_write32(target, DBGMCU_CR, + target_mem32_read32(target, DBGMCU_CR) | DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); + target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); + + /* Build the RAM map */ + switch (target->part_id) { + case ID_STM32H7RS: { + /* Table 6. Memory map and default device memory area attributes RM0477, pg151 */ + target_add_ram32(target, 0x00000000, 0x30000); /* ITCM RAM, 192 KiB */ + target_add_ram32(target, 0x20000000, 0x30000); /* DTCM RAM, 192 KiB */ + target_add_ram32(target, 0x24000000, 0x72000); /* AXI RAM1+2+3+4, 456 KiB [128+128+128+72] contiguous, */ + target_add_ram32(target, 0x30000000, 0x8000); /* AHB SRAM1+2, 32 KiB [16+16] contiguous, */ + break; + } + default: + break; + } + + /* + * Note on SRD from AN5293, 3. System architecture differences between STM32F7 and STM32H7 Series + * > The D3 domain evolved into a domain called SRD domain (or smart-run domain). + */ + + /* Build the Flash map */ + switch (target->part_id) { + case ID_STM32H7RS: + stm32h7rs_add_flash(target, STM32H7RS_FLASH_BANK1_BASE, STM32H7RS_FLASH_BANK_SIZE, FLASH_SECTOR_SIZE); + break; + default: + break; + } + + return true; +} + +static bool stm32h7rs_attach(target_s *target) +{ + if (!cortexm_attach(target)) + return false; +#ifdef CONFIG_EXPERIMENTAL_DBGMCU + /* + * Make sure that both domain D1 and D3 debugging are enabled and that we can keep + * debugging through sleep, stop and standby states for domain D1 - this is duplicated as it's undone by detach. + */ + target_mem32_write32(target, DBGMCU_CR, DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN); + target_mem32_write32(target, STM32H7RS_WWDG_CR, STM32H7RS_WWDG_CR_RESET); + target_mem32_write32(target, STM32H7RS_IWDG_KEY, STM32H7RS_IWDG_KEY_RESET); +#endif + return true; +} + +static void stm32h7rs_detach(target_s *target) +{ +#ifdef CONFIG_EXPERIMENTAL_DBGMCU + /* + * undo DBGMCU setup done in attach() + */ + target_mem32_write32(target, DBGMCU_CR, + target_mem32_read32(target, DBGMCU_CR) & ~(DBGSLEEP_D1 | DBGSTOP_D1 | DBGSTBY_D1 | D1DBGCKEN | D3DBGCKEN)); +#endif + cortexm_detach(target); +} + +static bool stm32h7rs_flash_wait_complete(target_s *const target, const uint32_t regbase) +{ + uint32_t status = FLASH_SR_QW; + uint32_t istatus = 0U; + /* Loop waiting for the queuewait bit to clear and EOP to set, indicating completion of all ongoing operations */ + while (!(istatus & FLASH_ISR_EOP) && (status & FLASH_SR_QW)) { + status = target_mem32_read32(target, regbase + FLASH_SR); + istatus = target_mem32_read32(target, regbase + FLASH_ISR); + /* If an error occurs, make noises */ + if (target_check_error(target)) { + DEBUG_ERROR("%s: error reading status\n", __func__); + return false; + } + } + /* Now the operation's complete, we can check the error bits */ + if (istatus & FLASH_ISR_ERROR_MASK) + DEBUG_ERROR("%s: Flash error: %08" PRIx32 "\n", __func__, istatus); + target_mem32_write32(target, regbase + FLASH_ICR, istatus & (FLASH_ISR_EOP | FLASH_ISR_ERROR_MASK)); + /* Return whether any errors occured */ + return !(istatus & FLASH_ISR_ERROR_MASK); +} + +static bool stm32h7rs_flash_unlock(target_s *const target, const uint32_t regbase) +{ + /* clear any pending flash interrupts that could hurt us */ + uint32_t istatus = target_mem32_read32(target, FPEC1_BASE + FLASH_ISR); + if (istatus & FLASH_ISR_ERROR_MASK) { + DEBUG_INFO("%s: FLASH_ISR %08" PRIx32 " - clearing\n", __func__, istatus); + target_mem32_write32(target, FPEC1_BASE + FLASH_ICR, istatus & FLASH_ISR_ERROR_MASK); + } + + /* Read out the Flash status and tend to any pending conditions */ + const uint32_t status = target_mem32_read32(target, regbase + FLASH_SR); + /* Start by checking if there are any pending ongoing operations */ + if (status & FLASH_SR_QW) { + /* Wait for any pending operations to complete */ + if (!stm32h7rs_flash_wait_complete(target, regbase)) + return false; + } + + /* Unlock the device Flash if not already unlocked (it's an error to re-key the controller if it is) */ + if (target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK) { + /* Enable Flash controller access */ + target_mem32_write32(target, regbase + FLASH_KEYR, STM32H7RS_FLASH_KEY1); + target_mem32_write32(target, regbase + FLASH_KEYR, STM32H7RS_FLASH_KEY2); + } + /* Return whether we were able to put the device into unlocked mode */ + return !(target_mem32_read32(target, regbase + FLASH_CR) & FLASH_CR_LOCK); +} + +static bool stm32h7rs_flash_prepare(target_flash_s *const target_flash) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + + /* Unlock the Flash controller to prepare it for operations */ + return stm32h7rs_flash_unlock(target, flash->regbase); +} + +static bool stm32h7rs_flash_done(target_flash_s *const target_flash) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + /* Lock the Flash controller to complete operations */ + target_mem32_write32(target, flash->regbase + FLASH_CR, FLASH_CR_LOCK); + return true; +} + +static bool stm32h7rs_flash_erase(target_flash_s *const target_flash, target_addr_t addr, const size_t len) +{ + (void)len; + /* Erases are always done one sector at a time - the target Flash API guarantees this */ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + + /* Calculate the sector to erase and set the operation runnning */ + const uint32_t sector = (addr - target_flash->start) / target_flash->blocksize; + const uint32_t ctrl = FLASH_CR_SER | (sector << FLASH_CR_SSN_SHIFT); + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl); + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl | FLASH_CR_START); + + /* Wait for the operation to complete and report errors */ + DEBUG_INFO("Erasing, ctrl = %08" PRIx32 " status = %08" PRIx32 "\n", + target_mem32_read32(target, flash->regbase + FLASH_CR), target_mem32_read32(target, flash->regbase + FLASH_SR)); + + /* Wait for the operation to complete and report errors */ + return stm32h7rs_flash_wait_complete(target, flash->regbase); +} + +static bool stm32h7rs_flash_write( + target_flash_s *const target_flash, const target_addr_t dest, const void *const src, const size_t len) +{ + target_s *target = target_flash->t; + const stm32h7rs_flash_s *const flash = (stm32h7rs_flash_s *)target_flash; + + /* Prepare the Flash write operation */ + const uint32_t ctrl_pg = FLASH_CR_PG; + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg); + + /* Write the data to the Flash */ + for (size_t offset = 0U; offset < len; offset += 16U) { + const size_t amount = MIN(len - offset, 16U); + target_mem32_write(target, dest + offset, ((const uint8_t *)src) + offset, amount); + /* + * If this is the final chunk and the amount is not a multiple of 16 + * bytes, make sure the write is forced to complete per RM0468 §5.3.8 + * "Single write sequence" pg215 + */ + if (amount < 16U) + target_mem32_write32(target, flash->regbase + FLASH_CR, ctrl_pg | FLASH_CR_FW); + + /* wait for QW bit to clear */ + while (target_mem32_read32(target, flash->regbase + FLASH_SR) & FLASH_SR_QW) + continue; + } + + /* Wait for the operation to complete and report errors */ + return stm32h7rs_flash_wait_complete(target, flash->regbase); +} + +static bool stm32h7rs_erase_bank(target_s *const target, const uint32_t reg_base) +{ + if (!stm32h7rs_flash_unlock(target, reg_base)) { + DEBUG_ERROR("Bank erase: Unlock bank failed\n"); + return false; + } + /* BER and start can be merged (§3.3.10). */ + const uint32_t ctrl = FLASH_CR_BER | FLASH_CR_START; + target_mem32_write32(target, reg_base + FLASH_CR, ctrl); + DEBUG_INFO("Mass erase of bank started\n"); + return true; +} + +static bool stm32h7rs_wait_erase_bank( + target_s *const target, platform_timeout_s *const timeout, const uint32_t reg_base) +{ + while (target_mem32_read32(target, reg_base + FLASH_SR) & FLASH_SR_QW) { + if (target_check_error(target)) { + DEBUG_ERROR("mass erase bank: comm failed\n"); + return false; + } + target_print_progress(timeout); + } + return true; +} + +static bool stm32h7rs_check_bank(target_s *const target, const uint32_t reg_base) +{ + uint32_t status = target_mem32_read32(target, reg_base + FLASH_ISR); + if (status & FLASH_ISR_ERROR_MASK) + DEBUG_ERROR("mass erase bank: error sr %" PRIx32 "\n", status); + return !(status & FLASH_ISR_ERROR_MASK); +} + +static bool stm32h7rs_mass_erase(target_s *target, platform_timeout_s *const print_progess) +{ + /* Send mass erase Flash start instruction */ + if (!stm32h7rs_erase_bank(target, FPEC1_BASE)) + return false; + + /* Wait for the banks to finish erasing */ + if (!stm32h7rs_wait_erase_bank(target, print_progess, FPEC1_BASE)) + return false; + + /* Check the banks for final errors */ + return stm32h7rs_check_bank(target, FPEC1_BASE); +} + +static bool stm32h7rs_uid(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + const uint32_t uid_addr = 0x08fff800U; + return stm32_uid(target, uid_addr); +} + +static bool stm32h7rs_crc_bank(target_s *target) +{ + const uint32_t reg_base = FPEC1_BASE; + if (!stm32h7rs_flash_unlock(target, reg_base)) + return false; + + target_mem32_write32(target, reg_base + FLASH_CR, FLASH_CR_CRC_EN); + const uint32_t crc_ctrl = FLASH_CRCCR_CLEAN_CRC | FLASH_CRCCR_CRC_BURST_3 | FLASH_CRCCR_ALL_BANK; + target_mem32_write32(target, reg_base + FLASH_CRCCR, crc_ctrl | FLASH_CRCCR_START_CRC); + uint32_t status = FLASH_SR_CRC_BUSY; + while (status & FLASH_SR_CRC_BUSY) { + status = target_mem32_read32(target, reg_base + FLASH_SR); + if (target_check_error(target)) { + DEBUG_ERROR("CRC comm failed\n"); + return false; + } + uint32_t istatus = target_mem32_read32(target, reg_base + FLASH_ISR); + if (istatus & FLASH_ISR_ERROR_READ) { + DEBUG_ERROR("CRC error status %08" PRIx32 "\n", istatus); + return false; + } + } + return true; +} + +static bool stm32h7rs_crc(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + if (!stm32h7rs_crc_bank(target)) + return false; + uint32_t crc1 = target_mem32_read32(target, FPEC1_BASE + FLASH_CRCDATA); + tc_printf(target, "CRC: 0x%08" PRIx32 "\n", crc1); + return true; +} + +static bool stm32h7rs_cmd_psize(target_s *target, int argc, const char **argv) +{ + if (argc == 1) { + const align_e psize = ((const stm32h7rs_priv_s *)target->target_storage)->psize; + tc_printf(target, "Flash write parallelism: %s\n", stm32_psize_to_string(psize)); + } else { + align_e psize = ALIGN_64BIT; + if (!stm32_psize_from_string(target, argv[1], &psize)) + return false; + ((stm32h7rs_priv_s *)target->target_storage)->psize = psize; + } + return true; +} + +static const struct { + uint16_t rev_id; + char revision; +} stm32h7rs_revisions[] = { + {0x1003U, 'Y'}, + {0x2000U, 'B'}, +}; + +static bool stm32h7rs_cmd_rev(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* DBGMCU identity code register */ + const uint32_t dbgmcu_idc = target_mem32_read32(target, DBGMCU_IDC); + const uint16_t rev_id = dbgmcu_idc >> STM32H7RS_DBGMCU_IDCODE_REV_SHIFT; + const uint16_t dev_id = dbgmcu_idc & STM32H7RS_DBGMCU_IDCODE_DEV_MASK; + + /* Print device */ + switch (dev_id) { + case ID_STM32H7RS: + tc_printf(target, "STM32H7Rx/Sx\n"); + break; + default: + tc_printf(target, "Unknown %s. BMP may not correctly support it!\n", target->driver); + return false; + } + /* Print revision */ + char rev = '?'; + for (size_t i = 0; i < ARRAY_LENGTH(stm32h7rs_revisions); i++) { + /* Check for matching revision */ + if (stm32h7rs_revisions[i].rev_id == rev_id) + rev = stm32h7rs_revisions[i].revision; + } + tc_printf(target, "Revision %c\n", rev); + + return true; +} diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index 0bb0a4e55f8..c05d559975b 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -2,9 +2,10 @@ * This file is part of the Black Magic Debug project. * * Copyright (C) 2015, 2017-2022 Uwe Bonnes - * Copyright (C) 2022-2025 1BitSquared + * Copyright (C) 2022-2026 1BitSquared * Written by Uwe Bonnes * Modified by Rachel Mant + * Modified by Eric Brombaugh * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,6 +47,8 @@ * RM0434 - Multiprotocol wireless 32-bit MCU Arm®-based Cortex®-M4 with * FPU, Bluetooth® Low-Energy and 802.15.4 radio solution Rev 10 * - https://www.st.com/resource/en/reference_manual/rm0434-multiprotocol-wireless-32bit-mcu-armbased-cortexm4-with-fpu-bluetooth-lowenergy-and-802154-radio-solution-stmicroelectronics.pdf + * RM0487 - STM32U3 Series Arm®-based 32-bit MCUs - Reference manual Rev 3 + * - https://www.st.com/resource/en/reference_manual/rm0487-stm32u3-series-armbased-32bit-mcus-stmicroelectronics.pdf */ #include "general.h" @@ -139,6 +142,13 @@ #define STM32L5_PWR_CR1 0x50007000U #define STM32L5_PWR_CR1_VOS (3U << 9U) +#define STM32U3_RCC_AHB1ENR2 0x40030c94U +#define STM32U3_RCC_AHB1ENR2_PWREN (1U << 2U) +#define STM32U3_PWR_VOSR 0x4003080cU +#define STM32U3_PWR_VOSR_R1EN (1 << 0U) +#define STM32U3_PWR_VOSR_R2EN (1 << 1U) +#define STM32U3_PWR_VOSR_R1RDY (1 << 16U) + #define STM32L4_FLAG_DUAL_BANK 0x80U /* TODO: add block size constants for other MCUs */ @@ -198,6 +208,10 @@ * - RM0478, Rev.8 §31.4.8 DP_TARGETID pg 980 * - RM0478, Rev.8 §31.8.1 DBGMCU_IDCODE pg 1011 (at address 0xe0042000) * - RM0478, Rev.8 §31.13.3 CPU1 ROM table PIDR pg 1057 + * References for the U3 parts: + * - RM0487, Rev.3 §57.3.3 DP_TARGETID pg 2745 + * - RM0487, Rev.3 §57.5.1 MCU ROM table PIDR pg 2754 + * - RM0487, Rev.3 §57.12.4 DBGMCU_IDCODE pg 2848 (at address 0xe0044000) * * NB: For WL5x parts, core 2's AP requires using DBGMCU_IDCODE for identification. * The outer ROM table for this core carries the ARM core ID, not the part ID. @@ -213,6 +227,9 @@ #define ID_STM32WLxx 0x497U #define ID_STM32WB35 0x495U /* STM32WB35/55 */ #define ID_STM32WB1x 0x494U +#define ID_STM32U3B5 0x42aU /* STM32U3B5/3C5 */ +#define ID_STM32U356 0x42bU /* STM32U356/366 */ +#define ID_STM32U375 0x454U /* STM32U375/385 */ static bool stm32l4_cmd_erase_bank1(target_s *target, int argc, const char **argv); static bool stm32l4_cmd_erase_bank2(target_s *target, int argc, const char **argv); @@ -241,6 +258,7 @@ typedef enum stm32l4_family { STM32L4_FAMILY_L55x, STM32L4_FAMILY_U5xx, STM32L4_FAMILY_WLxx, + STM32L4_FAMILY_U3xx, } stm32l4_family_e; /* @@ -483,6 +501,33 @@ static stm32l4_device_info_s const stm32l4_device_info[] = { .flash_regs_map = stm32wb_flash_regs_map, .flash_size_reg = STM32L4_FLASH_SIZE_REG, }, + { + .device_id = ID_STM32U3B5, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U3B5/3C5", + .sram1 = 192U + 64U + 320U, /* SRAM1+2+3 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, + { + .device_id = ID_STM32U356, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U356/366", + .sram1 = 128U + 64U, /* SRAM1+2 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, + { + .device_id = ID_STM32U375, + .family = STM32L4_FAMILY_U3xx, + .designator = "STM32U375/385", + .sram1 = 192U + 64U, /* SRAM1+2 continuous */ + .flags = 2U | STM32L4_FLAG_DUAL_BANK, + .flash_regs_map = stm32l5_flash_regs_map, + .flash_size_reg = STM32U5_FLASH_SIZE_REG, + }, { /* Sentinel entry */ .device_id = 0, @@ -493,6 +538,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 +595,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 +622,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) @@ -619,6 +687,23 @@ static void stm32l5_flash_enable(target_s *const target) target_mem32_write32(target, STM32L5_PWR_CR1, pwr_ctrl1); } +/* stm32u3 needs to be put in VOS 1 after reset in order to flash */ +static bool stm32u3_enter_flash_mode(target_s *target) +{ + /* Reset target on flash command */ + /* This saves us if we're interrupted in IRQ context */ + target_reset(target); + /* enable PWR */ + target_mem32_write32(target, STM32U3_RCC_AHB1ENR2, STM32U3_RCC_AHB1ENR2_PWREN); + /* switch to VOS 1 */ + target_mem32_write32(target, STM32U3_PWR_VOSR, STM32U3_PWR_VOSR_R1EN); + /* wait for R1RDY */ + uint32_t vosr = 0; + while ((vosr & STM32U3_PWR_VOSR_R1RDY) == 0U) + vosr = target_mem32_read32(target, STM32U3_PWR_VOSR); + return true; +} + static uint32_t stm32l4_main_sram_length(const target_s *const target) { const stm32l4_priv_s *const priv = (const stm32l4_priv_s *)target->target_storage; @@ -742,6 +827,18 @@ 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; + case ID_STM32U3B5: + case ID_STM32U356: + case ID_STM32U375: + target->enter_flash_mode = stm32u3_enter_flash_mode; + target->target_options |= TOPT_NON_HALTING_MEM_IO; + break; default: break; } @@ -768,7 +865,8 @@ static bool stm32l4_attach(target_s *const target) /* Free any previously built memory map */ target_mem_map_free(target); /* And rebuild the RAM map */ - if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx) + if (device->family == STM32L4_FAMILY_L55x || device->family == STM32L4_FAMILY_U5xx || + device->family == STM32L4_FAMILY_U3xx) target_add_ram32(target, 0x0a000000, (device->sram1 + device->sram2) * 1024U); else target_add_ram32(target, 0x10000000, device->sram2 * 1024U); @@ -847,6 +945,10 @@ static bool stm32l4_attach(target_s *const target) } else stm32l4_add_flash(target, STM32L4_FLASH_BANK1_BASE, flash_len * 1024U, 0x800, UINT32_MAX); + /* On STM32G47x SoC, Cortex-M4F allows SRAM access without halting */ + if (device->device_id == ID_STM32G47) + target->target_options |= TOPT_NON_HALTING_MEM_IO; + /* Clear all errors in the status register. */ stm32l4_flash_write32(target, STM32L4_FPEC_STATUS, stm32l4_flash_read32(target, STM32L4_FPEC_STATUS)); return true; @@ -886,6 +988,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 +1000,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 +1135,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), @@ -1076,6 +1181,18 @@ static bool stm32l4_cmd_option(target_s *const target, const int argc, const cha tc_printf(target, "%s options not implemented!\n", "STM32WLxx"); return false; } + if (target->part_id == ID_STM32U3B5) { + tc_printf(target, "%s options not implemented!\n", "STM32U3B5/3C5"); + return false; + } + if (target->part_id == ID_STM32U356) { + tc_printf(target, "%s options not implemented!\n", "STM32U356/366"); + return false; + } + if (target->part_id == ID_STM32U375) { + tc_printf(target, "%s options not implemented!\n", "STM32U375/385"); + return false; + } const stm32l4_option_bytes_info_s info = stm32l4_get_opt_bytes_info(target->part_id); const uint32_t fpec_base = stm32l4_fpec_base_addr(target); diff --git a/src/target/stm32mp15.c b/src/target/stm32mp15.c index 914d0278a31..7be4f39b6d2 100644 --- a/src/target/stm32mp15.c +++ b/src/target/stm32mp15.c @@ -33,6 +33,8 @@ #include "target_internal.h" #include "cortexm.h" #include "stm32_common.h" +#include "adiv5.h" +#include "cortexar.h" /* Memory map constants for STM32MP15x */ #define STM32MP15_CM4_RETRAM_BASE 0x00000000U @@ -45,6 +47,8 @@ #define STM32MP15_SYSRAM_SIZE 0x00040000U #define STM32MP15_CAN_SRAM_BASE 0x44011000U #define STM32MP15_CAN_SRAM_SIZE 0x00002800U +#define STM32MP15_DRAM_BASE 0xc0000000U +#define STM32MP15_CA7_BOOTROM_BASE 0x00000000U /* Access from processor address space. * Access via the debug APB is at 0xe0081000 over AP1. */ @@ -74,16 +78,25 @@ /* Taken from CM4ROM_PIDRx in 2.3.21 of ES0438 rev 7, pg18 */ #define ID_STM32MP15x_ERRATA 0x450U +#define SWO_BASE 0xe0083000 +#define SWO_ACPR (SWO_BASE + 0x00010) +#define SWO_SPPR (SWO_BASE + 0x000f0) + +#define STM32MP15_AP1_DBGMCU_IDCODE 0xe0081000U + typedef struct stm32mp15_priv { uint32_t dbgmcu_config; + adiv5_access_port_s *ap; } stm32mp15_priv_s; static bool stm32mp15_uid(target_s *target, int argc, const char **argv); static bool stm32mp15_cmd_rev(target_s *target, int argc, const char **argv); +static bool stm32mp15_cmd_swo(target_s *target, int argc, const char **argv); const command_s stm32mp15_cmd_list[] = { {"uid", stm32mp15_uid, "Print unique device ID"}, {"revision", stm32mp15_cmd_rev, "Returns the Device ID and Revision"}, + {"conf_swo", stm32mp15_cmd_swo, "Set up SWO mode <1/2> and divisor <0x42>"}, {NULL, NULL, NULL}, }; @@ -92,7 +105,7 @@ static void stm32mp15_cm4_detach(target_s *target); static bool stm32mp15_ident(target_s *const target, const bool cortexm) { - const adiv5_access_port_s *const ap = cortex_ap(target); + adiv5_access_port_s *const ap = cortex_ap(target); /* Check if the part's a STM32MP15 */ if (ap->partno != ID_STM32MP15x) { /* If it's not a Cortex-M core or it doesn't match the errata ID code, return false */ @@ -100,6 +113,20 @@ static bool stm32mp15_ident(target_s *const target, const bool cortexm) return false; } + if (!cortexm) { + /* + * After Linux has booted on CA7, 0x50081000 (system bus alias) + * becomes unreadable via cortexar_mem_read, so use debug APB on AP1 + */ + uint32_t idcode = 0; + adiv5_mem_read(ap, &idcode, STM32MP15_AP1_DBGMCU_IDCODE, sizeof(idcode)); + const uint16_t dev_id = idcode & STM32MP15_DBGMCU_IDCODE_DEV_MASK; + DEBUG_TARGET("%s: looking at device ID 0x%03x at 0x%08" PRIx32 "\n", __func__, dev_id, + (uint32_t)STM32MP15_AP1_DBGMCU_IDCODE); + if (dev_id == ID_STM32MP15x) { + return true; + } + } /* By now it's established that this is likely an MP15x_CM4, but check that it's not an H74x */ const uint32_t idcode = target_mem32_read32(target, STM32MP15_DBGMCU_IDCODE); const uint16_t dev_id = idcode & STM32MP15_DBGMCU_IDCODE_DEV_MASK; @@ -118,6 +145,55 @@ static bool stm32mp15_ident(target_s *const target, const bool cortexm) return true; } +static bool stm32mp15_cmd_swo(target_s *target, int argc, const char **argv) +{ + (void)argc; + (void)argv; + /* TODO: argv parsing for mode and baudrate */ + stm32mp15_priv_s *priv = (stm32mp15_priv_s *)target->target_storage; + /* Prefer AP1 over AP0 when attached to CA7 (because no CoreSight components are on AP0) */ + adiv5_access_port_s *const ap1 = (priv->ap->apsel == 1U) ? priv->ap : cortex_ap(target); + /* Pin Protocol: change Manchester to UART */ + uint32_t sppr = 0; + adiv5_mem_read(ap1, &sppr, SWO_SPPR, 4); + sppr &= ~(0x3U); + sppr |= 0x2U; + adiv5_mem_write(ap1, SWO_SPPR, &sppr, 4); + + /* + * Prescaler: set to fixed 66; trace clk freq of 133/(66+1) gives ~2Mbaud (+-0.7%) + * assuming AXI clk of 266 and default divisor of 2 + * Or, if you are not restricted by swlink 2.25M, set to fixed 32; 133/(32+1) is ~4Mbaud (+-0.7%) + */ + uint32_t acpr = 0; + adiv5_mem_read(ap1, &acpr, SWO_ACPR, 4); + acpr = 32; + adiv5_mem_write(ap1, SWO_ACPR, &acpr, 4); + + return true; +} + +static void stm32mp15_cm4_setup_apbd_ap(target_s *const target) +{ + adiv5_access_port_s *ap1 = calloc(1, sizeof(*ap1)); + if (!ap1) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + adiv5_access_port_s *const ap2 = cortex_ap(target); + memcpy(ap1, ap2, sizeof(*ap1)); + ap1->refcnt = 0; + + ap1->apsel = 1; // Set to APB-D AP + ap1->idr = adiv5_ap_read(ap1, ADIV5_AP_IDR); + ap1->base = adiv5_ap_read(ap1, ADIV5_AP_BASE_LOW); + ap1->csw = adiv5_ap_read(ap1, ADIV5_AP_CSW); + + adiv5_ap_ref(ap1); + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + priv->ap = ap1; +} + static bool stm32mp15_cm4_configure_dbgmcu(target_s *const target) { /* If we're in the probe phase */ @@ -167,12 +243,88 @@ bool stm32mp15_cm4_probe(target_s *const target) } #ifdef CONFIG_CORTEXAR +/* + * Override memory r/w operations to go via the AXI (AP0) MEM-AP + * (instead of halting the core and using DTRTX, which cortexar_mem_read/write do by default) + */ +static void stm32mp15_ca7_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len) +{ + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + adiv5_mem_read(priv->ap, dest, src, len); +} + +static void stm32mp15_ca7_mem_write(target_s *target, target_addr64_t dest, const void *src, size_t len) +{ + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + adiv5_mem_write(priv->ap, dest, src, len); +} + +static void stm32mp15_ca7_setup_axi_ap(target_s *const target) +{ + stm32mp15_priv_s *priv_storage = (stm32mp15_priv_s *)target->target_storage; + if (!priv_storage) { + /* Allocate target-specific storage */ + priv_storage = calloc(1, sizeof(*priv_storage)); + if (!priv_storage) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + target->target_storage = priv_storage; + } + if (priv_storage->ap) + return; + adiv5_access_port_s *ap0 = calloc(1, sizeof(*ap0)); + if (!ap0) { /* calloc failed: heap exhaustion */ + DEBUG_ERROR("calloc: failed in %s\n", __func__); + return; + } + adiv5_access_port_s *const ap = cortex_ap(target); + memcpy(ap0, ap, sizeof(*ap0)); + ap0->refcnt = 0; + + ap0->apsel = 0; // Set to AXI-AP + ap0->idr = adiv5_ap_read(ap0, ADIV5_AP_IDR); + ap0->base = adiv5_ap_read(ap0, ADIV5_AP_BASE_LOW); + ap0->csw = adiv5_ap_read(ap0, ADIV5_AP_CSW); + + adiv5_ap_ref(ap0); + priv_storage->ap = ap0; +} + +static bool stm32mp15_ca7_attach(target_s *const target) +{ + if (!cortexar_attach(target)) + return false; + stm32mp15_ca7_setup_axi_ap(target); + adiv5_access_port_s *ap0 = (adiv5_access_port_s *)target->target_storage; + if (ap0) { + target->mem_read = stm32mp15_ca7_mem_read; + target->mem_write = stm32mp15_ca7_mem_write; + } + return true; +} + +static void stm32mp15_ca7_detach(target_s *target) +{ + /* Deallocate any extra AP */ + stm32mp15_priv_s *const priv = (stm32mp15_priv_s *)target->target_storage; + if (priv && priv->ap) { + adiv5_ap_unref(priv->ap); + priv->ap = NULL; + target->mem_read = cortexar_mem_read; + target->mem_write = cortexar_mem_write; + } + cortexar_detach(target); +} + bool stm32mp15_ca7_probe(target_s *const target) { if (!stm32mp15_ident(target, false)) return false; target->driver = "STM32MP15"; + target->attach = stm32mp15_ca7_attach; + target->detach = stm32mp15_ca7_detach; target_add_commands(target, stm32mp15_cmd_list, target->driver); /* Figure 4. Memory map from §2.5.2 in RM0436 rev 6, pg158 */ @@ -186,6 +338,12 @@ bool stm32mp15_ca7_probe(target_s *const target) target_add_ram32(target, STM32MP15_CA7_AHBSRAM_ALIAS_BASE, STM32MP15_AHBSRAM_SIZE); target_add_ram32(target, STM32MP15_SYSRAM_BASE, STM32MP15_SYSRAM_SIZE); target_add_ram32(target, STM32MP15_CAN_SRAM_BASE, STM32MP15_CAN_SRAM_SIZE); + + /* DRAM 512 MiB at identity mapping base */ + target_add_ram32(target, STM32MP15_DRAM_BASE, 512U * 1024U * 1024U); + /* Boot ROM (CA7 stays here in Engi boot) */ + target_add_ram32(target, STM32MP15_CA7_BOOTROM_BASE, 128U * 1024U); + return true; } #endif @@ -196,7 +354,13 @@ static bool stm32mp15_cm4_attach(target_s *const target) * Try to attach to the part, and then ensure that the WDTs + WFI and WFE * instructions can't cause problems (this is duplicated as it's undone by detach.) */ - return cortexm_attach(target) && stm32mp15_cm4_configure_dbgmcu(target); + if (!cortexm_attach(target)) + return false; + if (!stm32mp15_cm4_configure_dbgmcu(target)) + return false; + /* Reference the APB-D in target storage for External PPB (0xe0000000) manipulations */ + stm32mp15_cm4_setup_apbd_ap(target); + return true; } static void stm32mp15_cm4_detach(target_s *const target) @@ -204,6 +368,11 @@ static void stm32mp15_cm4_detach(target_s *const target) stm32mp15_priv_s *priv = (stm32mp15_priv_s *)target->target_storage; /* Reverse all changes to the DBGMCU config register */ target_mem32_write32(target, STM32MP15_DBGMCU_CONFIG, priv->dbgmcu_config); + + /* Deallocate any extra AP */ + adiv5_ap_unref(priv->ap); + priv->ap = NULL; + /* Now defer to the normal Cortex-M detach routine to complete the detach */ cortexm_detach(target); } 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..33d23debcb3 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; @@ -199,6 +201,10 @@ struct target { uint16_t part_id; }; +#ifdef PLATFORM_HAS_CUSTOM_COMMANDS +extern const command_s platform_cmd_list[]; +#endif + void target_print_progress(platform_timeout_s *timeout); void target_ram_map_free(target_s *target); void target_flash_map_free(target_s *target); diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 49bc3fc0845..8fbc15c6fca 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -151,12 +151,14 @@ TARGET_PROBE_WEAK_NOP(stm32f4_probe) TARGET_PROBE_WEAK_NOP(stm32g0_probe) TARGET_PROBE_WEAK_NOP(stm32h5_probe) TARGET_PROBE_WEAK_NOP(stm32h7_probe) +TARGET_PROBE_WEAK_NOP(stm32h7rs_probe) TARGET_PROBE_WEAK_NOP(stm32l0_probe) TARGET_PROBE_WEAK_NOP(stm32l1_probe) TARGET_PROBE_WEAK_NOP(stm32l4_probe) TARGET_PROBE_WEAK_NOP(stm32mp15_ca7_probe) TARGET_PROBE_WEAK_NOP(stm32mp15_cm4_probe) TARGET_PROBE_WEAK_NOP(stm32wb0_probe) +TARGET_PROBE_WEAK_NOP(stm32c5_probe) TARGET_PROBE_WEAK_NOP(zynq7_probe) LPC55_DP_PREPARE_WEAK_NOP(lpc55_dp_prepare) diff --git a/src/target/target_probe.h b/src/target/target_probe.h index 932fe8f0632..4463906d1c3 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -102,12 +102,14 @@ bool stm32f4_probe(target_s *target); bool stm32g0_probe(target_s *target); bool stm32h5_probe(target_s *target); bool stm32h7_probe(target_s *target); +bool stm32h7rs_probe(target_s *target); bool stm32l0_probe(target_s *target); bool stm32l1_probe(target_s *target); bool stm32l4_probe(target_s *target); bool stm32mp15_ca7_probe(target_s *target); bool stm32mp15_cm4_probe(target_s *target); bool stm32wb0_probe(target_s *target); +bool stm32c5_probe(target_s *target); bool zynq7_probe(target_s *target); void lpc55_dp_prepare(adiv5_debug_port_s *dp);