diff --git a/.clang-format b/.clang-format new file mode 100755 index 00000000000..b09e0effa81 --- /dev/null +++ b/.clang-format @@ -0,0 +1,45 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +# This is currently broken: +# https://github.com/llvm/llvm-project/issues/53442 +#AlignArrayOfStructures: Left +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBraces: Linux +BasedOnStyle: LLVM +ColumnLimit: 120 +Cpp11BracedListStyle: false +IndentCaseLabels: false +IndentWidth: 8 +KeepEmptyLinesAtTheStartOfBlocks: false + +# Taken from git's rules +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SpacesBeforeTrailingComments: 1 +SortIncludes: false +TabWidth: 8 +UseTab: Always \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..8158731fe78 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [esden, dragonmux] +patreon: 1bitsquared diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml new file mode 100644 index 00000000000..64a5fca782d --- /dev/null +++ b/.github/workflows/build-and-upload.yml @@ -0,0 +1,39 @@ +name: build and upload + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Use embedded toolchain + - uses: numworks/setup-arm-toolchain@2020-q4 + + - name: Install BMDA dependencies + run: sudo apt-get -y install libftdi1-dev libhidapi-dev + + # Runs a single command using the runners shell + - name: Build all platform firmwares and Linux BMDA + run: make all_platforms + + - name: Archive firmware build artifacts as a zip + uses: actions/upload-artifact@v2.2.4 + with: + name: blackmagic-firmware.zip + path: src/artifacts/* + if-no-files-found: error diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml new file mode 100644 index 00000000000..18c5d5f24cd --- /dev/null +++ b/.github/workflows/build-pr.yml @@ -0,0 +1,38 @@ +name: build PR + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Use embedded toolchain + - uses: numworks/setup-arm-toolchain@2020-q4 + + # Build all the firmwares + - name: Build all platform firmwares and BMP only BMPA + run: make all_platforms HOSTED_BMP_ONLY=1 + + - name: Clean + run: make clean + + - name: Install BMDA dependencies + run: sudo apt-get -y install libftdi1-dev libhidapi-dev + + - name: Build full BMDA binary + run: make PROBE_HOST=hosted diff --git a/.gitignore b/.gitignore index 1e555038d43..6d918f422f3 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ blackmagic_upgrade .vscode .gdb_history src/artifacts/ + diff --git a/Makefile b/Makefile index b0d56566b9f..9effa35a52f 100644 --- a/Makefile +++ b/Makefile @@ -17,13 +17,18 @@ ifndef NO_LIBOPENCM3 git submodule init ;\ git submodule update ;\ fi - $(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib + $(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/stm32/f1 lib/stm32/f4 lib/lm4f endif $(Q)$(MAKE) $(MFLAGS) -C src +all_platforms: + $(Q)$(MAKE) $(MFLAGS) -C src $@ + + clean: ifndef NO_LIBOPENCM3 $(Q)$(MAKE) $(MFLAGS) -C libopencm3 $@ endif $(Q)$(MAKE) $(MFLAGS) -C src $@ +.PHONY: clean all_platforms diff --git a/README.md b/README.md index 0eac1e65bd7..e322d0f45e9 100644 --- a/README.md +++ b/README.md @@ -94,14 +94,20 @@ arguments starts the server. When several BMP supported probes are connected, their types, position and serial number is displayed and the program exits. Add "-P (position)" to the next invocation to select one. For the setup from the sample session above: + In another terminal: ```console > blackmagic -Using 1d50:6018 E2E489E7 Black Sphere Technologies Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty) -Remote is Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty) v1.6.1-477-g70bb131-dirty +Black Magic Debug App v1.8.0 + for Black Magic Probe, ST-Link v2 and v3, CMSIS-DAP, JLink and libftdi/MPSSE +Using 1d50:6018 8BB20A03 Black Magic Debug + Black Magic Probe v1.8.0 Listening on TCP: 2000 +``` + And in the GDB terminal: -(gdb) target ext :2000 +```console +(gdb) tar ext :2000 Remote debugging using :2000 (gdb) mon s ... diff --git a/UsingRTT.md b/UsingRTT.md new file mode 100644 index 00000000000..578b86eec2a --- /dev/null +++ b/UsingRTT.md @@ -0,0 +1,271 @@ +# Using RTT + +When debugging arm processors, there are three ways for the target to print debug messages on the host: Semihosting, Serial Wire Output SWO, and Real-Time Transfer RTT. + +[Black Magic Probe](https://github.com/blacksphere/blackmagic) (BMP) is an open source debugger probe that already implements Semihosting and Single Wire Output. This patch adds Real-Time Transfer RTT output to usb serial port. + +- RTT is implemented, not as a user program, but as a serial port device. To read RTT output, use a terminal emulator and connect to the serial port. + +- A novel way to detect RTT automatically, fast and convenient. + +## Use +This example uses linux as operating system. For Windows and MacOS see the *Operating Systems* section. + +In one window open a terminal emulator (minicom, putty) and connect to the usb uart: +``` +$ minicom -c on -D /dev/ttyBmpTarg +``` + +In another window open a debugger: +``` +$ gdb +(gdb) target extended-remote /dev/ttyBmpGdb +(gdb) monitor swdp_scan +(gdb) attach 1 +(gdb) monitor rtt +(gdb) run +^C +(gdb) monitor rtt status +rtt: on found: yes ident: off halt: off channels: auto 0 1 3 +max poll ms: 256 min poll ms: 8 max errs: 10 +``` + +The terminal emulator displays RTT output from the target, +and characters typed in the terminal emulator are sent via RTT to the target. + + +## gdb commands + +The following new gdb commands are available: + +- ``monitor rtt`` + + switch rtt on + +- ``monitor rtt enable`` + + switch rtt on + +- ``monitor rtt disable`` + + switch rtt off + +- ``monitor rtt poll `` max_poll_ms min_poll_ms max_errs + + sets maximum time between polls, minimum time between polls, and the maximum number of errors before RTT disconnects from the target. Times in milliseconds. It is best if max_poll_ms/min_poll_ms is a power of two. As an example, if you wish to check for RTT output between once per second to eight times per second: ``monitor rtt poll 1000 125 10``. + +- ``monitor rtt status`` + + show status. + +rtt|found|state +---|---|--- +rtt: off|found: no|rtt inactive +rtt: on|found: no|searching for rtt control block +rtt: on|found: yes|rtt active +rtt: off|found: yes|corrupt rtt control block, or target memory access error + +A status of `rtt: on found: no` indicates bmp is still searching for the rtt control block in target ram, but has not found anything yet. A status of `rtt: on found: yes` indicates the control block has been found and rtt is active. + +- ``monitor rtt channel`` + + enables the first two output channels, and the first input channel. (default) + +- ``monitor rtt channel number...`` + + enables the given RTT channel numbers. Channels are numbers from 0 to 15, inclusive. Eg. ``monitor rtt channel 0 1 4`` to enable channels 0, 1, and 4. + +- ``monitor rtt ident string`` + + sets RTT ident to *string*. If *string* contains a space, replace the space with an underscore _. Setting ident string is optional, RTT works fine without. + +- ``monitor rtt ident`` + + clears ident string. (default) + +- ``monitor rtt cblock`` + + shows rtt control block data, and which channels are enabled. This is an example control block: + +``` +(gdb) mon rtt cb +cbaddr: 0x200000a0 +ch ena cfg i/o buf@ size head@ tail@ flg + 0 y y out 0x20000148 1024 0x200000c4 0x200000c8 2 + 1 y n out 0x00000000 0 0x200000dc 0x200000e0 0 + 2 n n out 0x00000000 0 0x200000f4 0x200000f8 0 + 3 y y in 0x20000548 16 0x2000010c 0x20000110 0 + 4 n n in 0x00000000 0 0x20000124 0x20000128 0 + 5 n n in 0x00000000 0 0x2000013c 0x20000140 0 + 6 n n in 0x00000000 0 0x00000000 0x00000000 0 + 7 n n in 0x00000000 0 0x00000000 0x00000000 0 + 8 n n in 0x00000000 0 0x00000000 0x00000000 0 + 9 n n in 0x00000000 0 0x00000000 0x00000000 0 +10 n n in 0x00000000 0 0x00000000 0x00000000 0 +11 n n in 0x00000000 0 0x00000000 0x00000000 0 +12 n n in 0x00000000 0 0x00000000 0x00000000 0 +13 n n in 0x00000000 0 0x00000000 0x00000000 0 +14 n n in 0x00000000 0 0x00000000 0x00000000 0 +15 n n in 0x00000000 0 0x00000000 0x00000000 0 +``` + +Channels are listed, one channel per line. The columns are: channel, enabled, configured, input/output, buffer address, buffer size, address of head pointer, address of tail pointer, flag. Each channel is a circular buffer with head and tail pointer. + +Note the columns `ena` for enabled, `cfg` for configured. + +Configured channels have a non-zero buffer address and non-zero size. Configured channels are marked yes `y` in the column `cfg` . What channels are configured depends upon target software. + +Channels the user wants to see are marked yes `y` in the column enabled `ena`. The user can change which channels are shown with the `monitor rtt channel` command. + +Output channels are displayed, and Input channels receive keyboard input, if they are marked yes in both *enabled* and *configured*. + +The control block is cached for speed. In an interrupted program, `monitor rtt` will force a reload of the control block when the program continues. + +## Identifier string +It is possible to set an RTT identifier string. +As an example, if the RTT identifier is "IDENT STR": +``` +$ gdb +(gdb) target extended-remote /dev/ttyBmpGdb +(gdb) monitor swdp_scan +(gdb) attach 1 +(gdb) monitor rtt ident IDENT_STR +(gdb) monitor rtt +(gdb) run +^C +(gdb) monitor rtt status +rtt: on found: yes ident: "IDENT STR" halt: off channels: auto 0 1 3 +max poll ms: 256 min poll ms: 8 max errs: 10 +``` +Note replacing space with underscore _ in *monitor rtt ident*. + +Setting an identifier string is optional. RTT gives the same output at the same speed, with or without specifying identifier string. + +## Operating systems + +[Configuration](https://github.com/blacksphere/blackmagic/wiki/Getting-Started) instructions for windows, linux and macos. + +### Windows + +After configuration, Black Magic Probe shows up in Windows as two _USB Serial (CDC)_ ports. + +Connect arm-none-eabi-gdb, the gnu debugger for arm processors, to the lower numbered of the two COM ports. Connect an ansi terminal emulator to the higher numbered of the two COM ports. + +Sample gdb session: +``` +(gdb) target extended-remote COM3 +(gdb) monitor swdp_scan +(gdb) attach 1 +(gdb) monitor rtt +(gdb) run +``` + +For COM port COM10 and higher, add the prefix `\\.\`, e.g. +``` +target extended-remote \\.\COM10 +``` + +Target RTT output will appear in the terminal, and what you type in the terminal will be sent to the RTT input of the target. + +### linux +On linux, install [udev rules](https://github.com/blacksphere/blackmagic/blob/master/driver/99-blackmagic.rules). Disconnect and re-connect the BMP. Check the device shows up in /dev/ : +``` +$ ls -l /dev/ttyBmp* +lrwxrwxrwx 1 root root 7 Dec 13 07:29 /dev/ttyBmpGdb -> ttyACM0 +lrwxrwxrwx 1 root root 7 Dec 13 07:29 /dev/ttyBmpTarg -> ttyACM2 +``` +Connect terminal emulator to /dev/ttyBmpTarg and gdb to /dev/ttyBmpGdb . + +In one window: +``` +minicom -c on -D /dev/ttyBmpTarg +``` +In another window : +``` +gdb +(gdb) target extended-remote /dev/ttyBmpGdb +(gdb) monitor swdp_scan +(gdb) attach 1 +(gdb) monitor rtt +(gdb) run +``` + +### MacOS + +On MacOS the tty devices have different names than on linux. On connecting blackmagic to the computer 4 devices are created, 2 'tty' and 2 'cu' devices. Gdb connects to the first cu device (e.g.: `target extended-remote /dev/cu.usbmodemDDCEC9EC1`), while RTT is connected to the second tty device (`minicom -c on -D /dev/tty.usbmodemDDCEC9EC3`). In full: + +In one Terminal window, connect a terminal emulator to /dev/tty.usbmodemDDCEC9EC3 : + +``` +minicom -c on -D /dev/tty.usbmodemDDCEC9EC3 +``` +In another Terminal window, connect gdb to /dev/cu.usbmodemDDCEC9EC1 : +``` +gdb +(gdb) target extended-remote /dev/cu.usbmodemDDCEC9EC1 +(gdb) monitor swdp_scan +(gdb) attach 1 +(gdb) monitor rtt +(gdb) run +``` +RTT input/output is in the window running _minicom_. + +## Notes + +- Design goal was smallest, simplest implementation that has good practical use. + +- RTT code size is 3.5 kbyte - the whole debugger 110 kbyte. + +- Because RTT is implemented as a serial port device, there is no need to write and maintain software for different host operating systems. A serial port works everywhere - linux, windows and mac. You can even use an Android mobile phone as RTT terminal. + +- Because polling occurs between debugger probe and target, the load on the host is small. There is no constant usb traffic, there are no real-time requirements on the host. + +- RTT polling frequency is adaptive and goes up and down with RTT activity. Use *monitor rtt poll* to balance response speed and target load for your use. + +- Detects RTT automatically, very convenient. + +- When using RTT as a terminal, sending data from host to target, you may need to change local echo, carriage return and/or line feed settings in your terminal emulator. + +- Architectures such as risc-v may not allow the debugger access to target memory while the target is running. As a workaround, on these architectures RTT briefly halts the target during polling. If the target is halted during polling, `monitor rtt status` shows `halt: on`. + +- Measured RTT speed. + +| debugger | char/s | +| ------------------------- | ------ | +| bmp stm32f723 stlinkv3 | 49811 | +| bmp stm32f411 black pill | 50073 | +| bmp stm32f103 blue pill | 50142 | + +This is the speed at which characters can be sent from target to debugger probe, in reasonable circumstances. Test target is an stm32f103 blue pill running an [Arduino sketch](https://github.com/koendv/Arduino-RTTStream/blob/main/examples/SpeedTest/SpeedTest.ino). Default *monitor rtt poll* settings on debugger. Default RTT buffer size in target and debugger. Overhead for printf() calls included. + +## Compiling firmware +To compile with RTT support, add *ENABLE_RTT=1*. + +Eg. for STM32F103 blue pill: +``` +make clean +make PROBE_HOST=stlink ENABLE_RTT=1 +``` +or for the STM32F411 *[Black Pill](https://www.aliexpress.com/item/1005001456186625.html)*: +``` +make clean +make PROBE_HOST=f4discovery BLACKPILL=1 ENABLE_RTT=1 +``` +Setting an ident string is optional. But if you wish, you can set the default RTT ident at compile time. +For STM32F103 *Blue Pill*: +``` +make clean +make PROBE_HOST=stlink ENABLE_RTT=1 "RTT_IDENT=IDENT\ STR" +``` +or for STM32F411 *Black Pill*: +``` +make clean +make PROBE_HOST=f4discovery BLACKPILL=1 ENABLE_RTT=1 "RTT_IDENT=IDENT\ STR" +``` +Note the backslash \\ before the space. + +## Links + - [OpenOCD](https://openocd.org/doc/html/General-Commands.html#Real-Time-Transfer-_0028RTT_0029) + - [probe-rs](https://probe.rs/) and [rtt-target](https://github.com/mvirkkunen/rtt-target) for the _rust_ programming language. + - [RTT Stream](https://github.com/koendv/Arduino-RTTStream) for Arduino on arm processors + - [\[WIP\] RTT support - PR from katyo](https://github.com/blacksphere/blackmagic/pull/833) diff --git a/contrib/zsh/_blackmagic b/contrib/zsh/_blackmagic new file mode 100644 index 00000000000..4d786d085a7 --- /dev/null +++ b/contrib/zsh/_blackmagic @@ -0,0 +1,77 @@ +#compdef blackmagic +# SPDX-License-Identifier: BSD-3-Clause + +# Copyright (c) 2022 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. + +_blackmagic_frequency() { + _numbers -u 'Hz' 'frequency' 'k:kilohertz' 'M:megahertz' +} + +_blackmagic_size() { + _numbers -u 'B' 'size in bytes' 'k:kibibytes' 'M:mebibytes' +} + +local arguments + +arguments=( + '-h[show version and help then exit]' + '-l[list available supported probes]' + '-v=[set the output verbosity level]:number' + '(-d -P -s -c)-d=[use a serial device at the given path]:path:_files' + '(-d -P -s -c)-P=[use the th debug probe found while scanning the system, see the output from list for the order]:number' + '(-d -P -s -c)-s=[select the debug probe with the given serial number]:serial' + '(-d -P -s -c)-c=[select the FTDI-based debug probe with of the given type]:type' + '-n=[select the target device at the given position in the scan chain]:number' + '(-j -A)-j[use JTAG instead of SWD]' + '(-j -A)-A[use automatic chain scanning]' + '-C[connect to target under hardware reset]' + '(-t -T)-t[perform a chain scan and display information about the conected devices]' + '(-t -T)-T[perform continues read- or write-back of a value to allow measurement of protocol timing. Aborted by ^C]' + '-e[assume external resistors for FTDI devices]' + '(-E -w -V -r)-E[erase the target device Flash]' + '(-E -w -V -r)-w[write the specified binary file to the target device Flash (the default)]' + '(-E -w -V -r)-V[verify the target device Flash against the specified binary file]' + '(-E -w -V -r)-r[read the target device Flash]' + '-p[power the target from the probe (if possible)]' + '-R-[reset the device. If followed by "h", this will be done using the hardware reset line instead of over the debug link]:: :(h)' + '-H[do not use the high level command API (bmp-remote)]' + '*-M[run target-specific monitor commands]:command' + '-a=[start address for the given Flash operation (defaults to the start of Flash)]:address:_numbers "address"' + '-S=[number of bytes to work on in the Flash operation (default is till the operation fails or is complete)]:_blackmagic_size' + ':binary file to use in Flash operations:_files "*.bin"' +) + +if ! (( $words[(Ie)-j] )); then + arguments+=( + '-f=[set an operating frequency for SWD]:frequency:_blackmagic_frequency' + '-m=[use the given target ID for selection in SWD multi-drop]:target' + ) +fi + +_arguments -s : $arguments diff --git a/driver/99-blackmagic-plugdev.rules b/driver/99-blackmagic-plugdev.rules new file mode 100644 index 00000000000..af75f3bf491 --- /dev/null +++ b/driver/99-blackmagic-plugdev.rules @@ -0,0 +1,10 @@ +# Black Magic Probe +# there are two connections, one for GDB and one for UART debugging +# copy this to /etc/udev/rules.d/99-blackmagic.rules +# and run sudo udevadm control -R +ACTION!="add|change", GOTO="blackmagic_rules_end" +SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb" +SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg" +SUBSYSTEM=="usb", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666", GROUP="plugdev", TAG+="uaccess" +SUBSYSTEM=="usb", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6018", MODE="0666", GROUP="plugdev", TAG+="uaccess" +LABEL="blackmagic_rules_end" diff --git a/driver/99-blackmagic.rules b/driver/99-blackmagic-uucp.rules similarity index 50% rename from driver/99-blackmagic.rules rename to driver/99-blackmagic-uucp.rules index f8e749b3f5a..414c07172d2 100644 --- a/driver/99-blackmagic.rules +++ b/driver/99-blackmagic-uucp.rules @@ -1,7 +1,10 @@ # Black Magic Probe # there are two connections, one for GDB and one for UART debugging # copy this to /etc/udev/rules.d/99-blackmagic.rules +# and run sudo udevadm control -R +ACTION!="add|change", GOTO="blackmagic_rules_end" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6018", MODE="0666" +SUBSYSTEM=="usb", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666", GROUP="uucp", TAG+="uaccess" +SUBSYSTEM=="usb", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6018", MODE="0666", GROUP="uucp", TAG+="uaccess" +LABEL="blackmagic_rules_end" diff --git a/driver/README.md b/driver/README.md new file mode 100644 index 00000000000..10a15f41043 --- /dev/null +++ b/driver/README.md @@ -0,0 +1,51 @@ +# Driver and access files for BMP + +This directory contains a few different important files described in the sections below. These are: + +* [99-blackmagic-plugdev.rules](#99-blackmagic-plugdevrules) +* [99-blackmagic-uucp.rules](#99-blackmagic-uucprules) +* [blackmagic.inf](#blackmagicinf) +* [blackmagic_upgrade.inf](#blackmagic_upgradeinf) + +## 99-blackmagic-plugdev.rules + +This file contains [udev](https://www.freedesktop.org/wiki/Software/systemd) rules for Linux +systems that use 'plugdev' as the access group for plug-in devices. This allows GDB, dfu-util, +and other utilities access to your Black Magic Probe hardware without needing `sudo` / root on +the computer you're trying to access the hardware from. + +Distros that use 'plugdev' for this purpose include: + +* Debian +* Ubuntu +* Linux Mint + +## 99-blackmagic-uucp.rules + +This file contains [udev](https://www.freedesktop.org/wiki/Software/systemd) rules for Linux +systems that use 'uucp' as the access group for plug-in devices. This allows GDB, dfu-util, +and other utilities access to your Black Magic Probe hardware without needing `sudo` / root on +the computer you're trying to access the hardware from. + +Distros that use 'uucp' for this purpose include: + +* Arch +* Manjaro + +## Notes about udev rules + +Our udev rules include use of the 'uaccess' tag which means either rules file should just work +on any modern distro that uses systemd. The specific user access group only matters when not using +systemd, or on older distros that predate the user access tag. + +## blackmagic.inf + +This is a windows driver "installation" (actually binding) information file which when +right-clicked and after clicking Install will name the serial ports Black Magic Probe provides +properly and provide them their proper vendor names. + +## blackmagic_upgrade.inf + +This is a windows driver "installation" (binding) information file which when right-clicked and +after clicking Install will bind suitable drivers and names to the DFU (firmware upgrade) and +Trace/Capture interfaces that Black Magic Probe provides + give them their proper vendor names. diff --git a/driver/blackmagic.inf b/driver/blackmagic.inf index 91949371ae4..b2345b37688 100644 --- a/driver/blackmagic.inf +++ b/driver/blackmagic.inf @@ -13,17 +13,17 @@ Signature="$Windows NT$" Class=Ports ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} -Provider=%BLACKSPHERE% +Provider=%BLACKMAGIC% DriverVer=28/12/2011,0.0.1.1 [Manufacturer] %VendorName%=DeviceList, NTamd64 [Strings] -VendorName = "Black Sphere Technologies" +VendorName = "Black Magic Debug" BLACKMAGICGDB = "Black Magic GDB Server" BLACKMAGICUART = "Black Magic UART Port" -BLACKSPHERE_DISPLAY_NAME = "Black Magic Probe Driver" +BLACKMAGIC_DISPLAY_NAME = "Black Magic Probe Driver" [DeviceList] %BLACKMAGICGDB%=DriverInstall, USB\VID_1d50&PID_6018&Rev_0100&MI_00 @@ -55,7 +55,7 @@ HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" AddService = usbser,0x0002,DriverService.nt [DriverService.nt] -DisplayName = %BLACKSPHERE_DISPLAY_NAME% +DisplayName = %BLACKMAGIC_DISPLAY_NAME% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL @@ -80,7 +80,7 @@ HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" AddService = usbser,0x0002,DriverService.NTamd64 [DriverService.NTamd64] -DisplayName = %BLACKSPHERE_DISPLAY_NAME% +DisplayName = %BLACKMAGIC_DISPLAY_NAME% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL diff --git a/driver/blackmagic_upgrade.inf b/driver/blackmagic_upgrade.inf index d37036f7356..78bbbe6b50e 100644 --- a/driver/blackmagic_upgrade.inf +++ b/driver/blackmagic_upgrade.inf @@ -4,7 +4,7 @@ DeviceName = "Black Magic Firmware Upgrade" DeviceNameDFU = "Black Magic Probe (Upgrade)" DeviceNameTPA = "Black Magic Trace Capture" -VendorName = "Black Sphere Technologies" +VendorName = "Black Magic Debug" SourceName = "Black Magic Firmware Upgrade Install Disk" DeviceID = "VID_1d50&PID_6018&Rev_0100&MI_04" DeviceIDDFU= "VID_1d50&PID_6017&Rev_0100" diff --git a/scripts/bootprog.py b/scripts/bootprog.py index 968fcd68186..18a3ad649b1 100755 --- a/scripts/bootprog.py +++ b/scripts/bootprog.py @@ -35,10 +35,11 @@ def __init__(self, port, baud=115200): def _sync(self): # Send sync byte #print("sending sync byte") - self.serial.write(bytes((0x7F,))) + self.serial.write(b"\x7f") self._checkack() def _sendcmd(self, cmd): + cmd = ord(cmd) cmd = bytes((cmd, cmd ^ 0xff)) #print("sendcmd:", repr(cmd)) self.serial.write(cmd) @@ -51,7 +52,7 @@ def _send(self, data): self.serial.write(data) def _checkack(self): - ACK = bytes((0x79,)) + ACK = b"\x79" b = self.serial.read(1) if b != ACK: raise Exception("Invalid ack: %r" % b) #print("got ack!") @@ -59,7 +60,7 @@ def _checkack(self): def get(self): - self._sendcmd(0x00) + self._sendcmd(b"\x00") self._checkack() num = self.serial.read(1)[0] data = self.serial.read(num+1) @@ -68,27 +69,27 @@ def get(self): def eraseall(self): # Send erase cmd - self._sendcmd(0x43) + self._sendcmd(b"\x43") self._checkack() # Global erase - self._sendcmd(0xff) + self._sendcmd(b"\xff") self._checkack() def read(self, addr, len): # Send read cmd - self._sendcmd(0x11) + self._sendcmd(b"\x11") self._checkack() # Send address self._send(struct.pack(">L", addr)) self._checkack() # Send length - self._sendcmd(len-1) + self._sendcmd(bytes((len-1,))) self._checkack() return self.serial.read(len) def write(self, addr, data): # Send write cmd - self._sendcmd(0x31) + self._sendcmd(b"\x31") self._checkack() # Send address self._send(struct.pack(">L", addr)) @@ -99,7 +100,7 @@ def write(self, addr, data): def write_protect(self, sectors): # Send WP cmd - self._sendcmd(0x63) + self._sendcmd(b"\x63") self._checkack() # Send sector list self._send(bytes((len(sectors)-1,)) + bytes(sectors)) @@ -108,19 +109,19 @@ def write_protect(self, sectors): self._sync() def write_unprotect(self): - self._sendcmd(0x73) + self._sendcmd(b"\x73") self._checkack() self._checkack() self._sync() def read_protect(self): - self._sendcmd(0x82) + self._sendcmd(b"\x82") self._checkack() self._checkack() self._sync() def read_unprotect(self): - self._sendcmd(0x92) + self._sendcmd(b"\x92") self._checkack() self._checkack() self._sync() diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000000..f9cb82f2887 --- /dev/null +++ b/shell.nix @@ -0,0 +1,29 @@ +{ + pkgs ? import (fetchGit { + name = "nixos-21.11-2022-05-17"; + url = "https://github.com/nixos/nixpkgs/"; + ref = "refs/heads/nixos-21.11"; + # `git ls-remote https://github.com/nixos/nixpkgs nixos-21.11` + rev = "8b3398bc7587ebb79f93dfeea1b8c574d3c6dba1"; + }) {} +}: + +with pkgs; +mkShell { + buildInputs = [ + gnumake + gcc-arm-embedded + dfu-util + + blackmagic + pkg-config + libftdi1 + libusb-compat-0_1 + hidapi + + (python3.withPackages (python-packages: with python-packages; [ + pyusb + pyserial + ])) + ]; +} diff --git a/src/Makefile b/src/Makefile index 188ecfa4a75..b618bfc4a7e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,6 +31,7 @@ SRC = \ gdb_hostio.c \ gdb_packet.c \ hex_utils.c \ + jtag_devs.c \ jtag_scan.c \ lmi.c \ lpc_common.c \ @@ -53,6 +54,7 @@ SRC = \ samd.c \ samx5x.c \ stm32f1.c \ + ch32f1.c \ stm32f4.c \ stm32h7.c \ stm32l0.c \ @@ -60,8 +62,14 @@ SRC = \ stm32g0.c \ target.c \ + include $(PLATFORM_DIR)/Makefile.inc +ifneq ($(PC_HOSTED),1) +# Output memory usage information +LDFLAGS += -Wl,--print-memory-usage +endif + OPT_FLAGS ?= -Os CFLAGS += $(OPT_FLAGS) LDFLAGS += $(OPT_FLAGS) @@ -87,6 +95,15 @@ VPATH += platforms/common CFLAGS += -Iplatforms/common endif +ifeq ($(ENABLE_RTT), 1) +CFLAGS += -DENABLE_RTT +SRC += rtt.c rtt_if.c +endif + +ifdef RTT_IDENT +CFLAGS += -DRTT_IDENT=$(RTT_IDENT) +endif + OBJ = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SRC))) $(TARGET): include/version.h $(OBJ) @@ -119,16 +136,27 @@ clean: host_clean -$(Q)$(RM) platforms/*/*.o platforms/*/*.d mapfile include/version.h all_platforms: + $(Q)if [ ! -f ../libopencm3/Makefile ]; then \ + echo "Initialising git submodules..." ;\ + git submodule init ;\ + git submodule update ;\ + fi + $(Q)$(MAKE) $(MFLAGS) -C ../libopencm3 lib/stm32/f1 lib/stm32/f4 lib/lm4f $(Q)set -e ;\ mkdir -p artifacts/$(shell git describe --always --dirty --tags) ;\ echo "" >> artifacts/index.html ;\ - cp artifacts/*.bin artifacts/$(shell git describe --always --dirty --tags) + cp artifacts/blackmagic* artifacts/$(shell git describe --always --dirty --tags) command.c: include/version.h +GIT_VERSION := $(shell git describe --always --dirty --tags) +VERSION_HEADER := \#define FIRMWARE_VERSION "$(GIT_VERSION)" + include/version.h: FORCE - $(Q)echo " GIT include/version.h" - $(Q)echo "#define FIRMWARE_VERSION \"$(shell git describe --always --dirty --tags)\"" > $@ + @# If git isn't found then GIT_VERSION will be an empty string. +ifeq ($(GIT_VERSION),) + @echo Git not found, assuming up to date include/version.h +else + @# Note that when we echo the version to the header file, echo writes a final newline + @# to the file. This is fine and probably makes the file more human-readable, but + @# also means we have to account for that newline in this comparison. + $(Q)if [ ! -f $@ ] || [ "$$(cat $@)" != "$$(echo '$(VERSION_HEADER)\n')" ]; then \ + echo " GEN $@"; \ + echo '$(VERSION_HEADER)' > $@; \ + fi +endif + -include *.d diff --git a/src/command.c b/src/command.c index fdda86741b9..edfd3dc533e 100644 --- a/src/command.c +++ b/src/command.c @@ -34,6 +34,10 @@ #include "version.h" #include "serialno.h" +#ifdef ENABLE_RTT +#include "rtt.h" +#endif + #ifdef PLATFORM_HAS_TRACESWO # include "traceswo.h" #endif @@ -43,6 +47,7 @@ static bool cmd_help(target *t, int argc, char **argv); static bool cmd_jtag_scan(target *t, int argc, char **argv); static bool cmd_swdp_scan(target *t, int argc, char **argv); +static bool cmd_auto_scan(target *t, int argc, char **argv); static bool cmd_frequency(target *t, int argc, char **argv); static bool cmd_targets(target *t, int argc, char **argv); static bool cmd_morse(target *t, int argc, char **argv); @@ -56,6 +61,9 @@ static bool cmd_target_power(target *t, int argc, const char **argv); static bool cmd_traceswo(target *t, int argc, const char **argv); #endif static bool cmd_heapinfo(target *t, int argc, const char **argv); +#ifdef ENABLE_RTT +static bool cmd_rtt(target *t, int argc, const char **argv); +#endif #if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0) static bool cmd_debug_bmp(target *t, int argc, const char **argv); #endif @@ -65,6 +73,7 @@ const struct command_s cmd_list[] = { {"help", (cmd_handler)cmd_help, "Display help for monitor commands"}, {"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" }, {"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" }, + {"auto_scan", (cmd_handler)cmd_auto_scan, "Automatically scan all chain types for devices"}, {"frequency", (cmd_handler)cmd_frequency, "set minimum high and low times" }, {"targets", (cmd_handler)cmd_targets, "Display list of available targets" }, {"morse", (cmd_handler)cmd_morse, "Display morse error message" }, @@ -74,6 +83,9 @@ const struct command_s cmd_list[] = { #ifdef PLATFORM_HAS_POWER_SWITCH {"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"}, #endif +#ifdef ENABLE_RTT + {"rtt", (cmd_handler)cmd_rtt, "enable|disable|status|channel 0..15|ident (str)|cblock|poll maxms minms maxerr" }, +#endif #ifdef PLATFORM_HAS_TRACESWO #if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" }, @@ -141,7 +153,7 @@ bool cmd_version(target *t, int argc, char **argv) #else gdb_out(BOARD_IDENT); gdb_outf(", Hardware Version %d\n", platform_hwversion()); - gdb_out("Copyright (C) 2015 Black Sphere Technologies Ltd.\n"); + gdb_out("Copyright (C) 2022 Black Magic Debug Project\n"); gdb_out("License GPLv3+: GNU GPL version 3 or later " "\n\n"); #endif @@ -188,7 +200,7 @@ static bool cmd_jtag_scan(target *t, int argc, char **argv) int devs = -1; volatile struct exception e; - TRY_CATCH (e, EXCEPTION_ALL) { + TRY_CATCH(e, EXCEPTION_ALL) { #if PC_HOSTED == 1 devs = platform_jtag_scan(argc > 1 ? irlens : NULL); #else @@ -209,6 +221,7 @@ static bool cmd_jtag_scan(target *t, int argc, char **argv) gdb_out("JTAG device scan failed!\n"); return false; } + cmd_targets(NULL, 0, NULL); morse(NULL, false); return true; @@ -228,7 +241,7 @@ bool cmd_swdp_scan(target *t, int argc, char **argv) int devs = -1; volatile struct exception e; - TRY_CATCH (e, EXCEPTION_ALL) { + TRY_CATCH(e, EXCEPTION_ALL) { #if PC_HOSTED == 1 devs = platform_adiv5_swdp_scan(targetid); #else @@ -253,7 +266,60 @@ bool cmd_swdp_scan(target *t, int argc, char **argv) cmd_targets(NULL, 0, NULL); morse(NULL, false); return true; +} + +bool cmd_auto_scan(target *t, int argc, char **argv) +{ + (void)t; + (void)argc; + (void)argv; + + if (platform_target_voltage()) + gdb_outf("Target voltage: %s\n", platform_target_voltage()); + if (connect_assert_srst) + platform_srst_set_val(true); /* will be deasserted after attach */ + + int devs = -1; + volatile struct exception e; + TRY_CATCH(e, EXCEPTION_ALL) { +#if PC_HOSTED == 1 + devs = platform_jtag_scan(NULL); +#else + devs = jtag_scan(NULL); +#endif + if (devs > 0) + break; + gdb_out("JTAG scan found no devices, trying SWD!\n"); + +#if PC_HOSTED == 1 + devs = platform_adiv5_swdp_scan(0); +#else + devs = adiv5_swdp_scan(0); +#endif + if (devs > 0) + break; + + platform_srst_set_val(false); + gdb_out("SW-DP scan failed!\n"); + return false; + } + switch (e.type) { + case EXCEPTION_TIMEOUT: + gdb_outf("Timeout during scan. Is target stuck in WFI?\n"); + break; + case EXCEPTION_ERROR: + gdb_outf("Exception: %s\n", e.msg); + break; + } + if (devs <= 0) { + platform_srst_set_val(false); + gdb_out("auto scan failed!\n"); + return false; + } + cmd_targets(NULL, 0, NULL); + morse(NULL, false); + return true; } bool cmd_frequency(target *t, int argc, char **argv) @@ -410,6 +476,93 @@ static bool cmd_target_power(target *t, int argc, const char **argv) } #endif +#ifdef ENABLE_RTT +const char* onoroffstr[2] = {"off", "on"}; +static const char* onoroff(bool bval) { + return bval ? onoroffstr[1] : onoroffstr[0]; +} + +static bool cmd_rtt(target *t, int argc, const char **argv) +{ + (void)t; + if ((argc == 1) || ((argc == 2) && !strncmp(argv[1], "enabled", strlen(argv[1])))) { + rtt_enabled = true; + rtt_found = false; + } + else if ((argc == 2) && !strncmp(argv[1], "disabled", strlen(argv[1]))) { + rtt_enabled = false; + rtt_found = false; + } + else if ((argc == 2) && !strncmp(argv[1], "status", strlen(argv[1]))) { + gdb_outf("rtt: %s found: %s ident: ", + onoroff(rtt_enabled), rtt_found ? "yes" : "no"); + if (rtt_ident[0] == '\0') + gdb_out("off"); + else + gdb_outf("\"%s\"", rtt_ident); + gdb_outf(" halt: %s", onoroff(target_no_background_memory_access(t))); + gdb_out(" channels: "); + if (rtt_auto_channel) gdb_out("auto "); + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) + if (rtt_channel[i].is_enabled) gdb_outf("%d ", i); + gdb_outf("\nmax poll ms: %u min poll ms: %u max errs: %u\n", + rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs); + } + else if ((argc >= 2) && !strncmp(argv[1], "channel", strlen(argv[1]))) { + /* mon rtt channel switches to auto rtt channel selection + mon rtt channel number... selects channels given */ + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) + rtt_channel[i].is_enabled = false; + if (argc == 2) { + rtt_auto_channel = true; + } else { + rtt_auto_channel = false; + for (int i = 2; i < argc; i++) { + int chan = atoi(argv[i]); + if ((chan >= 0) && (chan < MAX_RTT_CHAN)) + rtt_channel[chan].is_enabled = true; + } + } + } + else if ((argc == 2) && !strncmp(argv[1], "ident", strlen(argv[1]))) { + rtt_ident[0] = '\0'; + } + else if ((argc == 2) && !strncmp(argv[1], "poll", strlen(argv[1]))) + gdb_outf("%u %u %u\n", rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs); + else if ((argc == 2) && !strncmp(argv[1], "cblock", strlen(argv[1]))) { + gdb_outf("cbaddr: 0x%x\n", rtt_cbaddr); + gdb_out("ch ena cfg i/o buf@ size head@ tail@ flg\n"); + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) { + gdb_outf("%2d %c %c %s 0x%08x %5d 0x%08x 0x%08x %d\n", + i, rtt_channel[i].is_enabled ? 'y' : 'n', rtt_channel[i].is_configured ? 'y' : 'n', + rtt_channel[i].is_output ? "out" : "in ", rtt_channel[i].buf_addr, rtt_channel[i].buf_size, + rtt_channel[i].head_addr, rtt_channel[i].tail_addr, rtt_channel[i].flag); + } + } + else if ((argc == 3) && !strncmp(argv[1], "ident", strlen(argv[1]))) { + strncpy(rtt_ident, argv[2], sizeof(rtt_ident)); + rtt_ident[sizeof(rtt_ident)-1] = '\0'; + for (uint32_t i = 0; i < sizeof(rtt_ident); i++) + if (rtt_ident[i] == '_') rtt_ident[i] = ' '; + } + else if ((argc == 5) && !strncmp(argv[1], "poll", strlen(argv[1]))) { + /* set polling params */ + int32_t new_max_poll_ms = atoi(argv[2]); + int32_t new_min_poll_ms = atoi(argv[3]); + int32_t new_max_poll_errs = atoi(argv[4]); + if ((new_max_poll_ms >= 0) && (new_min_poll_ms >= 0) && (new_max_poll_errs >= 0) + && (new_max_poll_ms >= new_min_poll_ms)) { + rtt_max_poll_ms = new_max_poll_ms; + rtt_min_poll_ms = new_min_poll_ms; + rtt_max_poll_errs = new_max_poll_errs; + } + else gdb_out("how?\n"); + } + else gdb_out("what?\n"); + return true; +} +#endif + #ifdef PLATFORM_HAS_TRACESWO static bool cmd_traceswo(target *t, int argc, const char **argv) { diff --git a/src/crc32.c b/src/crc32.c index 8ce25dff903..80e16f3a18f 100644 --- a/src/crc32.c +++ b/src/crc32.c @@ -132,7 +132,7 @@ int generic_crc32(target *t, uint32_t *crc_res, uint32_t base, size_t len) base += read_len; len -= read_len; } - DEBUG_WARN("%d ms\n", platform_time_ms() - start_time); + DEBUG_WARN("%" PRIu32 " ms\n", platform_time_ms() - start_time); *crc_res = crc; return 0; } diff --git a/src/gdb_main.c b/src/gdb_main.c index bdff229ac5b..0713dae88c6 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -35,6 +35,9 @@ #include "command.h" #include "crc32.h" #include "morse.h" +#ifdef ENABLE_RTT +#include "rtt.h" +#endif enum gdb_signal { GDB_SIGINT = 2, @@ -48,7 +51,13 @@ enum gdb_signal { #define ERROR_IF_NO_TARGET() \ if(!cur_target) { gdb_putpacketz("EFF"); break; } -static char pbuf[BUF_SIZE+1]; +typedef struct +{ + const char *cmd_prefix; + void (*func)(const char *packet, int len); +} cmd_executer; + +static char pbuf[BUF_SIZE + 1]; static target *cur_target; static target *last_target; @@ -111,7 +120,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) gdb_putpacket(hexify(pbuf, arm_regs, sizeof(arm_regs)), sizeof(arm_regs) * 2); break; - } + } case 'm': { /* 'm addr,len': Read len bytes from addr */ uint32_t addr, len; ERROR_IF_NO_TARGET(); @@ -126,9 +135,9 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) if (target_mem_read(cur_target, mem, addr, len)) gdb_putpacketz("E01"); else - gdb_putpacket(hexify(pbuf, mem, len), len*2); + gdb_putpacket(hexify(pbuf, mem, len), len * 2); break; - } + } case 'G': { /* 'G XX': Write general registers */ ERROR_IF_NO_TARGET(); uint8_t arm_regs[target_regs_size(cur_target)]; @@ -136,7 +145,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) target_regs_write(cur_target, arm_regs); gdb_putpacketz("OK"); break; - } + } case 'M': { /* 'M addr,len:XX': Write len bytes to addr */ uint32_t addr, len; int hex; @@ -155,7 +164,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) else gdb_putpacketz("OK"); break; - } + } case 's': /* 's [addr]': Single step [start at addr] */ single_step = true; /* fall through */ @@ -187,6 +196,9 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) if((c == '\x03') || (c == '\x04')) { target_halt_request(cur_target); } + #ifdef ENABLE_RTT + if (rtt_enabled) poll_rtt(cur_target); + #endif } SET_RUN_STATE(0); @@ -209,7 +221,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) gdb_putpacket_f("T%02X", GDB_SIGTRAP); } break; - } + } /* Optional GDB packet support */ case 'p': { /* Read single register */ @@ -218,32 +230,31 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) sscanf(pbuf, "p%" SCNx32, ®); uint8_t val[8]; size_t s = target_reg_read(cur_target, reg, val, sizeof(val)); - if (s > 0) { + if (s > 0) gdb_putpacket(hexify(pbuf, val, s), s * 2); - } else { + else gdb_putpacketz("EFF"); - } break; - } + } case 'P': { /* Write single register */ ERROR_IF_NO_TARGET(); uint32_t reg; int n; sscanf(pbuf, "P%" SCNx32 "=%n", ®, &n); - uint8_t val[strlen(&pbuf[n])/2]; + // TODO: FIXME, VLAs considered harmful. + uint8_t val[strlen(&pbuf[n]) / 2]; unhexify(val, pbuf + n, sizeof(val)); - if (target_reg_write(cur_target, reg, val, sizeof(val)) > 0) { + if (target_reg_write(cur_target, reg, val, sizeof(val)) > 0) gdb_putpacketz("OK"); - } else { + else gdb_putpacketz("EFF"); - } break; - } + } case 'F': /* Semihosting call finished */ - if (in_syscall) { + if (in_syscall) return hostio_reply(tc, pbuf, size); - } else { + else { DEBUG_GDB("*** F packet when not in syscall! '%s'\n", pbuf); gdb_putpacketz(""); } @@ -301,12 +312,12 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) } DEBUG_GDB("X packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len); - if (target_mem_write(cur_target, addr, pbuf+bin, len)) + if (target_mem_write(cur_target, addr, pbuf + bin, len)) gdb_putpacketz("E01"); else gdb_putpacketz("OK"); break; - } + } case 'q': /* General query packet */ handle_q_packet(pbuf, size); @@ -330,102 +341,143 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall) } } +static bool exec_command(char *packet, int len, const cmd_executer *exec) +{ + while (exec->cmd_prefix) { + const int l = strlen(exec->cmd_prefix); + if (!strncmp(packet, exec->cmd_prefix, l)) { + exec->func(packet + l, len - l); + return true; + } + ++exec; + } + return false; +} + +static void exec_q_rcmd(const char *packet,int len) +{ + char *data; + int datalen; + + /* calculate size and allocate buffer for command */ + datalen = len / 2; + data = alloca(datalen + 1); + /* dehexify command */ + unhexify(data, packet, datalen); + data[datalen] = 0; /* add terminating null */ + + int c = command_process(cur_target, data); + if (c < 0) + gdb_putpacketz(""); + else if (c == 0) + gdb_putpacketz("OK"); + else + gdb_putpacket(hexify(pbuf, "Failed\n", strlen("Failed\n")), + 2 * strlen("Failed\n")); +} + static void handle_q_string_reply(const char *str, const char *param) { unsigned long addr, len; + const size_t str_len = strlen(str); if (sscanf(param, "%08lx,%08lx", &addr, &len) != 2) { gdb_putpacketz("E01"); return; } - if (addr < strlen (str)) { - char reply[len+2]; - reply[0] = 'm'; - strncpy (reply + 1, &str[addr], len); - if(len > strlen(&str[addr])) - len = strlen(&str[addr]); - gdb_putpacket(reply, len + 1); - } else if (addr == strlen (str)) { - gdb_putpacketz("l"); - } else + else if (addr > str_len) { gdb_putpacketz("E01"); + return; + } + else if (addr == str_len) { + gdb_putpacketz("l"); + return; + } + unsigned long output_len = str_len - addr; + if (output_len > len) + output_len = len; + gdb_putpacket2("m", 1, str + addr, output_len); } -static void -handle_q_packet(char *packet, int len) +static void exec_q_supported(const char *packet, int len) { - uint32_t addr, alen; - - if(!strncmp(packet, "qRcmd,", 6)) { - char *data; - int datalen; - - /* calculate size and allocate buffer for command */ - datalen = (len - 6) / 2; - data = alloca(datalen+1); - /* dehexify command */ - unhexify(data, packet+6, datalen); - data[datalen] = 0; /* add terminating null */ + (void)packet; + (void)len; + gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE); +} - int c = command_process(cur_target, data); - if(c < 0) - gdb_putpacketz(""); - else if(c == 0) - gdb_putpacketz("OK"); - else - gdb_putpacket(hexify(pbuf, "Failed\n", strlen("Failed\n")), - 2 * strlen("Failed\n")); +static void exec_q_memory_map(const char *packet, int len) +{ + (void)packet; + (void)len; + /* Read target XML memory map */ + if ((!cur_target) && last_target) { + /* Attach to last target if detached. */ + cur_target = target_attach(last_target, + &gdb_controller); + } + if (!cur_target) { + gdb_putpacketz("E01"); + return; + } + char buf[1024]; + target_mem_map(cur_target, buf, sizeof(buf)); /* Fixme: Check size!*/ + handle_q_string_reply(buf, packet); +} - } else if (!strncmp (packet, "qSupported", 10)) { - /* Query supported protocol features */ - gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE); +static void exec_q_feature_read(const char *packet, int len) +{ + (void)len; + /* Read target description */ + if ((!cur_target) && last_target) { + /* Attach to last target if detached. */ + cur_target = target_attach(last_target, &gdb_controller); + } + if (!cur_target) { + gdb_putpacketz("E01"); + return; + } + handle_q_string_reply(target_tdesc(cur_target), packet); +} - } else if (strncmp (packet, "qXfer:memory-map:read::", 23) == 0) { - /* Read target XML memory map */ - if((!cur_target) && last_target) { - /* Attach to last target if detached. */ - cur_target = target_attach(last_target, - &gdb_controller); - } - if (!cur_target) { - gdb_putpacketz("E01"); - return; - } - char buf[1024]; - target_mem_map(cur_target, buf, sizeof(buf)); /* Fixme: Check size!*/ - handle_q_string_reply(buf, packet + 23); - - } else if (strncmp (packet, "qXfer:features:read:target.xml:", 31) == 0) { - /* Read target description */ - if((!cur_target) && last_target) { - /* Attach to last target if detached. */ - cur_target = target_attach(last_target, - &gdb_controller); - } +static void exec_q_crc(const char *packet, int len) +{ + (void)len; + uint32_t addr, alen; + if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) { if (!cur_target) { gdb_putpacketz("E01"); return; } - handle_q_string_reply(target_tdesc(cur_target), packet + 31); - } else if (sscanf(packet, "qCRC:%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) { - if(!cur_target) { - gdb_putpacketz("E01"); - return; - } uint32_t crc; int res = generic_crc32(cur_target, &crc, addr, alen); if (res) gdb_putpacketz("E03"); else gdb_putpacket_f("C%lx", crc); - - } else { - DEBUG_GDB("*** Unsupported packet: %s\n", packet); - gdb_putpacket("", 0); } } +static const cmd_executer q_commands[]= +{ + {"qRcmd,", exec_q_rcmd}, + {"qSupported", exec_q_supported}, + {"qXfer:memory-map:read::", exec_q_memory_map}, + {"qXfer:features:read:target.xml:",exec_q_feature_read}, + {"qCRC:", exec_q_crc}, + {NULL, NULL}, +}; + +static void +handle_q_packet(char *packet, int len) +{ + if (exec_command(packet, len, q_commands)) + return; + DEBUG_GDB("*** Unsupported packet: %s\n", packet); + gdb_putpacket("", 0); +} + static void handle_v_packet(char *packet, int plen) { @@ -447,35 +499,41 @@ handle_v_packet(char *packet, int plen) char cmdline[83]; char *pcmdline = cmdline; char *tok = packet + 4; - if (*tok == ';') tok++; - *cmdline='\0'; + if (*tok == ';') + ++tok; + cmdline[0] = '\0'; while(*tok != '\0') { - if(strlen(cmdline)+3 >= sizeof(cmdline)) break; + if (strlen(cmdline) + 3 >= sizeof(cmdline)) + break; if (*tok == ';') { - *pcmdline++=' '; - *pcmdline='\0'; + *pcmdline++ = ' '; + pcmdline[0] = '\0'; tok++; continue; } - if (isxdigit(*tok) && isxdigit(*(tok+1))) { + if (isxdigit(tok[0]) && isxdigit(tok[1])) { unhexify(pcmdline, tok, 2); if ((*pcmdline == ' ') || (*pcmdline == '\\')) { - *(pcmdline+1)=*pcmdline; - *pcmdline++='\\'; + pcmdline[1] = *pcmdline; + *pcmdline++ = '\\'; } pcmdline++; - tok+=2; - *pcmdline='\0'; + tok += 2; + pcmdline[0] = '\0'; continue; } break; } + #ifdef ENABLE_RTT + /* force searching rtt control block */ + rtt_found = false; + #endif /* Run target program. For us (embedded) this means reset. */ - if(cur_target) { + if (cur_target) { target_set_cmdline(cur_target, cmdline); target_reset(cur_target); gdb_putpacketz("T05"); - } else if(last_target) { + } else if (last_target) { cur_target = target_attach(last_target, &gdb_controller); @@ -485,24 +543,29 @@ handle_v_packet(char *packet, int plen) target_reset(cur_target); morse(NULL, false); gdb_putpacketz("T05"); - } else gdb_putpacketz("E01"); + } else + gdb_putpacketz("E01"); - } else gdb_putpacketz("E01"); + } else + gdb_putpacketz("E01"); } else if (sscanf(packet, "vFlashErase:%08lx,%08lx", &addr, &len) == 2) { /* Erase Flash Memory */ DEBUG_GDB("Flash Erase %08lX %08lX\n", addr, len); - if(!cur_target) { gdb_putpacketz("EFF"); return; } + if (!cur_target) { + gdb_putpacketz("EFF"); + return; + } - if(!flash_mode) { + if (!flash_mode) { /* Reset target if first flash command! */ /* This saves us if we're interrupted in IRQ context */ target_reset(cur_target); flash_mode = 1; } - if(target_flash_erase(cur_target, addr, len) == 0) { + if (target_flash_erase(cur_target, addr, len) == 0) gdb_putpacketz("OK"); - } else { + else { flash_mode = 0; gdb_putpacketz("EFF"); } @@ -511,9 +574,9 @@ handle_v_packet(char *packet, int plen) /* Write Flash Memory */ len = plen - bin; DEBUG_GDB("Flash Write %08lX %08lX\n", addr, len); - if(cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0) { + if (cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0) gdb_putpacketz("OK"); - } else { + else { flash_mode = 0; gdb_putpacketz("EFF"); } @@ -539,23 +602,18 @@ handle_z_packet(char *packet, int plen) uint32_t addr; int ret; - /* I have no idea why this doesn't work. Seems to work - * with real sscanf() though... */ - //sscanf(packet, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len); - type = packet[1] - '0'; - sscanf(packet + 2, ",%" PRIx32 ",%d", &addr, &len); + sscanf(packet, "%*[zZ]%d,%08" PRIx32 ",%d", &type, &addr, &len); if(set) ret = target_breakwatch_set(cur_target, type, addr, len); else ret = target_breakwatch_clear(cur_target, type, addr, len); - if (ret < 0) { + if (ret < 0) gdb_putpacketz("E01"); - } else if (ret > 0) { + else if (ret > 0) gdb_putpacketz(""); - } else { + else gdb_putpacketz("OK"); - } } void gdb_main(void) diff --git a/src/gdb_packet.c b/src/gdb_packet.c index 41920f266c1..33dada53a02 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -37,7 +37,7 @@ int gdb_getpacket(char *packet, int size) char recv_csum[3]; int i; - while(1) { + while (1) { /* Wait for packet start */ do { /* Spin waiting for a start of packet character - either a gdb @@ -45,38 +45,39 @@ int gdb_getpacket(char *packet, int size) */ do { packet[0] = gdb_if_getchar(); - if (packet[0]==0x04) return 1; + if (packet[0] == 0x04) + return 1; } while ((packet[0] != '$') && (packet[0] != REMOTE_SOM)); #if PC_HOSTED == 0 - if (packet[0]==REMOTE_SOM) { + if (packet[0] == REMOTE_SOM) { /* This is probably a remote control packet * - get and handle it */ - i=0; - bool gettingRemotePacket=true; + i = 0; + bool gettingRemotePacket = true; while (gettingRemotePacket) { - c=gdb_if_getchar(); + c = gdb_if_getchar(); switch (c) { case REMOTE_SOM: /* Oh dear, packet restarts */ - i=0; + i = 0; break; case REMOTE_EOM: /* Complete packet for processing */ - packet[i]=0; - remotePacketProcess(i,packet); - gettingRemotePacket=false; + packet[i] = 0; + remotePacketProcess(i, packet); + gettingRemotePacket = false; break; case '$': /* A 'real' gdb packet, best stop squatting now */ - packet[0]='$'; - gettingRemotePacket=false; + packet[0] = '$'; + gettingRemotePacket = false; break; default: - if (i= 32) && (c < 127)) + DEBUG_GDB_WIRE("%c", c); + else + DEBUG_GDB_WIRE("\\x%02X", c); +#endif + if ((c == '$') || (c == '#') || (c == '}') || (c == '*')) { + gdb_if_putchar('}', 0); + gdb_if_putchar(c ^ 0x20, 0); + *csum += '}' + (c ^ 0x20); + } + else { + gdb_if_putchar(c, 0); + *csum += c; + } +} + +void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int size2) +{ + int i; + unsigned char csum; + char xmit_csum[3]; + int tries = 0; + + do { + DEBUG_GDB_WIRE("%s : ", __func__); + csum = 0; + gdb_if_putchar('$', 0); + + for (i = 0; i < size1; ++i) + gdb_next_char(packet1[i], &csum); + for (i = 0; i < size2; ++i) + gdb_next_char(packet2[i], &csum); + + gdb_if_putchar('#', 0); + snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum); + gdb_if_putchar(xmit_csum[0], 0); + gdb_if_putchar(xmit_csum[1], 1); + DEBUG_GDB_WIRE("\n"); + } while ((gdb_if_getchar_to(2000) != '+') && (tries++ < 3)); +} + void gdb_putpacket(const char *packet, int size) { int i; unsigned char csum; - unsigned char c; char xmit_csum[3]; int tries = 0; @@ -150,29 +197,14 @@ void gdb_putpacket(const char *packet, int size) DEBUG_GDB_WIRE("%s : ", __func__); csum = 0; gdb_if_putchar('$', 0); - for(i = 0; i < size; i++) { - c = packet[i]; -#if PC_HOSTED == 1 - if ((c >= 32) && (c < 127)) - DEBUG_GDB_WIRE("%c", c); - else - DEBUG_GDB_WIRE("\\x%02X", c); -#endif - if((c == '$') || (c == '#') || (c == '}') || (c == '*')) { - gdb_if_putchar('}', 0); - gdb_if_putchar(c ^ 0x20, 0); - csum += '}' + (c ^ 0x20); - } else { - gdb_if_putchar(c, 0); - csum += c; - } - } + for (i = 0; i < size; ++i) + gdb_next_char(packet[i], &csum); gdb_if_putchar('#', 0); snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum); gdb_if_putchar(xmit_csum[0], 0); gdb_if_putchar(xmit_csum[1], 1); DEBUG_GDB_WIRE("\n"); - } while((gdb_if_getchar_to(2000) != '+') && (tries++ < 3)); + } while ((gdb_if_getchar_to(2000) != '+') && (tries++ < 3)); } void gdb_putpacket_f(const char *fmt, ...) @@ -190,13 +222,13 @@ void gdb_putpacket_f(const char *fmt, ...) void gdb_out(const char *buf) { - char *hexdata; - int i; - - hexdata = alloca((i = strlen(buf)*2 + 1) + 1); - hexdata[0] = 'O'; - hexify(hexdata+1, buf, strlen(buf)); - gdb_putpacket(hexdata, i); + int l = strlen(buf); + char *hexdata = calloc(1, 2 * l + 1); + if (!hexdata) + return; + hexify(hexdata, buf, l); + gdb_putpacket2("O", 1, hexdata, 2 * l); + free(hexdata); } void gdb_voutf(const char *fmt, va_list ap) diff --git a/src/include/gdb_packet.h b/src/include/gdb_packet.h index 789cfa1c17a..af23c5b2756 100644 --- a/src/include/gdb_packet.h +++ b/src/include/gdb_packet.h @@ -25,6 +25,7 @@ int gdb_getpacket(char *packet, int size); void gdb_putpacket(const char *packet, int size); +void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int size2); #define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet)) void gdb_putpacket_f(const char *packet, ...); diff --git a/src/include/rtt.h b/src/include/rtt.h new file mode 100644 index 00000000000..7d8f04adc7b --- /dev/null +++ b/src/include/rtt.h @@ -0,0 +1,34 @@ +#ifndef RTT_H +#define RTT_H +#include + +#define MAX_RTT_CHAN 16 + +extern char rtt_ident[16]; // string +extern bool rtt_enabled; // rtt on/off +extern bool rtt_found; // control block found +extern uint32_t rtt_cbaddr; // control block address +extern uint32_t rtt_min_poll_ms; // min time between polls (ms) +extern uint32_t rtt_max_poll_ms; // max time between polls (ms) +extern uint32_t rtt_max_poll_errs; // max number of errors before disconnect +extern bool rtt_auto_channel; // manual or auto channel selection +extern bool rtt_flag_skip; // skip if host-to-target fifo full +extern bool rtt_flag_block; // block if host-to-target fifo full + +struct rtt_channel_struct { + bool is_enabled; // does user want to see this channel? + bool is_configured; // is channel configured in control block? + bool is_output; + uint32_t buf_addr; + uint32_t buf_size; + uint32_t head_addr; + uint32_t tail_addr; + uint32_t flag; +}; + +extern struct rtt_channel_struct rtt_channel[MAX_RTT_CHAN]; + +// true if target memory access does not work when target running +extern bool target_no_background_memory_access(target *cur_target); +extern void poll_rtt(target *cur_target); +#endif diff --git a/src/include/rtt_if.h b/src/include/rtt_if.h new file mode 100644 index 00000000000..711be5ea5a2 --- /dev/null +++ b/src/include/rtt_if.h @@ -0,0 +1,36 @@ +#ifndef RTT_IF_H +#define RTT_IF_H +/* rtt i/o to terminal */ + +/* default buffer sizes, 8 bytes added to up buffer for alignment and padding */ +/* override RTT_UP_BUF_SIZE and RTT_DOWN_BUF_SIZE in platform.h if needed */ + +#if !defined(RTT_UP_BUF_SIZE) || !defined(RTT_DOWN_BUF_SIZE) +#if (PC_HOSTED == 1) +#define RTT_UP_BUF_SIZE (4096 + 8) +#define RTT_DOWN_BUF_SIZE (512) +#elif defined(STM32F7) +#define RTT_UP_BUF_SIZE (4096 + 8) +#define RTT_DOWN_BUF_SIZE (2048) +#elif defined(STM32F4) +#define RTT_UP_BUF_SIZE (2048 + 8) +#define RTT_DOWN_BUF_SIZE (256) +#else /* stm32f103 */ +#define RTT_UP_BUF_SIZE (1024 + 8) +#define RTT_DOWN_BUF_SIZE (256) +#endif +#endif + +/* hosted initialisation */ +extern int rtt_if_init(void); +/* hosted teardown */ +extern int rtt_if_exit(void); + +/* target to host: write len bytes from the buffer starting at buf. return number bytes written */ +extern uint32_t rtt_write(const char *buf, uint32_t len); +/* host to target: read one character, non-blocking. return character, -1 if no character */ +extern int32_t rtt_getchar(); +/* host to target: true if no characters available for reading */ +extern bool rtt_nodata(); + +#endif diff --git a/src/platforms/96b_carbon/Makefile.inc b/src/platforms/96b_carbon/Makefile.inc new file mode 100644 index 00000000000..2107c4f37ec --- /dev/null +++ b/src/platforms/96b_carbon/Makefile.inc @@ -0,0 +1,29 @@ +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -Istm32/include -mcpu=cortex-m4 -mthumb \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -DSTM32F4 -D_96B_CARBON -I../libopencm3/include \ + -Iplatforms/stm32 -DDFU_SERIAL_LENGTH=9 + +LDFLAGS = -lopencm3_stm32f4 -Wl,--defsym,_stack=0x20006000 \ + -Wl,-T,platforms/stm32/96b_carbon.ld -nostartfiles -lc -lnosys \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m4 -Wl,-gc-sections \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -L../libopencm3/lib + +VPATH += platforms/stm32 + +SRC += cdcacm.c \ + traceswodecode.c \ + traceswo.c \ + usbuart.c \ + serialno.c \ + timing.c \ + timing_stm32.c + +all: blackmagic.bin + +host_clean: + -$(Q)$(RM) blackmagic.bin diff --git a/src/platforms/96b_carbon/Readme.md b/src/platforms/96b_carbon/Readme.md new file mode 100644 index 00000000000..0604d6e3c7f --- /dev/null +++ b/src/platforms/96b_carbon/Readme.md @@ -0,0 +1,71 @@ +Connections +=========== + +The pinout for the programmer is concentrated within a single part of +the Low Speed connector. The target control pins at the even pins 2 +through 16 with UART on LS-03 and LS-05. + +The pinout for the programmer allows a Carbon to program another Carbon +(either the STM32 or the nRF51) with adjacent pins from LS-06 to LS-12. +The order matches that of the SWD pins for easy hook up. + +JTAG/SWD +-------- + + * LS-02 (PB12): TDO/TRACESWO + * LS-04 (PB15): TDI + * LS-06 (PB14): TMS/SWDIO + * LS-08 (PB13): TCK/SWCLK + * LS-10 : GND + * LS-12 : Vcc + * LS-14 (PC3) : TRST (optional test reset) + * LS-16 (PC5) : SRST (system reset) + +LEDs +---- + + * USR1 (green): Debug activity indicator + * USR2 (green): UART activity indicator + * BT (blue) : Error indicator + +UART +---- + + * LS-03 (PA2): UART TX + * LS-05 (PA3): UART RX + +How to Build +============ + + cd blackmagic + make clean + make PROBE_HOST=96b_carbon + +Flashing using dfu-util +======================= + +Connect to the USB OTG socket on the Carbon and force the device into +system bootloader mode by holding down the BOOT0 switch whilst pressing +and releasing the RST switch. To program the device try: + + sudo dfu-util -d [0483:df11] -a 0 -D src/blackmagic.bin -s 0x08000000 + +Self-programming +================ + +A Carbon is capable of self-programming its own nRF51 by connecting two +jumper wires from LS-06 to BLE_SWD-4 (DIO) and LS-08 to BLE_SWD-3 (CLK). + + +------------------------------------------------------------------+ + | +-2--4--6--8-10-12-14-16-18-20-22-24-26-28-30-+ | + | | . . .-+.-+. . . . . . . . . . . | | + | | . . . |. |. . . . . . . . . . . | | + | +-1--3--5-|7-|9-11-13-15-17-19-21-23-25-27-29-+ | + | | | | + | +--+-----------------------------+ | + | | | | + | +--------------------------+ | | + | | | | + | . . . . . | + | . . . . . . . . . | + +------------------------------------------------------------------+ diff --git a/src/platforms/96b_carbon/platform.c b/src/platforms/96b_carbon/platform.c new file mode 100644 index 00000000000..f7b6ec98560 --- /dev/null +++ b/src/platforms/96b_carbon/platform.c @@ -0,0 +1,103 @@ +/* + * 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 implements the platform specific functions for the STM32 + * implementation. + */ + +#include "general.h" +#include "cdcacm.h" +#include "usbuart.h" +#include "morse.h" + +#include +#include +#include +#include +#include +#include +#include + +jmp_buf fatal_error_jmpbuf; + +void platform_init(void) +{ + rcc_clock_setup_pll(&rcc_hse_16mhz_3v3[RCC_CLOCK_3V3_84MHZ]); + + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_OTGFS); + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_GPIOD); + rcc_periph_clock_enable(RCC_CRC); + + /* Set up USB Pins and alternate function*/ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); + + gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + TMS_PIN | TCK_PIN | TDI_PIN); + gpio_set_output_options(JTAG_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, + TMS_PIN | TCK_PIN | TDI_PIN); + gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, + TDO_PIN); + + gpio_mode_setup(TRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TRST_PIN); + gpio_mode_setup(SRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SRST_PIN); + + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_IDLE_RUN); + gpio_mode_setup(LED_PORT_UART, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_UART); + gpio_mode_setup(LED_PORT_ERROR, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_ERROR); + + platform_timing_init(); + usbuart_init(); + cdcacm_init(); +} + +void platform_srst_set_val(bool assert) +{ + if (assert) + gpio_clear(SRST_PORT, SRST_PIN); + else + gpio_set(SRST_PORT, SRST_PIN); +} + +bool platform_srst_get_val(void) +{ + return gpio_get(SRST_PORT, SRST_PIN); +} + +const char *platform_target_voltage(void) +{ + return "ABSENT!"; +} + +void platform_request_boot(void) +{ + /* Disconnect USB cable */ + usbd_disconnect(usbdev, 1); + nvic_disable_irq(USB_IRQ); + + /* Jump to the built in bootloader by mapping System flash */ + rcc_periph_clock_enable(RCC_SYSCFG); + SYSCFG_MEMRM &= ~3; + SYSCFG_MEMRM |= 1; +} diff --git a/src/platforms/96b_carbon/platform.h b/src/platforms/96b_carbon/platform.h new file mode 100644 index 00000000000..2091e8c02b6 --- /dev/null +++ b/src/platforms/96b_carbon/platform.h @@ -0,0 +1,165 @@ +/* + * 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 implements the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include "gpio.h" +#include "timing.h" +#include "timing_stm32.h" +#include "version.h" + +#include + +#define PLATFORM_HAS_TRACESWO +#define PLATFORM_IDENT "(Carbon)" +# +/* Important pin mappings for Carbon implementation: + * + * LED0 = PA15 (Green USR2 : Idle)) + * LED1 = PD5 (Green USR1 : UART) + * LED2 = PB5 (Blue BT : Error) + * + * TDO = PB12 (LS-02) + * TDI = PB15 (LS-04) + * TMS/SWDIO = PB14 (LS-06) The pinout for the programmer allows a Carbon to + * TCK/SWCLK = PB13 (LS-08) program another Carbon (either the STM32 or the + * GND (LS-10) nRF51) with adjacent pins from LS-06 to LS-12. + * VCC (LS-12) The order matches the SWD pins for easy hook up. + * nTRST = PC3 (LS-14) + * nSRST = PC5 (LS-16) + */ + +/* Hardware definitions... */ +#define JTAG_PORT GPIOB +#define TDO_PORT JTAG_PORT +#define TDI_PORT JTAG_PORT +#define TMS_PORT JTAG_PORT +#define TCK_PORT JTAG_PORT +#define TDO_PIN GPIO12 +#define TDI_PIN GPIO15 +#define TMS_PIN GPIO14 +#define TCK_PIN GPIO13 + +#define SWDIO_PORT JTAG_PORT +#define SWCLK_PORT JTAG_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define TRST_PORT GPIOC +#define TRST_PIN GPIO3 +#define SRST_PORT GPIOC +#define SRST_PIN GPIO5 + +#define LED_PORT GPIOA +#define LED_IDLE_RUN GPIO15 +#define LED_PORT_UART GPIOD +#define LED_UART GPIO2 +#define LED_PORT_ERROR GPIOB +#define LED_ERROR GPIO5 + +#define TMS_SET_MODE() \ + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, \ + GPIO_PUPD_NONE, TMS_PIN); +#define SWDIO_MODE_FLOAT() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, \ + GPIO_PUPD_NONE, SWDIO_PIN); + +#define SWDIO_MODE_DRIVE() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \ + GPIO_PUPD_NONE, SWDIO_PIN); + + +#define USB_DRIVER otgfs_usb_driver +#define USB_IRQ NVIC_OTG_FS_IRQ +#define USB_ISR otg_fs_isr +/* Interrupt priorities. Low numbers are high priority. + * TIM3 is used for traceswo capture and must be highest priority. + * USBUSART can be lowest priority as it is using DMA to transfer + * data to the buffer and thus is less critical than USB. + */ +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) +#define IRQ_PRI_TRACE (0 << 4) + +#define USBUSART USART2 +#define USBUSART_CR1 USART2_CR1 +#define USBUSART_IRQ NVIC_USART2_IRQ +#define USBUSART_CLK RCC_USART2 +#define USBUSART_TX_PORT GPIOA +#define USBUSART_TX_PIN GPIO2 +#define USBUSART_RX_PORT GPIOA +#define USBUSART_RX_PIN GPIO3 +#define USBUSART_ISR(x) usart2_isr(x) +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_STREAM6 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_STREAM6_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_stream6_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_STREAM5 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_STREAM5_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_stream5_isr(x) +/* For STM32F4 DMA trigger source must be specified */ +#define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 + +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ + USBUSART_TX_PIN); \ + gpio_mode_setup(USBUSART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ + USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_TX_PORT, GPIO_AF7, USBUSART_TX_PIN); \ + gpio_set_af(USBUSART_RX_PORT, GPIO_AF7, USBUSART_RX_PIN); \ + } while(0) + +#define TRACE_TIM TIM3 +#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) +#define TRACE_IRQ NVIC_TIM3_IRQ +#define TRACE_ISR(x) tim3_isr(x) + +#define DEBUG(...) + +#define gpio_set_val(port, pin, val) do { \ + if(val) \ + gpio_set((port), (pin)); \ + else \ + gpio_clear((port), (pin)); \ +} while(0) + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} +#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT_ERROR, LED_ERROR, state);} + +static inline int platform_hwversion(void) +{ + return 0; +} + +/* Use newlib provided integer only stdio functions */ +#define sscanf siscanf +#define sprintf siprintf +#define vasprintf vasiprintf +#define snprintf sniprintf + +#endif + diff --git a/src/platforms/96b_carbon/usbdfu.c b/src/platforms/96b_carbon/usbdfu.c new file mode 100644 index 00000000000..39257e86ac6 --- /dev/null +++ b/src/platforms/96b_carbon/usbdfu.c @@ -0,0 +1,74 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2013 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 . + */ + +#include "general.h" +#include "usbdfu.h" + +#include +#include +#include +#include + +void dfu_detach(void) +{ + /* USB device must detach, we just reset... */ + scb_reset_system(); +} + +int main(void) +{ + /* Check the force bootloader pin*/ + rcc_periph_enable_clock(RCC_GPIOA); + if(!gpio_get(GPIOA, GPIO0)) + dfu_jump_app_if_valid(); + + dfu_protect_enable(); + + /* Set up clock*/ + rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); + systick_set_reload(2100000); + + systick_interrupt_enable(); + systick_counter_enable(); + + /* Handle LEDs */ + rcc_periph_enable_clock(RCC_GPIOD); + gpio_clear(GPIOD, GPIO12 | GPIO13 | GPIO14 |GPIO15); + gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + GPIO12 | GPIO13 | GPIO14 |GPIO15); + + /* Set up USB*/ + rcc_periph_enable_clock(RCC_OTGFS); + + /* Set up USB Pins and alternate function*/ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO10 | GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO10 | GPIO11 | GPIO12); + dfu_init(&stm32f107_usb_driver); + + dfu_main(); +} + + +void sys_tick_handler(void) +{ + gpio_toggle(GPIOD, GPIO12); /* Green LED on/off */ +} + diff --git a/src/platforms/blackpillv2/Makefile.inc b/src/platforms/blackpillv2/Makefile.inc new file mode 100644 index 00000000000..f0d2b8e82d4 --- /dev/null +++ b/src/platforms/blackpillv2/Makefile.inc @@ -0,0 +1,52 @@ +CROSS_COMPILE ?= arm-none-eabi- +BMP_BOOTLOADER ?= +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -Istm32/include -mcpu=cortex-m4 -mthumb \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -DSTM32F4 -I../libopencm3/include \ + -Iplatforms/stm32 + +LINKER_SCRIPT=platforms/stm32/blackpillv2.ld + +LDFLAGS_BOOT = -lopencm3_stm32f4 \ + -Wl,-T,$(LINKER_SCRIPT) -nostartfiles -lc -lnosys \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m4 -Wl,-gc-sections \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -L../libopencm3/lib + +ifeq ($(BMP_BOOTLOADER), 1) +$(info Load address 0x08004000 for BMPBootloader) +LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8004000 +CFLAGS += -DDFU_SERIAL_LENGTH=9 +else +LDFLAGS += $(LDFLAGS_BOOT) +CFLAGS += -DDFU_SERIAL_LENGTH=13 +endif + +VPATH += platforms/stm32 + +SRC += cdcacm.c \ + traceswodecode.c \ + traceswo.c \ + usbuart.c \ + serialno.c \ + timing.c \ + timing_stm32.c \ + +ifneq ($(BMP_BOOTLOADER), 1) +all: blackmagic.bin +else +all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex +blackmagic_dfu: usbdfu.o dfucore.o dfu_f4.o serialno.o + $(CC) $^ -o $@ $(LDFLAGS_BOOT) + +blackmagic_dfu.bin: blackmagic_dfu + $(OBJCOPY) -O binary $^ $@ + +blackmagic_dfu.hex: blackmagic_dfu + $(OBJCOPY) -O ihex $^ $@ +endif +host_clean: + -$(Q)$(RM) blackmagic.bin diff --git a/src/platforms/blackpillv2/Readme.md b/src/platforms/blackpillv2/Readme.md new file mode 100644 index 00000000000..5f1b61ec166 --- /dev/null +++ b/src/platforms/blackpillv2/Readme.md @@ -0,0 +1,53 @@ +# Firmware BMP for STM32F401/stm32f411 MiniF4 aka BlackPillV2 boards + +Allows the use of BlackPillV2 as a Black Magic Probe + +https://github.com/WeActTC/MiniSTM32F4x1 + +## Connections: + +* JTAG/SWD + * PA1: TDI + * PA13: TMS/SWDIO + * PA14: TCK/SWCLK + * PB3: TDO/TRACESWO + * PB5: TRST + * PB4: SRST + +* USB USART + * PB6: USART1 TX (usbuart_xxx) + * PB7: USART1 RX (usbuart_xxx) + +* +3V3. + * PB8 - turn on IRLML5103 transistor + +How to Build +======================================== +``` +cd blackmagic +make clean +make PROBE_HOST=blackpill +``` + +How to Flash with dfu +======================================== +* After build: + * 1) `apt install dfu-util` + * 2) Force the F4 into system bootloader mode by jumpering "BOOT0" to "3V3" and "PB2/BOOT1" to "GND" and reset (RESET button). System bootloader should appear. + * 3) `dfu-util -a 0 --dfuse-address 0x08000000 -D blackmagic.bin` + +To exit from dfu mode press a "key" and "reset", release reset. BMP firmware should appear + + +10 pin male from pins +======================================== + +| PB3/TDO | PB7/RX | PB6/TX | X | PA1/TDI | +| -------- | ----------- | ---------- | ---------- | ------- | +| PB4/SRST | +3V3/PB8 SW | PA13/SWDIO | PA14/SWCLK | GND | + +SWJ frequency setting +==================================== +https://github.com/blackmagic-debug/blackmagic/pull/783#issue-529197718 + +`mon freq 900k` helps at most diff --git a/src/platforms/blackpillv2/platform.c b/src/platforms/blackpillv2/platform.c new file mode 100644 index 00000000000..1391ff7400e --- /dev/null +++ b/src/platforms/blackpillv2/platform.c @@ -0,0 +1,144 @@ +/* + * 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 implements the platform specific functions for the STM32 + * implementation. + */ + +#include "general.h" +#include "cdcacm.h" +#include "usbuart.h" +#include "morse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +jmp_buf fatal_error_jmpbuf; +extern char _ebss[]; + +void platform_init(void) +{ + volatile uint32_t *magic = (uint32_t *)_ebss; + /* Enable GPIO peripherals */ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_GPIOB); + + /* Check the USER button*/ + if (gpio_get(GPIOA, GPIO0) || + ((magic[0] == BOOTMAGIC0) && (magic[1] == BOOTMAGIC1))) + { + magic[0] = 0; + magic[1] = 0; + /* Assert blue LED as indicator we are in the bootloader */ + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, LED_BOOTLOADER); + gpio_set(LED_PORT, LED_BOOTLOADER); + /* Jump to the built in bootloader by mapping System flash. + As we just come out of reset, no other deinit is needed!*/ + rcc_periph_clock_enable(RCC_SYSCFG); + SYSCFG_MEMRM &= ~3; + SYSCFG_MEMRM |= 1; + scb_reset_core(); + } + rcc_clock_setup_pll(&rcc_hse_25mhz_3v3[RCC_CLOCK_3V3_84MHZ]); + + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_OTGFS); + rcc_periph_clock_enable(RCC_CRC); + + /* Set up USB Pins and alternate function*/ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO10 | GPIO11 | GPIO12); + + GPIOA_OSPEEDR &= 0x3C00000C; + GPIOA_OSPEEDR |= 0x28000008; + + gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, + TCK_PIN | TDI_PIN); + gpio_mode_setup(JTAG_PORT, GPIO_MODE_INPUT, + GPIO_PUPD_NONE, TMS_PIN); + gpio_set_output_options(JTAG_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, + TCK_PIN | TDI_PIN | TMS_PIN); + gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, + GPIO_PUPD_NONE, + TDO_PIN); + gpio_set_output_options(TDO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, + TDO_PIN | TMS_PIN); + + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, + LED_IDLE_RUN | LED_ERROR | LED_BOOTLOADER); + + gpio_mode_setup(LED_PORT_UART, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_UART); + +#ifdef PLATFORM_HAS_POWER_SWITCH + gpio_set(PWR_BR_PORT, PWR_BR_PIN); + gpio_mode_setup(PWR_BR_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, + PWR_BR_PIN); +#endif + + platform_timing_init(); + usbuart_init(); + cdcacm_init(); + + // https://github.com/libopencm3/libopencm3/pull/1256#issuecomment-779424001 + OTG_FS_GCCFG |= OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; + OTG_FS_GCCFG &= ~(OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN); +} + +void platform_srst_set_val(bool assert) { (void)assert; } +bool platform_srst_get_val(void) { return false; } + +const char *platform_target_voltage(void) +{ + return NULL; +} + +void platform_request_boot(void) +{ + uint32_t *magic = (uint32_t *)&_ebss; + magic[0] = BOOTMAGIC0; + magic[1] = BOOTMAGIC1; + scb_reset_system(); +} + +#ifdef PLATFORM_HAS_POWER_SWITCH +bool platform_target_get_power(void) +{ + return !gpio_get(PWR_BR_PORT, PWR_BR_PIN); +} + +void platform_target_set_power(bool power) +{ + gpio_set_val(PWR_BR_PORT, PWR_BR_PIN, !power); +} +#endif diff --git a/src/platforms/blackpillv2/platform.h b/src/platforms/blackpillv2/platform.h new file mode 100644 index 00000000000..3cf40670a27 --- /dev/null +++ b/src/platforms/blackpillv2/platform.h @@ -0,0 +1,194 @@ +/* + * 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 implements the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include "gpio.h" +#include "timing.h" +#include "timing_stm32.h" + +#include + +#define PLATFORM_HAS_TRACESWO +#define PLATFORM_IDENT "(BlackPillV2) " +/* Important pin mappings for STM32 implementation: + * JTAG/SWD + * PA1: TDI
+ * PA13: TMS/SWDIO
+ * PA14: TCK/SWCLK
+ * PB3: TDO/TRACESWO
+ * PB5: TRST
+ * PB4: SRST
+ * USB USART + * PB6: USART1 TX + * PB7: USART1 RX + * +3V3 + * PB8 - turn on IRLML5103 transistor + * Force DFU mode button: PA0 + */ + +/* Hardware definitions... */ +#define JTAG_PORT GPIOA +#define TDI_PORT JTAG_PORT +#define TMS_PORT JTAG_PORT +#define TCK_PORT JTAG_PORT +#define TDO_PORT GPIOB +#define TDI_PIN GPIO1 +#define TMS_PIN GPIO13 +#define TCK_PIN GPIO14 +#define TDO_PIN GPIO3 + +#define SWDIO_PORT JTAG_PORT +#define SWCLK_PORT JTAG_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define TRST_PORT GPIOB +#define TRST_PIN GPIO5 +#define SRST_PORT GPIOB +#define SRST_PIN GPIO4 + +#define PWR_BR_PORT GPIOB +#define PWR_BR_PIN GPIO8 + +#define LED_PORT GPIOC +#define LED_PORT_UART GPIOA +#define LED_UART GPIO1 +#define LED_IDLE_RUN GPIO15 +#define LED_ERROR GPIO14 +#define LED_BOOTLOADER GPIO13 + +#define USBUSART USART1 +#define USBUSART_CR1 USART1_CR1 +#define USBUSART_DR USART1_DR +#define USBUSART_IRQ NVIC_USART1_IRQ +#define USBUSART_CLK RCC_USART1 +#define USBUSART_PORT GPIOB +#define USBUSART_TX_PIN GPIO6 +#define USBUSART_RX_PIN GPIO7 +#define USBUSART_ISR(x) usart1_isr(x) +#define USBUSART_DMA_BUS DMA2 +#define USBUSART_DMA_CLK RCC_DMA2 +#define USBUSART_DMA_TX_CHAN DMA_STREAM7 +#define USBUSART_DMA_TX_IRQ NVIC_DMA2_STREAM7_IRQ +#define USBUSART_DMA_TX_ISR(x) dma2_stream7_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_STREAM5 +#define USBUSART_DMA_RX_IRQ NVIC_DMA2_STREAM5_IRQ +#define USBUSART_DMA_RX_ISR(x) dma2_stream5_isr(x) +/* For STM32F4 DMA trigger source must be specified */ +#define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 + +#define BOOTMAGIC0 0xb007da7a +#define BOOTMAGIC1 0xbaadfeed + +#define TMS_SET_MODE() \ + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, \ + GPIO_PUPD_NONE, TMS_PIN); +#define SWDIO_MODE_FLOAT() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, \ + GPIO_PUPD_NONE, SWDIO_PIN); + +#define SWDIO_MODE_DRIVE() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, \ + GPIO_PUPD_NONE, SWDIO_PIN); +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, \ + USBUSART_TX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_PP, \ + GPIO_OSPEED_100MHZ, USBUSART_TX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_TX_PIN); \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, \ + USBUSART_RX_PIN); \ + gpio_set_output_options(USBUSART_PORT, GPIO_OTYPE_OD, \ + GPIO_OSPEED_100MHZ, USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, USBUSART_RX_PIN); \ +} while(0) + +#define USB_DRIVER stm32f107_usb_driver +#define USB_IRQ NVIC_OTG_FS_IRQ +#define USB_ISR(x) otg_fs_isr(x) +/* Interrupt priorities. Low numbers are high priority. + * TIM3 is used for traceswo capture and must be highest priority. + */ +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) +#define IRQ_PRI_TRACE (0 << 4) + + +#define TRACE_TIM TIM3 +#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) +#define TRACE_IRQ NVIC_TIM3_IRQ +#define TRACE_ISR(x) tim3_isr(x) + +#define gpio_set_val(port, pin, val) do { \ + if(val) \ + gpio_set((port), (pin)); \ + else \ + gpio_clear((port), (pin)); \ +} while(0) + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} +#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);} + +static inline int platform_hwversion(void) +{ + return 0; +} + +/* + * Use newlib provided integer only stdio functions + */ + +/* sscanf */ +#ifdef sscanf +#undef sscanf +#define sscanf siscanf +#else +#define sscanf siscanf +#endif +/* sprintf */ +#ifdef sprintf +#undef sprintf +#define sprintf siprintf +#else +#define sprintf siprintf +#endif +/* vasprintf */ +#ifdef vasprintf +#undef vasprintf +#define vasprintf vasiprintf +#else +#define vasprintf vasiprintf +#endif +/* snprintf */ +#ifdef snprintf +#undef snprintf +#define snprintf sniprintf +#else +#define snprintf sniprintf +#endif + +#endif diff --git a/src/platforms/blackpillv2/usbdfu.c b/src/platforms/blackpillv2/usbdfu.c new file mode 100644 index 00000000000..cdc68d33416 --- /dev/null +++ b/src/platforms/blackpillv2/usbdfu.c @@ -0,0 +1,73 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include + +#include "usbdfu.h" +#include "general.h" +#include "platform.h" + +uint32_t app_address = 0x08004000; +extern char _ebss[]; + +void dfu_detach(void) +{ + scb_reset_system(); +} + +int main(void) +{ + volatile uint32_t *magic = (uint32_t *)_ebss; + rcc_periph_clock_enable(RCC_GPIOA); + if (gpio_get(GPIOA, GPIO0) || + ((magic[0] == BOOTMAGIC0) && (magic[1] == BOOTMAGIC1))) { + magic[0] = 0; + magic[1] = 0; + } else { + dfu_jump_app_if_valid(); + } + rcc_clock_setup_pll(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); + + /* Assert blue LED as indicator we are in the bootloader */ + rcc_periph_clock_enable(RCC_GPIOD); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, LED_BOOTLOADER); + gpio_set(LED_PORT, LED_BOOTLOADER); + + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_OTGFS); + + /* Set up USB Pins and alternate function*/ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); + + dfu_protect(false); + dfu_init(&USB_DRIVER); + dfu_main(); + +} + +void dfu_event(void) +{ +} + diff --git a/src/platforms/common/cdcacm.c b/src/platforms/common/cdcacm.c index 399dd3d6b52..fe90c495885 100644 --- a/src/platforms/common/cdcacm.c +++ b/src/platforms/common/cdcacm.c @@ -414,7 +414,7 @@ static char serial_no[DFU_SERIAL_LENGTH]; #define BOARD_IDENT "Black Magic Probe " PLATFORM_IDENT FIRMWARE_VERSION static const char *usb_strings[] = { - "Black Sphere Technologies", + "Black Magic Debug", BOARD_IDENT, serial_no, "Black Magic GDB Server", @@ -450,13 +450,21 @@ static enum usbd_request_return_codes cdcacm_control_request(usbd_device *dev, switch(req->bRequest) { case USB_CDC_REQ_SET_CONTROL_LINE_STATE: cdcacm_set_modem_state(dev, req->wIndex, true, true); - /* Ignore if not for GDB interface */ - if(req->wIndex != GDB_IF_NO) - return USBD_REQ_HANDLED; - - cdcacm_gdb_dtr = req->wValue & 1; - - return USBD_REQ_HANDLED; + switch(req->wIndex) { + case UART_IF_NO: + #ifdef USBUSART_DTR_PIN + gpio_set_val(USBUSART_PORT, USBUSART_DTR_PIN, !(req->wValue & 1)); + #endif + #ifdef USBUSART_RTS_PIN + gpio_set_val(USBUSART_PORT, USBUSART_RTS_PIN, !((req->wValue >> 1) & 1)); + #endif + return USBD_REQ_HANDLED; + case GDB_IF_NO: + cdcacm_gdb_dtr = req->wValue & 1; + return USBD_REQ_HANDLED; + default: + return USBD_REQ_NOTSUPP; + } case USB_CDC_REQ_SET_LINE_CODING: if(*len < sizeof(struct usb_cdc_line_coding)) return USBD_REQ_NOTSUPP; diff --git a/src/platforms/f072/Makefile.inc b/src/platforms/f072/Makefile.inc new file mode 100644 index 00000000000..693deb4b132 --- /dev/null +++ b/src/platforms/f072/Makefile.inc @@ -0,0 +1,33 @@ +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -Istm32/include -mcpu=cortex-m0 -mthumb \ + -DSTM32F0 -I../libopencm3/include \ + -DDFU_SERIAL_LENGTH=13 -Iplatforms/stm32 + +LDFLAGS = --specs=nano.specs -lopencm3_stm32f0 \ + -Wl,-T,platforms/stm32/stm32f07xzb.ld \ + -nostartfiles -lc -lnosys -Wl,-Map=mapfile -mthumb \ + -mcpu=cortex-m0 -Wl,-gc-sections -L../libopencm3/lib + +VPATH += platforms/stm32 + +SRC += cdcacm.c \ + traceswodecode.c\ + traceswo.c \ + usbuart.c \ + serialno.c \ + timing.c \ + timing_stm32.c \ + +all: blackmagic.bin +blackmagic.elf: libopencm3_stm32f0 + +libopencm3_stm32f0: + $(Q)$(MAKE) $(MFLAGS) -C ../libopencm3 lib/stm32/f0 + +host_clean: + -$(Q)$(RM) blackmagic.bin + +.PHONY: libopencm3_stm32f0 diff --git a/src/platforms/f072/Readme b/src/platforms/f072/Readme new file mode 100644 index 00000000000..5cff6bef529 --- /dev/null +++ b/src/platforms/f072/Readme @@ -0,0 +1,24 @@ +System vs BMP Bootloader +======================== +For the BMP bootloader, flashing was not reliable. So we use the system +bootloder unconditional. + +Connections: +==================== + +PA0: User button to force system bootloader entry with reset +PA2/PA3 eventual connected to the STLINK/ STM32F103C8 + +PA0: TDI +PA1: TMS/SWDIO +PA7: TCK/SWCLK +PA6: TDO/TRACESWO + +PA5: TRST + +PB5: LED green +PB6: LED yellow +PB7: LED red + +PB0: VTARGET +PB1: VUSB \ No newline at end of file diff --git a/src/platforms/f072/platform.c b/src/platforms/f072/platform.c new file mode 100644 index 00000000000..10ffc7e1c00 --- /dev/null +++ b/src/platforms/f072/platform.c @@ -0,0 +1,112 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 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 + * 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 the platform specific functions for the STM32F072-IF + * implementation. + */ + +#include "general.h" +#include "cdcacm.h" +#include "usbuart.h" +#include "morse.h" + +#include +#include +#include +#include +#include +#include +#include + +extern uint32_t _ebss[]; + +#define SYSCFG_MEMRM MMIO32(0x40010000) +#define SYSMEM_RESET_VECTOR 0x1fffC804 + +void platform_init(void) +{ + volatile uint32_t *magic = (uint32_t *) &_ebss; + /* If RCC_CFGR is not at it's reset value, the bootloader was executed + * and SET_ADDRESS got us to this place. On F3 ???, without further efforts, + * DFU does not start in that case. + * So issue an reset to allow a clean start! + */ + if (RCC_CFGR) + scb_reset_system(); + SYSCFG_MEMRM &= ~3; + /* Buttom is BOOT0, so buttom is already evaluated!*/ + if (((magic[0] == BOOTMAGIC0) && (magic[1] == BOOTMAGIC1))) { + magic[0] = 0; + magic[1] = 0; + /* Jump to the built in bootloader by mapping System flash. + As we just come out of reset, no other deinit is needed!*/ + SYSCFG_MEMRM |= 1; + void (*bootloader)(void) = + (void (*)(void)) (*((uint32_t *) SYSMEM_RESET_VECTOR)); + /* We come out of reset, so MSP is already set*/ + bootloader(); + while (1); + } + rcc_clock_setup_in_hse_8mhz_out_48mhz(); + + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_CRC); + rcc_set_usbclk_source(RCC_PLL); + + GPIOA_OSPEEDR &= ~0xF00C; + GPIOA_OSPEEDR |= 0x5004; /* Set medium speed on PA1, PA6,PA7*/ + gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + TMS_PIN | TCK_PIN |TDI_PIN); + gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, + GPIO_PUPD_NONE, + LED_UART | LED_IDLE_RUN | LED_ERROR | LED_BOOTLOADER); + gpio_mode_setup(SRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SRST_PIN); + gpio_set(SRST_PORT, SRST_PIN); + gpio_set_output_options(SRST_PORT, GPIO_OTYPE_OD, + GPIO_OSPEED_2MHZ, SRST_PIN); + platform_timing_init(); + cdcacm_init(); + usbuart_init(); +} + +void platform_srst_set_val(bool assert) +{ + gpio_set_val(SRST_PORT, SRST_PIN, !assert); +} + +bool platform_srst_get_val(void) +{ + return (gpio_get(SRST_PORT, SRST_PIN)) ? false : true; +} + +const char *platform_target_voltage(void) +{ + return "ABSENT!"; +} + +void platform_request_boot(void) +{ + uint32_t *magic = (uint32_t *) &_ebss; + magic[0] = BOOTMAGIC0; + magic[1] = BOOTMAGIC1; + scb_reset_system(); +} diff --git a/src/platforms/f072/platform.h b/src/platforms/f072/platform.h new file mode 100644 index 00000000000..da37cbe2bda --- /dev/null +++ b/src/platforms/f072/platform.h @@ -0,0 +1,169 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 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 + * 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 the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include "gpio.h" +#include "timing.h" +#include "timing_stm32.h" + +#include + +#define PLATFORM_HAS_TRACESWO + +#ifdef ENABLE_DEBUG +# define PLATFORM_HAS_DEBUG +# define USBUART_DEBUG +#endif + +#define PLATFORM_IDENT "(F072-IF) " + +/* Important pin mappings for STM32 implementation: + * + * LED0 = PB5 (Green LED : Running) + * LED1 = PB6 (Orange LED : Idle) + * LED2 = PB7 (Red LED : Error) + * + * TDI = PA0 + * TMS = PA1 (input for SWDP) + * TCK = PA7/SWCLK + * TDO = PA6 (input for TRACESWO + * nSRST = PA5 + * + * Force DFU mode button: BOOT0 + */ + +/* Hardware definitions... */ +#define JTAG_PORT GPIOA +#define TDI_PORT JTAG_PORT +#define TMS_PORT JTAG_PORT +#define TCK_PORT JTAG_PORT +#define TDO_PORT JTAG_PORT +#define TDI_PIN GPIO0 +#define TMS_PIN GPIO1 +#define TCK_PIN GPIO7 +#define TDO_PIN GPIO6 + +#define SWDIO_PORT JTAG_PORT +#define SWCLK_PORT JTAG_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define SRST_PORT GPIOA +#define SRST_PIN GPIO5 + +#define LED_PORT GPIOB +#define LED_PORT_UART GPIOB +#define LED_UART GPIO6 +#define LED_IDLE_RUN GPIO5 +#define LED_ERROR GPIO7 +/* PORTB does not stay active in system bootloader!*/ +#define LED_BOOTLOADER GPIO6 + +#define BOOTMAGIC0 0xb007da7a +#define BOOTMAGIC1 0xbaadfeed + +#define TMS_SET_MODE() \ + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); +#define SWDIO_MODE_FLOAT() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWDIO_PIN); +#define SWDIO_MODE_DRIVE() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); + +/* FIXME: Does st_usbfs_v2_usb_driver work on F3 with 128 Bytes buffer*/ +#define USART_DMA_BUF_SIZE 64 +#define USB_DRIVER st_usbfs_v2_usb_driver +#define USB_IRQ NVIC_USB_IRQ +#define USB_ISR(x) usb_isr(x) + +/* Interrupt priorities. Low numbers are high priority. + * For now USART1 preempts USB which may spin while buffer is drained. + * TIM3 is used for traceswo capture and must be highest priority. + */ +#define IRQ_PRI_USB (1 << 6) +#define IRQ_PRI_USBUSART (2 << 6) +#define IRQ_PRI_USBUSART_DMA (2 << 6) +#define IRQ_PRI_TRACE (0 << 6) + +#define USBUSART USART2 +#define USBUSART_CR1 USART2_CR1 +#define USBUSART_TDR USART2_TDR +#define USBUSART_RDR USART2_RDR +#define USBUSART_IRQ NVIC_USART2_IRQ +#define USBUSART_CLK RCC_USART2 +#define USBUSART_PORT GPIOA +#define USBUSART_TX_PIN GPIO3 +#define USBUSART_RX_PIN GPIO2 +#define USBUSART_ISR(x) usart2_isr(x) + +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +/* This needs corresponding remapping bit cleared in the SYSCFG_CFGR1. + * As we come out of reset, the bit is cleared!*/ +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL4 +#define USBUSART_DMA_TX_ISR(x) dma1_channel4_7_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL5 +#define USBUSART_DMA_RX_ISR(x) dma2_channel3_5_isr(x) +#define USBUSART_DMA_RXTX_IRQ NVIC_DMA1_CHANNEL4_7_DMA2_CHANNEL3_5_IRQ +#define USBUSART_DMA_RXTX_ISR(x) dma1_channel4_7_dma2_channel3_5_isr(x) + +#define STK_CSR_CLKSOURCE_AHB_DIV8 STK_CSR_CLKSOURCE_AHB + +/* TX/RX on the REV 0/1 boards are swapped against ftdijtag.*/ +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, \ + USBUSART_TX_PIN | USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF1, \ + USBUSART_TX_PIN | USBUSART_RX_PIN); \ + USART2_CR2 |= USART_CR2_SWAP; \ + } while(0) + +#define TRACE_TIM TIM3 +#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) +#define TRACE_IRQ NVIC_TIM3_IRQ +#define TRACE_ISR tim3_isr + +#ifdef ENABLE_DEBUG +extern bool debug_bmp; +int usbuart_debug_write(const char *buf, size_t len); +# define DEBUG printf +#else +# define DEBUG(...) +#endif + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} +#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);} + +static inline int platform_hwversion(void) +{ + return 0; +} + +/* Use newlib provided integer only stdio functions */ +#define sscanf siscanf +#define sprintf siprintf +#define vasprintf vasiprintf +#define snprintf sniprintf + +#endif diff --git a/src/platforms/f3/Makefile.inc b/src/platforms/f3/Makefile.inc new file mode 100644 index 00000000000..c7dc25abd12 --- /dev/null +++ b/src/platforms/f3/Makefile.inc @@ -0,0 +1,35 @@ +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -Istm32/include -mcpu=cortex-m4 -mthumb \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -DSTM32F3 -I../libopencm3/include \ + -DDFU_SERIAL_LENGTH=13 -Iplatforms/stm32 + +LDFLAGS = --specs=nano.specs -lopencm3_stm32f3 \ + -Wl,-T,platforms/stm32/stm32f303xc.ld -nostartfiles -lc -lnosys \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m4 -Wl,-gc-sections \ + -mfloat-abi=hard -mfpu=fpv4-sp-d16 \ + -L../libopencm3/lib + +VPATH += platforms/stm32 + +SRC += cdcacm.c \ + traceswodecode.c\ + traceswo.c \ + usbuart.c \ + serialno.c \ + timing.c \ + timing_stm32.c \ + +all: blackmagic.bin +blackmagic.elf: libopencm3_stm32f3 + +libopencm3_stm32f3: + $(Q)$(MAKE) $(MFLAGS) -C ../libopencm3 lib/stm32/f3 + +host_clean: + -$(Q)$(RM) blackmagic.bin + +.PHONY: libopencm3_stm32f3 diff --git a/src/platforms/f3/Readme b/src/platforms/f3/Readme new file mode 100644 index 00000000000..14b9c606cf2 --- /dev/null +++ b/src/platforms/f3/Readme @@ -0,0 +1,20 @@ +Connections: +==================== + +BOOT0: Pull low to force system bootloader entry with reset +PA2/PA3 eventual connected to the STLINK/ STM32F103C8 + +PA0: TDI +PA1: TMS/SWDIO +PA5: NRST +PA6: TDO/TRACESWO +PA7: TCK/SWCLK + +PA9: USB_DETACH_N + +PA2: UART_RX +PA3: UART_TX + +Reflash: +==================== +dfu-util -a0 -s 0x08000000 -D blackmagic.bin \ No newline at end of file diff --git a/src/platforms/f3/platform.c b/src/platforms/f3/platform.c new file mode 100644 index 00000000000..6e3a53fd013 --- /dev/null +++ b/src/platforms/f3/platform.c @@ -0,0 +1,117 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 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 + * 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 the platform specific functions for the STM32F3-IF + * implementation. + */ + +#include "general.h" +#include "cdcacm.h" +#include "usbuart.h" +#include "morse.h" + +#include +#include +#include +#include +#include +#include +#include + +extern uint32_t _ebss[]; + +void platform_init(void) +{ + volatile uint32_t *magic = (uint32_t *) &_ebss; + /* If RCC_CFGR is not at it's reset value, the bootloader was executed + * and SET_ADDRESS got us to this place. On F3, without further efforts, + * application does not start in that case. + * So issue an reset to allow a clean start! + */ + if (RCC_CFGR) + scb_reset_system(); + SYSCFG_MEMRM &= ~3; + /* Buttom is BOOT0, so buttom is already evaluated!*/ + if (((magic[0] == BOOTMAGIC0) && (magic[1] == BOOTMAGIC1))) { + magic[0] = 0; + magic[1] = 0; + /* Jump to the built in bootloader by mapping System flash. + As we just come out of reset, no other deinit is needed!*/ + SYSCFG_MEMRM |= 1; + scb_reset_core(); + } + + rcc_clock_setup_pll(&rcc_hse8mhz_configs[RCC_CLOCK_HSE8_72MHZ]); + + /* Enable peripherals */ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_CRC); + rcc_periph_clock_enable(RCC_USB); + + /* Disconnect USB after reset: + * Pull USB_DP low. Device will reconnect automatically + * when USB is set up later, as Pull-Up is hard wired*/ + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); + gpio_clear(GPIOA, GPIO12); + gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO12); + rcc_periph_reset_pulse(RST_USB); + + GPIOA_OSPEEDR &= ~0xF00C; + GPIOA_OSPEEDR |= 0x5004; /* Set medium speed on PA1, PA6 and PA7*/ + gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + TMS_PIN | TCK_PIN |TDI_PIN); + gpio_mode_setup(TDO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TDO_PIN); + gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + LED_UART | LED_IDLE_RUN | LED_ERROR | LED_BOOTLOADER); + gpio_mode_setup(SRST_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SRST_PIN); + gpio_set(SRST_PORT, SRST_PIN); + gpio_set_output_options(SRST_PORT, GPIO_OTYPE_OD, + GPIO_OSPEED_2MHZ, SRST_PIN); + platform_timing_init(); + /* Set up USB Pins and alternate function*/ + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF14, GPIO11 | GPIO12); + cdcacm_init(); + usbuart_init(); +} + +void platform_srst_set_val(bool assert) +{ + gpio_set_val(SRST_PORT, SRST_PIN, !assert); +} + +bool platform_srst_get_val(void) +{ + return (gpio_get(SRST_PORT, SRST_PIN)) ? false : true; +} + +const char *platform_target_voltage(void) +{ + return "ABSENT!"; +} + +void platform_request_boot(void) +{ + /* Bootloader cares for reenumeration */ + uint32_t *magic = (uint32_t *) &_ebss; + magic[0] = BOOTMAGIC0; + magic[1] = BOOTMAGIC1; + scb_reset_system(); +} diff --git a/src/platforms/f3/platform.h b/src/platforms/f3/platform.h new file mode 100644 index 00000000000..5ca84871131 --- /dev/null +++ b/src/platforms/f3/platform.h @@ -0,0 +1,162 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2017 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 + * 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 the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include "gpio.h" +#include "timing.h" +#include "timing_stm32.h" + +#include + +#define PLATFORM_HAS_TRACESWO + +#ifdef ENABLE_DEBUG +# define PLATFORM_HAS_DEBUG +# define USBUART_DEBUG +#endif + +#define PLATFORM_IDENT "(F3-IF) " + +/* Important pin mappings for STM32 implementation: + * + * LED0 = PB5 (Green LED : Running) + * LED1 = PB6 (Orange LED : Idle) + * LED2 = PB7 (Red LED : Error) + * + * TDI = PA0 + * TMS = PA1 (input for SWDP) + * TCK = PA7/SWCLK + * TDO = PA6 (input for TRACESWO + * nSRST = PA5 + * + * Force DFU mode button: BOOT0 + */ + +/* Hardware definitions... */ +#define JTAG_PORT GPIOA +#define TDI_PORT JTAG_PORT +#define TMS_PORT JTAG_PORT +#define TCK_PORT JTAG_PORT +#define TDO_PORT JTAG_PORT +#define TDI_PIN GPIO0 +#define TMS_PIN GPIO1 +#define TCK_PIN GPIO7 +#define TDO_PIN GPIO6 + +#define SWDIO_PORT JTAG_PORT +#define SWCLK_PORT JTAG_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define SRST_PORT GPIOA +#define SRST_PIN GPIO5 + +#define LED_PORT GPIOB +#define LED_PORT_UART GPIOB +#define LED_UART GPIO6 +#define LED_IDLE_RUN GPIO5 +#define LED_ERROR GPIO7 +/* PORTB does not stay active in system bootloader!*/ +#define LED_BOOTLOADER GPIO6 + +#define BOOTMAGIC0 0xb007da7a +#define BOOTMAGIC1 0xbaadfeed + +#define TMS_SET_MODE() \ + gpio_mode_setup(TMS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, TMS_PIN); +#define SWDIO_MODE_FLOAT() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SWDIO_PIN); +#define SWDIO_MODE_DRIVE() \ + gpio_mode_setup(SWDIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SWDIO_PIN); + +#define USB_DRIVER st_usbfs_v1_usb_driver +#define USB_IRQ NVIC_USB_LP_CAN1_RX0_IRQ +#define USB_ISR(x) usb_lp_can1_rx0_isr(x) + +/* Interrupt priorities. Low numbers are high priority. + * TIM3 is used for traceswo capture and must be highest priority. + */ +#define IRQ_PRI_USB (1 << 4) +#define IRQ_PRI_USBUSART (2 << 4) +#define IRQ_PRI_USBUSART_DMA (2 << 4) +#define IRQ_PRI_TRACE (0 << 4) + +#define USBUSART USART2 +#define USBUSART_CR1 USART2_CR1 +#define USBUSART_TDR USART2_TDR +#define USBUSART_RDR USART2_RDR +#define USBUSART_IRQ NVIC_USART2_EXTI26_IRQ +#define USBUSART_CLK RCC_USART2 +#define USBUSART_PORT GPIOA +#define USBUSART_TX_PIN GPIO3 +#define USBUSART_RX_PIN GPIO2 +#define USBUSART_ISR(x) usart2_exti26_isr(x) + +#define USBUSART_DMA_BUS DMA1 +#define USBUSART_DMA_CLK RCC_DMA1 +#define USBUSART_DMA_TX_CHAN DMA_CHANNEL7 +#define USBUSART_DMA_TX_IRQ NVIC_DMA1_CHANNEL7_IRQ +#define USBUSART_DMA_TX_ISR(x) dma1_channel7_isr(x) +#define USBUSART_DMA_RX_CHAN DMA_CHANNEL6 +#define USBUSART_DMA_RX_IRQ NVIC_DMA1_CHANNEL6_IRQ +#define USBUSART_DMA_RX_ISR(x) dma1_channel6_isr(x) + +/* TX/RX on the REV 0/1 boards are swapped against ftdijtag.*/ +#define UART_PIN_SETUP() do { \ + gpio_mode_setup(USBUSART_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, \ + USBUSART_TX_PIN | USBUSART_RX_PIN); \ + gpio_set_af(USBUSART_PORT, GPIO_AF7, \ + USBUSART_TX_PIN | USBUSART_RX_PIN); \ + USART2_CR2 |= USART_CR2_SWAP; \ + } while(0) + +#define TRACE_TIM TIM3 +#define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) +#define TRACE_IRQ NVIC_TIM3_IRQ +#define TRACE_ISR tim3_isr + +#ifdef ENABLE_DEBUG +extern bool debug_bmp; +int usbuart_debug_write(const char *buf, size_t len); +# define DEBUG printf +#else +# define DEBUG(...) +#endif + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} +#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);} + +static inline int platform_hwversion(void) +{ + return 0; +} + +/* Use newlib provided integer only stdio functions */ +#define sscanf siscanf +#define sprintf siprintf +#define vasprintf vasiprintf +#define snprintf sniprintf + +#endif diff --git a/src/platforms/f4discovery/Makefile.inc b/src/platforms/f4discovery/Makefile.inc index ae6b75a628e..cdcddac74ad 100644 --- a/src/platforms/f4discovery/Makefile.inc +++ b/src/platforms/f4discovery/Makefile.inc @@ -8,12 +8,7 @@ CFLAGS += -Istm32/include -mcpu=cortex-m4 -mthumb \ -DSTM32F4 -I../libopencm3/include \ -Iplatforms/stm32 -ifeq ($(BLACKPILL), 1) -LINKER_SCRIPT=platforms/stm32/blackpillv2.ld -CFLAGS += -DBLACKPILL=1 -else LINKER_SCRIPT=platforms/stm32/f4discovery.ld -endif LDFLAGS_BOOT = -lopencm3_stm32f4 \ -Wl,-T,$(LINKER_SCRIPT) -nostartfiles -lc -lnosys \ diff --git a/src/platforms/f4discovery/Readme.md b/src/platforms/f4discovery/Readme.md index 425e34cf54a..f8f02ed3a10 100644 --- a/src/platforms/f4discovery/Readme.md +++ b/src/platforms/f4discovery/Readme.md @@ -1,6 +1,6 @@ # Firmware BMP for STM32F407 DISCO boards -Kept for historical reasons to load BMP bootloader to the STM32F103 of the onboard STLINK or external STLINKs. As stlink-tool now allows to load BMP firmware via the original STLINK bootloader is no longer really needed. +Allows the use of the STM32F407 Discovery board main cpu as a Black Magic Probe. Historically it was used to program the on board built in debugger before ST-Link bootloader use was possible. ## Connections: @@ -12,35 +12,6 @@ PC6: TDO/TRACESWO
PC1: TRST
PC8: SRST
-# Alternate build for stm32f401 stm32f411 MiniF4 aka BlackPillV2 boards. - -https://github.com/WeActTC/MiniSTM32F4x1 - -## Connections: - -* JTAG/SWD - * PA1: TDI - * PA13: TMS/SWDIO - * PA14: TCK/SWCLK - * PB3: TDO/TRACESWO - * PB5: TRST - * PB4: SRST - -* USB USART - * PB6: USART1 TX (usbuart_xxx) - * PB7: USART1 RX (usbuart_xxx) - -* +3V3. - * PB8 - turn on IRLML5103 transistor - -How to Build -======================================== -``` -cd blackmagic -make clean -make PROBE_HOST=f4discovery BLACKPILL=1 -``` - How to Flash with dfu ======================================== * After build: @@ -60,6 +31,6 @@ To exit from dfu mode press a "key" and "reset", release reset. BMP firmware sho SWJ frequency setting ==================================== -https://github.com/blacksphere/blackmagic/pull/783#issue-529197718 +https://github.com/blackmagic-debug/blackmagic/pull/783#issue-529197718 `mon freq 900k` helps at most diff --git a/src/platforms/f4discovery/platform.c b/src/platforms/f4discovery/platform.c index 4233f10a386..f0802f24e40 100644 --- a/src/platforms/f4discovery/platform.c +++ b/src/platforms/f4discovery/platform.c @@ -37,10 +37,6 @@ #include #include -#ifdef BLACKPILL -#include -#endif - jmp_buf fatal_error_jmpbuf; extern char _ebss[]; @@ -51,11 +47,8 @@ void platform_init(void) /* Enable GPIO peripherals */ rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOC); -#ifdef BLACKPILL - rcc_periph_clock_enable(RCC_GPIOB); -#else rcc_periph_clock_enable(RCC_GPIOD); -#endif + /* Check the USER button*/ if (gpio_get(GPIOA, GPIO0) || ((magic[0] == BOOTMAGIC0) && (magic[1] == BOOTMAGIC1))) @@ -73,11 +66,7 @@ void platform_init(void) SYSCFG_MEMRM |= 1; scb_reset_core(); } -#ifdef BLACKPILL - rcc_clock_setup_pll(&rcc_hse_25mhz_3v3[RCC_CLOCK_3V3_84MHZ]); -#else rcc_clock_setup_pll(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); -#endif /* Enable peripherals */ rcc_periph_clock_enable(RCC_OTGFS); @@ -87,13 +76,8 @@ void platform_init(void) gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO11 | GPIO12); gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO10 | GPIO11 | GPIO12); -#ifdef BLACKPILL - GPIOA_OSPEEDR &= 0x3C00000C; - GPIOA_OSPEEDR |= 0x28000008; -#else GPIOC_OSPEEDR &= ~0xF30; GPIOC_OSPEEDR |= 0xA20; -#endif gpio_mode_setup(JTAG_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, @@ -124,11 +108,6 @@ void platform_init(void) platform_timing_init(); usbuart_init(); cdcacm_init(); -#ifdef BLACKPILL - // https://github.com/libopencm3/libopencm3/pull/1256#issuecomment-779424001 - OTG_FS_GCCFG |= OTG_GCCFG_NOVBUSSENS | OTG_GCCFG_PWRDWN; - OTG_FS_GCCFG &= ~(OTG_GCCFG_VBUSBSEN | OTG_GCCFG_VBUSASEN); -#endif } void platform_srst_set_val(bool assert) { (void)assert; } diff --git a/src/platforms/f4discovery/platform.h b/src/platforms/f4discovery/platform.h index dbec2214151..ceb9aa73688 100644 --- a/src/platforms/f4discovery/platform.h +++ b/src/platforms/f4discovery/platform.h @@ -31,75 +31,6 @@ #include #define PLATFORM_HAS_TRACESWO -#ifdef BLACKPILL -#define PLATFORM_IDENT "(F4Discovery/BlackPillV2) " -/* Important pin mappings for STM32 implementation: - * JTAG/SWD - * PA1: TDI
- * PA13: TMS/SWDIO
- * PA14: TCK/SWCLK
- * PB3: TDO/TRACESWO
- * PB5: TRST
- * PB4: SRST
- * USB USART - * PB6: USART1 TX - * PB7: USART1 RX - * +3V3 - * PB8 - turn on IRLML5103 transistor - * Force DFU mode button: PA0 - */ - -/* Hardware definitions... */ -#define JTAG_PORT GPIOA -#define TDI_PORT JTAG_PORT -#define TMS_PORT JTAG_PORT -#define TCK_PORT JTAG_PORT -#define TDO_PORT GPIOB -#define TDI_PIN GPIO1 -#define TMS_PIN GPIO13 -#define TCK_PIN GPIO14 -#define TDO_PIN GPIO3 - -#define SWDIO_PORT JTAG_PORT -#define SWCLK_PORT JTAG_PORT -#define SWDIO_PIN TMS_PIN -#define SWCLK_PIN TCK_PIN - -#define TRST_PORT GPIOB -#define TRST_PIN GPIO5 -#define SRST_PORT GPIOB -#define SRST_PIN GPIO4 - -#define PWR_BR_PORT GPIOB -#define PWR_BR_PIN GPIO8 - -#define LED_PORT GPIOC -#define LED_PORT_UART GPIOA -#define LED_UART GPIO1 -#define LED_IDLE_RUN GPIO15 -#define LED_ERROR GPIO14 -#define LED_BOOTLOADER GPIO13 - -#define USBUSART USART1 -#define USBUSART_CR1 USART1_CR1 -#define USBUSART_DR USART1_DR -#define USBUSART_IRQ NVIC_USART1_IRQ -#define USBUSART_CLK RCC_USART1 -#define USBUSART_PORT GPIOB -#define USBUSART_TX_PIN GPIO6 -#define USBUSART_RX_PIN GPIO7 -#define USBUSART_ISR(x) usart1_isr(x) -#define USBUSART_DMA_BUS DMA2 -#define USBUSART_DMA_CLK RCC_DMA2 -#define USBUSART_DMA_TX_CHAN DMA_STREAM7 -#define USBUSART_DMA_TX_IRQ NVIC_DMA2_STREAM7_IRQ -#define USBUSART_DMA_TX_ISR(x) dma2_stream7_isr(x) -#define USBUSART_DMA_RX_CHAN DMA_STREAM5 -#define USBUSART_DMA_RX_IRQ NVIC_DMA2_STREAM5_IRQ -#define USBUSART_DMA_RX_ISR(x) dma2_stream5_isr(x) -/* For STM32F4 DMA trigger source must be specified */ -#define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 -#else #define PLATFORM_IDENT "(F4Discovery) " /* Important pin mappings for STM32 implementation: @@ -167,7 +98,6 @@ #define USBUSART_DMA_RX_ISR(x) dma1_stream1_isr(x) /* For STM32F4 DMA trigger source must be specified */ #define USBUSART_DMA_TRG DMA_SxCR_CHSEL_4 -#endif #define BOOTMAGIC0 0xb007da7a #define BOOTMAGIC1 0xbaadfeed diff --git a/src/platforms/hosted/Makefile.inc b/src/platforms/hosted/Makefile.inc index f3e3248bcf5..582a9e624e6 100644 --- a/src/platforms/hosted/Makefile.inc +++ b/src/platforms/hosted/Makefile.inc @@ -1,7 +1,7 @@ CC ?= gcc SYS = $(shell $(CC) -dumpmachine) CFLAGS += -DENABLE_DEBUG -DPLATFORM_HAS_DEBUG -CFLAGS +=-I ./target -I./platforms/pc +CFLAGS +=-I ./target # Define HOSTED_BMP_ONLY to '0' in order to build the hosted blackmagic # executable with support for other probes beside BMP. Default HOSTED_BMP_ONLY @@ -40,9 +40,9 @@ LDFLAGS += -lsetupapi #https://github.com/dmlc/xgboost/issues/1945 indicates macosx as indicator else ifneq (filter, macosx darwin, $(SYS)) SRC += serial_unix.c -LDFLAGS += -lhidapi +LDFLAGS += $(pkg-config --libs hidapi) LDFLAGS += -framework CoreFoundation -CFLAGS += -Ihidapi/hidapi +CFLAGS += -Ihidapi/hidapi $(pkg-config --cflags hidapi) HIDAPILIB = hidapi endif @@ -72,8 +72,7 @@ ifneq ($(HOSTED_BMP_ONLY), 1) endif endif -VPATH += platforms/pc -SRC += timing.c cl_utils.c utils.c jtag_devs.c +SRC += timing.c cli.c utils.c SRC += bmp_remote.c remote_swdptap.c remote_jtagtap.c ifneq ($(HOSTED_BMP_ONLY), 1) SRC += bmp_libusb.c stlinkv2.c @@ -83,3 +82,8 @@ else SRC += bmp_serial.c endif PC_HOSTED = 1 + +all: blackmagic + +host_clean: + -$(Q)$(RM) blackmagic diff --git a/src/platforms/hosted/Readme.md b/src/platforms/hosted/Readme.md index aa0d45b74f3..1762e1683f7 100644 --- a/src/platforms/hosted/Readme.md +++ b/src/platforms/hosted/Readme.md @@ -171,7 +171,7 @@ cables already listed and propose other cable. A link to the schematics is welcome. ## Feedback -### Issues and Pull request on https://github.com/blacksphere/blackmagic/ +### Issues and Pull request on https://github.com/blackmagic-debug/blackmagic/ ### Discussions on Discord. You can find the Discord link here: https://1bitsquared.com/pages/chat ### Blackmagic mailing list http://sourceforge.net/mail/?group_id=407419 diff --git a/src/platforms/hosted/bmp_hosted.h b/src/platforms/hosted/bmp_hosted.h index fe4ce0beaf5..4783f2c8699 100644 --- a/src/platforms/hosted/bmp_hosted.h +++ b/src/platforms/hosted/bmp_hosted.h @@ -1,7 +1,7 @@ #if !defined(__BMP_LIBUSB_H) #define __BMP_LIBUSB_H -#include "cl_utils.h" +#include "cli.h" #if HOSTED_BMP_ONLY != 1 # include @@ -49,4 +49,12 @@ void bmp_ident(bmp_info_t *info); int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info); void libusb_exit_function(bmp_info_t *info); +#if defined(_WIN32) || defined(__CYGWIN__) +#include +#define PRINT_INFO(fmt, ...) wprintf(L ## fmt, ##__VA_ARGS__) +#else +#include +#define PRINT_INFO(fmt, ...) printf((fmt), ##__VA_ARGS__) +#endif + #endif diff --git a/src/platforms/hosted/bmp_libusb.c b/src/platforms/hosted/bmp_libusb.c index 79d4a85b2f5..11faa168a22 100644 --- a/src/platforms/hosted/bmp_libusb.c +++ b/src/platforms/hosted/bmp_libusb.c @@ -20,7 +20,7 @@ /* Find all known usb connected debuggers */ #include "general.h" #include "libusb-1.0/libusb.h" -#include "cl_utils.h" +#include "cli.h" #include "ftdi_bmp.h" #include "version.h" @@ -28,13 +28,13 @@ void bmp_ident(bmp_info_t *info) { - DEBUG_INFO("BMP hosted %s\n for ST-Link V2/3, CMSIS_DAP, JLINK and " - "LIBFTDI/MPSSE\n", FIRMWARE_VERSION); - if (info && info->vid && info->pid) - DEBUG_INFO("Using %04x:%04x %s %s\n %s\n", info->vid, info->pid, - (info->serial[0]) ? info->serial : NO_SERIAL_NUMBER, - info->manufacturer, - info->product); + PRINT_INFO("Black Magic Debug App %s\n for Black Magic Probe, ST-Link v2 and v3, CMSIS-DAP," + " JLink and libftdi/MPSSE\n", FIRMWARE_VERSION); + if (info && info->vid && info->pid) { + PRINT_INFO("Using %04x:%04x %s %s\n %s\n", info->vid, info->pid, + (info->serial[0]) ? info->serial : NO_SERIAL_NUMBER, + info->manufacturer, info->product); + } } void libusb_exit_function(bmp_info_t *info) @@ -114,7 +114,7 @@ static bmp_type_t find_cmsis_dap_interface(libusb_device *dev,bmp_info_t *info) return type; } -int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) +int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) { libusb_device **devs; int res = libusb_init(&info->libusb_ctx); @@ -124,13 +124,15 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) exit(-1); } if (cl_opts->opt_cable) { - if ((!strcmp(cl_opts->opt_cable, "list")) || - (!strcmp(cl_opts->opt_cable, "l"))) { - cable_desc_t *cable = &cable_desc[0]; + if (!strcmp(cl_opts->opt_cable, "list") || + !strcmp(cl_opts->opt_cable, "l")) { + cable_desc_t *cable = cable_desc; DEBUG_WARN("Available cables:\n"); - for (; cable->name; cable++) { - DEBUG_WARN("\t%s\n", cable->name); + for (; cable->name; ++cable) { + DEBUG_WARN("\t%s%c\n", cable->name, cable->description ? ' ' : '*'); } + DEBUG_WARN("*: No auto-detection possible!" + " Give cable name as argument!\n"); exit(0); } info->bmp_type = BMP_TYPE_LIBFTDI; @@ -149,7 +151,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) bool access_problems = false; char *active_cable = NULL; bool ftdi_unknown = false; - rescan: +rescan: found_debuggers = 0; serial[0] = 0; manufacturer[0] = 0; @@ -157,9 +159,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) access_problems = false; active_cable = NULL; ftdi_unknown = false; - for (int i = 0; devs[i]; i++) { + for (size_t i = 0; devs[i]; ++i) { bmp_type_t type = BMP_TYPE_NONE; - libusb_device *dev = devs[i]; + libusb_device *dev = devs[i]; int res = libusb_get_device_descriptor(dev, &desc); if (res < 0) { DEBUG_WARN( "WARN: libusb_get_device_descriptor() failed: %s", @@ -173,7 +175,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) case LIBUSB_CLASS_WIRELESS: continue; } - libusb_device_handle *handle; + libusb_device_handle *handle = NULL; res = libusb_open(dev, &handle); if (res != LIBUSB_SUCCESS) { if (!access_problems) { @@ -183,70 +185,104 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) } continue; } - res = libusb_get_string_descriptor_ascii( - handle, desc.iSerialNumber, (uint8_t*)serial, - sizeof(serial)); - if (cl_opts->opt_serial && ((res <= 0) || - !strstr(serial, cl_opts->opt_serial))) { + /* If the device even has a serial number string, fetch it */ + if (desc.iSerialNumber) { + res = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, + (uint8_t *)serial, sizeof(serial)); + /* If the call fails and it's not because the device gave us STALL, continue to the next one */ + if (res < 0 && res != LIBUSB_ERROR_PIPE) { + libusb_close(handle); + continue; + } + /* Device has no serial and that's ok. */ + else if (res <= 0) + serial[0] = '\0'; + } + else + serial[0] = '\0'; + if (cl_opts->opt_serial && !strstr(serial, cl_opts->opt_serial)) { libusb_close(handle); continue; } - if (res < 0) - serial[0] = 0; - manufacturer[0] = 0; - res = libusb_get_string_descriptor_ascii( - handle, desc.iManufacturer, (uint8_t*)manufacturer, - sizeof(manufacturer)); - product[0] = 0; - res = libusb_get_string_descriptor_ascii( - handle, desc.iProduct, (uint8_t*)product, - sizeof(product)); + /* Attempt to get the manufacturer string */ + if (desc.iManufacturer) { + res = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, + (uint8_t *)manufacturer, sizeof(manufacturer)); + /* If the call fails and it's not because the device gave us STALL, continue to the next one */ + if (res < 0 && res != LIBUSB_ERROR_PIPE) { + DEBUG_WARN("WARN: libusb_get_string_descriptor_ascii() call to fetch manufacturer string failed: %s\n", + libusb_strerror(res)); + libusb_close(handle); + continue; + } + /* Device has no manufacturer string and that's ok. */ + else if (res <= 0) + manufacturer[0] = '\0'; + } + else + manufacturer[0] = '\0'; + /* Attempt to get the product string */ + if (desc.iProduct) { + res = libusb_get_string_descriptor_ascii(handle, desc.iProduct, + (uint8_t *)product, sizeof(product)); + /* If the call fails and it's not because the device gave us STALL, continue to the next one */ + if (res < 0 && res != LIBUSB_ERROR_PIPE) { + DEBUG_WARN("WARN: libusb_get_string_descriptor_ascii() call to fetch product string failed: %s\n", + libusb_strerror(res)); + libusb_close(handle); + continue; + } + /* Device has no product string and that's ok. */ + else if (res <= 0) + product[0] = '\0'; + } + else + product[0] = '\0'; libusb_close(handle); if (cl_opts->opt_ident_string) { char *match_manu = NULL; char *match_product = NULL; - match_manu = strstr(manufacturer, cl_opts->opt_ident_string); + match_manu = strstr(manufacturer, cl_opts->opt_ident_string); match_product = strstr(product, cl_opts->opt_ident_string); - if (!match_manu && !match_product) { + if (!match_manu && !match_product) continue; - } } /* Either serial and/or ident_string match or are not given. * Check type.*/ if (desc.idVendor == VENDOR_ID_BMP) { - if (desc.idProduct == PRODUCT_ID_BMP) { + if (desc.idProduct == PRODUCT_ID_BMP) type = BMP_TYPE_BMP; - } else { + else { if (desc.idProduct == PRODUCT_ID_BMP_BL) DEBUG_WARN("BMP in bootloader mode found. Restart or reflash!\n"); continue; } - } else if ((type == BMP_TYPE_NONE) && - ((type = find_cmsis_dap_interface(dev, info)) != BMP_TYPE_NONE)) { + } else if (type == BMP_TYPE_NONE && + (type = find_cmsis_dap_interface(dev, info)) != BMP_TYPE_NONE) { /* find_cmsis_dap_interface has set valid type*/ - } else if ((strstr(manufacturer, "CMSIS")) || (strstr(product, "CMSIS"))) { + } else if (strstr(manufacturer, "CMSIS") || strstr(product, "CMSIS")) type = BMP_TYPE_CMSIS_DAP; - } else if (desc.idVendor == VENDOR_ID_STLINK) { - if ((desc.idProduct == PRODUCT_ID_STLINKV2) || - (desc.idProduct == PRODUCT_ID_STLINKV21) || - (desc.idProduct == PRODUCT_ID_STLINKV21_MSD) || - (desc.idProduct == PRODUCT_ID_STLINKV3_NO_MSD) || - (desc.idProduct == PRODUCT_ID_STLINKV3_BL) || - (desc.idProduct == PRODUCT_ID_STLINKV3) || - (desc.idProduct == PRODUCT_ID_STLINKV3E)) { + else if (desc.idVendor == VENDOR_ID_STLINK) { + if (desc.idProduct == PRODUCT_ID_STLINKV2 || + desc.idProduct == PRODUCT_ID_STLINKV21 || + desc.idProduct == PRODUCT_ID_STLINKV21_MSD || + desc.idProduct == PRODUCT_ID_STLINKV3_NO_MSD || + desc.idProduct == PRODUCT_ID_STLINKV3_BL || + desc.idProduct == PRODUCT_ID_STLINKV3 || + desc.idProduct == PRODUCT_ID_STLINKV3E) type = BMP_TYPE_STLINKV2; - } else { + else { if (desc.idProduct == PRODUCT_ID_STLINKV1) DEBUG_WARN( "INFO: STLINKV1 not supported\n"); continue; } - } else if (desc.idVendor == VENDOR_ID_SEGGER) { + } else if (desc.idVendor == VENDOR_ID_SEGGER) type = BMP_TYPE_JLINK; - } else { - cable_desc_t *cable = &cable_desc[0]; - for (; cable->name; cable++) { + else { + cable_desc_t *cable = cable_desc; + for (; cable->name; ++cable) { bool found = false; - if ((cable->vendor != desc.idVendor) || (cable->product != desc.idProduct)) + if (cable->vendor != desc.idVendor || cable->product != desc.idProduct) continue; /* VID/PID do not match*/ if (cl_opts->opt_cable) { if (strncmp(cable->name, cl_opts->opt_cable, strlen(cable->name))) @@ -260,10 +296,10 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) else found = true; } else { /* VID/PID fits, but no cl_opts->opt_cable and no description*/ - if ((cable->vendor == 0x0403) && /* FTDI*/ - ((cable->product == 0x6010) || /* FT2232C/D/H*/ - (cable->product == 0x6011) || /* FT4232H Quad HS USB-UART/FIFO IC */ - (cable->product == 0x6014))) { /* FT232H Single HS USB-UART/FIFO IC */ + if (cable->vendor == 0x0403 && /* FTDI*/ + (cable->product == 0x6010 || /* FT2232C/D/H*/ + cable->product == 0x6011 || /* FT4232H Quad HS USB-UART/FIFO IC */ + cable->product == 0x6014)) { /* FT232H Single HS USB-UART/FIFO IC */ ftdi_unknown = true; continue; /* Cable name is needed */ } @@ -279,8 +315,8 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) } if (report) { DEBUG_WARN("%2d: %s, %s, %s\n", found_debuggers + 1, - (serial[0]) ? serial : NO_SERIAL_NUMBER, - manufacturer,product); + serial[0] ? serial : NO_SERIAL_NUMBER, + manufacturer,product); } info->vid = desc.idVendor; info->pid = desc.idProduct; @@ -289,21 +325,20 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) strncpy(info->product, product, sizeof(info->product)); strncpy(info->manufacturer, manufacturer, sizeof(info->manufacturer)); if (cl_opts->opt_position && - (cl_opts->opt_position == (found_debuggers + 1))) { + cl_opts->opt_position == found_debuggers + 1) { found_debuggers = 1; break; - } else { - found_debuggers++; - } + } else + ++found_debuggers; } - if ((found_debuggers == 0) && ftdi_unknown) + if (found_debuggers == 0 && ftdi_unknown && !cl_opts->opt_cable) DEBUG_WARN("Generic FTDI MPSSE VID/PID found. Please specify exact type with \"-c \" !\n"); - if ((found_debuggers == 1) && !cl_opts->opt_cable && (info->bmp_type == BMP_TYPE_LIBFTDI)) + if (found_debuggers == 1 && !cl_opts->opt_cable && info->bmp_type == BMP_TYPE_LIBFTDI) cl_opts->opt_cable = active_cable; if (!found_debuggers && cl_opts->opt_list_only) DEBUG_WARN("No usable debugger found\n"); - if ((found_debuggers > 1) || - ((found_debuggers == 1) && (cl_opts->opt_list_only))) { + if (found_debuggers > 1 || + (found_debuggers == 1 && cl_opts->opt_list_only)) { if (!report) { if (found_debuggers > 1) DEBUG_WARN("%d debuggers found!\nSelect with -P " @@ -321,8 +356,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts,bmp_info_t *info) DEBUG_WARN( "No debugger found. Please check access rights to USB devices!\n"); libusb_free_device_list(devs, 1); - return (found_debuggers == 1) ? 0 : -1; + return found_debuggers == 1 ? 0 : -1; } + static void LIBUSB_CALL on_trans_done(struct libusb_transfer *trans) { struct trans_ctx * const ctx = trans->user_data; @@ -330,15 +366,14 @@ static void LIBUSB_CALL on_trans_done(struct libusb_transfer *trans) if (trans->status != LIBUSB_TRANSFER_COMPLETED) { DEBUG_WARN("on_trans_done: "); - if(trans->status == LIBUSB_TRANSFER_TIMED_OUT) { + if (trans->status == LIBUSB_TRANSFER_TIMED_OUT) DEBUG_WARN(" Timeout\n"); - } else if (trans->status == LIBUSB_TRANSFER_CANCELLED) { + else if (trans->status == LIBUSB_TRANSFER_CANCELLED) DEBUG_WARN(" cancelled\n"); - } else if (trans->status == LIBUSB_TRANSFER_NO_DEVICE) { + else if (trans->status == LIBUSB_TRANSFER_NO_DEVICE) DEBUG_WARN(" no device\n"); - } else { + else DEBUG_WARN(" unknown\n"); - } ctx->flags |= TRANS_FLAGS_HAS_ERROR; } ctx->flags |= TRANS_FLAGS_IS_DONE; @@ -390,23 +425,22 @@ int send_recv(usb_link_t *link, uint8_t *rxbuf, size_t rxsize) { int res = 0; - if( txsize) { - int txlen = txsize; + if (txsize) { libusb_fill_bulk_transfer(link->req_trans, link->ul_libusb_device_handle, link->ep_tx | LIBUSB_ENDPOINT_OUT, - txbuf, txlen, + txbuf, txsize, NULL, NULL, 0); - int i = 0; - DEBUG_WIRE(" Send (%3d): ", txlen); - for (; i < txlen; i++) { + size_t i = 0; + DEBUG_WIRE(" Send (%3zu): ", txsize); + for (; i < txsize; ++i) { DEBUG_WIRE("%02x", txbuf[i]); - if ((i & 7) == 7) + if ((i & 7U) == 7U) DEBUG_WIRE("."); - if ((i & 31) == 31) + if ((i & 31U) == 31U) DEBUG_WIRE("\n "); } - if (!(i & 31)) + if (!(i & 31U)) DEBUG_WIRE("\n"); if (submit_wait(link, link->req_trans)) { libusb_clear_halt(link->ul_libusb_device_handle, link->ep_tx); @@ -426,14 +460,13 @@ int send_recv(usb_link_t *link, return -1; } res = link->rep_trans->actual_length; - if (res >0) { - int i; - uint8_t *p = rxbuf; - DEBUG_WIRE(" Rec (%zu/%d)", rxsize, res); - for (i = 0; i < res && i < 32 ; i++) { - if ( i && ((i & 7) == 0)) + if (res > 0) { + const size_t rxlen = (size_t)res; + DEBUG_WIRE(" Rec (%zu/%zu)", rxsize, rxlen); + for (size_t i = 0; i < rxlen && i < 32 ; ++i) { + if (i && ((i & 7U) == 0U)) DEBUG_WIRE("."); - DEBUG_WIRE("%02x", p[i]); + DEBUG_WIRE("%02x", rxbuf[i]); } } } diff --git a/src/platforms/hosted/bmp_remote.c b/src/platforms/hosted/bmp_remote.c index 8a586e2a18e..621abd40f93 100644 --- a/src/platforms/hosted/bmp_remote.c +++ b/src/platforms/hosted/bmp_remote.c @@ -25,7 +25,7 @@ #include "remote.h" #include "target.h" #include "bmp_remote.h" -#include "cl_utils.h" +#include "cli.h" #include "hex_utils.h" #include diff --git a/src/platforms/hosted/bmp_serial.c b/src/platforms/hosted/bmp_serial.c index c6d3e52bef9..9fe84b61c9f 100644 --- a/src/platforms/hosted/bmp_serial.c +++ b/src/platforms/hosted/bmp_serial.c @@ -27,10 +27,10 @@ void bmp_ident(bmp_info_t *info) { + PRINT_INFO("Black Magic Debug App (for BMP only) %s\n", FIRMWARE_VERSION); if (!info) return; - DEBUG_INFO("BMP hosted (BMP Only) %s\n", FIRMWARE_VERSION); - DEBUG_INFO("Using:\n %s %s %s\n", info->manufacturer, info->version, info->serial); + PRINT_INFO("Using:\n %s %s %s\n", info->manufacturer, info->version, info->serial); } void libusb_exit_function(bmp_info_t *info) {(void)info;}; @@ -159,7 +159,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) * Recent: Black_Sphere_Technologies_Black_Magic_Probe_v1.7.1-212-g212292ab_7BAE7AB8-if00 * usb-Black_Sphere_Technologies_Black_Magic_Probe__SWLINK__v1.7.1-155-gf55ad67b-dirty_DECB8811-if00 */ -#define BMP_IDSTRING "usb-Black_Sphere_Technologies_Black_Magic_Probe" +#define BMP_IDSTRING_BLACKSPHERE "usb-Black_Sphere_Technologies_Black_Magic_Probe" +#define BMP_IDSTRING_BLACKMAGIC "usb-Black_Magic_Debug_Black_Magic_Probe" +#define BMP_IDSTRING_1BITSQUARED "usb-1BitSquared_Black_Magic_Probe" #define DEVICE_BY_ID "/dev/serial/by-id/" /* @@ -170,7 +172,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) */ static int scan_linux_id(char *name, char *type, char *version, char *serial) { - name += strlen(BMP_IDSTRING) + 1; + name += strlen(BMP_IDSTRING_BLACKSPHERE) + 1; while (*name == '_') name++; if (!*name) { @@ -231,7 +233,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) struct dirent *dp; int i = 0; while ((dp = readdir(dir)) != NULL) { - if ((strstr(dp->d_name, BMP_IDSTRING)) && + if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || + strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || + strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) { i++; char type[256], version[256], serial[256]; @@ -264,7 +268,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) dir = opendir(DEVICE_BY_ID); i = 0; while ((dp = readdir(dir)) != NULL) { - if ((strstr(dp->d_name, BMP_IDSTRING)) && + if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || + strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || + strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) { i++; char type[256], version[256], serial[256]; @@ -279,7 +285,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) strncpy(info->version, version, sizeof(info->version)); break; } else if (found_bmps > 0) { - DEBUG_WARN("%2d: %s, Black Sphere Technologies, Black Magic " + DEBUG_WARN("%2d: %s, Black Magic Debug, Black Magic " "Probe (%s), %s\n", i, serial, type, version); } } diff --git a/src/platforms/pc/cl_utils.c b/src/platforms/hosted/cli.c similarity index 73% rename from src/platforms/pc/cl_utils.c rename to src/platforms/hosted/cli.c index 7a438da171a..b97fd4eff81 100644 --- a/src/platforms/pc/cl_utils.c +++ b/src/platforms/hosted/cli.c @@ -28,12 +28,13 @@ #include #include #include +#include #include "version.h" #include "target_internal.h" #include "cortexm.h" #include "command.h" -#include "cl_utils.h" +#include "cli.h" #include "bmp_hosted.h" #ifndef O_BINARY @@ -130,55 +131,102 @@ static void bmp_munmap(struct mmap_data *map) static void cl_help(char **argv) { bmp_ident(NULL); - DEBUG_WARN("Usage: %s [options]\n", argv[0]); - DEBUG_WARN("\t-h\t\t: This help.\n"); - DEBUG_WARN("\t-v[bitmask]\t: Increasing verbosity. Bitmask:\n"); - DEBUG_WARN("\t\t\t 1 = INFO, 2 = GDB, 4 = TARGET, 8 = PROBE, 16 = WIRE\n"); - DEBUG_WARN("\t-l\t\t: List available probes\n"); - DEBUG_WARN("Probe selection arguments:\n"); - DEBUG_WARN("\t-d \"path\"\t: Use serial BMP device at "); -#if HOSTED_BMP_ONLY == 1 && defined(__APPLE__) - DEBUG_WARN("\n"); -#else - DEBUG_WARN(". Deprecated!\n"); -#endif - DEBUG_WARN("\t-P \t: Use debugger found at position \n"); - DEBUG_WARN("\t-n \t: Use target device found at position \n"); - DEBUG_WARN("\t-s \"serial\"\t: Use dongle with (partial) " - "serial number \"serial\"\n"); - DEBUG_WARN("\t-c \"string\"\t: Use ftdi dongle with type \"string\"\n"); - DEBUG_WARN("\t\t Use \"list\" to list available cables\n"); - DEBUG_WARN("Run mode related options:\n"); - DEBUG_WARN("\tDefault mode is to start the debug server at :2000\n"); - DEBUG_WARN("\t-j\t\t: Use JTAG. SWD is default.\n"); - DEBUG_WARN("\t-f\t\t: Set minimum high and low times of SWJ waveform.\n"); - DEBUG_WARN("\t-C\t\t: Connect under hardware reset\n"); - DEBUG_WARN("\t-t\t\t: Scan SWD or JTAG and display information about \n" - "\t\t\t connected devices\n"); - DEBUG_WARN("\t-T\t\t: Continuous read/write-back some value to allow\n" - "\t\t\t timing insection of SWJ. Abort with ^C\n"); - DEBUG_WARN("\t-e\t\t: Assume \"resistor SWD connection\" on FTDI: TDI\n" - "\t\t\t connected to TMS, TDO to TDI with eventual resistor\n"); - DEBUG_WARN("\t-E\t\t: Erase flash until flash end or for given size\n"); - DEBUG_WARN("\t-w\t\t: Write binary file to target flash (default).\n"); - DEBUG_WARN("\t-V\t\t: Verify flash against binary file. Can be combined\n" - "\t\t\t with -w to verify right after programming.\n"); - DEBUG_WARN("\t-r\t\t: Read flash and write to binary file\n"); - DEBUG_WARN("\t-p\t\t: Supplies power to the target (where applicable)\n"); - DEBUG_WARN("\t-R[h]\t\t: Reset device. Default via SWJ or by hardware(h)\n"); - DEBUG_WARN("\t-H\t\t: Do not use high level commands (BMP-Remote)\n"); - DEBUG_WARN("\t-m \t: Use (target)id for SWD multi-drop.\n"); - DEBUG_WARN("\t-M \t: Run target specific monitor commands. Quote multi\n"); - DEBUG_WARN("\t\t\t word strings. Run \"-M help\" for help.\n"); - DEBUG_WARN("Flash operation modifiers options:\n"); - DEBUG_WARN("\tDefault action with given file is to write to flash\n"); - DEBUG_WARN("\t-a \t: Start flash operation at flash address \n" - "\t\t\t Default start is start of flash in memory map\n"); - DEBUG_WARN("\t-S \t: Read bytes. Default is until read fails.\n"); - DEBUG_WARN("\t \t\t: Use (binary) file for flash operation\n"); + PRINT_INFO( + "\n" + "Usage: %s [-h | -l | [-vBITMASK] [-d PATH | -P NUMBER | -s SERIAL | -c TYPE]\n" + "\t[-n NUMBER] [-j | -A] [-C] [-t | -T] [-e] [-p] [-R[h]] [-H] [-M STRING ...]\n" + "\t[-f | -m] [-E | -w | -V | -r] [-a ADDR] [-S number] [file]]\n" + "\n" + "The default is to start a debug server at localhost:2000\n\n" + "Single-shot and verbosity options [-h | -l | -vBITMASK]:\n" + "\t-h, --help Show the version and this help, then exit\n" + "\t-l, --list List available supported probes\n" + "\t-v, --verbose Set the output verbosity level based on some combination of:\n" + "\t 1 = INFO, 2 = GDB, 4 = TARGET, 8 = PROBE, 16 = WIRE\n" + "\n" + "Probe selection arguments [-d PATH | -P NUMBER | -s SERIAL | -c TYPE]:\n" + "\t-d, --device Use a serial device at the given path (Deprecated!)\n" + "\t-P, --probe Use the th debug probe found while scanning the\n" + "\t system, see the output from list for the order\n" + "\t-s, --serial Select the debug probe with the given serial number\n" + "\t-c, --ftdi-type Select the FTDI-based debug probe with of the given\n" + "\t type (cable)\n" + "\n" + "General configuration options: [-n NUMBER] [-j] [-C] [-t | -T] [-e] [-p] [-R[h]]\n" + "\t\t[-H] [-M STRING ...]\n" + "\t-n, --number Select the target device at the given position in the\n" + "\t scan chain (use the -t option to get a scan chain listing)\n" + "\t-j, --jtag Use JTAG instead of SWD\n" + "\t-A, --auto-scan Automatic scanning - try JTAG first, then SWD\n" + "\t-C, --hw-reset Connect to target under hardware reset\n" + "\t-t, --list-chain Perform a chain scan and display information about the\n" + "\t conected devices\n" + "\t-T, --timing Perform continues read- or write-back of a value to allow\n" + "\t measurement of protocol timing. Aborted by ^C\n" + "\t-e, --ext-res Assume external resistors for FTDI devices, that is having the\n" + "\t FTDI chip connected through resistors to TMS, TDI and TDO\n" + "\t-p, --power Power the target from the probe (if possible)\n" + "\t-R, --reset Reset the device. If followed by 'h', this will be done using\n" + "\t the hardware reset line instead of over the debug link\n" + "\t-H, --high-level Do not use the high level command API (bmp-remote)\n" + "\t-M, --monitor Run target-specific monitor commands. This option\n" + "\t can be repeated for as many commands you wish to run.\n" + "\t If the command contains spaces, use quotes around the\n" + "\t complete command\n" + "\n" + "SWD-specific configuration options [-f FREQUENCY | -m TARGET]:\n" + "\t-f, --freq Set an operating frequency for SWD\n" + "\t-m, --mult-drop Use the given target ID for selection in SWD multi-drop\n" + "\n" + "Flash operation selection options [-E | -w | -V | -r]:\n" + "\t-E, --erase Erase the target device Flash\n" + "\t-w, --write Write the specified binary file to the target device\n" + "\t Flash (the default)\n" + "\t-V, --verify Verify the target device Flash against the specified\n" + "\t binary file\n" + "\t-r, --read Read the target device Flash\n" + "\n" + "Flash operation modifiers options: [-a ADDR] [-S number] [FILE]\n" + "\t-a, --addr Start address for the given Flash operation (defaults to\n" + "\t the start of Flash)\n" + "\t-S, --byte-count Number of bytes to work on in the Flash operation (default\n" + "\t is till the operation fails or is complete)\n" + "\t Binary file to use in Flash operations\n", + argv[0] + ); exit(0); } +static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"list", no_argument, NULL, 'l'}, + {"verbose", required_argument, NULL, 'v'}, + {"device", required_argument, NULL, 'd'}, + {"probe", required_argument, NULL, 'P'}, + {"serial", required_argument, NULL, 's'}, + {"ftdi-type", required_argument, NULL, 'c'}, + {"number", required_argument, NULL, 'n'}, + {"jtag", no_argument, NULL, 'j'}, + {"auto-scan", no_argument, NULL, 'A'}, + {"hw-reset", no_argument, NULL, 'C'}, + {"list-chain", no_argument, NULL, 't'}, + {"timing", no_argument, NULL, 'T'}, + {"ext-res", no_argument, NULL, 'e'}, + {"power", no_argument, NULL, 'p'}, + {"reset", optional_argument, NULL, 'R'}, + {"high-level", no_argument, NULL, 'H'}, + {"monitor", required_argument, NULL, 'M'}, + {"freq", required_argument, NULL, 'f'}, + {"multi-drop", required_argument, NULL, 'm'}, + {"erase", no_argument, NULL, 'E'}, + {"write", no_argument, NULL, 'W'}, + {"verify", no_argument, NULL, 'V'}, + {"read", no_argument, NULL, 'r'}, + {"addr", required_argument, NULL, 'a'}, + {"byte-count", required_argument, NULL, 'S'}, + {NULL, 0, NULL, 0} +} ; + void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) { int c; @@ -186,7 +234,8 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) opt->opt_flash_size = 0xffffffff; opt->opt_flash_start = 0xffffffff; opt->opt_max_swj_frequency = 4000000; - while((c = getopt(argc, argv, "eEhHv:d:f:s:I:c:Cln:m:M:wVtTa:S:jpP:rR::")) != -1) { + opt->opt_scanmode = BMP_SCAN_SWD; + while((c = getopt_long(argc, argv, "eEhHv:d:f:s:I:c:Cln:m:M:wVtTa:S:jApP:rR::", long_options, NULL)) != -1) { switch(c) { case 'c': if (optarg) @@ -204,7 +253,10 @@ void cl_init(BMP_CL_OPTIONS_t *opt, int argc, char **argv) cl_debuglevel = strtol(optarg, NULL, 0) & (BMP_DEBUG_MAX - 1); break; case 'j': - opt->opt_usejtag = true; + opt->opt_scanmode = BMP_SCAN_JTAG; + break; + case 'A': + opt->opt_scanmode = BMP_SCAN_AUTO; break; case 'l': opt->opt_list_only = true; @@ -373,15 +425,24 @@ int cl_execute(BMP_CL_OPTIONS_t *opt) if (opt->opt_mode == BMP_MODE_TEST) DEBUG_INFO("Running in Test Mode\n"); DEBUG_INFO("Target voltage: %s Volt\n", platform_target_voltage()); - if (opt->opt_usejtag) { + + if (opt->opt_scanmode == BMP_SCAN_JTAG) num_targets = platform_jtag_scan(NULL); - } else { + else if (opt->opt_scanmode == BMP_SCAN_SWD) num_targets = platform_adiv5_swdp_scan(opt->opt_targetid); - if (!num_targets) { - DEBUG_WARN("Scan SWD failed, trying JTAG!\n"); - num_targets = platform_jtag_scan(NULL); - } + else + { + num_targets = platform_jtag_scan(NULL); + if (num_targets > 0) + goto found_targets; + DEBUG_INFO("JTAG scan found no devices, trying SWD.\n"); + num_targets = platform_adiv5_swdp_scan(opt->opt_targetid); + if (num_targets > 0) + goto found_targets; + DEBUG_INFO("SW-DP scan failed!\n"); } + +found_targets: if (!num_targets) { DEBUG_WARN("No target found\n"); return -1; diff --git a/src/platforms/pc/cl_utils.h b/src/platforms/hosted/cli.h similarity index 91% rename from src/platforms/pc/cl_utils.h rename to src/platforms/hosted/cli.h index ff43610d999..78de1f02038 100644 --- a/src/platforms/pc/cl_utils.h +++ b/src/platforms/hosted/cli.h @@ -21,8 +21,8 @@ /* This file implements the interface to command line command for PC-Hosted * platforms. */ -#if !defined(__CL_UTILS_H) -#define __CL_UTILS_H +#if !defined(__CLI_H) +#define __CLI_H #include "cortexm.h" @@ -40,9 +40,15 @@ enum bmp_cl_mode { BMP_MODE_MONITOR, }; +typedef enum bmp_scan_mode_e { + BMP_SCAN_JTAG, + BMP_SCAN_SWD, + BMP_SCAN_AUTO +} bmp_scan_mode_t; + typedef struct BMP_CL_OPTIONS_s { enum bmp_cl_mode opt_mode; - bool opt_usejtag; + bmp_scan_mode_t opt_scanmode; bool opt_tpwr; bool opt_list_only; bool opt_connect_under_reset; diff --git a/src/platforms/hosted/cmsis_dap.c b/src/platforms/hosted/cmsis_dap.c index dd5d3d11e50..7b652fd681a 100644 --- a/src/platforms/hosted/cmsis_dap.c +++ b/src/platforms/hosted/cmsis_dap.c @@ -38,18 +38,21 @@ #include "dap.h" #include "cmsis_dap.h" -#include "cl_utils.h" +#include "cli.h" #include "target.h" #include "target_internal.h" uint8_t dap_caps; uint8_t mode; -typedef enum cmsis_type_s { +#define TRANSFER_TIMEOUT_MS (100) + +typedef enum cmsis_type_e { CMSIS_TYPE_NONE = 0, CMSIS_TYPE_HID, CMSIS_TYPE_BULK } cmsis_type_t; + /*- Variables ---------------------------------------------------------------*/ static cmsis_type_t type; static libusb_device_handle *usb_handle = NULL; @@ -221,40 +224,46 @@ int dbg_dap_cmd(uint8_t *data, int size, int rsize) memcpy(&buffer[1], data, rsize); DEBUG_WIRE("cmd : "); - for(int i = 0; (i < 32) && (i < rsize + 1); i++) + for(int i = (type == CMSIS_TYPE_HID) ? 0 : 1; (i < rsize + 1); i++) DEBUG_WIRE("%02x.", buffer[i]); DEBUG_WIRE("\n"); if (type == CMSIS_TYPE_HID) { res = hid_write(handle, buffer, 65); if (res < 0) { - DEBUG_WARN( "Error: %ls\n", hid_error(handle)); - exit(-1); - } - res = hid_read_timeout(handle, buffer, 65, 1000); - if (res < 0) { - DEBUG_WARN( "debugger read(): %ls\n", hid_error(handle)); - exit(-1); - } else if (res == 0) { - DEBUG_WARN( "timeout\n"); + DEBUG_WARN("Error: %ls\n", hid_error(handle)); exit(-1); } + do { + res = hid_read_timeout(handle, buffer, 65, 1000); + if (res < 0) { + DEBUG_WARN("debugger read(): %ls\n", hid_error(handle)); + exit(-1); + } else if (res == 0) { + DEBUG_WARN("timeout\n"); + exit(-1); + } + } while (buffer[0] != cmd); } else if (type == CMSIS_TYPE_BULK) { int transferred = 0; - res = libusb_bulk_transfer(usb_handle, out_ep, data, rsize, &transferred, 500); + res = libusb_bulk_transfer(usb_handle, out_ep, data, rsize, &transferred, TRANSFER_TIMEOUT_MS); if (res < 0) { DEBUG_WARN("OUT error: %d\n", res); return res; } - res = libusb_bulk_transfer(usb_handle, in_ep, buffer, report_size, &transferred, 500); - if (res < 0) { - DEBUG_WARN("IN error: %d\n", res); - return res; - } + + /* We repeat the read in case we're out of step with the transmitter */ + do { + res = libusb_bulk_transfer(usb_handle, in_ep, buffer, report_size, &transferred, TRANSFER_TIMEOUT_MS); + if (res < 0) { + DEBUG_WARN("IN error: %d\n", res); + return res; + } + } while (buffer[0] != cmd); res = transferred; } DEBUG_WIRE("cmd res:"); - for(int i = 0; (i < 16) && (i < size + 1); i++) + for (int i = 0; i < res; i++) DEBUG_WIRE("%02x.", buffer[i]); DEBUG_WIRE("\n"); if (buffer[0] != cmd) { diff --git a/src/platforms/hosted/cmsis_dap.h b/src/platforms/hosted/cmsis_dap.h index cd834d8ee4c..dcb3ddfaa89 100644 --- a/src/platforms/hosted/cmsis_dap.h +++ b/src/platforms/hosted/cmsis_dap.h @@ -20,7 +20,7 @@ #define __CMSIS_DAP_H_ #include "adiv5.h" -#include "cl_utils.h" +#include "cli.h" #if defined(CMSIS_DAP) int dap_init(bmp_info_t *info); diff --git a/src/platforms/hosted/dap.c b/src/platforms/hosted/dap.c index 39318b3145e..d710bbf495a 100644 --- a/src/platforms/hosted/dap.c +++ b/src/platforms/hosted/dap.c @@ -278,7 +278,10 @@ void dap_reset_pin(int state) buf[1] = state ? DAP_SWJ_nRESET : 0; // Value buf[2] = DAP_SWJ_nRESET; // Select buf[3] = 0; // Wait - dbg_dap_cmd(buf, sizeof(buf), 4); + buf[4] = 0; // Wait + buf[5] = 0; // Wait + buf[6] = 0; // Wait + dbg_dap_cmd(buf, sizeof(buf), 7); } void dap_trst_reset(void) @@ -721,9 +724,9 @@ void dap_jtagtap_tdi_tdo_seq(uint8_t *DO, bool final_tms, const uint8_t *TMS, *p++ = transfers; for (int i = 0; i < transfers; i++) { *p++ = 1 | ((DO) ? DAP_JTAG_TDO_CAPTURE : 0) | - ((TMS[i / 8] & (1 << (i & 7))) ? DAP_JTAG_TMS : 0); + ((TMS[i >> 3] & (1 << (i & 7))) ? DAP_JTAG_TMS : 0); if (DI) - *p++ = (DI[i / 8] & (1 << (i & 7))) ? 1 : 0; + *p++ = (DI[i >> 3] & (1 << (i & 7))) ? 1 : 0; else *p++ = 1; } @@ -733,9 +736,9 @@ void dap_jtagtap_tdi_tdo_seq(uint8_t *DO, bool final_tms, const uint8_t *TMS, if (DO) { for (int i = 0; i < transfers; i++) { if (buf[i + 1]) - DO[i / 8] |= (1 << (i & 7)); + DO[i >> 3] |= (1 << (i & 7)); else - DO[i / 8] &= ~(1 << (i & 7)); + DO[i >> 3] &= ~(1 << (i & 7)); } } ticks -= transfers; @@ -797,8 +800,9 @@ bool dap_sequence_test(void) { uint8_t buf[4] = { ID_DAP_SWD_SEQUENCE, - 1, - 0 /* one idle cycle */ + 0x1, + 0x81, /* Read one bit */ + 0 /* one idle cycle */ }; dbg_dap_cmd(buf, sizeof(buf), 3); return (buf[0] == DAP_OK); diff --git a/src/platforms/hosted/ftdi_bmp.c b/src/platforms/hosted/ftdi_bmp.c index fb67d00878e..bd393d1f086 100644 --- a/src/platforms/hosted/ftdi_bmp.c +++ b/src/platforms/hosted/ftdi_bmp.c @@ -260,6 +260,85 @@ cable_desc_t cable_desc[] = { .init.ddr_high = PIN4 | PIN3 | PIN1 | PIN0, .name = "arm-usb-ocd-h" }, + /* ESP Prog: + * + * JTAG buffered on Interface A -> No SWD + * Standard VID/PID/Product + * No nRST on the 10 pin connectors + * + * JTAG enabled by default, ESP_EN pulled up, + * inverted by U4 and enabling JTAG by U5 + */ + { + .vendor = 0x0403, + .product = 0x6010, + .interface = INTERFACE_A, + .name = "esp-prog" + // No explicit reset + // SWD not possible + }, + { + /* MPSSE_SK (DB0) ----------- SWDCK/JTCK + * Mode-Switch 1-2/4-5: JTAG + * MPSSE-DO (DB1) ----------- JTAG/TDI + * MPSSE-DI (DB2) ----------- JTAG/TDO + * MPSSE-CS (DB3) ----------- JTAG/TMS + * Mode-Switch 3-2/6-5: SWD + * MPSSE-DO (DB1) -- 330 R -- SWD/SWDIO + * MPSSE-DI (DB2) ----------- SWD/SWDIO + * Indicate Mode-SW set to SWD with "-e" on the command line + * TRST is Push/Pull, not OD! + * PIN4 (DB5) ----------- TRST + * SRST is Push/Pull, not OD! Keep DDR set. + * PIN5 (DB5) ----------- NRST */ + .vendor = 0x0403, + .product = 0x6010,/*FT2232H*/ + .interface = INTERFACE_B, + .init.data_high = PIN4 | PIN5, /* High on PIN4/5 */ + .init.ddr_high = PIN4 | PIN5, /* Output on PIN4/5 */ + .assert_srst.data_low = ~PIN5, + .assert_srst.ddr_low = PIN5, + .deassert_srst.data_low = PIN5, + .deassert_srst.ddr_low = PIN5, + .target_voltage_cmd = GET_BITS_LOW, + .description = "Tigard", + .name = "tigard" + }, + { + /* https://sifive.cdn.prismic.io/sifive/b5c95ddd-22af-4be0-8021-50327e186b07_hifive1-a-schematics.pdf + * Direct Connection on Interface-A + * Reset on PIN5, Open-Drain, pulled up tp 3.3 Volt + * and decoupled from FE310 reset voa Schottky + */ + .vendor = 0x0403, + .product = 0x6010, + .interface = INTERFACE_A, + .assert_srst.data_low = ~PIN5, + .assert_srst.ddr_low = PIN5, + .deassert_srst.data_low = PIN5, + .deassert_srst.ddr_low = ~PIN5, + .bb_swdio_in_port_cmd = GET_BITS_LOW, + .bb_swdio_in_pin = MPSSE_CS, + .name = "hifive1" + }, + { /* https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/ + * + * schmeatics not available + */ + .vendor = 0x15b1, + .product = 0x002a, + .interface = INTERFACE_A, + .init.data_low = PIN4, + .init.ddr_low = PIN4 | PIN5, + .init.data_high = PIN2 | PIN4, + .init.ddr_high = PIN4, + .assert_srst.data_high = ~PIN2, + .assert_srst.ddr_high = PIN2, + .deassert_srst.data_high = PIN2, + .deassert_srst.ddr_high = ~PIN2, + .name = "arm-usb-tiny-h", + .description = "Olimex OpenOCD JTAG ARM-USB-TINY-H", + }, { } }; @@ -296,9 +375,9 @@ int ftdi_bmp_init(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info) active_cable->mpsse_swd_read.set_data_low = MPSSE_DO; active_cable->mpsse_swd_write.set_data_low = MPSSE_DO; } else if (!libftdi_swd_possible(NULL, NULL) && - !cl_opts->opt_usejtag) { + cl_opts->opt_scanmode != BMP_SCAN_JTAG) { DEBUG_WARN("SWD with cable not possible, trying JTAG\n"); - cl_opts->opt_usejtag = true; + cl_opts->opt_scanmode = BMP_SCAN_JTAG; } if(ftdic) { ftdi_usb_close(ftdic); diff --git a/src/platforms/hosted/ftdi_bmp.h b/src/platforms/hosted/ftdi_bmp.h index e5553a44ff3..e18e669508f 100644 --- a/src/platforms/hosted/ftdi_bmp.h +++ b/src/platforms/hosted/ftdi_bmp.h @@ -22,7 +22,7 @@ #ifndef __FTDI_BMP_H #define __FTDI_BMP_H -#include "cl_utils.h" +#include "cli.h" #include "jtagtap.h" #include "bmp_hosted.h" diff --git a/src/platforms/pc/gdb_if.c b/src/platforms/hosted/gdb_if.c similarity index 100% rename from src/platforms/pc/gdb_if.c rename to src/platforms/hosted/gdb_if.c diff --git a/src/platforms/pc/hid.c b/src/platforms/hosted/hid.c old mode 100755 new mode 100644 similarity index 100% rename from src/platforms/pc/hid.c rename to src/platforms/hosted/hid.c diff --git a/src/platforms/pc/hidapi.h b/src/platforms/hosted/hidapi.h similarity index 100% rename from src/platforms/pc/hidapi.h rename to src/platforms/hosted/hidapi.h diff --git a/src/platforms/hosted/jlink.c b/src/platforms/hosted/jlink.c index d1d1220fc2c..0f9c22f6d1c 100644 --- a/src/platforms/hosted/jlink.c +++ b/src/platforms/hosted/jlink.c @@ -30,7 +30,7 @@ #include #include -#include "cl_utils.h" +#include "cli.h" #include "jlink.h" #define USB_PID_SEGGER 0x1366 diff --git a/src/platforms/hosted/jlink_adiv5_swdp.c b/src/platforms/hosted/jlink_adiv5_swdp.c index 44aacd00e81..fa6a5747382 100644 --- a/src/platforms/hosted/jlink_adiv5_swdp.c +++ b/src/platforms/hosted/jlink_adiv5_swdp.c @@ -30,7 +30,7 @@ #include "target_internal.h" #include "adiv5.h" #include "jlink.h" -#include "cl_utils.h" +#include "cli.h" #define SWDP_ACK_OK 0x01 #define SWDP_ACK_WAIT 0x02 diff --git a/src/platforms/hosted/jlink_jtagtap.c b/src/platforms/hosted/jlink_jtagtap.c index 17dd3a29c1c..c70da7f2cf5 100644 --- a/src/platforms/hosted/jlink_jtagtap.c +++ b/src/platforms/hosted/jlink_jtagtap.c @@ -28,7 +28,7 @@ #include "exception.h" #include "jlink.h" -#include "cl_utils.h" +#include "cli.h" static void jtagtap_reset(void) { diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index a90294d487a..b5b701c14cd 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -26,17 +26,20 @@ #include "target_internal.h" #include "adiv5.h" #include "timing.h" -#include "cl_utils.h" +#include "cli.h" #include "gdb_if.h" #include +#ifdef ENABLE_RTT +#include "rtt_if.h" +#endif + #include "bmp_remote.h" #include "bmp_hosted.h" #include "stlinkv2.h" #include "ftdi_bmp.h" #include "jlink.h" #include "cmsis_dap.h" -#include "cl_utils.h" bmp_info_t info; @@ -58,6 +61,9 @@ static void exit_function(void) default: break; } + #ifdef ENABLE_RTT + rtt_if_exit(); + #endif fflush(stdout); } @@ -76,11 +82,10 @@ void platform_init(int argc, char **argv) atexit(exit_function); signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); - if (cl_opts.opt_device) { + if (cl_opts.opt_device) info.bmp_type = BMP_TYPE_BMP; - } else if (find_debuggers(&cl_opts, &info)) { + else if (find_debuggers(&cl_opts, &info)) exit(-1); - } bmp_ident(&info); switch (info.bmp_type) { case BMP_TYPE_BMP: @@ -89,11 +94,11 @@ void platform_init(int argc, char **argv) remote_init(); break; case BMP_TYPE_STLINKV2: - if (stlink_init( &info)) + if (stlink_init(&info)) exit(-1); break; case BMP_TYPE_CMSIS_DAP: - if (dap_init( &info)) + if (dap_init(&info)) exit(-1); break; case BMP_TYPE_LIBFTDI: @@ -107,14 +112,15 @@ void platform_init(int argc, char **argv) default: exit(-1); } - int ret = -1; - if (cl_opts.opt_mode != BMP_MODE_DEBUG) { - ret = cl_execute(&cl_opts); - } else { + if (cl_opts.opt_mode != BMP_MODE_DEBUG) + exit(cl_execute(&cl_opts)); + else { gdb_if_init(); + #ifdef ENABLE_RTT + rtt_if_init(); + #endif return; } - exit(ret); } int platform_adiv5_swdp_scan(uint32_t targetid) @@ -385,6 +391,38 @@ void platform_target_set_power(bool power) } } +bool platform_target_get_power(void) +{ + switch (info.bmp_type) { + case BMP_TYPE_BMP: + return remote_target_get_power() ; + + default: + return false ; + } +} + +uint32_t platform_target_voltage_sense(void) +{ + uint32_t targetVoltage = 0 ; + switch (info.bmp_type) { + case BMP_TYPE_BMP: { + const char * result ; + uint32_t units = 0, tenths = 0 ; + result = remote_target_voltage() ; + if (result != NULL) { + sscanf(result,"%"PRIu32".%"PRIu32, &units, &tenths) ; + targetVoltage = (units * 10) + tenths ; + } + break ; + } + + default: + break ; + } + return targetVoltage ; +} + void platform_buffer_flush(void) { switch (info.bmp_type) { diff --git a/src/platforms/hosted/platform.h b/src/platforms/hosted/platform.h index 3ac453decd0..50f1cbccd4f 100644 --- a/src/platforms/hosted/platform.h +++ b/src/platforms/hosted/platform.h @@ -9,6 +9,7 @@ void platform_buffer_flush(void); #define PLATFORM_IDENT "(PC-Hosted) " #define SET_IDLE_STATE(x) #define SET_RUN_STATE(x) +#define PLATFORM_HAS_POWER_SWITCH #define SYSTICKHZ 1000 diff --git a/src/platforms/hosted/rtt_if.c b/src/platforms/hosted/rtt_if.c new file mode 100644 index 00000000000..b303a58361d --- /dev/null +++ b/src/platforms/hosted/rtt_if.c @@ -0,0 +1,127 @@ +/* + * This file is part of the Black Magic Debug project. + * + * MIT License + * + * Copyright (c) 2021 Koen De Vleeschauwer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +/* maybe rewrite this as tcp server */ + +#ifndef WIN32 +#include + +/* linux */ +static struct termios saved_ttystate; +static bool tty_saved = false; + +/* set up and tear down */ + +int rtt_if_init() +{ + struct termios ttystate; + tcgetattr(STDIN_FILENO, &saved_ttystate); + tty_saved = true; + tcgetattr(STDIN_FILENO, &ttystate); + ttystate.c_lflag &= ~ICANON; + ttystate.c_lflag &= ~ECHO; + ttystate.c_cc[VMIN] = 1; + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); + int flags = fcntl(0, F_GETFL, 0); + fcntl(0, F_SETFL, flags | O_NONBLOCK); + return 0; +} + +int rtt_if_exit() +{ + if (tty_saved) + tcsetattr(STDIN_FILENO, TCSANOW, &saved_ttystate); + return 0; +} + +/* write buffer to terminal */ + +uint32_t rtt_write(const char *buf, uint32_t len) +{ + write(1, buf, len); + return len; +} + +/* read character from terminal */ + +int32_t rtt_getchar() +{ + char ch; + int len; + len = read(0, &ch, 1); + if (len == 1) return ch; + return -1; +} + +/* true if no characters available */ + +bool rtt_nodata() +{ + return false; +} + +#else + +/* windows, output only */ + +int rtt_if_init() +{ + return 0; +} + +int rtt_if_exit() +{ + return 0; +} + +/* write buffer to terminal */ + +uint32_t rtt_write(const char *buf, uint32_t len) +{ + write(1, buf, len); + return len; +} + +/* read character from terminal */ + +int32_t rtt_getchar() +{ + return -1; +} + +/* true if no characters available */ + +bool rtt_nodata() +{ + return false; +} + +#endif diff --git a/src/platforms/pc/serial_unix.c b/src/platforms/hosted/serial_unix.c similarity index 90% rename from src/platforms/pc/serial_unix.c rename to src/platforms/hosted/serial_unix.c index 08f01134499..563b1fdc0ab 100644 --- a/src/platforms/pc/serial_unix.c +++ b/src/platforms/hosted/serial_unix.c @@ -27,7 +27,7 @@ #include #include "remote.h" -#include "cl_utils.h" +#include "cli.h" #include "cortexm.h" static int fd; /* File descriptor for connection to GDB remote */ @@ -79,7 +79,7 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) DEBUG_WARN("No serial device found\n"); return -1; } else { - sprintf(name, "/dev/tty.usbmodem%s1", serial); + sprintf(name, "/dev/cu.usbmodem%s1", serial); } } else { strncpy(name, cl_opts->opt_device, sizeof(name) - 1); @@ -95,7 +95,9 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) return set_interface_attribs(); } #else -#define BMP_IDSTRING "usb-Black_Sphere_Technologies_Black_Magic_Probe" +#define BMP_IDSTRING_BLACKSPHERE "usb-Black_Sphere_Technologies_Black_Magic_Probe" +#define BMP_IDSTRING_BLACKMAGIC "usb-Black_Magic_Debug_Black_Magic_Probe" +#define BMP_IDSTRING_1BITSQUARED "usb-1BitSquared_Black_Magic_Probe" #define DEVICE_BY_ID "/dev/serial/by-id/" int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) { @@ -111,7 +113,9 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) int num_devices = 0; int num_total = 0; while ((dp = readdir(dir)) != NULL) { - if ((strstr(dp->d_name, BMP_IDSTRING)) && + if ((strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || + strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || + strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) { num_total++; if ((serial) && (!strstr(dp->d_name, serial))) @@ -130,7 +134,9 @@ int serial_open(BMP_CL_OPTIONS_t *cl_opts, char *serial) dir = opendir(DEVICE_BY_ID); if (dir) { while ((dp = readdir(dir)) != NULL) { - if ((strstr(dp->d_name, BMP_IDSTRING)) && + if ((strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) || + strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) || + strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) && (strstr(dp->d_name, "-if00"))) DEBUG_WARN("%s\n", dp->d_name); } @@ -173,7 +179,7 @@ int platform_buffer_write(const uint8_t *data, int size) s = write(fd, data, size); if (s < 0) { DEBUG_WARN("Failed to write\n"); - return(-2); + exit(-2); } return size; diff --git a/src/platforms/pc/serial_win.c b/src/platforms/hosted/serial_win.c similarity index 99% rename from src/platforms/pc/serial_win.c rename to src/platforms/hosted/serial_win.c index b08e87ea489..e28c9f70599 100644 --- a/src/platforms/pc/serial_win.c +++ b/src/platforms/hosted/serial_win.c @@ -20,7 +20,7 @@ #include "general.h" #include #include "remote.h" -#include "cl_utils.h" +#include "cli.h" static HANDLE hComm; diff --git a/src/platforms/hosted/stlinkv2.c b/src/platforms/hosted/stlinkv2.c index 42e81dd9079..08c9b4ad3a7 100644 --- a/src/platforms/hosted/stlinkv2.c +++ b/src/platforms/hosted/stlinkv2.c @@ -40,7 +40,7 @@ #include #include -#include "cl_utils.h" +#include "cli.h" #define STLINK_SWIM_ERR_OK 0x00 #define STLINK_SWIM_BUSY 0x01 @@ -211,6 +211,7 @@ static int stlink_usb_get_rw_status(bool verbose); int debug_level = 0; #define STLINK_ERROR_DP_FAULT -2 +#define STLINK_ERROR_AP_FAULT -3 /** Converts an STLINK status code held in the first byte of a response to @@ -273,7 +274,7 @@ static int stlink_usb_error_check(uint8_t *data, bool verbose) Stlink.ap_error = true; if (verbose) DEBUG_WARN("STLINK_SWD_AP_FAULT\n"); - return STLINK_ERROR_DP_FAULT; + return STLINK_ERROR_AP_FAULT; case STLINK_SWD_AP_ERROR: if (verbose) DEBUG_WARN("STLINK_SWD_AP_ERROR\n"); @@ -331,17 +332,27 @@ static int stlink_send_recv_retry(uint8_t *txbuf, size_t txsize, uint8_t *rxbuf, size_t rxsize) { uint32_t start = platform_time_ms(); - int res; + int res, first_res = STLINK_ERROR_OK; usb_link_t *link = info.usb_link; while(1) { send_recv(link, txbuf, txsize, rxbuf, rxsize); res = stlink_usb_error_check(rxbuf, false); if (res == STLINK_ERROR_OK) return res; + if ((res == STLINK_ERROR_AP_FAULT) && (first_res == STLINK_ERROR_WAIT)) { + /* STLINKV3 while AP is busy answers once with ERROR_WAIT, then + * with AP_FAULT and finally with ERROR_OK and the pending result. + * Interpret AP_FAULT as AP_WAIT in this case. + */ + Stlink.ap_error = false; + res = STLINK_ERROR_WAIT; + } + if (first_res == STLINK_ERROR_OK) + first_res = res; uint32_t now = platform_time_ms(); if (((now - start) > cortexm_wait_timeout) || (res != STLINK_ERROR_WAIT)) { - DEBUG_WARN("write_retry failed. "); + DEBUG_WARN("send_recv_retry failed. "); return res; } } @@ -510,23 +521,39 @@ int stlink_init(bmp_info_t *info) bool found = false; while ((dev = devs[i++]) != NULL) { struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(dev, &desc); - if (r < 0) { + int result = libusb_get_device_descriptor(dev, &desc); + if (result != LIBUSB_SUCCESS) { DEBUG_WARN("libusb_get_device_descriptor failed %s", - libusb_strerror(r)); + libusb_strerror(result)); return -1; } - if ((desc.idVendor != info->vid) || - (desc.idProduct != info->pid) || - (libusb_open(dev, &sl->ul_libusb_device_handle) - != LIBUSB_SUCCESS)) { + if (desc.idVendor != info->vid || + desc.idProduct != info->pid) { + continue; + } + if ((result = libusb_open(dev, &sl->ul_libusb_device_handle)) != LIBUSB_SUCCESS) + { + DEBUG_WARN("Failed to open STLink device %04x:%04x - %s\n", + desc.idVendor, desc.idProduct, libusb_strerror(result)); + DEBUG_WARN("Are you sure the permissions on the device are set correctly?\n"); continue; } char serial[64]; - r = libusb_get_string_descriptor_ascii( - sl->ul_libusb_device_handle, desc.iSerialNumber, - (uint8_t*)serial,sizeof(serial)); - if (r <= 0 || !strstr(serial, info->serial)) { + if (desc.iSerialNumber) { + int result = libusb_get_string_descriptor_ascii(sl->ul_libusb_device_handle, + desc.iSerialNumber, (uint8_t *)serial, sizeof(serial)); + /* If the call fails and it's not because the device gave us STALL, continue to the next one */ + if (result < 0 && result != LIBUSB_ERROR_PIPE) { + libusb_close(sl->ul_libusb_device_handle); + continue; + } + else if (result <= 0) + serial[0] = '\0'; + } + else + serial[0] = '\0'; + /* Likewise, if the serial number returned doesn't match the one in info, go to next */ + if (!strstr(serial, info->serial)) { libusb_close(sl->ul_libusb_device_handle); continue; } @@ -535,7 +562,7 @@ int stlink_init(bmp_info_t *info) } libusb_free_device_list(devs, 1); if (!found) - return 0; + return 1; if (info->vid != VENDOR_ID_STLINK) return 0; switch (info->pid) { @@ -1010,7 +1037,7 @@ int jtag_scan_stlinkv2(bmp_info_t *info, const uint8_t *irlens) if((jtag_devs[i].jd_idcode & dev_descr[j].idmask) == dev_descr[j].idcode) { if(dev_descr[j].handler) - dev_descr[j].handler(&jtag_devs[i]); + dev_descr[j].handler(i); break; } diff --git a/src/platforms/pc/utils.c b/src/platforms/hosted/utils.c similarity index 100% rename from src/platforms/pc/utils.c rename to src/platforms/hosted/utils.c diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index 09f21d2f75d..c732aaa78d7 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -35,15 +35,42 @@ #include #include #include +#include static void adc_init(void); static void setup_vbus_irq(void); +/* Starting with hardware version 4 we are storing the hardware version in the + * flash option user Data1 byte. + * The hardware version 4 was the transition version that had it's hardware + * pins strapped to 3 but contains version 4 in the Data1 byte. + * The hardware 4 is backward compatible with V3 but provides the new jumper + * connecting STRACE target pin to the UART1 pin. + * Hardware version 5 does not have the physically strapped version encoding + * any more and the hardware version has to be read out of the option bytes. + * This means that older firmware versions that don't do the detection won't + * work on the newer hardware. + */ +#define BMP_HWVERSION_BYTE FLASH_OPTION_BYTE_2 + /* Pins PB[7:5] are used to detect hardware revision. - * 000 - Original production build. - * 001 - Mini production build. - * 010 - Mini V2.0e and later. - * 011 - Mini V2.1e and later. + * User option byte Data1 is used starting with hardware revision 4. + * Pin - OByte - Rev - Description + * 000 - 0xFFFF - 0 - Original production build. + * 001 - 0xFFFF - 1 - Mini production build. + * 010 - 0xFFFF - 2 - Mini V2.0e and later. + * 011 - 0xFFFF - 3 - Mini V2.1a and later. + * 011 - 0xFB04 - 4 - Mini V2.1d and later. + * xxx - 0xFB05 - 5 - Mini V2.2a and later. + * xxx - 0xFB06 - 6 - Mini V2.3a and later. + * + * This function will return -2 if the version number does not make sense. + * This can happen when the Data1 byte contains "garbage". For example a + * hardware revision that is <4 or the high byte is not the binary inverse of + * the lower byte. + * Note: The high byte of the Data1 option byte should always be the binary + * inverse of the lower byte unless the byte is not set, then all bits in both + * high and low byte are 0xFF. */ int platform_hwversion(void) { @@ -51,7 +78,26 @@ int platform_hwversion(void) uint16_t hwversion_pins = GPIO7 | GPIO6 | GPIO5; uint16_t unused_pins = hwversion_pins ^ 0xFFFF; - /* Only check for version if this is the first time. */ + /* Check if the hwversion is set in the user option byte. */ + if (hwversion == -1) { + if ((BMP_HWVERSION_BYTE != 0xFFFF) && + (BMP_HWVERSION_BYTE != 0x00FF)) { + /* Check if the data is valid. + * When valid it should only have values 4 and higher. + */ + if (((BMP_HWVERSION_BYTE >> 8) != + (~BMP_HWVERSION_BYTE & 0xFF)) || + ((BMP_HWVERSION_BYTE & 0xFF) < 4)) { + return -2; + } else { + hwversion = BMP_HWVERSION_BYTE & 0xFF; + } + } + } + + /* If the hwversion is not set in option bytes check + * the hw pin strapping. + */ if (hwversion == -1) { /* Configure the hardware version pins as input pull-up/down */ gpio_set_mode(GPIOB, GPIO_MODE_INPUT, @@ -299,7 +345,18 @@ void platform_request_boot(void) void exti15_10_isr(void) { - if (gpio_get(USB_VBUS_PORT, USB_VBUS_PIN)) { + uint32_t usb_vbus_port; + uint16_t usb_vbus_pin; + + if (platform_hwversion() < 5) { + usb_vbus_port = USB_VBUS_PORT; + usb_vbus_pin = USB_VBUS_PIN; + } else { + usb_vbus_port = USB_VBUS5_PORT; + usb_vbus_pin = USB_VBUS5_PIN; + } + + if (gpio_get(usb_vbus_port, usb_vbus_pin)) { /* Drive pull-up high if VBUS connected */ gpio_set_mode(USB_PU_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, USB_PU_PIN); @@ -309,24 +366,35 @@ void exti15_10_isr(void) GPIO_CNF_INPUT_FLOAT, USB_PU_PIN); } - exti_reset_request(USB_VBUS_PIN); + exti_reset_request(usb_vbus_pin); } static void setup_vbus_irq(void) { + uint32_t usb_vbus_port; + uint16_t usb_vbus_pin; + + if (platform_hwversion() < 5) { + usb_vbus_port = USB_VBUS_PORT; + usb_vbus_pin = USB_VBUS_PIN; + } else { + usb_vbus_port = USB_VBUS5_PORT; + usb_vbus_pin = USB_VBUS5_PIN; + } + nvic_set_priority(USB_VBUS_IRQ, IRQ_PRI_USB_VBUS); nvic_enable_irq(USB_VBUS_IRQ); - gpio_set(USB_VBUS_PORT, USB_VBUS_PIN); + gpio_set(usb_vbus_port, usb_vbus_pin); gpio_set(USB_PU_PORT, USB_PU_PIN); - gpio_set_mode(USB_VBUS_PORT, GPIO_MODE_INPUT, - GPIO_CNF_INPUT_PULL_UPDOWN, USB_VBUS_PIN); + gpio_set_mode(usb_vbus_port, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, usb_vbus_pin); /* Configure EXTI for USB VBUS monitor */ - exti_select_source(USB_VBUS_PIN, USB_VBUS_PORT); - exti_set_trigger(USB_VBUS_PIN, EXTI_TRIGGER_BOTH); - exti_enable_request(USB_VBUS_PIN); + exti_select_source(usb_vbus_pin, usb_vbus_port); + exti_set_trigger(usb_vbus_pin, EXTI_TRIGGER_BOTH); + exti_enable_request(usb_vbus_pin); exti15_10_isr(); } diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h index 87ce5ea8611..e5ea0c49ff9 100644 --- a/src/platforms/native/platform.h +++ b/src/platforms/native/platform.h @@ -41,27 +41,66 @@ int usbuart_debug_write(const char *buf, size_t len); #define PLATFORM_IDENT " " #define UPD_IFACE_STRING "@Internal Flash /0x08000000/8*001Kg" +/* Hardware version switcher helper + * when the hardware version is smaller than ver + * it outputs opt1, otherwise opt2 */ +#define HW_SWITCH(ver, opt1, opt2) \ + ((platform_hwversion() < (ver))?(opt1):(opt2)) + /* Important pin mappings for STM32 implementation: * - * LED0 = PB2 (Yellow LED : Running) - * LED1 = PB10 (Yellow LED : Idle) - * LED2 = PB11 (Red LED : Error) + * LED0 = PB2 (Yellow LED : Running) + * LED1 = PB10 (Orange LED : Idle) + * LED2 = PB11 (Red LED : Error) + * + * TPWR = PB0 (input) -- analogue on mini design ADC1, CH8 + * nTRST = PB1 (output) [blackmagic] + * PWR_BR = PB1 (output) [blackmagic_mini] -- supply power to the target, active low + * TMS_DIR = PA1 (output) [blackmagic_mini v2.1] -- choose direction of the TCK pin, input low, output high + * SRST_OUT = PA2 (output) -- Hardware 5 and older + * = PA9 (output) -- Hardware 6 and newer + * TDI = PA3 (output) -- Hardware 5 and older + * = PA7 (output) -- Hardware 6 and newer + * TMS = PA4 (input/output for SWDIO) + * TCK = PA5 (output SWCLK) + * TDO = PA6 (input) + * TRACESWO = PB7 (input) -- To allow trace decoding using USART1 + * Hardware 4 has a normally open jumper between TDO and TRACESWO + * Hardware 5 has hardwired connection between TDO and TRACESWO + * = PA10 (input) -- Hardware 6 and newer + * nSRST = PA7 (input) -- Hardware 5 and older + * = PC13 (input) -- Hardware 6 and newer + * + * USB_PU = PA8 (output) + * USB_VBUS = PB13 (input) -- New on mini design. + * Enable pull up for compatibility. + * Hardware 4 and older. (we needed the pin for SPI on 5) + * = PA15 (input) -- Hardware 5 and newer. + * BTN1 = PB12 (input) -- Force DFU bootloader when pressed during powerup. * - * TPWR = PB0 (input) -- analogue on mini design ADC1, ch8 - * nTRST = PB1 (output) [blackmagic] - * PWR_BR = PB1 (output) [blackmagic_mini] -- supply power to the target, active low - * TMS_DIR = PA1 (output) [blackmagic_mini v2.1] -- choose direction of the TCK pin, input low, output high - * SRST_OUT = PA2 (output) - * TDI = PA3 (output) - * TMS = PA4 (input/output for SWDIO) - * TCK = PA5 (output SWCLK) - * TDO = PA6 (input) - * nSRST = PA7 (input) + * UART_TX = PA9 (output) -- USART1 Hardware 5 and older + * = PA2 (output) -- USART2 Hardware 6 and newer + * UART_RX = PA10 (input) -- USART1 Hardware 5 and older + * = PA3 (input) -- USART2 Hardware 6 and newer * - * USB cable pull-up: PA8 - * USB VBUS detect: PB13 -- New on mini design. - * Enable pull up for compatibility. - * Force DFU mode button: PB12 + * On Board OTG Flash: -- Optional on Hardware 5 and newer, since Hardware 6 can be on the main board + * FLASH_CS = PB5 (output) + * SCLK = PB13 (output) + * COPI = PB15 (output) + * CIPO = PB14 (input) + * + * AUX Interface: -- Hardware 5 and newer + * SCLK = PB13 (output) + * COPI = PB15 (output) + * CIPO = PB14 (input) + * FLASH_CS = PB5 (output) -- Only Hardware 5 + * SD_CS = PB6 (output) -- Hardware 6 and newer + * DISPLAY_CS = PB6 (output) -- OnlyHardware 5 + * = PB7 (output) -- Hardware 6 and newer + * DISPLAY_DC = PB8 (output) + * BTN1 = PB12 (input) -- Shared with the DFU bootloader button + * BTN2 = PB9 (input) + * VBAT = PA0 (input) -- Battery voltage sense ADC2, CH0 */ /* Hardware definitions... */ @@ -71,7 +110,7 @@ int usbuart_debug_write(const char *buf, size_t len); #define TMS_PORT JTAG_PORT #define TCK_PORT JTAG_PORT #define TDO_PORT JTAG_PORT -#define TDI_PIN GPIO3 +#define TDI_PIN HW_SWITCH(6, GPIO3, GPIO7) #define TMS_DIR_PIN GPIO1 #define TMS_PIN GPIO4 #define TCK_PIN GPIO5 @@ -89,17 +128,23 @@ int usbuart_debug_write(const char *buf, size_t len); #define PWR_BR_PORT GPIOB #define PWR_BR_PIN GPIO1 #define SRST_PORT GPIOA -#define SRST_PIN GPIO2 +#define SRST_PIN HW_SWITCH(6, GPIO2, GPIO9) #define SRST_SENSE_PORT GPIOA -#define SRST_SENSE_PIN GPIO7 +#define SRST_SENSE_PIN HW_SWITCH(6, GPIO7, GPIO13) #define USB_PU_PORT GPIOA #define USB_PU_PIN GPIO8 +/* For HW Rev 4 and older */ #define USB_VBUS_PORT GPIOB #define USB_VBUS_PIN GPIO13 +/* IRQ stays the same for all hw revisions. */ #define USB_VBUS_IRQ NVIC_EXTI15_10_IRQ +/* For HW Rev 5 and newer */ +#define USB_VBUS5_PORT GPIOA +#define USB_VBUS5_PIN GPIO15 + #define LED_PORT GPIOB #define LED_PORT_UART GPIOB #define LED_0 GPIO2 @@ -109,6 +154,37 @@ int usbuart_debug_write(const char *buf, size_t len); #define LED_IDLE_RUN LED_1 #define LED_ERROR LED_2 +/* OTG Flash HW Rev 5 and newer */ +#define OTG_PORT GPIOB +#define OTG_CS GPIO5 +#define OTG_SCLK GPIO13 +#define OTG_COPI GPIO15 +#define OTG_CIPO GPIO14 + +/* AUX Port HW Rev 5 and newer */ +#define AUX_PORT GPIOB +#define AUX_SCLK_PORT AUX_PORT +#define AUX_COPI_PORT AUX_PORT +#define AUX_CIPO_PORT AUX_PORT +#define AUX_FCS_PORT AUX_PORT +#define AUX_SDCS_PORT AUX_PORT +#define AUX_DCS_PORT AUX_PORT +#define AUX_DDC_PORT AUX_PORT +#define AUX_BTN1_PORT AUX_PORT +#define AUX_BTN2_PORT AUX_PORT +#define AUX_VBAT_PORT GPIOA +#define AUX_SCLK GPIO13 +#define AUX_COPI GPIO15 +#define AUX_CIPO GPIO14 +#define AUX_FCS GPIO5 +#define AUX_SDCS GPIO6 +#define AUX_DCS GPIO6 +#define AUX_DCS6 GPIO7 +#define AUX_DDC GPIO8 +#define AUX_BTN1 GPIO12 +#define AUX_BTN2 GPIO9 +#define AUX_VBAT GPIO0 + # define SWD_CR GPIO_CRL(SWDIO_PORT) # define SWD_CR_MULT (1 << (4 << 2)) @@ -151,23 +227,39 @@ int usbuart_debug_write(const char *buf, size_t len); #define IRQ_PRI_USB_VBUS (14 << 4) #define IRQ_PRI_TRACE (0 << 4) -#define USBUSART USART1 -#define USBUSART_CR1 USART1_CR1 -#define USBUSART_DR USART1_DR -#define USBUSART_IRQ NVIC_USART1_IRQ -#define USBUSART_CLK RCC_USART1 +#define USBUSART HW_SWITCH(6, USBUSART1, USBUSART2) +#define USBUSART_IRQ HW_SWITCH(6, NVIC_USART1_IRQ, NVIC_USART2_IRQ) +#define USBUSART_CLK HW_SWITCH(6, RCC_USART1, RCC_USART2) #define USBUSART_PORT GPIOA -#define USBUSART_TX_PIN GPIO9 -#define USBUSART_RX_PIN GPIO10 -#define USBUSART_ISR(x) usart1_isr(x) +#define USBUSART_TX_PIN HW_SWITCH(6, GPIO9, GPIO2) +#define USBUSART_RX_PIN HW_SWITCH(6, GPIO10, GPIO3) + #define USBUSART_DMA_BUS DMA1 #define USBUSART_DMA_CLK RCC_DMA1 -#define USBUSART_DMA_TX_CHAN DMA_CHANNEL4 -#define USBUSART_DMA_TX_IRQ NVIC_DMA1_CHANNEL4_IRQ -#define USBUSART_DMA_TX_ISR(x) dma1_channel4_isr(x) -#define USBUSART_DMA_RX_CHAN DMA_CHANNEL5 -#define USBUSART_DMA_RX_IRQ NVIC_DMA1_CHANNEL5_IRQ -#define USBUSART_DMA_RX_ISR(x) dma1_channel5_isr(x) +#define USBUSART_DMA_TX_CHAN HW_SWITCH(6, USBUSART1_DMA_TX_CHAN, USBUSART2_DMA_TX_CHAN) +#define USBUSART_DMA_RX_CHAN HW_SWITCH(6, USBUSART1_DMA_RX_CHAN, USBUSART2_DMA_RX_CHAN) +#define USBUSART_DMA_TX_IRQ HW_SWITCH(6, USBUSART1_DMA_TX_IRQ, USBUSART2_DMA_TX_IRQ) +#define USBUSART_DMA_RX_IRQ HW_SWITCH(6, USBUSART1_DMA_RX_IRQ, USBUSART2_DMA_RX_IRQ) + +#define USBUSART1 USART1 +#define USBUSART1_IRQ NVIC_USART1_IRQ +#define USBUSART1_ISR(x) usart1_isr(x) +#define USBUSART1_DMA_TX_CHAN DMA_CHANNEL4 +#define USBUSART1_DMA_TX_IRQ NVIC_DMA1_CHANNEL4_IRQ +#define USBUSART1_DMA_TX_ISR(x) dma1_channel4_isr(x) +#define USBUSART1_DMA_RX_CHAN DMA_CHANNEL5 +#define USBUSART1_DMA_RX_IRQ NVIC_DMA1_CHANNEL5_IRQ +#define USBUSART1_DMA_RX_ISR(x) dma1_channel5_isr(x) + +#define USBUSART2 USART2 +#define USBUSART2_IRQ NVIC_USART2_IRQ +#define USBUSART2_ISR(x) usart2_isr(x) +#define USBUSART2_DMA_TX_CHAN DMA_CHANNEL7 +#define USBUSART2_DMA_TX_IRQ NVIC_DMA1_CHANNEL7_IRQ +#define USBUSART2_DMA_TX_ISR(x) dma1_channel7_isr(x) +#define USBUSART2_DMA_RX_CHAN DMA_CHANNEL6 +#define USBUSART2_DMA_RX_IRQ NVIC_DMA1_CHANNEL6_IRQ +#define USBUSART2_DMA_RX_ISR(x) dma1_channel6_isr(x) #define TRACE_TIM TIM3 #define TRACE_TIM_CLK_EN() rcc_periph_clock_enable(RCC_TIM3) diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h index f848ae79fd7..cbcdf984f34 100644 --- a/src/platforms/stlink/platform.h +++ b/src/platforms/stlink/platform.h @@ -114,6 +114,7 @@ int usbuart_debug_write(const char *buf, size_t len); #define USBUSART_TX_PIN GPIO2 #define USBUSART_RX_PIN GPIO3 #define USBUSART_ISR(x) usart2_isr(x) + #define USBUSART_DMA_BUS DMA1 #define USBUSART_DMA_CLK RCC_DMA1 #define USBUSART_DMA_TX_CHAN DMA_CHANNEL7 diff --git a/src/platforms/stm32/96b_carbon.ld b/src/platforms/stm32/96b_carbon.ld new file mode 100644 index 00000000000..c4c0ec3a494 --- /dev/null +++ b/src/platforms/stm32/96b_carbon.ld @@ -0,0 +1,29 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * + * 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 = 512K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K +} + +/* Include the common ld script from libopenstm32. */ +INCLUDE cortex-m-generic.ld + diff --git a/src/platforms/stm32/dfucore.c b/src/platforms/stm32/dfucore.c index b94a11423bf..4f1a5b6ae18 100644 --- a/src/platforms/stm32/dfucore.c +++ b/src/platforms/stm32/dfucore.c @@ -130,7 +130,7 @@ static char if_string[] = DFU_IFACE_STRING; #define BOARD_IDENT_DFU(BOARD_TYPE) "Black Magic Probe DFU " PLATFORM_IDENT "" FIRMWARE_VERSION static const char *usb_strings[] = { - "Black Sphere Technologies", + "Black Magic Debug", BOARD_IDENT_DFU(PLATFORM_IDENT), serial_no, /* This string is used by ST Microelectronics' DfuSe utility */ diff --git a/src/platforms/stm32/rtt_if.c b/src/platforms/stm32/rtt_if.c new file mode 100644 index 00000000000..b6f4ccc1f36 --- /dev/null +++ b/src/platforms/stm32/rtt_if.c @@ -0,0 +1,134 @@ +/* + * This file is part of the Black Magic Debug project. + * + * MIT License + * + * Copyright (c) 2021 Koen De Vleeschauwer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "general.h" +#include "platform.h" +#include +#include "cdcacm.h" +#include "rtt.h" +#include "rtt_if.h" + +/********************************************************************* +* +* rtt terminal i/o +* +********************************************************************** +*/ + +/* usb uart receive buffer */ +static char recv_buf[RTT_DOWN_BUF_SIZE]; +static uint32_t recv_head = 0; +static uint32_t recv_tail = 0; + +/* data from host to target: number of free bytes in usb receive buffer */ +inline static uint32_t recv_bytes_free() +{ + if (recv_tail <= recv_head) + return sizeof(recv_buf) - recv_head + recv_tail - 1; + else + return recv_tail - recv_head - 1; +} + +/* data from host to target: true if not enough free buffer space and we need to close flow control */ +inline static bool recv_set_nak() +{ + assert(sizeof(recv_buf) > 2 * CDCACM_PACKET_SIZE); + return recv_bytes_free() < 2 * CDCACM_PACKET_SIZE; +} + +/* usbuart_usb_out_cb is called when usb uart has received new data for target. + this routine has to be fast */ + +void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) +{ + (void)dev; + (void)ep; + char usb_buf[CDCACM_PACKET_SIZE]; + + /* close flow control while processing packet */ + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 1); + + const uint16_t len = usbd_ep_read_packet(usbdev, CDCACM_UART_ENDPOINT, usb_buf, CDCACM_PACKET_SIZE); + + /* skip flag: drop packet if not enough free buffer space */ + if (rtt_flag_skip && len > recv_bytes_free()) { + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); + return; + } + + /* copy data to recv_buf */ + for (int i = 0; i < len; i++) { + uint32_t next_recv_head = (recv_head + 1) % sizeof(recv_buf); + if (next_recv_head == recv_tail) + break; /* overflow */ + recv_buf[recv_head] = usb_buf[i]; + recv_head = next_recv_head; + } + + /* block flag: flow control closed if not enough free buffer space */ + if (!(rtt_flag_block && recv_set_nak())) + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); + + return; +} + +/* rtt host to target: read one character */ +int32_t rtt_getchar() +{ + int retval; + + if (recv_head == recv_tail) + return -1; + retval = recv_buf[recv_tail]; + recv_tail = (recv_tail + 1) % sizeof(recv_buf); + + /* open flow control if enough free buffer space */ + if (!recv_set_nak()) + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); + + return retval; +} + +/* rtt host to target: true if no characters available for reading */ +bool rtt_nodata() +{ + return recv_head == recv_tail; +} + +/* rtt target to host: write string */ +uint32_t rtt_write(const char *buf, uint32_t len) +{ + if (len != 0 && usbdev && cdcacm_get_config() && cdcacm_get_dtr()) { + for (uint32_t p = 0; p < len; p += CDCACM_PACKET_SIZE) { + uint32_t plen = MIN(CDCACM_PACKET_SIZE, len - p); + while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, buf + p, plen) <= 0); + } + /* flush 64-byte packet on full-speed */ + if (CDCACM_PACKET_SIZE == 64 && (len % CDCACM_PACKET_SIZE) == 0) + while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, NULL, 0) <= 0); + } + return len; +} diff --git a/src/platforms/stm32/serialno.c b/src/platforms/stm32/serialno.c index a8b2898ba3a..b47d74b5eb0 100644 --- a/src/platforms/stm32/serialno.c +++ b/src/platforms/stm32/serialno.c @@ -23,44 +23,42 @@ char *serial_no_read(char *s) { #if DFU_SERIAL_LENGTH == 9 - int i; - volatile uint32_t *unique_id_p = (volatile uint32_t *)DESIG_UNIQUE_ID_BASE; - uint32_t unique_id = *unique_id_p + - *(unique_id_p + 1) + - *(unique_id_p + 2); + const volatile uint32_t *const unique_id_p = (uint32_t *)DESIG_UNIQUE_ID_BASE; + const uint32_t unique_id = unique_id_p[0] + unique_id_p[1] + unique_id_p[2]; /* Fetch serial number from chip's unique ID */ - for(i = 0; i < 8; i++) { - s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0'; + for(size_t i = 0; i < 8U; ++i) { + s[7U - i] = ((unique_id >> (i * 4U)) & 0x0FU) + '0'; + /* If the character is something above 9, then add the offset to make it ASCII A-F */ + if (s[7U - i] > '9') + s[7U - i] += 7; /* 'A' - '9' = 8, less 1 gives 7. */ } - for(i = 0; i < 8; i++) - if(s[i] > '9') - s[i] += 'A' - '9' - 1; #elif DFU_SERIAL_LENGTH == 13 /* Use the same serial number as the ST DFU Bootloader.*/ - uint16_t *uid = (uint16_t *)DESIG_UNIQUE_ID_BASE; + const volatile uint16_t *const uid = (uint16_t *)DESIG_UNIQUE_ID_BASE; # if defined(STM32F4) || defined(STM32F7) int offset = 3; # elif defined(STM32L0) || defined(STM32F0) || defined(STM32F3) int offset = 5; # endif - sprintf(s, "%04X%04X%04X", - uid[1] + uid[5], uid[0] + uid[4], uid[offset]); + sprintf(s, "%04X%04X%04X", uid[1] + uid[5], uid[0] + uid[4], uid[offset]); #elif DFU_SERIAL_LENGTH == 25 - int i; - uint32_t unique_id[3]; - memcpy(unique_id, (uint32_t *)DESIG_UNIQUE_ID_BASE, 12); - for(i = 0; i < 8; i++) { - s[ 7-i] = ((unique_id[0] >> (4*i)) & 0xF) + '0'; - s[15-i] = ((unique_id[1] >> (4*i)) & 0xF) + '0'; - s[23-i] = ((unique_id[2] >> (4*i)) & 0xF) + '0'; + const volatile uint32_t *const unique_id_p = (uint32_t *)DESIG_UNIQUE_ID_BASE; + uint32_t unique_id = 0; + for (size_t i = 0; i < 24U; ++i) { + const size_t chunk = i >> 3U; + const size_t nibble = i & 7U; + const size_t idx = (chunk << 3U) + (7U - nibble); + if (nibble == 0) + unique_id = unique_id_p[chunk]; + s[idx] = ((unique_id >> (i * 4)) & 0x0F) + '0'; + + /* If the character is something above 9, then add the offset to make it ASCII A-F */ + if (s[idx] > '9') + s[idx] += 7; /* 'A' - '9' = 8, less 1 gives 7. */ } - for (i = 0; i < 24; i++) - if(s[i] > '9') - s[i] += 'A' - '9' - 1; #else # WARNING "Unhandled DFU_SERIAL_LENGTH" #endif - s[DFU_SERIAL_LENGTH - 1] = 0; + s[DFU_SERIAL_LENGTH - 1] = '\0'; return s; } - diff --git a/src/platforms/stm32/stm32f07xzb.ld b/src/platforms/stm32/stm32f07xzb.ld new file mode 100644 index 00000000000..395bef396f1 --- /dev/null +++ b/src/platforms/stm32/stm32f07xzb.ld @@ -0,0 +1,30 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F07xzB, 128k flash, 16k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K +} + +/* Include the common ld script. */ +INCLUDE cortex-m-generic.ld diff --git a/src/platforms/stm32/stm32f303xc.ld b/src/platforms/stm32/stm32f303xc.ld new file mode 100644 index 00000000000..f22ce015df0 --- /dev/null +++ b/src/platforms/stm32/stm32f303xc.ld @@ -0,0 +1,30 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for the STM32F303xC chip. */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 40K +} + +/* Include the common ld script. */ +INCLUDE cortex-m-generic.ld diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index c7f4a1bd59d..2ac7da9cc86 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -119,14 +119,18 @@ void usbuart_init(void) usart_set_mode(USBUSART, USART_MODE_TX_RX); usart_set_parity(USBUSART, USART_PARITY_NONE); usart_set_flow_control(USBUSART, USART_FLOWCONTROL_NONE); - USBUSART_CR1 |= USART_CR1_IDLEIE; + USART_CR1(USBUSART) |= USART_CR1_IDLEIE; /* Setup USART TX DMA */ #if !defined(USBUSART_TDR) && defined(USBUSART_DR) # define USBUSART_TDR USBUSART_DR +#elif !defined(USBUSART_TDR) +# define USBUSART_TDR USART_DR(USBUSART) #endif #if !defined(USBUSART_RDR) && defined(USBUSART_DR) # define USBUSART_RDR USBUSART_DR +#elif !defined(USBUSART_RDR) +# define USBUSART_RDR USART_DR(USBUSART) #endif dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uint32_t)&USBUSART_TDR); @@ -255,6 +259,7 @@ static void usbuart_change_dma_tx_buf(void) buf_tx_act_idx ^= 1; } +#ifndef ENABLE_RTT void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) { (void)ep; @@ -270,7 +275,8 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) /* Don't bother if uart is disabled. * This will be the case on mini while we're being debugged. */ - if(!(RCC_APB2ENR & RCC_APB2ENR_USART1EN)) + if(!(RCC_APB2ENR & RCC_APB2ENR_USART1EN) && + !(RCC_APB1ENR & RCC_APB1ENR_USART2EN)) { usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0); return; @@ -296,6 +302,7 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE) usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0); } +#endif #ifdef USBUART_DEBUG int usbuart_debug_write(const char *buf, size_t len) @@ -402,72 +409,151 @@ static void usbuart_run(void) nvic_enable_irq(USB_IRQ); } +#if defined(USART_ICR) +#define USBUSART_ISR_TEMPLATE(USART, DMA_IRQ) do { \ + nvic_disable_irq(DMA_IRQ); \ + \ + /* Get IDLE flag and reset interrupt flags */ \ + const bool isIdle = usart_get_flag(USART, USART_FLAG_IDLE); \ + usart_recv(USART); \ + \ + /* If line is now idle, then transmit a packet */ \ + if (isIdle) { \ + USART_ICR(USART) = USART_ICR_IDLECF; \ + usbuart_run(); \ + } \ + \ + nvic_enable_irq(DMA_IRQ); \ +} while(0) +#else +#define USBUSART_ISR_TEMPLATE(USART, DMA_IRQ) do { \ + nvic_disable_irq(DMA_IRQ); \ + \ + /* Get IDLE flag and reset interrupt flags */ \ + const bool isIdle = usart_get_flag(USART, USART_FLAG_IDLE); \ + usart_recv(USART); \ + \ + /* If line is now idle, then transmit a packet */ \ + if (isIdle) { \ + /* On the older uarts, the sequence "read flags", */ \ + /* "read DR" clears the flags */ \ + \ + usbuart_run(); \ + } \ + \ + nvic_enable_irq(DMA_IRQ); \ +} while(0) +#endif + +#if defined(USBUSART_ISR) void USBUSART_ISR(void) { #if defined(USBUSART_DMA_RXTX_IRQ) - nvic_disable_irq(USBUSART_DMA_RXTX_IRQ); + USBUSART_ISR_TEMPLATE(USBUSART, USBUSART_DMA_RXTX_IRQ); #else - nvic_disable_irq(USBUSART_DMA_RX_IRQ); + USBUSART_ISR_TEMPLATE(USBUSART, USBUSART_DMA_RX_IRQ); +#endif +} #endif - /* Get IDLE flag and reset interrupt flags */ - const bool isIdle = usart_get_flag(USBUSART, USART_FLAG_IDLE); - usart_recv(USBUSART); - - /* If line is now idle, then transmit a packet */ - if (isIdle) { -#if defined(USART_ICR) - USART_ICR(USBUSART) = USART_ICR_IDLECF; +#if defined(USBUSART1_ISR) +void USBUSART1_ISR(void) +{ +#if defined(USBUSART1_DMA_RXTX_IRQ) + USBUSART_ISR_TEMPLATE(USBUSART1, USBUSART1_DMA_RXTX_IRQ); #else - /* On the older uarts, the sequence "read flags", "read DR" - * as above cleared the flags */ + USBUSART_ISR_TEMPLATE(USBUSART1, USBUSART1_DMA_RX_IRQ); +#endif +} #endif - usbuart_run(); - } -#if defined(USBUSART_DMA_RXTX_IRQ) - nvic_enable_irq(USBUSART_DMA_RXTX_IRQ); +#if defined(USBUSART2_ISR) +void USBUSART2_ISR(void) +{ +#if defined(USBUSART2_DMA_RXTX_IRQ) + USBUSART_ISR_TEMPLATE(USBUSART2, USBUSART2_DMA_RXTX_IRQ); #else - nvic_enable_irq(USBUSART_DMA_RX_IRQ); + USBUSART_ISR_TEMPLATE(USBUSART2, USBUSART2_DMA_RX_IRQ); #endif } +#endif +#define USBUSART_DMA_TX_ISR_TEMPLATE(DMA_TX_CHAN) do { \ + nvic_disable_irq(USB_IRQ); \ + \ + /* Stop DMA */ \ + dma_disable_channel(USBUSART_DMA_BUS, DMA_TX_CHAN); \ + dma_clear_interrupt_flags(USBUSART_DMA_BUS, DMA_TX_CHAN, DMA_CGIF); \ + \ + /* If new buffer is ready, continue transmission. \ + * Otherwise report transfer completion. \ + */ \ + if (buf_tx_act_sz) \ + { \ + usbuart_change_dma_tx_buf(); \ + usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); \ + } \ + else \ + { \ + usbuart_set_led_state(TX_LED_ACT, false); \ + tx_trfr_cplt = true; \ + } \ + \ + nvic_enable_irq(USB_IRQ); \ +} while(0) + +#if defined(USBUSART_DMA_TX_ISR) void USBUSART_DMA_TX_ISR(void) { - nvic_disable_irq(USB_IRQ); - - /* Stop DMA */ - dma_disable_channel(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); - dma_clear_interrupt_flags(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_CGIF); + USBUSART_DMA_TX_ISR_TEMPLATE(USBUSART_DMA_TX_CHAN); +} +#endif - /* If new buffer is ready, continue transmission. - * Otherwise report transfer completion. - */ - if (buf_tx_act_sz) - { - usbuart_change_dma_tx_buf(); - usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); - } - else - { - usbuart_set_led_state(TX_LED_ACT, false); - tx_trfr_cplt = true; - } +#if defined(USBUSART1_DMA_TX_ISR) +void USBUSART1_DMA_TX_ISR(void) +{ + USBUSART_DMA_TX_ISR_TEMPLATE(USBUSART1_DMA_TX_CHAN); +} +#endif - nvic_enable_irq(USB_IRQ); +#if defined(USBUSART2_DMA_TX_ISR) +void USBUSART2_DMA_TX_ISR(void) +{ + USBUSART_DMA_TX_ISR_TEMPLATE(USBUSART2_DMA_TX_CHAN); } +#endif +#define USBUSART_DMA_RX_ISR_TEMPLATE(USART_IRQ, DMA_RX_CHAN) do { \ + nvic_disable_irq(USART_IRQ); \ + \ + /* Clear flags */ \ + dma_clear_interrupt_flags(USBUSART_DMA_BUS, DMA_RX_CHAN, DMA_CGIF); \ + /* Transmit a packet */ \ + usbuart_run(); \ + \ + nvic_enable_irq(USART_IRQ); \ +} while(0) + +#if defined(USBUSART_DMA_RX_ISR) void USBUSART_DMA_RX_ISR(void) { - nvic_disable_irq(USBUSART_IRQ); + USBUSART_DMA_RX_ISR_TEMPLATE(USBUSART_IRQ, USBUSART_DMA_RX_CHAN); +} +#endif - /* Clear flags */ - dma_clear_interrupt_flags(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_CGIF); - /* Transmit a packet */ - usbuart_run(); +#if defined(USBUSART1_DMA_RX_ISR) +void USBUSART1_DMA_RX_ISR(void) +{ + USBUSART_DMA_RX_ISR_TEMPLATE(USBUSART1_IRQ, USBUSART1_DMA_RX_CHAN); +} +#endif - nvic_enable_irq(USBUSART_IRQ); +#if defined(USBUSART2_DMA_RX_ISR) +void USBUSART2_DMA_RX_ISR(void) +{ + USBUSART_DMA_RX_ISR_TEMPLATE(USBUSART2_IRQ, USBUSART2_DMA_RX_CHAN); } +#endif #if defined(USBUSART_DMA_RXTX_ISR) void USBUSART_DMA_RXTX_ISR(void) diff --git a/src/platforms/swlink/platform.h b/src/platforms/swlink/platform.h index 36cab71ce3d..140742959ec 100644 --- a/src/platforms/swlink/platform.h +++ b/src/platforms/swlink/platform.h @@ -88,6 +88,8 @@ int usbuart_debug_write(const char *buf, size_t len); gpio_set_mode(USBUSART_PORT, GPIO_MODE_INPUT, \ GPIO_CNF_INPUT_PULL_UPDOWN, USBUSART_RX_PIN); \ gpio_set(USBUSART_PORT, USBUSART_RX_PIN); \ + gpio_set_mode(USBUSART_PORT, GPIO_MODE_OUTPUT_50_MHZ, \ + GPIO_CNF_OUTPUT_PUSHPULL, USBUSART_RTS_PIN | USBUSART_DTR_PIN); \ } while(0) #define USB_DRIVER st_usbfs_v1_usb_driver @@ -110,6 +112,8 @@ int usbuart_debug_write(const char *buf, size_t len); #define USBUSART_PORT GPIOB #define USBUSART_TX_PIN GPIO6 #define USBUSART_RX_PIN GPIO7 +#define USBUSART_RTS_PIN GPIO8 +#define USBUSART_DTR_PIN GPIO9 #define USBUSART_ISR(x) usart1_isr(x) #define USBUSART_DMA_BUS DMA1 #define USBUSART_DMA_CLK RCC_DMA1 diff --git a/src/platforms/tm4c/usbuart.c b/src/platforms/tm4c/usbuart.c index da821986725..23946847d91 100644 --- a/src/platforms/tm4c/usbuart.c +++ b/src/platforms/tm4c/usbuart.c @@ -99,6 +99,7 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) } } +#ifndef ENABLE_RTT void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) { (void)ep; @@ -110,7 +111,7 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep) for(int i = 0; i < len; i++) uart_send_blocking(USBUART, buf[i]); } - +#endif void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep) { diff --git a/src/rtt.c b/src/rtt.c new file mode 100644 index 00000000000..026c7bf3049 --- /dev/null +++ b/src/rtt.c @@ -0,0 +1,456 @@ +/* + * This file is part of the Black Magic Debug project. + * + * MIT License + * + * Copyright (c) 2021 Koen De Vleeschauwer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "general.h" +#include "platform.h" +#include "gdb_packet.h" +#include "target.h" +#include "target/target_internal.h" +#include "rtt.h" +#include "rtt_if.h" + +bool rtt_enabled = false; +bool rtt_found = false; +static bool rtt_halt = false; // true if rtt needs to halt target to access memory +uint32_t rtt_cbaddr = 0; +bool rtt_auto_channel = true; +struct rtt_channel_struct rtt_channel[MAX_RTT_CHAN]; + +uint32_t rtt_min_poll_ms = 8; /* 8 ms */ +uint32_t rtt_max_poll_ms = 256; /* 0.256 s */ +uint32_t rtt_max_poll_errs = 10; +static uint32_t poll_ms; +static uint32_t poll_errs; +static uint32_t last_poll_ms; +/* flags for data from host to target */ +bool rtt_flag_skip = false; +bool rtt_flag_block = false; + +typedef enum rtt_retval { + RTT_OK, + RTT_IDLE, + RTT_ERR +} rtt_retval; + +#ifdef RTT_IDENT +#define Q(x) #x +#define QUOTE(x) Q(x) +char rtt_ident[16] = QUOTE(RTT_IDENT); +#else +char rtt_ident[16] = {0}; +#endif + +/* usb uart transmit buffer */ +static char xmit_buf[RTT_UP_BUF_SIZE]; + +/********************************************************************* +* +* rtt control block +* +********************************************************************** +*/ + +uint32_t fastsrch(target *cur_target) +{ + const uint32_t m = 16; + const uint64_t q = 0x797a9691; /* prime */ + const uint64_t rm = 0x73b07d01; + const uint64_t p = 0x444110cd; + const uint32_t stride = 128; + uint64_t t = 0; + uint8_t srch_buf[m+stride]; + + for (struct target_ram *r = cur_target->ram; r; r = r->next) { + const uint32_t ram_start = r->start; + const uint32_t ram_end = r->start + r->length; + + t = 0; + memset(srch_buf, 0, sizeof(srch_buf)); + + for (uint32_t addr = ram_start; addr < ram_end; addr += stride) { + uint32_t buf_siz = MIN(stride, ram_end - addr); + memcpy(srch_buf, srch_buf + stride, m); + if (target_mem_read(cur_target, srch_buf + m, addr, buf_siz)) { + gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr); + return 0; + } + for (uint32_t i = 0; i < buf_siz; i++) { + t = (t + q - rm * srch_buf[i] % q) % q; + t = ((t << 8) + srch_buf[i + m]) % q; + if (p == t) { + uint32_t offset = i - m + 1; + return addr + offset; + } + } + } + } + /* no match */ + return 0; +} + +uint32_t memsrch(target *cur_target) +{ + char *srch_str = rtt_ident; + uint32_t srch_str_len = strlen(srch_str); + uint8_t srch_buf[128]; + + if (srch_str_len == 0 || srch_str_len > sizeof(srch_buf) / 2) + return 0; + + if (rtt_cbaddr && !target_mem_read(cur_target, srch_buf, rtt_cbaddr, srch_str_len) + && strncmp((const char *)(srch_buf), srch_str, srch_str_len) == 0) + /* still at same place */ + return rtt_cbaddr; + + for (struct target_ram *r = cur_target->ram; r; r = r->next) { + uint32_t ram_end = r->start + r->length; + for (uint32_t addr = r->start; addr < ram_end; addr += sizeof(srch_buf) - srch_str_len - 1) { + uint32_t buf_siz = MIN(ram_end - addr, sizeof(srch_buf)); + if (target_mem_read(cur_target, srch_buf, addr, buf_siz)) { + gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr); + continue; + } + for (uint32_t offset = 0; offset + srch_str_len + 1 < buf_siz; offset++) { + if (strncmp((const char *)(srch_buf + offset), srch_str, srch_str_len) == 0) { + uint32_t cb_addr = addr + offset; + return cb_addr; + } + } + } + } + return 0; +} + +static void find_rtt(target *cur_target) +{ + rtt_found = false; + poll_ms = rtt_max_poll_ms; + poll_errs = 0; + last_poll_ms = 0; + + if (!cur_target || !rtt_enabled) + return; + + if (rtt_ident[0] == 0) + rtt_cbaddr = fastsrch(cur_target); + else + rtt_cbaddr = memsrch(cur_target); + DEBUG_INFO("rtt: match at 0x%" PRIx32 "\r\n", rtt_cbaddr); + + if (rtt_cbaddr) { + uint32_t num_buf[2]; + int32_t num_up_buf; + int32_t num_down_buf; + if (target_mem_read(cur_target, num_buf, rtt_cbaddr + 16, sizeof(num_buf))) + return; + num_up_buf = num_buf[0]; + num_down_buf = num_buf[1]; + + if (num_up_buf > 255 || num_down_buf > 255) { + gdb_out("rtt: bad cblock\r\n"); + rtt_enabled = false; + return; + } else if (num_up_buf == 0 && num_down_buf == 0) + gdb_out("rtt: empty cblock\r\n"); + + for (int32_t i = 0; i < MAX_RTT_CHAN; i++) { + uint32_t buf_desc[6]; + + rtt_channel[i].is_configured = false; + rtt_channel[i].is_output = false; + rtt_channel[i].buf_addr = 0; + rtt_channel[i].buf_size = 0; + rtt_channel[i].head_addr = 0; + rtt_channel[i].tail_addr = 0; + rtt_channel[i].flag = 0; + + if (i >= num_up_buf + num_down_buf) + continue; + if (target_mem_read(cur_target, buf_desc, rtt_cbaddr + 24 + i * 24, sizeof(buf_desc))) + return; + rtt_channel[i].is_output = i < num_up_buf; + rtt_channel[i].buf_addr = buf_desc[1]; + rtt_channel[i].buf_size = buf_desc[2]; + rtt_channel[i].head_addr = rtt_cbaddr + 24 + i * 24 + 12; + rtt_channel[i].tail_addr = rtt_cbaddr + 24 + i * 24 + 16; + rtt_channel[i].flag = buf_desc[5]; + rtt_channel[i].is_configured = (rtt_channel[i].buf_addr != 0) && (rtt_channel[i].buf_size != 0); + } + + /* auto channel: enable output channels 0 and 1 and first input channel */ + if (rtt_auto_channel) { + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) + rtt_channel[i].is_enabled = false; + rtt_channel[0].is_enabled = num_up_buf > 0; + rtt_channel[1].is_enabled = num_up_buf > 1; + if ((num_up_buf < MAX_RTT_CHAN) && (num_down_buf > 0)) + rtt_channel[num_up_buf].is_enabled = true; + } + + /* get flags for data from host to target */ + rtt_flag_skip = false; + rtt_flag_block = false; + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) + if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured && !rtt_channel[i].is_output) { + rtt_flag_skip = rtt_channel[i].flag == 0; + rtt_flag_block = rtt_channel[i].flag == 2; + break; + } + + rtt_found = true; + DEBUG_INFO("rtt found\n"); + } + return; +} + +/********************************************************************* +* +* rtt from host to target +* +********************************************************************** +*/ + +/* poll if host has new data for target */ +static rtt_retval read_rtt(target *cur_target, uint32_t i) +{ + uint32_t head_tail[2]; + uint32_t buf_head; + uint32_t buf_tail; + uint32_t next_head; + int ch; + + /* copy data from recv_buf to target rtt 'down' buffer */ + if (rtt_nodata()) + return RTT_IDLE; + + if (cur_target == NULL || rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].buf_size == 0) + return RTT_IDLE; + + /* read down buffer head and tail from target */ + if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail))) + return RTT_ERR; + + buf_head = head_tail[0]; + buf_tail = head_tail[1]; + + if (buf_head >= rtt_channel[i].buf_size || buf_tail >= rtt_channel[i].buf_size) + return RTT_ERR; + + /* write recv_buf to target rtt 'down' buf */ + while ((next_head = ((buf_head + 1) % rtt_channel[i].buf_size)) != buf_tail && (ch = rtt_getchar()) != -1) { + if (target_mem_write(cur_target, rtt_channel[i].buf_addr + buf_head, &ch, 1)) + return RTT_ERR; + + /* advance pointers */ + buf_head = next_head; + } + + /* update head of target 'down' buffer */ + if (target_mem_write(cur_target, rtt_channel[i].head_addr, &buf_head, sizeof(buf_head))) + return RTT_ERR; + return RTT_OK; +} + + +/********************************************************************* +* +* rtt from target to host +* +********************************************************************** +*/ + +/* target_mem_read, word aligned for speed. + note: dest has to be len + 8 bytes, to allow for alignment and padding. + */ +int target_aligned_mem_read(target *t, void *dest, target_addr src, size_t len) +{ + uint32_t src0 = src; + uint32_t len0 = len; + uint32_t offset = src & 0x3; + src0 -= offset; + len0 += offset; + if ((len0 & 0x3) != 0) + len0 = (len0 + 4) & ~0x3; + + if (src0 == src && len0 == len) + return target_mem_read(t, dest, src, len); + else { + uint32_t retval = target_mem_read(t, dest, src0, len0); + memmove(dest, dest + offset, len); + return retval; + } +} + +/* poll if target has new data for host */ +static rtt_retval print_rtt(target *cur_target, uint32_t i) +{ + uint32_t head; + uint32_t tail; + + if (!cur_target || !rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].head_addr == 0) + return RTT_IDLE; + + uint32_t head_tail[2]; + if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail))) + return RTT_ERR; + head = head_tail[0]; + tail = head_tail[1]; + + if (head >= rtt_channel[i].buf_size || tail >= rtt_channel[i].buf_size) + return RTT_ERR; + else if (head == tail) + return RTT_IDLE; + + uint32_t bytes_free = sizeof(xmit_buf) - 8; /* need 8 bytes for alignment and padding */ + uint32_t bytes_read = 0; + + if (tail > head) { + uint32_t len = rtt_channel[i].buf_size - tail; + if (len > bytes_free) + len = bytes_free; + if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len)) + return RTT_ERR; + bytes_free -= len; + bytes_read += len; + tail = (tail + len) % rtt_channel[i].buf_size; + } + + if (head > tail && bytes_free > 0) { + uint32_t len = head - tail; + if (len > bytes_free) + len = bytes_free; + if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len)) + return RTT_ERR; + bytes_read += len; + tail = (tail + len) % rtt_channel[i].buf_size; + } + + /* update tail on target */ + if (target_mem_write(cur_target, rtt_channel[i].tail_addr, &tail, sizeof(tail))) + return RTT_ERR; + + /* write buffer to usb */ + rtt_write(xmit_buf, bytes_read); + + return RTT_OK; +} + + +/********************************************************************* +* +* target background memory access +* +********************************************************************** +*/ + +/* target_no_background_memory_access() is true if the target needs to be halted during jtag memory access + target_no_background_memory_access() is false if the target allows jtag memory access while running */ + +bool target_no_background_memory_access(target *cur_target) +{ + /* if error message is 'rtt: read fail at' add target to expression below. + As a first approximation, assume all arm processors allow memory access while running, and no riscv does. */ + bool riscv_core = cur_target && target_core_name(cur_target) && strstr(target_core_name(cur_target), "RVDBG"); + return riscv_core; +} + +/********************************************************************* +* +* rtt top level +* +********************************************************************** +*/ + +void poll_rtt(target *cur_target) +{ + /* rtt off */ + if (!cur_target || !rtt_enabled) + return; + /* target present and rtt enabled */ + uint32_t now = platform_time_ms(); + bool rtt_err = false; + bool rtt_busy = false; + + if (last_poll_ms + poll_ms <= now || now < last_poll_ms) { + target_addr watch; + enum target_halt_reason reason; + bool resume_target = false; + if (!rtt_found) + /* check if target needs to be halted during memory access */ + rtt_halt = target_no_background_memory_access(cur_target); + if (rtt_halt && target_halt_poll(cur_target, &watch) == TARGET_HALT_RUNNING) { + /* briefly halt target during target memory access */ + target_halt_request(cur_target); + while((reason = target_halt_poll(cur_target, &watch)) == TARGET_HALT_RUNNING) + continue; + resume_target = reason == TARGET_HALT_REQUEST; + } + if (!rtt_found) + /* find rtt control block in target memory */ + find_rtt(cur_target); + /* do rtt i/o if control block found */ + if (rtt_found) { + for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) { + rtt_retval v; + if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured) { + if (rtt_channel[i].is_output) + v = print_rtt(cur_target, i); + else + v = read_rtt(cur_target, i); + if (v == RTT_OK) rtt_busy = true; + else if (v == RTT_ERR) rtt_err = true; + } + } + } + /* continue target if halted */ + if (resume_target) + target_halt_resume(cur_target, false); + + /* update last poll time */ + last_poll_ms = now; + + /* rtt polling frequency goes up and down with rtt activity */ + if (rtt_busy && !rtt_err) + poll_ms /= 2; + else + poll_ms *= 2; + + if (poll_ms > rtt_max_poll_ms) + poll_ms = rtt_max_poll_ms; + else if (poll_ms < rtt_min_poll_ms) + poll_ms = rtt_min_poll_ms; + + if (rtt_err) { + gdb_out("rtt: err\r\n"); + poll_errs++; + if (rtt_max_poll_errs != 0 && poll_errs > rtt_max_poll_errs) { + gdb_out("\r\nrtt lost\r\n"); + rtt_enabled = false; + } + } + } + return; +} diff --git a/src/target/adiv5.c b/src/target/adiv5.c index 7d167093a13..dbd8512548c 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -232,6 +232,7 @@ static const struct { {0x975, 0x13, 0x4a13, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 ETM", "(Embedded Trace)")}, {0x9a0, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("CoreSight PMU", "(Performance Monitoring Unit)")}, {0x9a1, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M4 TPIU", "(Trace Port Interface Unit)")}, + {0x9a6, 0x14, 0x1a14, aa_nosupport, cidc_dc, PIDR_PN_BIT_STRINGS("Cortex-M0+ CTI", "(Cross Trigger Interface)")}, {0x9a9, 0x11, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-M7 TPIU", "(Trace Port Interface Unit)")}, {0x9a5, 0x00, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A5 ETM", "(Embedded Trace)")}, {0x9a7, 0x16, 0, aa_nosupport, cidc_unknown, PIDR_PN_BIT_STRINGS("Cortex-A7 PMU", "(Performance Monitor Unit)")}, @@ -294,9 +295,10 @@ static uint32_t adiv5_mem_read32(ADIv5_AP_t *ap, uint32_t addr) static uint32_t adiv5_ap_read_id(ADIv5_AP_t *ap, uint32_t addr) { uint32_t res = 0; + uint8_t data[16]; + adiv5_mem_read(ap, data, addr, sizeof(data)); for (int i = 0; i < 4; i++) { - uint32_t x = adiv5_mem_read32(ap, addr + 4 * i); - res |= (x & 0xff) << (i * 8); + res |= (data[4 * i] << (i * 8)); } return res; } @@ -429,23 +431,6 @@ static bool cortexm_prepare(ADIv5_AP_t *ap) return false; } } - /* Apply device specific settings for successfull Romtable scan - * - * STM32F7 in WFI will not read ROMTABLE when using WFI - */ - if ((ap->dp->targetid >> 1 & 0x7ff) == 0x20) { - uint32_t dbgmcu_cr = 7; - uint32_t dbgmcu_cr_addr = 0xE0042004; - switch ((ap->dp->targetid >> 16) & 0xfff) { - case 0x449: - case 0x451: - case 0x452: - ap->ap_storage = adiv5_mem_read32(ap, dbgmcu_cr_addr); - dbgmcu_cr = ap->ap_storage | 7; - adiv5_mem_write(ap, dbgmcu_cr_addr, &dbgmcu_cr, sizeof(dbgmcu_cr)); - break; - } - } return true; } diff --git a/src/target/adiv5.h b/src/target/adiv5.h index 95c62f62a50..10fc93a882a 100644 --- a/src/target/adiv5.h +++ b/src/target/adiv5.h @@ -89,23 +89,28 @@ #define ADIV5_AP_BASE ADIV5_AP_REG(0xF8) #define ADIV5_AP_IDR ADIV5_AP_REG(0xFC) -/* Known designers seen in SYSROM-PIDR and JTAG IDCode. - * Ignore Bit 0 from the designer bits to get JEDEC Ids. - * Should get it's one file as not only related to Adiv5! - */ +/* Known designers seen in SYSROM-PIDR. Ignore Bit 0 from + * the designer bits to get JEDEC Ids with bit 7 ignored.*/ #define AP_DESIGNER_FREESCALE 0x00e #define AP_DESIGNER_TEXAS 0x017 #define AP_DESIGNER_ATMEL 0x01f #define AP_DESIGNER_STM 0x020 +/* CPU2 for STM32W(L|B) uses ARM JEDEC continuation (4) and + * not STM ARM JEDEC continuation (0) as for CPU1. + * See RM0453 + * https://www.st.com/resource/en/reference_manual/rm0453-stm32wl5x-advanced-armbased-32bit-mcus-with-subghz-radio-solution-stmicroelectronics.pdf : + * 38.8.2 CPU1 ROM CoreSight peripheral identity register 4 (ROM_PIDR4) + * vs + * 38.13.2 CPU2 ROM1 CoreSight peripheral identity register 4 (C2ROM1_PIDR4) + */ +#define AP_DESIGNER_STM32WX 0x420 #define AP_DESIGNER_CYPRESS 0x034 #define AP_DESIGNER_INFINEON 0x041 -#define DESIGNER_XILINX 0x049 #define AP_DESIGNER_NORDIC 0x244 #define AP_DESIGNER_ARM 0x43b /*LPC845 with designer 501. Strange!? */ #define AP_DESIGNER_SPECULAR 0x501 #define AP_DESIGNER_CS 0x555 -#define DESIGNER_XAMBALA 0x61e #define AP_DESIGNER_ENERGY_MICRO 0x673 #define AP_DESIGNER_GIGADEVICE 0x751 #define AP_DESIGNER_RASPBERRY 0x927 @@ -289,7 +294,7 @@ void adiv5_ap_ref(ADIv5_AP_t *ap); void adiv5_ap_unref(ADIv5_AP_t *ap); void platform_add_jtag_dev(const int dev_index, const jtag_dev_t *jtag_dev); -void adiv5_jtag_dp_handler(jtag_dev_t *jd); +void adiv5_jtag_dp_handler(uint8_t jd_index); int platform_jtag_dp_init(ADIv5_DP_t *dp); int swdptap_init(ADIv5_DP_t *dp); diff --git a/src/target/adiv5_jtagdp.c b/src/target/adiv5_jtagdp.c index 5273746136e..cd027d28469 100644 --- a/src/target/adiv5_jtagdp.c +++ b/src/target/adiv5_jtagdp.c @@ -39,7 +39,7 @@ static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp); -void adiv5_jtag_dp_handler(jtag_dev_t *jd) +void adiv5_jtag_dp_handler(uint8_t jd_index) { ADIv5_DP_t *dp = (void*)calloc(1, sizeof(*dp)); if (!dp) { /* calloc failed: heap exhaustion */ @@ -47,8 +47,8 @@ void adiv5_jtag_dp_handler(jtag_dev_t *jd) return; } - dp->dp_jd_index = jd->jd_dev; - dp->idcode = jd->jd_idcode; + dp->dp_jd_index = jd_index; + dp->idcode = jtag_devs[jd_index].jd_idcode; if ((PC_HOSTED == 0 ) || (!platform_jtag_dp_init(dp))) { dp->dp_read = fw_adiv5_jtagdp_read; dp->error = adiv5_jtagdp_error; @@ -85,7 +85,7 @@ uint32_t fw_adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, jtag_dev_write_ir(&jtag_proc, dp->dp_jd_index, APnDP ? IR_APACC : IR_DPACC); - platform_timeout_set(&timeout, 20); + platform_timeout_set(&timeout, 250); do { jtag_dev_shift_dr(&jtag_proc, dp->dp_jd_index, (uint8_t*)&response, (uint8_t*)&request, 35); diff --git a/src/target/adiv5_swdp.c b/src/target/adiv5_swdp.c index d29d02ac13f..527592e5598 100644 --- a/src/target/adiv5_swdp.c +++ b/src/target/adiv5_swdp.c @@ -67,6 +67,7 @@ bool firmware_dp_low_write(ADIv5_DP_t *dp, uint16_t addr, const uint32_t data) */ int adiv5_swdp_scan(uint32_t targetid) { + volatile struct exception e; target_list_free(); ADIv5_DP_t idp = { .dp_low_write = firmware_dp_low_write, @@ -97,7 +98,7 @@ int adiv5_swdp_scan(uint32_t targetid) /* No targetID given on the command line or probe can not * handle multi-drop. Try to read ID */ dp_line_reset(initial_dp); - volatile struct exception e; + TRY_CATCH (e, EXCEPTION_ALL) { idcode = initial_dp->dp_read(initial_dp, ADIV5_DP_IDCODE); } @@ -109,13 +110,13 @@ int adiv5_swdp_scan(uint32_t targetid) initial_dp->seq_out(0xE79E, 16); /* 0b0111100111100111 */ dp_line_reset(initial_dp); initial_dp->fault = 0; - volatile struct exception e2; - TRY_CATCH (e2, EXCEPTION_ALL) { + + TRY_CATCH (e, EXCEPTION_ALL) { idcode = initial_dp->dp_read(initial_dp, ADIV5_DP_IDCODE); } - if (e2.type || initial_dp->fault) { + if (e.type || initial_dp->fault) { DEBUG_WARN("No usable DP found\n"); - return 0; + return -1; } } if ((idcode & ADIV5_DP_VERSION_MASK) == ADIV5_DPv2) { @@ -150,7 +151,6 @@ int adiv5_swdp_scan(uint32_t targetid) dp_targetid = (i << 28) | (target_id & 0x0fffffff); initial_dp->dp_low_write(initial_dp, ADIV5_DP_TARGETSEL, dp_targetid); - volatile struct exception e; TRY_CATCH (e, EXCEPTION_ALL) { idcode = initial_dp->dp_read(initial_dp, ADIV5_DP_IDCODE); } @@ -222,20 +222,21 @@ uint32_t firmware_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, { uint32_t request = make_packet_request(RnW, addr); uint32_t response = 0; - uint32_t ack; + uint32_t ack = SWDP_ACK_WAIT; platform_timeout timeout; - if((addr & ADIV5_APnDP) && dp->fault) return 0; + if ((addr & ADIV5_APnDP) && dp->fault) + return 0; - platform_timeout_set(&timeout, 20); + platform_timeout_set(&timeout, 250); do { dp->seq_out(request, 8); ack = dp->seq_in(3); if (ack == SWDP_ACK_FAULT) { - dp->fault = 1; - return 0; + /* On fault, abort the request and repeat */ + dp->error(dp); } - } while (ack == SWDP_ACK_WAIT && !platform_timeout_is_expired(&timeout)); + } while ((ack == SWDP_ACK_WAIT || ack == SWDP_ACK_FAULT) && !platform_timeout_is_expired(&timeout)); if (ack == SWDP_ACK_WAIT) { dp->abort(dp, ADIV5_DP_ABORT_DAPABORT); @@ -243,15 +244,15 @@ uint32_t firmware_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, return 0; } - if(ack == SWDP_ACK_FAULT) { + if (ack == SWDP_ACK_FAULT) { dp->fault = 1; return 0; } - if(ack != SWDP_ACK_OK) + if (ack != SWDP_ACK_OK) raise_exception(EXCEPTION_ERROR, "SWDP invalid ACK"); - if(RnW) { + if (RnW) { if (dp->seq_in_parity(&response, 32)) { /* Give up on parity error */ dp->fault = 1; raise_exception(EXCEPTION_ERROR, "SWDP Parity error"); @@ -267,7 +268,7 @@ uint32_t firmware_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW, * Implement last option to favour correctness over * slight speed decrease */ - dp->seq_out(0, 8); + dp->seq_out(0, 8); } return response; } diff --git a/src/target/ch32f1.c b/src/target/ch32f1.c new file mode 100644 index 00000000000..ec1c84b7bfb --- /dev/null +++ b/src/target/ch32f1.c @@ -0,0 +1,361 @@ +/* + * 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 implements CH32F1xx target specific functions. + The ch32 flash is rather slow so this code is using the so called fast mode (ch32 specific). + 128 bytes are copied to a write buffer, then the write buffer is committed to flash + /!\ There is some sort of bus stall/bus arbitration going on that does NOT work when + programmed through SWD/jtag + The workaround is to wait a few cycles before filling the write buffer. This is performed by reading the flash a few times + + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "cortexm.h" + +extern const struct command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff + +static int ch32f1_flash_erase(struct target_flash *f, + target_addr addr, size_t len); +static int ch32f1_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len); + +// these are common with stm32f1/gd32f1/... +#define FPEC_BASE 0x40022000 +#define FLASH_ACR (FPEC_BASE + 0x00) +#define FLASH_KEYR (FPEC_BASE + 0x04) +#define FLASH_SR (FPEC_BASE + 0x0C) +#define FLASH_CR (FPEC_BASE + 0x10) +#define FLASH_AR (FPEC_BASE + 0x14) +#define FLASH_CR_LOCK (1 << 7) +#define FLASH_CR_STRT (1 << 6) +#define FLASH_SR_BSY (1 << 0) +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB +#define SR_ERROR_MASK 0x14 +#define SR_EOP 0x20 +#define DBGMCU_IDCODE 0xE0042000 +#define FLASHSIZE 0x1FFFF7E0 + +// these are specific to ch32f1 +#define FLASH_MAGIC (FPEC_BASE + 0x34) +#define FLASH_MODEKEYR_CH32 (FPEC_BASE + 0x24) // Fast mode for CH32F10x +#define FLASH_CR_FLOCK_CH32 (1 << 15) // fast unlock +#define FLASH_CR_FTPG_CH32 (1 << 16) // fast page program +#define FLASH_CR_FTER_CH32 (1 << 17) // fast page erase +#define FLASH_CR_BUF_LOAD_CH32 (1 << 18) // Buffer load +#define FLASH_CR_BUF_RESET_CH32 (1 << 19) // Buffer reset +#define FLASH_SR_EOP (1 << 5) // End of programming +#define FLASH_BEGIN_ADDRESS_CH32 0x8000000 + +/** + \fn ch32f1_add_flash + \brief "fast" flash driver for CH32F10x chips +*/ +static void ch32f1_add_flash(target *t, uint32_t addr, size_t length, size_t erasesize) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + if (!f) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + f->start = addr; + f->length = length; + f->blocksize = erasesize; + f->erase = ch32f1_flash_erase; + f->write = ch32f1_flash_write; + f->buf_size = erasesize; + f->erased = 0xff; + target_add_flash(t, f); +} + +#define WAIT_BUSY() do { \ + sr = target_mem_read32(t, FLASH_SR); \ + if (target_check_error(t)) { \ + DEBUG_WARN("ch32f1 flash write: comm error\n"); \ + return -1; \ + } \ +} while (sr & FLASH_SR_BSY); + +#define WAIT_EOP() do { \ + sr = target_mem_read32(t, FLASH_SR); \ + if (target_check_error(t)) { \ + DEBUG_WARN("ch32f1 flash write: comm error\n"); \ + return -1; \ + } \ +} while (!(sr & FLASH_SR_EOP)); + +#define CLEAR_EOP() target_mem_write32(t, FLASH_SR,FLASH_SR_EOP) + +#define SET_CR(bit) do { \ + const uint32_t cr = target_mem_read32(t, FLASH_CR) | (bit); \ + target_mem_write32(t, FLASH_CR, cr); \ +} while(0) + +#define CLEAR_CR(bit) do { \ + const uint32_t cr = target_mem_read32(t, FLASH_CR) & (~(bit)); \ + target_mem_write32(t, FLASH_CR, cr); \ +} while(0) + +// Which one is the right value ? +#define MAGIC_WORD 0x100 +// #define MAGIC_WORD 0x1000 +#define MAGIC(addr) do { \ + magic = target_mem_read32(t, (addr) ^ MAGIC_WORD); \ + target_mem_write32(t, FLASH_MAGIC , magic); \ +} while(0) + +/** + \fn ch32f1_flash_unlock + \brief unlock ch32f103 in fast mode +*/ +static int ch32f1_flash_unlock(target *t) +{ + DEBUG_INFO("CH32: flash unlock \n"); + + target_mem_write32(t, FLASH_KEYR, KEY1); + target_mem_write32(t, FLASH_KEYR, KEY2); + // fast mode + target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY1); + target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY2); + uint32_t cr = target_mem_read32(t, FLASH_CR); + if (cr & FLASH_CR_FLOCK_CH32) { + DEBUG_WARN("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr); + return -1; + } + return 0; +} + +static int ch32f1_flash_lock(target *t) +{ + DEBUG_INFO("CH32: flash lock \n"); + SET_CR(FLASH_CR_LOCK); + return 0; +} + +/** + \brief identify the ch32f1 chip + Actually grab all cortex m3 with designer = arm not caught earlier... +*/ +bool ch32f1_probe(target *t) +{ + if ((t->cpuid & CPUID_PARTNO_MASK) != CORTEX_M3) + return false; + const uint32_t idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0x00000fffU; + if (idcode != 0x410) // only ch32f103 + return false; + + // try to flock + ch32f1_flash_lock(t); + // if this fails it is not a CH32 chip + if (ch32f1_flash_unlock(t)) + return false; + + t->idcode = idcode; + uint32_t signature = target_mem_read32(t, FLASHSIZE); + uint32_t flashSize = signature & 0xFFFF; + + target_add_ram(t, 0x20000000, 0x5000); + ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024, 128); + target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD"); + t->driver = "CH32F1 medium density (stm32f1 clone)"; + return true; +} + +/** + \fn ch32f1_flash_erase + \brief fast erase of CH32 +*/ +int ch32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len) +{ + volatile uint32_t sr, magic; + target *t = f->t; + DEBUG_INFO("CH32: flash erase \n"); + + if (ch32f1_flash_unlock(t)) { + DEBUG_WARN("CH32: Unlock failed\n"); + return -1; + } + // Fast Erase 128 bytes pages (ch32 mode) + while (len) { + SET_CR(FLASH_CR_FTER_CH32);// CH32 PAGE_ER + /* write address to FMA */ + target_mem_write32(t, FLASH_AR, addr); + /* Flash page erase start instruction */ + SET_CR(FLASH_CR_STRT); + WAIT_EOP(); + CLEAR_EOP(); + CLEAR_CR(FLASH_CR_STRT); + // Magic + MAGIC(addr); + if (len > 128) + len -= 128; + else + len = 0; + addr += 128; + } + sr = target_mem_read32(t, FLASH_SR); + ch32f1_flash_lock(t); + if (sr & SR_ERROR_MASK) { + DEBUG_WARN("ch32f1 flash erase error 0x%" PRIx32 "\n", sr); + return -1; + } + return 0; +} + +/** + \fn ch32f1_wait_flash_ready + \brief Wait a bit for the previous operation to finish + As per test result we need a time similar to 10 read operation over SWD + We do 32 to have a bit of headroom, then we check we read ffff (erased flash) + NB: Just reading fff is not enough as it could be a transient previous operation value +*/ + +static bool ch32f1_wait_flash_ready(target *t, uint32_t addr) +{ + uint32_t ff = 0; + for (size_t i = 0; i < 32; i++) + ff = target_mem_read32(t, addr); + if (ff != 0xffffffffUL) { + DEBUG_WARN("ch32f1 Not erased properly at %" PRIx32 " or flash access issue\n", addr); + return false; + } + return true; +} +/** + \fn ch32f1_flash_write + \brief fast flash for ch32. Load 128 bytes chunk and then flash them +*/ + +static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t offset) +{ + volatile uint32_t sr, magic; + const uint32_t *ss = (const uint32_t *)(src+offset); + uint32_t dd = dest + offset; + + SET_CR(FLASH_CR_FTPG_CH32); + target_mem_write32(t, dd + 0, ss[0]); + target_mem_write32(t, dd + 4, ss[1]); + target_mem_write32(t, dd + 8, ss[2]); + target_mem_write32(t, dd + 12, ss[3]); + SET_CR(FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */ + WAIT_EOP(); + CLEAR_EOP(); + CLEAR_CR(FLASH_CR_FTPG_CH32); + MAGIC(dest + offset); + return 0; +} +/** + \fn ch32f1_buffer_clear + \brief clear the write buffer +*/ +int ch32f1_buffer_clear(target *t) +{ + volatile uint32_t sr; + SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4- + SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5- + WAIT_BUSY(); // 6- + CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4- + return 0; +} +//#define CH32_VERIFY + +/** + +*/ +static int ch32f1_flash_write(struct target_flash *f, + target_addr dest, const void *src, size_t len) +{ + volatile uint32_t sr, magic; + target *t = f->t; + size_t length = len; +#ifdef CH32_VERIFY + target_addr org_dest = dest; + const void *org_src = src; +#endif + DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%zu\n", dest, len); + + while (length > 0) + { + if (ch32f1_flash_unlock(t)) { + DEBUG_WARN("ch32f1 cannot fast unlock\n"); + return -1; + } + WAIT_BUSY(); + + // Buffer reset... + ch32f1_buffer_clear(t); + // Load 128 bytes to buffer + if (!ch32f1_wait_flash_ready(t,dest)) + return -1; + + for (size_t i = 0; i < 8; i++) { + if (ch32f1_upload(t, dest, src, i * 16U)) { + DEBUG_WARN("Cannot upload to buffer\n"); + return -1; + } + } + // write buffer + SET_CR(FLASH_CR_FTPG_CH32); + target_mem_write32(t, FLASH_AR, dest); // 10 + SET_CR(FLASH_CR_STRT); // 11 Start + WAIT_EOP(); // 12 + CLEAR_EOP(); + CLEAR_CR(FLASH_CR_FTPG_CH32); + + MAGIC(dest); + + // next + if (length > 128) + length -=128; + else + length = 0; + dest += 128; + src += 128; + + sr = target_mem_read32(t, FLASH_SR); // 13 + ch32f1_flash_lock(t); + if (sr & SR_ERROR_MASK) { + DEBUG_WARN("ch32f1 flash write error 0x%" PRIx32 "\n", sr); + return -1; + } + } + +#ifdef CH32_VERIFY + DEBUG_INFO("Verifying\n"); + for (size_t i = 0; i < len; i += 4) + { + const uint32_t expected = *(uint32_t *)(org_src + i); + const uint32_t actual = target_mem_read32(t, org_dest + i); + if (expected != actual) + { + DEBUG_WARN(">>>>write mistmatch at address 0x%x\n", org_dest + i); + DEBUG_WARN(">>>>expected: 0x%x\n", expected); + DEBUG_WARN(">>>> actual: 0x%x\n", actual); + return -1; + } + } +#endif + + return 0; +} diff --git a/src/target/cortexa.c b/src/target/cortexa.c index e7ca21153fa..fda909d1380 100644 --- a/src/target/cortexa.c +++ b/src/target/cortexa.c @@ -73,6 +73,8 @@ struct cortexa_priv { uint16_t hw_breakpoint_mask; uint32_t bcr0; uint32_t bvr0; + unsigned hw_watchpoint_max; + uint16_t hw_watchpoint_mask; bool mmu_fault; }; @@ -98,6 +100,8 @@ struct cortexa_priv { #define DBGDSCR_SDABORT_L (1 << 6) #define DBGDSCR_MOE_MASK (0xf << 2) #define DBGDSCR_MOE_HALT_REQ (0x0 << 2) +#define DBGDSCR_MOE_WATCH_ASYNC (0x2 << 2) +#define DBGDSCR_MOE_WATCH_SYNC (0xa << 2) #define DBGDSCR_RESTARTED (1 << 1) #define DBGDSCR_HALTED (1 << 0) @@ -116,6 +120,17 @@ struct cortexa_priv { #define DBGBCR_BAS_HIGH_HW (0xc << 5) #define DBGBCR_EN (1 << 0) +#define DBGWVR(i) (96+(i)) +#define DBGWCR(i) (112+(i)) +#define DBGWCR_LSC_LOAD (0b01 << 3) +#define DBGWCR_LSC_STORE (0b10 << 3) +#define DBGWCR_LSC_ANY (0b11 << 3) +#define DBGWCR_BAS_BYTE (0b0001 << 5) +#define DBGWCR_BAS_HALFWORD (0b0011 << 5) +#define DBGWCR_BAS_WORD (0b1111 << 5) +#define DBGWCR_PAC_ANY (0b11 << 1) +#define DBGWCR_EN (1 << 0) + /* Instruction encodings for accessing the coprocessor interface */ #define MCR 0xee000010 #define MRC 0xee100010 @@ -349,6 +364,7 @@ bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base) adiv5_ap_write(apb, ADIV5_AP_CSW, csw); uint32_t dbgdidr = apb_read(t, DBGDIDR); priv->hw_breakpoint_max = ((dbgdidr >> 24) & 15)+1; + priv->hw_watchpoint_max = ((dbgdidr >> 28) & 15)+1; t->check_error = cortexa_check_error; @@ -608,8 +624,6 @@ static void cortexa_halt_request(target *t) static enum target_halt_reason cortexa_halt_poll(target *t, target_addr *watch) { - (void)watch; /* No watchpoint support yet */ - volatile uint32_t dbgdscr = 0; volatile struct exception e; TRY_CATCH (e, EXCEPTION_ALL) { @@ -636,11 +650,30 @@ static enum target_halt_reason cortexa_halt_poll(target *t, target_addr *watch) apb_write(t, DBGDSCR, dbgdscr); /* Find out why we halted */ - enum target_halt_reason reason; + enum target_halt_reason reason = TARGET_HALT_BREAKPOINT; switch (dbgdscr & DBGDSCR_MOE_MASK) { case DBGDSCR_MOE_HALT_REQ: reason = TARGET_HALT_REQUEST; break; + case DBGDSCR_MOE_WATCH_ASYNC: + case DBGDSCR_MOE_WATCH_SYNC: + /* How do we know which watchpoint was hit? */ + /* If there is only one set, it's that */ + for (struct breakwatch *bw = t->bw_list; bw; bw = bw->next) { + if ((bw->type != TARGET_WATCH_READ) && + (bw->type != TARGET_WATCH_WRITE) && + (bw->type != TARGET_WATCH_ACCESS)) + continue; + if (reason == TARGET_HALT_WATCHPOINT) { + /* More than one watchpoint set, + * we can't tell which triggered. */ + reason = TARGET_HALT_BREAKPOINT; + break; + } + *watch = bw->addr; + reason = TARGET_HALT_WATCHPOINT; + } + break; default: reason = TARGET_HALT_BREAKPOINT; } @@ -752,6 +785,49 @@ static int cortexa_breakwatch_set(target *t, struct breakwatch *bw) } return 0; + + case TARGET_WATCH_WRITE: + case TARGET_WATCH_READ: + case TARGET_WATCH_ACCESS: + for (i = 0; i < priv->hw_watchpoint_max; i++) + if ((priv->hw_watchpoint_mask & (1 << i)) == 0) + break; + + if (i == priv->hw_watchpoint_max) + return -1; + + bw->reserved[0] = i; + priv->hw_watchpoint_mask |= (1 << i); + + { + uint32_t wcr = DBGWCR_PAC_ANY | DBGWCR_EN; + uint32_t bas = 0; + switch(bw->size) { /* Convert bytes size to BAS bits */ + case 1: bas = DBGWCR_BAS_BYTE; break; + case 2: bas = DBGWCR_BAS_HALFWORD; break; + case 4: bas = DBGWCR_BAS_WORD; break; + default: + return -1; + } + /* Apply shift based on address LSBs */ + wcr |= bas << (bw->addr & 3); + + switch (bw->type) { /* Convert gdb type */ + case TARGET_WATCH_WRITE: wcr |= DBGWCR_LSC_STORE; break; + case TARGET_WATCH_READ: wcr |= DBGWCR_LSC_LOAD; break; + case TARGET_WATCH_ACCESS: wcr |= DBGWCR_LSC_ANY; break; + default: + return -1; + } + + apb_write(t, DBGWCR(i), wcr); + apb_write(t, DBGWVR(i), bw->addr & ~3); + DEBUG_INFO("Watchpoint set WCR = 0x%08"PRIx32", WVR = %08"PRIx32"\n", + apb_read(t, DBGWCR(i)), + apb_read(t, DBGWVR(i))); + } + return 0; + default: return 1; } @@ -779,6 +855,12 @@ static int cortexa_breakwatch_clear(target *t, struct breakwatch *bw) if (i == 0) priv->bcr0 = 0; return 0; + case TARGET_WATCH_WRITE: + case TARGET_WATCH_READ: + case TARGET_WATCH_ACCESS: + priv->hw_watchpoint_mask &= ~(1 << i); + apb_write(t, DBGWCR(i), 0); + return 0; default: return 1; } diff --git a/src/target/cortexm.c b/src/target/cortexm.c index 2288063e33b..70f00a8d045 100644 --- a/src/target/cortexm.c +++ b/src/target/cortexm.c @@ -378,8 +378,25 @@ bool cortexm_probe(ADIv5_AP_t *ap) } else { target_check_error(t); } +#if PC_HOSTED +#define STRINGIFY(x) #x #define PROBE(x) \ - do { if ((x)(t)) {return true;} else target_check_error(t); } while (0) + do { \ + DEBUG_INFO("Calling " STRINGIFY(x) "\n"); \ + if ((x)(t)) \ + return true; \ + else \ + target_check_error(t); \ + } while (0) +#else +#define PROBE(x) \ + do { \ + if ((x)(t)) \ + return true; \ + else \ + target_check_error(t); \ + } while (0) +#endif switch (ap->ap_designer) { case AP_DESIGNER_FREESCALE: @@ -395,6 +412,9 @@ bool cortexm_probe(ADIv5_AP_t *ap) case AP_DESIGNER_GIGADEVICE: PROBE(gd32f1_probe); break; + case AP_DESIGNER_STM32WX: + PROBE(stm32l4_probe); + break; case AP_DESIGNER_STM: PROBE(stm32f1_probe); PROBE(stm32f4_probe); @@ -413,6 +433,7 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(nrf51_probe); break; case AP_DESIGNER_ATMEL: + PROBE(samx7x_probe); PROBE(sam4l_probe); PROBE(samd_probe); PROBE(samx5x_probe); @@ -442,7 +463,8 @@ bool cortexm_probe(ADIv5_AP_t *ap) PROBE(rp_probe); PROBE(lpc11xx_probe); /* LPC8 */ } else if (ap->ap_partno == 0x4c3) { /* Cortex-M3 ROM */ - PROBE(stm32f1_probe); /* Care for STM32F1 clones */ + PROBE(ch32f1_probe); + PROBE(stm32f1_probe); /* Care for other STM32F1 clones (?) */ PROBE(lpc15xx_probe); /* Thanks to JojoS for testing */ } else if (ap->ap_partno == 0x471) { /* Cortex-M0 ROM */ PROBE(lpc11xx_probe); /* LPC24C11 */ @@ -501,7 +523,7 @@ bool cortexm_attach(target *t) priv->flash_patch_revision = (r >> 28); priv->hw_watchpoint_max = CORTEXM_MAX_WATCHPOINTS; r = target_mem_read32(t, CORTEXM_DWT_CTRL); - if ((r >> 28) > priv->hw_watchpoint_max) + if ((r >> 28) < priv->hw_watchpoint_max) priv->hw_watchpoint_max = r >> 28; /* Clear any stale breakpoints */ @@ -674,7 +696,7 @@ static int dcrsr_regnum(target *t, unsigned reg) return regnum_cortex_m[reg]; } else if ((t->target_options & TOPT_FLAVOUR_V7MF) && (reg < (sizeof(regnum_cortex_m) + - sizeof(regnum_cortex_mf) / 4))) { + sizeof(regnum_cortex_mf)) / 4)) { return regnum_cortex_mf[reg - sizeof(regnum_cortex_m)/4]; } else { return -1; diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index af55a636ee5..f0ddea43df6 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin + * Copyright (C) 2022 1bitsquared - 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 @@ -25,37 +26,47 @@ jtag_dev_descr_t dev_descr[] = { {.idcode = 0x0BA00477, .idmask = 0x0FFF0FFF, - .descr = "ARM Limited: ADIv5 JTAG-DP port.", + .descr = "ADIv5 JTAG-DP port.", .handler = adiv5_jtag_dp_handler}, + {.idcode = 0x00000477, .idmask = 0x00000FFF, + .descr = "Unknown ARM."}, {.idcode = 0x06410041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, Medium density."}, + .descr = "STM32, Medium density."}, {.idcode = 0x06412041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, Low density."}, + .descr = "STM32, Low density."}, {.idcode = 0x06414041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, High density."}, + .descr = "STM32, High density."}, {.idcode = 0x06416041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32L."}, + .descr = "STM32L."}, {.idcode = 0x06418041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, Connectivity Line."}, + .descr = "STM32, Connectivity Line."}, {.idcode = 0x06420041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, Value Line."}, + .descr = "STM32, Value Line."}, {.idcode = 0x06428041, .idmask = 0x0FFFFFFF, - .descr = "ST Microelectronics: STM32, Value Line, High density."}, + .descr = "STM32, Value Line, High density."}, {.idcode = 0x06411041, .idmask = 0xFFFFFFFF, - .descr = "ST Microelectronics: STM32F2xx."}, + .descr = "STM32F2xx."}, {.idcode = 0x06422041, .idmask = 0xFFFFFFFF, - .descr = "ST Microelectronics: STM32F3xx."}, - {.idcode = 0x06413041 , .idmask = 0xFFFFFFFF, - .descr = "ST Microelectronics: STM32F4xx."}, - {.idcode = 0x0BB11477 , .idmask = 0xFFFFFFFF, + .descr = "STM32F3xx."}, + {.idcode = 0x06413041, .idmask = 0xFFFFFFFF, + .descr = "STM32F4xx."}, + {.idcode = 0x00000041, .idmask = 0x00000FFF, + .descr = "STM32 BSD."}, + {.idcode = 0x0BB11477, .idmask = 0xFFFFFFFF, .descr = "NPX: LPC11C24."}, - {.idcode = 0x4BA00477 , .idmask = 0xFFFFFFFF, + {.idcode = 0x4BA00477, .idmask = 0xFFFFFFFF, .descr = "NXP: LPC17xx family."}, + {.idcode = 0x00000093, .idmask = 0x00000FFF, + .descr = "Xilinx."}, + {.idcode = 0x0000063D, .idmask = 0x00000FFF, + .descr = "Xambala: RVDBG013."}, + {.idcode = 0x000007A3, .idmask = 0x00000FFF, + .descr = "Gigadevice BSD."}, /* Just for fun, unsupported */ - {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: ATMega16."}, - {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "ATMEL: AT91SAM9261."}, - {.idcode = 0x20270013, .idmask = 0xFFFFFFFF, .descr = "Intel: i80386ex."}, - {.idcode = 0x07B7617F, .idmask = 0xFFFFFFFF, .descr = "Broadcom: BCM2835."}, - {.idcode = 0x4BA00477, .idmask = 0xFFFFFFFF, .descr = "Broadcom: BCM2836."}, + {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMega16."}, + {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "AT91SAM9261."}, + {.idcode = 0x20270013, .idmask = 0xFFFFFFFF, .descr = "i80386ex."}, + {.idcode = 0x07B7617F, .idmask = 0xFFFFFFFF, .descr = "BCM2835."}, + {.idcode = 0x4BA00477, .idmask = 0xFFFFFFFF, .descr = "BCM2836."}, {.idcode = 0, .idmask = 0, .descr = "Unknown"}, }; diff --git a/src/target/jtag_devs.h b/src/target/jtag_devs.h index 8f8779851e1..08adf0474bb 100644 --- a/src/target/jtag_devs.h +++ b/src/target/jtag_devs.h @@ -22,7 +22,7 @@ typedef const struct jtag_dev_descr_s { const uint32_t idcode; const uint32_t idmask; const char * const descr; - void (*const handler)(jtag_dev_t *jd); + void (*const handler)(uint8_t jd_index); } jtag_dev_descr_t; extern jtag_dev_descr_t dev_descr[]; diff --git a/src/target/jtag_scan.c b/src/target/jtag_scan.c index eb1efc80310..4addc4921e3 100644 --- a/src/target/jtag_scan.c +++ b/src/target/jtag_scan.c @@ -3,7 +3,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) 2022 1bitsquared - 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 @@ -26,14 +26,16 @@ #include "general.h" #include "jtagtap.h" +#include "jtag_scan.h" #include "target.h" #include "adiv5.h" +#include "jtag_devs.h" -jtag_dev_t jtag_devs[JTAG_MAX_DEVS+1]; +struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1]; int jtag_dev_count; /* bucket of ones for don't care TDI */ -static const uint8_t ones[] = {0xff, 0xFF, 0xFF, 0xFF}; +static const uint8_t ones[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #if PC_HOSTED == 0 void jtag_add_device(const int dev_index, const jtag_dev_t *jtag_dev) @@ -46,32 +48,36 @@ void jtag_add_device(const int dev_index, const jtag_dev_t *jtag_dev) #endif /* Scan JTAG chain for devices, store IR length and IDCODE (if present). + * Reset TAP state machine. + * Select Shift-IR state. + * Each device is assumed to shift out IR at 0x01. (this may not always be true) + * Shift in ones until we read two consecutive ones, then we have shifted out the + * IRs of all devices. * - * https://www.fpga4fun.com/JTAG3.html - * Count the number of devices in the JTAG chain - * - * shift enough ones in IR - * shift enough zeros in DR - * Now shift out ones and stop if first '1' is seen. This gets the number - * of devices - * - * Assume max 32 devices with max IR len 16 = 512 bits = 16 loops * 32 bit + * After this process all the IRs are loaded with the BYPASS command. + * Select Shift-DR state. + * Shift in ones and count zeros shifted out. Should be one for each device. + * Check this against device count obtained by IR scan above. * * Reset the TAP state machine again. This should load all IRs with IDCODE. - * Read 32 bit IDCODE for all devices. - */ - + * For each device, shift out one bit. If this is zero IDCODE isn't present, + * continue to next device. If this is one shift out the remaining 31 bits + * of the IDCODE register. + */ int jtag_scan(const uint8_t *irlens) { int i; - void (*jd_handlers[JTAG_MAX_DEVS])(jtag_dev_t *jd); + uint32_t j; + target_list_free(); - memset(jd_handlers, 0, sizeof(jd_handlers)); + jtag_dev_count = 0; + memset(&jtag_devs, 0, sizeof(jtag_devs)); - /* Run throught the SWD to JTAG sequence for the case where an - * attached SWJ-DP is in SW-DP mode. + /* Run throught the SWD to JTAG sequence for the case where an attached SWJ-DP is + * in SW-DP mode. */ + DEBUG_INFO("Resetting TAP\n"); #if PC_HOSTED == 1 if (platform_jtagtap_init()) { DEBUG_WARN("JTAG not available\n"); @@ -81,146 +87,139 @@ int jtag_scan(const uint8_t *irlens) jtagtap_init(); #endif jtag_proc.jtagtap_reset(); -#define LOOPS 16 - jtagtap_shift_ir(); - i = LOOPS; - uint8_t ir_chain[64], *din = ir_chain; - while (i--) { - jtag_proc.jtagtap_tdi_tdo_seq(din, (i == 0) ? 1 : 0, ones, - sizeof(ones) * 8); - din += sizeof(ones); - } - if (!(ir_chain[0] & 1)) { - DEBUG_WARN("Unexpected IR chain!\n"); - return 0; - } - jtagtap_return_idle(); - jtagtap_shift_dr(); - i = LOOPS; - uint8_t zeros[] = {0, 0, 0, 0}; - while(i--) { - jtag_proc.jtagtap_tdi_seq(0, zeros, sizeof(zeros) * 8); - } - int num_devices = 0; - while (!jtag_proc.jtagtap_next(0,1) && (i++ < 6)) - num_devices++; - jtag_proc.jtagtap_reset(); - jtagtap_shift_dr(); - jtag_dev_count = num_devices; - if (!num_devices) - return 0; - DEBUG_TARGET("Found %d devices\n", num_devices); - int irbit = 1; - int j = 0; - for (i = 0; i < num_devices; i++) { - uint8_t id[4]; - jtag_proc.jtagtap_tdi_tdo_seq(id, 0, ones, 32); - if (!(id[0] & 1)) { - DEBUG_WARN("Invalid IDCode!\n"); - return 0; - } - uint32_t idcode = id[3] << 24 | id[2] << 16 | id[1] << 8 | id[0]; - unsigned int designer = ((id[1] & 0xf) << 8) | (id[0] >> 1); - unsigned int product = id[2] | ((id[3] & 0xf) << 8); - unsigned int expected_irlen = 0; - switch (designer) { - case AP_DESIGNER_ARM: - switch (product) { - case 0xba0: - jtag_devs[i].jd_descr = "ADIv5 JTAG-DP port"; - jd_handlers[i] = adiv5_jtag_dp_handler; - expected_irlen = 4; - break; - default: - jtag_devs[i].jd_descr = "ARM"; - } - break; - case AP_DESIGNER_STM: - expected_irlen = 5; - jtag_devs[i].jd_descr = "STM32 BSD"; - break; - case AP_DESIGNER_ATMEL: - if ((product >= 0x940) & (product < 0x990)) { - jtag_devs[i].jd_descr = "ATMEL AVR8"; - expected_irlen = 4; + + if (irlens) { + DEBUG_WARN("Given list of IR lengths, skipping probe\n"); + DEBUG_INFO("Change state to Shift-IR\n"); + jtagtap_shift_ir(); + j = 0; + while((jtag_dev_count <= JTAG_MAX_DEVS) && + (jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) { + uint32_t irout; + if(*irlens == 0) break; + jtag_proc.jtagtap_tdi_tdo_seq((uint8_t*)&irout, 0, ones, *irlens); + if (!(irout & 1)) { + DEBUG_WARN("check failed: IR[0] != 1\n"); + return -1; } - jtag_devs[i].jd_descr = "ATMEL"; - break; - case DESIGNER_XILINX: - if (!irlens) { - /* Guessed irlen for XILINX devices is wrong. - * IR data contains status bits! - */ - DEBUG_WARN("Please provide irlens as chain contains XILINX devices!\n"); - return 0; - } - jtag_devs[i].jd_descr = "XILINX"; - break; - case DESIGNER_XAMBALA: - expected_irlen = 5; - jtag_devs[i].jd_descr = "RVDBG013"; - break; - case AP_DESIGNER_GIGADEVICE: - expected_irlen = 5; - jtag_devs[i].jd_descr = "GIGADEVICE BSD"; - break; + jtag_devs[jtag_dev_count].ir_len = *irlens; + jtag_devs[jtag_dev_count].ir_prescan = j; + jtag_devs[jtag_dev_count].jd_dev = jtag_dev_count; + j += *irlens; + irlens++; + jtag_dev_count++; } - if (!jtag_devs[i].jd_descr) { - DEBUG_WARN("Unhandled designer %x\n", designer); - jtag_devs[i].jd_descr = "Unknow"; + } else { + DEBUG_INFO("Change state to Shift-IR\n"); + jtagtap_shift_ir(); + + DEBUG_INFO("Scanning out IRs\n"); + if(!jtag_proc.jtagtap_next(0, 1)) { + DEBUG_WARN("jtag_scan: Sanity check failed: IR[0] shifted out " + "as 0\n"); + jtag_dev_count = -1; + return -1; /* must be 1 */ } - bool bit; - int guessed_irlen = 0; - int advance = irbit; - do { - /* Guess IR length from the IR scan after JTAG Reset - * First bit should be '1', following bits are '0', if not used - * for instruction capture, as for Xilinx parts. - */ - bit = (ir_chain[advance / 8] & (1 << (advance & 7))); - guessed_irlen++; - advance++; - } while (!bit && (advance < (JTAG_MAX_DEVS * 16))); - if (irlens) { /* Allow to overwrite from the command line!*/ - if (*irlens != guessed_irlen) { - DEBUG_TARGET("Provides irlen %d vs guessed %d for device %d\n", - *irlens, guessed_irlen, i + 1); - } - expected_irlen = *irlens++; + jtag_devs[0].ir_len = 1; j = 1; + while((jtag_dev_count <= JTAG_MAX_DEVS) && + (jtag_devs[jtag_dev_count].ir_len <= JTAG_MAX_IR_LEN)) { + if(jtag_proc.jtagtap_next(0, 1)) { + if(jtag_devs[jtag_dev_count].ir_len == 1) break; + jtag_devs[++jtag_dev_count].ir_len = 1; + jtag_devs[jtag_dev_count].ir_prescan = j; + jtag_devs[jtag_dev_count].jd_dev = jtag_dev_count; + } else jtag_devs[jtag_dev_count].ir_len++; + j++; + } + if(jtag_dev_count > JTAG_MAX_DEVS) { + DEBUG_WARN("jtag_scan: Maximum device count exceeded\n"); + jtag_dev_count = -1; + return -1; } - if (!expected_irlen) { - expected_irlen = guessed_irlen++; + if(jtag_devs[jtag_dev_count].ir_len > JTAG_MAX_IR_LEN) { + DEBUG_WARN("jtag_scan: Maximum IR length exceeded\n"); + jtag_dev_count = -1; + return -1; } - jtag_devs[i].ir_len = expected_irlen; - jtag_devs[i].ir_prescan = j; - jtag_devs[i].jd_dev = i; - jtag_devs[i].jd_idcode = idcode; + } + + DEBUG_INFO("Return to Run-Test/Idle\n"); + jtag_proc.jtagtap_next(1, 1); + jtagtap_return_idle(); + + /* All devices should be in BYPASS now */ + + /* Count device on chain */ + DEBUG_INFO("Change state to Shift-DR\n"); + jtagtap_shift_dr(); + for(i = 0; (jtag_proc.jtagtap_next(0, 1) == 0) && (i <= jtag_dev_count); i++) jtag_devs[i].dr_postscan = jtag_dev_count - i - 1; - jtag_devs[i].current_ir = -1; - j += expected_irlen; - irbit += expected_irlen; - DEBUG_INFO("%2d: IDCODE: 0x%08" PRIx32 ", IR len %d %s%s\n", i + 1, - idcode,jtag_devs[i].ir_len, jtag_devs[i].jd_descr, - (jd_handlers[i]) ? "" : " (Unhandled) "); + + if(i != jtag_dev_count) { + DEBUG_WARN("jtag_scan: Sanity check failed: " + "BYPASS dev count doesn't match IR scan\n"); + jtag_dev_count = -1; + return -1; } - jtag_proc.jtagtap_reset(); + + DEBUG_INFO("Return to Run-Test/Idle\n"); + jtag_proc.jtagtap_next(1, 1); + jtagtap_return_idle(); + if(!jtag_dev_count) { + return 0; + } + /* Fill in the ir_postscan fields */ - for(i = jtag_dev_count - 1; i; i--) { + for(i = jtag_dev_count - 1; i; i--) jtag_devs[i-1].ir_postscan = jtag_devs[i].ir_postscan + jtag_devs[i].ir_len; + + /* Reset jtagtap: should take all devs to IDCODE */ + jtag_proc.jtagtap_reset(); + jtagtap_shift_dr(); + for(i = 0; i < jtag_dev_count; i++) { + if(!jtag_proc.jtagtap_next(0, 1)) continue; + jtag_devs[i].jd_idcode = 1; + for(j = 2; j; j <<= 1) + if(jtag_proc.jtagtap_next(0, 1)) jtag_devs[i].jd_idcode |= j; + } + DEBUG_INFO("Return to Run-Test/Idle\n"); + jtag_proc.jtagtap_next(1, 1); + jtagtap_return_idle(); #if PC_HOSTED == 1 /*Transfer needed device information to firmware jtag_devs*/ - for(i = 0; i < jtag_dev_count; i++) { + for(i = 0; i < jtag_dev_count; i++) platform_add_jtag_dev(i, &jtag_devs[i]); + for(i = 0; i < jtag_dev_count; i++) { + DEBUG_INFO("Idcode 0x%08" PRIx32, jtag_devs[i].jd_idcode); + for(j = 0; dev_descr[j].idcode; j++) { + if((jtag_devs[i].jd_idcode & dev_descr[j].idmask) == + dev_descr[j].idcode) { + DEBUG_INFO(": %s", + (dev_descr[j].descr) ? dev_descr[j].descr : "unknown"); + break; + } + } + DEBUG_INFO("\n"); } #endif + /* Check for known devices and handle accordingly */ for(i = 0; i < jtag_dev_count; i++) - /* Call handler to initialise/probe device further */ - if (jd_handlers[i]) - jd_handlers[i](&jtag_devs[i]); + for(j = 0; dev_descr[j].idcode; j++) + if((jtag_devs[i].jd_idcode & dev_descr[j].idmask) == + dev_descr[j].idcode) { + jtag_devs[i].current_ir = -1; + /* Save description in table */ + jtag_devs[i].jd_descr = dev_descr[j].descr; + /* Call handler to initialise/probe device further */ + if(dev_descr[j].handler) + dev_descr[j].handler(i); + break; + } + return jtag_dev_count; } diff --git a/src/target/jtag_scan.h b/src/target/jtag_scan.h index bc76e0bc699..cc6d361a2c2 100644 --- a/src/target/jtag_scan.h +++ b/src/target/jtag_scan.h @@ -40,7 +40,7 @@ typedef struct jtag_dev_s { uint32_t current_ir; } jtag_dev_t; -extern jtag_dev_t jtag_devs[JTAG_MAX_DEVS+1]; +extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1]; extern int jtag_dev_count; void jtag_dev_write_ir(jtag_proc_t *jp, uint8_t jd_index, uint32_t ir); diff --git a/src/target/kinetis.c b/src/target/kinetis.c index 7ac9ffa5e04..5e44bfe8f14 100644 --- a/src/target/kinetis.c +++ b/src/target/kinetis.c @@ -373,7 +373,7 @@ bool kinetis_probe(target *t) } static bool -kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]) +kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint32_t *data, int n_items) { uint8_t fstat; @@ -390,8 +390,9 @@ kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]) addr |= (uint32_t)cmd << 24; target_mem_write32(t, FTFA_FCCOB_0, addr); if (data) { - target_mem_write32(t, FTFA_FCCOB_1, *(uint32_t*)&data[0]); - target_mem_write32(t, FTFA_FCCOB_2, *(uint32_t*)&data[4]); + target_mem_write32(t, FTFA_FCCOB_1, data[0]); + if (n_items > 1) + target_mem_write32(t, FTFA_FCCOB_2, data[1]); } /* Enable execution by clearing CCIF */ @@ -411,7 +412,7 @@ kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]) static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len) { while (len) { - if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL)) { + if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL, 0)) { /* Different targets have different flash erase sizes */ if (len > f->blocksize) len -= f->blocksize; @@ -450,7 +451,7 @@ static int kl_gen_flash_write(struct target_flash *f, } while (len) { - if (kl_gen_command(f->t, write_cmd, dest, src)) { + if (kl_gen_command(f->t, write_cmd, dest, src, 1)) { if (len > kf->write_len) len -= kf->write_len; else @@ -484,12 +485,13 @@ static int kl_gen_flash_done(struct target_flash *f) vals[1] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS); vals[1] = (vals[1] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED; kl_gen_command(f->t, FTFE_CMD_PROGRAM_PHRASE, - FLASH_SECURITY_BYTE_ADDRESS - 4, (uint8_t*)vals); + FLASH_SECURITY_BYTE_ADDRESS - 4, vals, 2); } else { - uint32_t vals[2] = {target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS), 0}; + uint32_t vals[1]; + vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS); vals[0] = (vals[0] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED; kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD, - FLASH_SECURITY_BYTE_ADDRESS, (uint8_t*)&vals); + FLASH_SECURITY_BYTE_ADDRESS, vals, 1); } return 0; diff --git a/src/target/rp.c b/src/target/rp.c index 62b2a146d32..aca31ae30cf 100644 --- a/src/target/rp.c +++ b/src/target/rp.c @@ -33,7 +33,7 @@ */ /* This file implements Raspberry Pico (RP2040) target specific functions - * for detecting the device, providing the XML memory map but not yet + * for detecting the device, providing the XML memory map and * Flash memory programming. */ @@ -43,10 +43,33 @@ #include "cortexm.h" #define RP_ID "Raspberry RP2040" +#define RP_MAX_TABLE_SIZE 0x80 #define BOOTROM_MAGIC ('M' | ('u' << 8) | (0x01 << 16)) #define BOOTROM_MAGIC_ADDR 0x00000010 #define XIP_FLASH_START 0x10000000 #define SRAM_START 0x20000000 +#define SRAM_SIZE 0x42000 +#define SSI_DR0_ADDR 0x18000060 +#define QSPI_CTRL_ADDR 0x4001800c + +#define FLASHSIZE_4K_SECTOR (4 * 1024) +#define FLASHSIZE_32K_BLOCK (32 * 1024) +#define FLASHSIZE_64K_BLOCK (64 * 1024) +#define MAX_FLASH (16 * 1024 * 1024) + +/* Instruction codes taken from Winbond W25Q16JV datasheet, as used on the + * original Pico board from Raspberry Pi. + * https://www.winbond.com/resource-files/w25q16jv%20spi%20revd%2008122016.pdf + * All dev boards supported by Pico SDK V1.3.1 use SPI flash chips which support + * these commands. Other custom boards using different SPI flash chips might + * not support these commands + */ + +#define FLASHCMD_SECTOR_ERASE 0x20 +#define FLASHCMD_BLOCK32K_ERASE 0x52 +#define FLASHCMD_BLOCK64K_ERASE 0xd8 +#define FLASHCMD_CHIP_ERASE 0x60 +#define FLASHCMD_READ_JEDEC_ID 0x9F struct rp_priv_s { uint16_t _debug_trampoline; @@ -197,59 +220,50 @@ static void rp_flash_resume(target *t) static int rp_flash_erase(struct target_flash *f, target_addr addr, size_t len) { - if (addr & 0xfff) { + DEBUG_INFO("Erase addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, (uint32_t)len); + target *t = f->t; + if (addr & (FLASHSIZE_4K_SECTOR - 1)) { DEBUG_WARN("Unaligned erase\n"); return -1; } - if (len & 0xfff) { - DEBUG_WARN("Unaligned len\n"); - len = (len + 0xfff) & ~0xfff; + if ((addr < t->flash->start) || (addr >= t->flash->start + t->flash->length)) { + DEBUG_WARN("Address is invalid\n"); + return -1; } - DEBUG_INFO("Erase addr %08" PRIx32 " len 0x%" PRIx32 "\n", addr, (uint32_t)len); - target *t = f->t; - rp_flash_prepare(t); + addr -= t->flash->start; + len = ALIGN(len, FLASHSIZE_4K_SECTOR); + len = MIN(len, t->flash->length - addr); struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; - /* Register playground*/ /* erase */ -#define MAX_FLASH (2 * 1024 * 1024) -#define FLASHCMD_SECTOR_ERASE 0x20 -#define FLASHCMD_BLOCK32K_ERASE 0x52 -#define FLASHCMD_BLOCK64K_ERASE 0xd8 -#define FLASHCMD_CHIP_ERASE 0x72 - addr -= XIP_FLASH_START; - if (len > MAX_FLASH) - len = MAX_FLASH; + rp_flash_prepare(t); bool ret = 0; while (len) { - ps->regs[0] = addr; - ps->regs[2] = -1; - if (len >= MAX_FLASH) { /* Bulk erase */ - ps->regs[1] = MAX_FLASH; - ps->regs[3] = FLASHCMD_CHIP_ERASE; - DEBUG_WARN("BULK_ERASE\n"); - ret = rp_rom_call(t, ps->regs, ps->_flash_range_erase, 25100); - len = 0; - } else if (len >= (64 * 1024)) { - uint32_t chunk = len & ~((1 << 16) - 1); + if (len >= FLASHSIZE_64K_BLOCK) { + uint32_t chunk = len & ~(FLASHSIZE_64K_BLOCK - 1); + ps->regs[0] = addr; ps->regs[1] = chunk; + ps->regs[2] = FLASHSIZE_64K_BLOCK; ps->regs[3] = FLASHCMD_BLOCK64K_ERASE; - DEBUG_WARN("64k_ERASE\n"); - ret = rp_rom_call(t, ps->regs, ps->_flash_range_erase, 2100); + DEBUG_WARN("64k_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, chunk); + ret = rp_rom_call(t, ps->regs, ps->_flash_range_erase, 25100); len -= chunk ; addr += chunk; - } else if (len >= (32 * 1024)) { - uint32_t chunk = len & ~((1 << 15) - 1); + } else if (len >= FLASHSIZE_32K_BLOCK) { + uint32_t chunk = len & ~(FLASHSIZE_32K_BLOCK - 1); + ps->regs[0] = addr; ps->regs[1] = chunk; + ps->regs[2] = FLASHSIZE_32K_BLOCK; ps->regs[3] = FLASHCMD_BLOCK32K_ERASE; - DEBUG_WARN("32k_ERASE\n"); + DEBUG_WARN("32k_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, chunk); ret = rp_rom_call(t, ps->regs, ps->_flash_range_erase, 1700); len -= chunk; addr += chunk; } else { + ps->regs[0] = addr; ps->regs[1] = len; - ps->regs[2] = MAX_FLASH; + ps->regs[2] = FLASHSIZE_4K_SECTOR; ps->regs[3] = FLASHCMD_SECTOR_ERASE; - DEBUG_WARN("Sector_ERASE\n"); + DEBUG_WARN("Sector_ERASE addr 0x%08" PRIx32 " len 0x%" PRIx32 "\n", addr, (uint32_t)len); ret = rp_rom_call(t, ps->regs, ps->_flash_range_erase, 410); len = 0; } @@ -266,16 +280,16 @@ static int rp_flash_erase(struct target_flash *f, target_addr addr, static int rp_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) { - DEBUG_INFO("RP Write %08" PRIx32 " len 0x%" PRIx32 "\n", dest, (uint32_t)len); + DEBUG_INFO("RP Write 0x%08" PRIx32 " len 0x%" PRIx32 "\n", dest, (uint32_t)len); + target *t = f->t; if ((dest & 0xff) || (len & 0xff)) { - DEBUG_WARN("Unaligned erase\n"); + DEBUG_WARN("Unaligned write\n"); return -1; } - target *t = f->t; - rp_flash_prepare(t); + dest -= t->flash->start; struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; /* Write payload to target ram */ - dest -= XIP_FLASH_START; + rp_flash_prepare(t); bool ret = 0; #define MAX_WRITE_CHUNK 0x1000 while (len) { @@ -327,13 +341,37 @@ static bool rp_cmd_erase_mass(target *t, int argc, const char *argv[]) f.t = t; struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; ps->is_monitor = true; - bool res = (rp_flash_erase(&f, XIP_FLASH_START, MAX_FLASH)) ? false: true; + bool res = (rp_flash_erase(&f, t->flash->start, t->flash->length)) ? false: true; + ps->is_monitor = false; + return res; +} + +static bool rp_cmd_erase_sector(target *t, int argc, const char *argv[]) +{ + uint32_t length = t->flash->length; + uint32_t start = t->flash->start; + + if (argc == 3) { + start = strtoul(argv[1], NULL, 0); + length = strtoul(argv[2], NULL, 0); + } + else if (argc == 2) + length = strtoul(argv[1], NULL, 0); + else + return -1; + + struct target_flash f; + f.t = t; + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + ps->is_monitor = true; + bool res = (rp_flash_erase(&f, start, length)) ? false: true; ps->is_monitor = false; return res; } const struct command_s rp_cmd_list[] = { {"erase_mass", rp_cmd_erase_mass, "Erase entire flash memory"}, + {"erase_sector", rp_cmd_erase_sector, "Erase a sector: [start address] length" }, {"reset_usb_boot", rp_cmd_reset_usb_boot, "Reboot the device into BOOTSEL mode"}, {NULL, NULL, NULL} }; @@ -351,65 +389,140 @@ static void rp_add_flash(target *t, uint32_t addr, size_t length) f->blocksize = 0x1000; f->erase = rp_flash_erase; f->write = rp_flash_write; - f->buf_size = 2048; /* Max buffer size used eotherwise */ + f->buf_size = 2048; /* Max buffer size used otherwise */ + f->erased = 0xFF; target_add_flash(t, f); } +void rp_ssel_active(target *t, bool active) +{ + const uint32_t qspi_ctrl_outover_low = 2UL << 8; + const uint32_t qspi_ctrl_outover_high = 3UL << 8; + uint32_t state = (active) ? qspi_ctrl_outover_low : qspi_ctrl_outover_high; + uint32_t val = target_mem_read32(t, QSPI_CTRL_ADDR); + val = (val & ~qspi_ctrl_outover_high) | state; + target_mem_write32(t, QSPI_CTRL_ADDR, val); +} + +uint32_t rp_read_flash_chip(target *t, uint32_t cmd) +{ + uint32_t value = 0; + + rp_ssel_active(t, true); + + /* write command into SPI peripheral's FIFO */ + for (size_t i = 0; i < 4; i++) + target_mem_write32(t, SSI_DR0_ADDR, cmd); + + /* now we have an entry in the receive FIFO for each write */ + for (size_t i = 0; i < 4; i++) { + uint32_t status = target_mem_read32(t, SSI_DR0_ADDR); + value |= (status & 0xFF) << 24; + value >>= 8; + } + + rp_ssel_active(t, false); + + return value; +} + +uint32_t rp_get_flash_length(target *t) +{ + uint32_t size = MAX_FLASH; + uint32_t bootsec[16]; + size_t i; + + target_mem_read(t, bootsec, XIP_FLASH_START, sizeof(bootsec)); + for (i = 0; i < 16; i++) { + if ((bootsec[i] != 0x00) && (bootsec[i] != 0xff)) + break; + } + + if (i < 16) { + // We have some data (hopefully a valid program) stored in the start + // of the flash memory. We can check if the start of this data is + // mirrored anywhere else in the flash as the flash region will repeat + // when we try to read out of bounds. + uint32_t mirrorsec[16]; + while (size > FLASHSIZE_4K_SECTOR) { + target_mem_read(t, mirrorsec, XIP_FLASH_START + size, sizeof(bootsec)); + if (memcmp(bootsec, mirrorsec, sizeof(bootsec))) + return size << 1; + size >>= 1; + } + } + + // That approach didn't work. Most likely because there was no data found in + // at the start of the flash memory. If we have no valid program it's ok to + // interrupt the flash execution to check the JEDEC ID of the flash chip. + size = MAX_FLASH; + + rp_flash_prepare(t); + uint32_t flash_id = rp_read_flash_chip(t, FLASHCMD_READ_JEDEC_ID); + rp_flash_resume(t); + + DEBUG_INFO("Flash device ID: %08" PRIx32 "\n", flash_id); + + uint8_t size_log2 = (flash_id & 0xff0000) >> 16; + if (size_log2 >= 8 || size_log2 <= 34) + size = 1 << size_log2; + + return size; +} + +static bool rp_attach(target *t) +{ + if (!cortexm_attach(t)) + return false; + + struct rp_priv_s *ps = (struct rp_priv_s*)t->target_storage; + uint16_t *table = alloca(RP_MAX_TABLE_SIZE); + uint16_t table_offset = target_mem_read32( t, BOOTROM_MAGIC_ADDR + 4); + if (!table || target_mem_read(t, table, table_offset, RP_MAX_TABLE_SIZE)) + return false; + if (rp2040_fill_table(ps, table, RP_MAX_TABLE_SIZE)) + return false; + + /* Free previously loaded memory map */ + target_mem_map_free(t); + + size_t size = rp_get_flash_length(t); + DEBUG_INFO("Flash size: %zu MB\n", size / (1024U * 1024U)); + + rp_add_flash(t, XIP_FLASH_START, size); + target_add_ram(t, SRAM_START, SRAM_SIZE); + + return true; +} + +static void rp_detach(target *t) +{ + cortexm_detach(t); +} + bool rp_probe(target *t) { /* Check bootrom magic*/ uint32_t boot_magic = target_mem_read32(t, BOOTROM_MAGIC_ADDR); if ((boot_magic & 0x00ffffff) != BOOTROM_MAGIC) { - DEBUG_WARN("Wrong Bootmagic %08" PRIx32 " found\n!", boot_magic); + DEBUG_WARN("Wrong Bootmagic %08" PRIx32 " found!\n", boot_magic); return false; } #if defined(ENABLE_DEBUG) if ((boot_magic >> 24) == 1) DEBUG_WARN("Old Bootrom Version 1!\n"); #endif -#define RP_MAX_TABLE_SIZE 0x80 - uint16_t *table = alloca(RP_MAX_TABLE_SIZE); - uint16_t table_offset = target_mem_read32( t, BOOTROM_MAGIC_ADDR + 4); - if (!table || target_mem_read(t, table, table_offset, RP_MAX_TABLE_SIZE)) - return false; struct rp_priv_s *priv_storage = calloc(1, sizeof(struct rp_priv_s)); if (!priv_storage) { /* calloc failed: heap exhaustion */ DEBUG_WARN("calloc: failed in %s\n", __func__); return false; } - if (rp2040_fill_table(priv_storage, table, RP_MAX_TABLE_SIZE)) { - free(priv_storage); - return false; - } t->target_storage = (void*)priv_storage; - uint32_t bootsec[16]; - target_mem_read( t, bootsec, XIP_FLASH_START, sizeof( bootsec)); - int i; - for (i = 0; i < 16; i++) - if (bootsec[i]) - break; - uint32_t size = 8 * 1024 *1024; - if (i == 16) { - DEBUG_WARN("Use default size\n"); - } else { - /* Find out size of connected SPI Flash - * - * Flash needs valid content to be mapped - * Low flash is mirrored when flash size is exceeded - */ - while (size) { - uint32_t mirrorsec[16]; - target_mem_read(t, mirrorsec, XIP_FLASH_START + size, - sizeof( bootsec)); - if (memcmp(bootsec, mirrorsec, sizeof( bootsec))) - break; - size >>= 1; - } - } - rp_add_flash(t, XIP_FLASH_START, size << 1); + t->driver = RP_ID; - target_add_ram(t, SRAM_START, 0x40000); - target_add_ram(t, 0x51000000, 0x1000); + t->target_options |= CORTEXM_TOPT_INHIBIT_SRST; + t->attach = rp_attach; + t->detach = rp_detach; target_add_commands(t, rp_cmd_list, RP_ID); return true; } diff --git a/src/target/sam3x.c b/src/target/sam3x.c index 4a1ba08b4ca..3d46910d82a 100644 --- a/src/target/sam3x.c +++ b/src/target/sam3x.c @@ -21,28 +21,29 @@ /* This file implements Atmel SAM3/4 target specific functions for detecting * the device, providing the XML memory map and Flash memory programming. * - * Supported devices: SAM3N, SAM3S, SAM3U, SAM3X, and SAM4S + * Supported devices: SAM3N, SAM3S, SAM3U, SAM3X, SAM4S, SAME70, SAMS70, SAMV71, SAMV70 */ #include "general.h" #include "target.h" #include "target_internal.h" -static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len); +static int sam_flash_erase(struct target_flash *f, target_addr addr, size_t len); static int sam3_flash_erase(struct target_flash *f, target_addr addr, size_t len); -static int sam3x_flash_write(struct target_flash *f, target_addr dest, +static int sam_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); -static bool sam3x_cmd_gpnvm_get(target *t, int argc, const char **argv); -static bool sam3x_cmd_gpnvm_set(target *t, int argc, const char **argv); +static uint32_t sam_gpnvm_get(target *t, uint32_t base); -const struct command_s sam3x_cmd_list[] = { - {"gpnvm_get", (cmd_handler)sam3x_cmd_gpnvm_get, "Get GPVNM value"}, - {"gpnvm_set", (cmd_handler)sam3x_cmd_gpnvm_set, "Set GPVNM bit"}, +static bool sam_cmd_gpnvm(target *t, int argc, const char **argv); + +const struct command_s sam_cmd_list[] = { + {"gpnvm", (cmd_handler)sam_cmd_gpnvm, "Set/Get GPVNM bits"}, {NULL, NULL, NULL} }; /* Enhanced Embedded Flash Controller (EEFC) Register Map */ +#define SAMX7X_EEFC_BASE 0x400E0C00 #define SAM3N_EEFC_BASE 0x400E0A00 #define SAM3X_EEFC_BASE(x) (0x400E0A00+((x)*0x200)) #define SAM3U_EEFC_BASE(x) (0x400E0800+((x)*0x200)) @@ -74,49 +75,102 @@ const struct command_s sam3x_cmd_list[] = { #define EEFC_FSR_FLOCKE (1 << 2) #define EEFC_FSR_ERROR (EEFC_FSR_FCMDE | EEFC_FSR_FLOCKE) -#define SAM3X_CHIPID_CIDR 0x400E0940 +#define SAM_SMALL_PAGE_SIZE 256 +#define SAM_LARGE_PAGE_SIZE 512 + +/* CHIPID Register Map */ +#define SAM_CHIPID_CIDR 0x400E0940 #define SAM34NSU_CHIPID_CIDR 0x400E0740 +#define SAM_CHIPID_EXID (SAM_CHIPID_CIDR + 0x4) + #define CHIPID_CIDR_VERSION_MASK (0x1F << 0) -#define CHIPID_CIDR_EPROC_CM3 (0x03 << 5) -#define CHIPID_CIDR_EPROC_CM4 (0x07 << 5) -#define CHIPID_CIDR_EPROC_MASK (0x07 << 5) -#define CHIPID_CIDR_NVPSIZ_MASK (0x0F << 8) -#define CHIPID_CIDR_NVPSIZ_8K (0x01 << 8) -#define CHIPID_CIDR_NVPSIZ_16K (0x02 << 8) -#define CHIPID_CIDR_NVPSIZ_32K (0x03 << 8) -#define CHIPID_CIDR_NVPSIZ_64K (0x05 << 8) -#define CHIPID_CIDR_NVPSIZ_128K (0x07 << 8) -#define CHIPID_CIDR_NVPSIZ_256K (0x09 << 8) -#define CHIPID_CIDR_NVPSIZ_512K (0x0A << 8) -#define CHIPID_CIDR_NVPSIZ_1024K (0x0C << 8) -#define CHIPID_CIDR_NVPSIZ_2048K (0x0E << 8) -#define CHIPID_CIDR_NVPSIZ2_MASK (0x0F << 12) -#define CHIPID_CIDR_SRAMSIZ_MASK (0x0F << 16) -#define CHIPID_CIDR_ARCH_MASK (0xFF << 20) -#define CHIPID_CIDR_ARCH_SAM3UxC (0x80 << 20) -#define CHIPID_CIDR_ARCH_SAM3UxE (0x81 << 20) -#define CHIPID_CIDR_ARCH_SAM3XxC (0x84 << 20) -#define CHIPID_CIDR_ARCH_SAM3XxE (0x85 << 20) -#define CHIPID_CIDR_ARCH_SAM3XxG (0x86 << 20) -#define CHIPID_CIDR_ARCH_SAM3NxA (0x93 << 20) -#define CHIPID_CIDR_ARCH_SAM3NxB (0x94 << 20) -#define CHIPID_CIDR_ARCH_SAM3NxC (0x95 << 20) -#define CHIPID_CIDR_ARCH_SAM3SxA (0x88 << 20) -#define CHIPID_CIDR_ARCH_SAM3SxB (0x89 << 20) -#define CHIPID_CIDR_ARCH_SAM3SxC (0x8A << 20) -#define CHIPID_CIDR_ARCH_SAM4SxA (0x88 << 20) -#define CHIPID_CIDR_ARCH_SAM4SxB (0x89 << 20) -#define CHIPID_CIDR_ARCH_SAM4SxC (0x8A << 20) -#define CHIPID_CIDR_ARCH_SAM4SDB (0x99 << 20) -#define CHIPID_CIDR_ARCH_SAM4SDC (0x9A << 20) -#define CHIPID_CIDR_NVPTYP_MASK (0x07 << 28) -#define CHIPID_CIDR_NVPTYP_FLASH (0x02 << 28) -#define CHIPID_CIDR_NVPTYP_ROM_FLASH (0x03 << 28) + +#define CHIPID_CIDR_EPROC_OFFSET (5) +#define CHIPID_CIDR_EPROC_MASK (0x7 << CHIPID_CIDR_EPROC_OFFSET) +#define CHIPID_CIDR_EPROC_CM7 (0x0 << CHIPID_CIDR_EPROC_OFFSET) +#define CHIPID_CIDR_EPROC_CM3 (0x3 << CHIPID_CIDR_EPROC_OFFSET) +#define CHIPID_CIDR_EPROC_CM4 (0x7 << CHIPID_CIDR_EPROC_OFFSET) + +#define CHIPID_CIDR_NVPSIZ_OFFSET (8) +#define CHIPID_CIDR_NVPSIZ_MASK (0xF << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_8K (0x1 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_16K (0x2 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_32K (0x3 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_64K (0x5 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_128K (0x7 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_256K (0x9 << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_512K (0xA << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_1024K (0xC << CHIPID_CIDR_NVPSIZ_OFFSET) +#define CHIPID_CIDR_NVPSIZ_2048K (0xE << CHIPID_CIDR_NVPSIZ_OFFSET) + +#define CHIPID_CIDR_NVPSIZ2_OFFSET (12) +#define CHIPID_CIDR_NVPSIZ2_MASK (0xF << CHIPID_CIDR_NVPSIZ2_OFFSET) + +#define CHIPID_CIDR_SRAMSIZ_OFFSET (16) +#define CHIPID_CIDR_SRAMSIZ_MASK (0xF << CHIPID_CIDR_SRAMSIZ_OFFSET) +#define CHIPID_CIDR_SRAMSIZ_384K (0x2 << CHIPID_CIDR_SRAMSIZ_OFFSET) +#define CHIPID_CIDR_SRAMSIZ_256K (0xD << CHIPID_CIDR_SRAMSIZ_OFFSET) + +#define CHIPID_CIDR_ARCH_OFFSET (20) +#define CHIPID_CIDR_ARCH_MASK (0xFF << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAME70 (0x10 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAMS70 (0x11 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAMV71 (0x12 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAMV70 (0x13 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3UxC (0x80 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3UxE (0x81 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3XxC (0x84 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3XxE (0x85 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3XxG (0x86 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3NxA (0x93 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3NxB (0x94 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3NxC (0x95 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3SxA (0x88 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3SxB (0x89 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM3SxC (0x8A << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM4SxA (0x88 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM4SxB (0x89 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM4SxC (0x8A << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM4SDB (0x99 << CHIPID_CIDR_ARCH_OFFSET) +#define CHIPID_CIDR_ARCH_SAM4SDC (0x9A << CHIPID_CIDR_ARCH_OFFSET) + +#define CHIPID_CIDR_NVPTYP_OFFSET (28) +#define CHIPID_CIDR_NVPTYP_MASK (0x7 << CHIPID_CIDR_NVPTYP_OFFSET) +#define CHIPID_CIDR_NVPTYP_FLASH (0x2 << CHIPID_CIDR_NVPTYP_OFFSET) +#define CHIPID_CIDR_NVPTYP_ROM_FLASH (0x3 << CHIPID_CIDR_NVPTYP_OFFSET) + #define CHIPID_CIDR_EXT (0x01 << 31) -#define SAM3_PAGE_SIZE 256 -#define SAM4_PAGE_SIZE 512 +#define CHIPID_EXID_SAMX7X_PINS_OFFSET (0) +#define CHIPID_EXID_SAMX7X_PINS_MASK (0x3 << CHIPID_EXID_SAMX7X_PINS_OFFSET) +#define CHIPID_EXID_SAMX7X_PINS_Q (0x2 << CHIPID_EXID_SAMX7X_PINS_OFFSET) +#define CHIPID_EXID_SAMX7X_PINS_N (0x1 << CHIPID_EXID_SAMX7X_PINS_OFFSET) +#define CHIPID_EXID_SAMX7X_PINS_J (0x0 << CHIPID_EXID_SAMX7X_PINS_OFFSET) + +/* GPNVM */ +#define GPNVM_SAMX7X_SECURITY_BIT_OFFSET (0) +#define GPNVM_SAMX7X_SECURITY_BIT_MASK (0x1 << GPNVM_SAMX7X_SECURITY_BIT_OFFSET) + +#define GPNVM_SAMX7X_BOOT_BIT_OFFSET (1) +#define GPNVM_SAMX7X_BOOT_BIT_MASK (0x1 << GPNVM_SAMX7X_BOOT_BIT_OFFSET) +#define GPNVM_SAMX7X_BOOT_ROM (0x0 << GPNVM_SAMX7X_BOOT_BIT_OFFSET) +#define GPNVM_SAMX7X_BOOT_FLASH (0x1 << GPNVM_SAMX7X_BOOT_BIT_OFFSET) + +#define GPNVM_SAMX7X_TCM_BIT_OFFSET (7) +#define GPNVM_SAMX7X_TCM_BIT_MASK (0x3 << GPNVM_SAMX7X_TCM_BIT_OFFSET) +#define GPNVM_SAMX7X_TCM_0K (0x0 << GPNVM_SAMX7X_TCM_BIT_OFFSET) +#define GPNVM_SAMX7X_TCM_32K (0x1 << GPNVM_SAMX7X_TCM_BIT_OFFSET) +#define GPNVM_SAMX7X_TCM_64K (0x2 << GPNVM_SAMX7X_TCM_BIT_OFFSET) +#define GPNVM_SAMX7X_TCM_128K (0x3 << GPNVM_SAMX7X_TCM_BIT_OFFSET) + +enum sam_driver { + DRIVER_SAM3X, + DRIVER_SAM3U, + DRIVER_SAM4S, + DRIVER_SAM3NS, + DRIVER_SAMX7X, +}; struct sam_flash { struct target_flash f; @@ -124,6 +178,20 @@ struct sam_flash { uint8_t write_cmd; }; +struct samx7x_descr { + char product_code; + uint8_t product_id; + char pins; + uint32_t ram_size; + uint32_t flash_size; + uint8_t density; + char revision; +}; +struct sam_priv_s { + struct samx7x_descr descr; + char sam_variant_string[16]; +}; + static void sam3_add_flash(target *t, uint32_t eefc_base, uint32_t addr, size_t length) { @@ -138,16 +206,16 @@ static void sam3_add_flash(target *t, f = &sf->f; f->start = addr; f->length = length; - f->blocksize = SAM3_PAGE_SIZE; + f->blocksize = SAM_SMALL_PAGE_SIZE; f->erase = sam3_flash_erase; - f->write = sam3x_flash_write; - f->buf_size = SAM3_PAGE_SIZE; + f->write = sam_flash_write; + f->buf_size = SAM_SMALL_PAGE_SIZE; sf->eefc_base = eefc_base; sf->write_cmd = EEFC_FCR_FCMD_EWP; target_add_flash(t, f); } -static void sam4_add_flash(target *t, +static void sam_add_flash(target *t, uint32_t eefc_base, uint32_t addr, size_t length) { struct sam_flash *sf = calloc(1, sizeof(*sf)); @@ -161,15 +229,44 @@ static void sam4_add_flash(target *t, f = &sf->f; f->start = addr; f->length = length; - f->blocksize = SAM4_PAGE_SIZE * 8; - f->erase = sam4_flash_erase; - f->write = sam3x_flash_write; - f->buf_size = SAM4_PAGE_SIZE; + f->blocksize = SAM_LARGE_PAGE_SIZE * 8; + f->erase = sam_flash_erase; + f->write = sam_flash_write; + f->buf_size = SAM_LARGE_PAGE_SIZE; sf->eefc_base = eefc_base; sf->write_cmd = EEFC_FCR_FCMD_WP; target_add_flash(t, f); } +static void samx7x_add_ram(target* t, uint32_t tcm_config, uint32_t ram_size) { + uint32_t itcm_size = 0, dtcm_size = 0; + + switch (tcm_config) + { + case GPNVM_SAMX7X_TCM_32K: + itcm_size = dtcm_size = 0x8000; + break; + case GPNVM_SAMX7X_TCM_64K: + itcm_size = dtcm_size = 0x10000; + break; + case GPNVM_SAMX7X_TCM_128K: + itcm_size = dtcm_size = 0x20000; + break; + } + + if (dtcm_size > 0) { + target_add_ram(t, 0x20000000, dtcm_size); + } + if (itcm_size > 0) { + target_add_ram(t, 0x00000000, itcm_size); + } + + uint32_t sram_size = ram_size - (itcm_size + dtcm_size); + if (sram_size > 0) { + target_add_ram(t, 0x20400000, sram_size); + } +} + static size_t sam_flash_size(uint32_t cidr) { switch (cidr & CHIPID_CIDR_NVPSIZ_MASK) { @@ -195,9 +292,146 @@ static size_t sam_flash_size(uint32_t cidr) return 0; } +static size_t sam_sram_size(uint32_t cidr) +{ + switch (cidr & CHIPID_CIDR_SRAMSIZ_MASK) { + case CHIPID_CIDR_SRAMSIZ_256K: + return 0x40000; + case CHIPID_CIDR_SRAMSIZ_384K: + return 0x60000; + default: + return 0; + } +} + +struct samx7x_descr samx7x_parse_id(uint32_t cidr, uint32_t exid) { + + struct samx7x_descr descr = {0}; + + switch (cidr & CHIPID_CIDR_ARCH_MASK) { + case CHIPID_CIDR_ARCH_SAME70: + descr.product_code = 'E'; + descr.product_id = 70; + break; + case CHIPID_CIDR_ARCH_SAMS70: + descr.product_code = 'S'; + descr.product_id = 70; + break; + case CHIPID_CIDR_ARCH_SAMV71: + descr.product_code = 'V'; + descr.product_id = 71; + break; + case CHIPID_CIDR_ARCH_SAMV70: + descr.product_code = 'V'; + descr.product_id = 70; + break; + } + + // A = Revision A, legacy version + // B = Revision B, current variant + switch (exid & CHIPID_CIDR_VERSION_MASK) { + case 0: + descr.revision = 'A'; + break; + case 1: + descr.revision = 'B'; + break; + default: + descr.revision = '_'; + break; + } + + // Q = 144 pins + // N = 100 pins + // J = 64 pins + switch (exid & CHIPID_EXID_SAMX7X_PINS_MASK) { + case CHIPID_EXID_SAMX7X_PINS_Q: + descr.pins = 'Q'; + break; + case CHIPID_EXID_SAMX7X_PINS_N: + descr.pins = 'N'; + break; + case CHIPID_EXID_SAMX7X_PINS_J: + descr.pins = 'J'; + break; + } + + descr.ram_size = sam_sram_size(cidr); + descr.flash_size = sam_flash_size(cidr); + + // 21 = 2048 KB + // 20 = 1024 KB + // 19 = 512 KB + switch (descr.flash_size) + { + case 0x200000: + descr.density = 21; + break; + case 0x100000: + descr.density = 20; + break; + case 0x80000: + descr.density = 19; + break; + default: + descr.density = 0; + break; + } + + return descr; +} + +bool samx7x_probe(target *t) +{ + uint32_t cidr = target_mem_read32(t, SAM_CHIPID_CIDR); + uint32_t exid = 0; + if (cidr & CHIPID_CIDR_EXT) { + exid = target_mem_read32(t, SAM_CHIPID_EXID); + } + + switch (cidr & CHIPID_CIDR_ARCH_MASK) { + case CHIPID_CIDR_ARCH_SAME70: + case CHIPID_CIDR_ARCH_SAMS70: + case CHIPID_CIDR_ARCH_SAMV71: + case CHIPID_CIDR_ARCH_SAMV70: + break; + default: + return false; + } + + struct sam_priv_s *priv_storage = calloc(1, sizeof(*priv_storage)); + if (!priv_storage) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return false; + } + t->target_storage = (void*)priv_storage; + + priv_storage->descr = samx7x_parse_id(cidr, exid); + + uint32_t tcm_config = sam_gpnvm_get(t, SAMX7X_EEFC_BASE) & GPNVM_SAMX7X_TCM_BIT_MASK; + + samx7x_add_ram(t, tcm_config, priv_storage->descr.ram_size); + + sam_add_flash(t, SAMX7X_EEFC_BASE, 0x00400000, priv_storage->descr.flash_size); + + target_add_commands(t, sam_cmd_list, "SAMX7X"); + + sprintf(priv_storage->sam_variant_string, + "SAM%c%02d%c%d%c", + priv_storage->descr.product_code, + priv_storage->descr.product_id, + priv_storage->descr.pins, + priv_storage->descr.density, + priv_storage->descr.revision); + + t->driver = priv_storage->sam_variant_string; + + return true; +} + bool sam3x_probe(target *t) { - uint32_t cidr = target_mem_read32(t, SAM3X_CHIPID_CIDR); + uint32_t cidr = target_mem_read32(t, SAM_CHIPID_CIDR); size_t size = sam_flash_size(cidr); switch (cidr & (CHIPID_CIDR_ARCH_MASK | CHIPID_CIDR_EPROC_MASK)) { case CHIPID_CIDR_ARCH_SAM3XxC | CHIPID_CIDR_EPROC_CM3: @@ -208,7 +442,7 @@ bool sam3x_probe(target *t) /* 2 Flash memories back-to-back starting at 0x80000 */ sam3_add_flash(t, SAM3X_EEFC_BASE(0), 0x80000, size/2); sam3_add_flash(t, SAM3X_EEFC_BASE(1), 0x80000 + size/2, size/2); - target_add_commands(t, sam3x_cmd_list, "SAM3X"); + target_add_commands(t, sam_cmd_list, "SAM3X"); return true; } @@ -226,7 +460,7 @@ bool sam3x_probe(target *t) /* These devices only have a single bank */ size = sam_flash_size(cidr); sam3_add_flash(t, SAM3N_EEFC_BASE, 0x400000, size); - target_add_commands(t, sam3x_cmd_list, "SAM3N/S"); + target_add_commands(t, sam_cmd_list, "SAM3N/S"); return true; case CHIPID_CIDR_ARCH_SAM3UxC | CHIPID_CIDR_EPROC_CM3: case CHIPID_CIDR_ARCH_SAM3UxE | CHIPID_CIDR_EPROC_CM3: @@ -239,7 +473,7 @@ bool sam3x_probe(target *t) sam3_add_flash(t, SAM3U_EEFC_BASE(1), 0x100000, 0x80000); } - target_add_commands(t, sam3x_cmd_list, "SAM3U"); + target_add_commands(t, sam_cmd_list, "SAM3U"); return true; case CHIPID_CIDR_ARCH_SAM4SxA | CHIPID_CIDR_EPROC_CM4: case CHIPID_CIDR_ARCH_SAM4SxB | CHIPID_CIDR_EPROC_CM4: @@ -251,14 +485,14 @@ bool sam3x_probe(target *t) size = sam_flash_size(cidr); if (size <= 0x80000) { /* Smaller devices have a single bank */ - sam4_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size); + sam_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size); } else { /* Larger devices are split evenly between 2 */ - sam4_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size/2); - sam4_add_flash(t, SAM4S_EEFC_BASE(1), + sam_add_flash(t, SAM4S_EEFC_BASE(0), 0x400000, size/2); + sam_add_flash(t, SAM4S_EEFC_BASE(1), 0x400000 + size/2, size/2); } - target_add_commands(t, sam3x_cmd_list, "SAM4S"); + target_add_commands(t, sam_cmd_list, "SAM4S"); return true; } @@ -266,7 +500,7 @@ bool sam3x_probe(target *t) } static int -sam3x_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg) +sam_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg) { DEBUG_INFO("%s: base = 0x%08"PRIx32" cmd = 0x%02X, arg = 0x%06X\n", __func__, base, cmd, arg); @@ -281,21 +515,24 @@ sam3x_flash_cmd(target *t, uint32_t base, uint8_t cmd, uint16_t arg) return sr & EEFC_FSR_ERROR; } -static uint32_t sam3x_flash_base(target *t) +static enum sam_driver sam_driver(target *t) { if (strcmp(t->driver, "Atmel SAM3X") == 0) { - return SAM3X_EEFC_BASE(0); + return DRIVER_SAM3X; } if (strcmp(t->driver, "Atmel SAM3U") == 0) { - return SAM3U_EEFC_BASE(0); + return DRIVER_SAM3U; } if (strcmp(t->driver, "Atmel SAM4S") == 0) { - return SAM4S_EEFC_BASE(0); + return DRIVER_SAM4S; + } + if (strcmp(t->driver, "Atmel SAM3N/S") == 0) { + return DRIVER_SAM3NS; } - return SAM3N_EEFC_BASE; + return DRIVER_SAMX7X; } -static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len) +static int sam_flash_erase(struct target_flash *f, target_addr addr, size_t len) { target *t = f->t; uint32_t base = ((struct sam_flash *)f)->eefc_base; @@ -305,11 +542,11 @@ static int sam4_flash_erase(struct target_flash *f, target_addr addr, size_t len * Erasing is done in 8-page chunks. arg[15:2] contains the page * number and arg[1:0] contains 0x1, indicating 8-page chunks. */ - unsigned chunk = offset / SAM4_PAGE_SIZE; + unsigned chunk = offset / SAM_LARGE_PAGE_SIZE; while (len) { int16_t arg = chunk | 0x1; - if(sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg)) + if(sam_flash_cmd(t, base, EEFC_FCR_FCMD_EPA, arg)) return -1; if (len > f->blocksize) @@ -330,7 +567,7 @@ static int sam3_flash_erase(struct target_flash *f, target_addr addr, size_t len return 0; } -static int sam3x_flash_write(struct target_flash *f, target_addr dest, +static int sam_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) { target *t = f->t; @@ -339,38 +576,97 @@ static int sam3x_flash_write(struct target_flash *f, target_addr dest, unsigned chunk = (dest - f->start) / f->buf_size; target_mem_write(t, dest, src, len); - if(sam3x_flash_cmd(t, base, sf->write_cmd, chunk)) + if(sam_flash_cmd(t, base, sf->write_cmd, chunk)) return -1; return 0; } -static bool sam3x_cmd_gpnvm_get(target *t, int argc, const char **argv) +static uint32_t sam_gpnvm_get(target *t, uint32_t base) { - (void)argc; - (void)argv; - uint32_t base = sam3x_flash_base(t); - - sam3x_flash_cmd(t, base, EEFC_FCR_FCMD_GGPB, 0); - tc_printf(t, "GPNVM: 0x%08X\n", target_mem_read32(t, EEFC_FRR(base))); - - return true; + sam_flash_cmd(t, base, EEFC_FCR_FCMD_GGPB, 0); + return target_mem_read32(t, EEFC_FRR(base)); } -static bool sam3x_cmd_gpnvm_set(target *t, int argc, const char **argv) +static bool sam_cmd_gpnvm(target *t, int argc, const char **argv) { - uint32_t bit, cmd; - uint32_t base = sam3x_flash_base(t); + if (argc != 2 && argc != 4) { + goto bad_usage; + } - if (argc != 3) { - tc_printf(t, "usage: monitor gpnvm_set \n"); - return false; + uint8_t arglen = strlen(argv[1]); + if (arglen == 0) { + goto bad_usage; } - bit = atol(argv[1]); - cmd = atol(argv[2]) ? EEFC_FCR_FCMD_SGPB : EEFC_FCR_FCMD_CGPB; - sam3x_flash_cmd(t, base, cmd, bit); - sam3x_cmd_gpnvm_get(t, 0, NULL); + uint32_t base = 0, gpnvm_mask = 0; + enum sam_driver drv = sam_driver(t); + switch(drv) { + case DRIVER_SAM3X: + gpnvm_mask = 0x7; + base = SAM3X_EEFC_BASE(0); + break; + case DRIVER_SAM3U: + gpnvm_mask = 0x7; + base = SAM3U_EEFC_BASE(0); + break; + case DRIVER_SAM4S: + gpnvm_mask = 0x7; + base = SAM4S_EEFC_BASE(0); + break; + case DRIVER_SAM3NS: + gpnvm_mask = 0x3; + base = SAM3N_EEFC_BASE; + break; + case DRIVER_SAMX7X: + gpnvm_mask = 0x1BF; + base = SAMX7X_EEFC_BASE; + break; + } + + uint32_t mask = 0, values = 0; + if (strncmp(argv[1], "get", arglen) == 0) { + /* nothing to do */ + } else if (strncmp(argv[1], "set", arglen) == 0) { + char *eos; + mask = strtoul(argv[2], &eos, 0); + values = strtoul(argv[3], &eos, 0); + + if (mask == 0 || mask & ~gpnvm_mask) { + /* trying to write invalid bits */ + goto bad_usage; + } + + uint32_t work_mask = mask; + uint32_t work_values = values; + for (uint16_t bit = 0; work_mask > 0; bit++) + { + if (work_mask & 1) { + uint8_t cmd = (work_values & 1) ? EEFC_FCR_FCMD_SGPB : EEFC_FCR_FCMD_CGPB; + sam_flash_cmd(t, base, cmd, bit); + } + work_mask >>= 1; + work_values >>= 1; + } + } else { + goto bad_usage; + } + + uint32_t gpnvm = sam_gpnvm_get(t, base); + tc_printf(t, "GPNVM: 0x%08X\n", gpnvm); + + if ((drv == DRIVER_SAMX7X) && (mask & GPNVM_SAMX7X_TCM_BIT_MASK)) { + struct sam_priv_s * storage = (struct sam_priv_s *)t->target_storage; + + target_ram_map_free(t); + samx7x_add_ram(t, gpnvm & GPNVM_SAMX7X_TCM_BIT_MASK, storage->descr.ram_size); + target_reset(t); + } return true; + +bad_usage: + tc_printf(t, "usage: monitor gpnvm get\n"); + tc_printf(t, "usage: monitor gpnvm set \n"); + return false; } diff --git a/src/target/samd.c b/src/target/samd.c index f61340880cc..8bde7f12055 100644 --- a/src/target/samd.c +++ b/src/target/samd.c @@ -715,11 +715,45 @@ static bool samd_set_flashlock(target *t, uint16_t value, const char **argv) return true; } +static bool parse_unsigned(const char *s, uint32_t *val) +{ + int l, st; + unsigned long num; + + l = strlen(s); + // TODO: port to use substrate::toInt_t<> style parser for robustness and smaller code size + if (l > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + st = sscanf(s + 2, "%lx", &num); + else + st = sscanf(s, "%lu", &num); + + if (st < 1) + return false; + + *val = (uint32_t)num; + return true; +} + static bool samd_cmd_lock_flash(target *t, int argc, const char **argv) { - (void)argc; - (void)argv; - return samd_set_flashlock(t, 0x0000, NULL); + if (argc > 2) { + tc_printf(t, "usage: monitor lock_flash [number]\n"); + return false; + } else if (argc == 2) { + uint32_t val = 0; + if (!parse_unsigned(argv[1], &val)) { + tc_printf(t, "number must be either decimal or 0x prefixed hexadecimal\n"); + return false; + } + + if (val > 0xffffu) { + tc_printf(t, "number must be between 0 and 65535\n"); + return false; + } + + return samd_set_flashlock(t, (uint16_t)val, NULL); + } else + return samd_set_flashlock(t, 0x0000, NULL); } static bool samd_cmd_unlock_flash(target *t, int argc, const char **argv) @@ -764,9 +798,25 @@ static bool samd_set_bootprot(target *t, uint16_t value, const char **argv) static bool samd_cmd_lock_bootprot(target *t, int argc, const char **argv) { - (void)argc; - (void)argv; - return samd_set_bootprot(t, 0, NULL); + /* locks first 0x7 .. 0, 0x6 .. 512, 0x5 .. 1024, ..., 0x0 .. 32768 bytes of flash*/ + if (argc > 2) { + tc_printf(t, "usage: monitor lock_bootprot [number]\n"); + return false; + } else if (argc == 2) { + uint32_t val = 0; + if (!parse_unsigned(argv[1], &val)) { + tc_printf(t, "number must be either decimal or 0x prefixed hexadecimal\n"); + return false; + } + + if (val > 7) { + tc_printf(t, "number must be between 0 and 7\n"); + return false; + } + + return samd_set_bootprot(t, (uint16_t)val, NULL); + } else + return samd_set_bootprot(t, 0, NULL); } static bool samd_cmd_unlock_bootprot(target *t, int argc, const char **argv) diff --git a/src/target/stm32f4.c b/src/target/stm32f4.c index a2bd45b2b14..b73c3ee91f6 100644 --- a/src/target/stm32f4.c +++ b/src/target/stm32f4.c @@ -110,6 +110,10 @@ static int stm32f4_flash_write(struct target_flash *f, #define AXIM_BASE 0x8000000 #define ITCM_BASE 0x0200000 +#define DBGMCU_CR_DBG_SLEEP (0x1U << 0U) +#define DBGMCU_CR_DBG_STOP (0x1U << 1U) +#define DBGMCU_CR_DBG_STANDBY (0x1U << 2U) + struct stm32f4_flash { struct target_flash f; enum align psize; @@ -117,6 +121,10 @@ struct stm32f4_flash { uint8_t bank_split; }; +struct stm32f4_priv_s { + uint32_t dbgmcu_cr; +}; + enum IDS_STM32F247 { ID_STM32F20X = 0x411, ID_STM32F40X = 0x413, @@ -194,10 +202,12 @@ static char *stm32f4_get_chip_name(uint32_t idcode) } } -static void stm32f7_detach(target *t) +static void stm32f4_detach(target *t) { - ADIv5_AP_t *ap = cortexm_ap(t); - target_mem_write32(t, DBGMCU_CR, ap->ap_storage); + struct stm32f4_priv_s *ps = (struct stm32f4_priv_s*)t->target_storage; + + /*reverse all changes to DBGMCU_CR*/ + target_mem_write32(t, DBGMCU_CR, ps->dbgmcu_cr); cortexm_detach(t); } @@ -214,8 +224,6 @@ bool stm32f4_probe(target *t) case ID_STM32F74X: /* F74x RM0385 Rev.4 */ case ID_STM32F76X: /* F76x F77x RM0410 */ case ID_STM32F72X: /* F72x F73x RM0431 */ - t->detach = stm32f7_detach; - /* fall through */ case ID_STM32F40X: case ID_STM32F42X: /* 427/437 */ case ID_STM32F46X: /* 469/479 */ @@ -226,6 +234,7 @@ bool stm32f4_probe(target *t) case ID_STM32F412: /* F412 RM0402 Rev.4, 256 kB Ram */ case ID_STM32F401E: /* F401 D/E RM0368 Rev.3 */ case ID_STM32F413: /* F413 RM0430 Rev.2, 320 kB Ram, 1.5 MB flash. */ + t->detach = stm32f4_detach; t->driver = stm32f4_get_chip_name(t->idcode); t->attach = stm32f4_attach; target_add_commands(t, stm32f4_cmd_list, t->driver); @@ -295,6 +304,19 @@ static bool stm32f4_attach(target *t) return false; } bool use_dual_bank = false; + /* Save DBGMCU_CR to restore it when detaching*/ + struct stm32f4_priv_s *priv_storage = calloc(1, sizeof(*priv_storage)); + if (!priv_storage) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return false; + } + priv_storage->dbgmcu_cr = target_mem_read32(t, DBGMCU_CR); + t->target_storage = (void*)priv_storage; + /* Enable debugging during all low power modes*/ + target_mem_write32(t, DBGMCU_CR, priv_storage->dbgmcu_cr | + DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STANDBY | DBGMCU_CR_DBG_STOP); + + /* Free previously loaded memory map */ target_mem_map_free(t); if (is_f7) { target_add_ram(t, 0x00000000, 0x4000); /* 16 k ITCM Ram */ diff --git a/src/target/stm32g0.c b/src/target/stm32g0.c index 43c58c30f78..83ab821cc88 100644 --- a/src/target/stm32g0.c +++ b/src/target/stm32g0.c @@ -295,6 +295,16 @@ static void stm32g0_detach(target *t) { struct stm32g0_priv_s *ps = (struct stm32g0_priv_s*)t->target_storage; + /* + * First re-enable DBGEN clock, in case it got disabled in the meantime + * (happens during flash), so that writes to DBG_* registers below succeed. + */ + target_mem_write32(t, RCC_APBENR1, ps->saved_regs.rcc_apbenr1 | + RCC_APBENR1_DBGEN); + + /* + * Then restore the DBG_* registers and clock settings. + */ target_mem_write32(t, DBG_APB_FZ1, ps->saved_regs.dbg_apb_fz1); target_mem_write32(t, DBG_CR, ps->saved_regs.dbg_cr); target_mem_write32(t, RCC_APBENR1, ps->saved_regs.rcc_apbenr1); diff --git a/src/target/stm32l4.c b/src/target/stm32l4.c index b0f7f81dd99..2e493285597 100644 --- a/src/target/stm32l4.c +++ b/src/target/stm32l4.c @@ -1,7 +1,7 @@ /* * This file is part of the Black Magic Debug project. * - * Copyright (C) 2015, 2017 - 2021 Uwe Bonnes + * Copyright (C) 2015, 2017 - 2022 Uwe Bonnes * * * This program is free software: you can redistribute it and/or modify @@ -29,6 +29,9 @@ * ARM®-based 32-bit MCUs Rev.3 * RM0432 STM32L4Rxxx and STM32L4Sxxx advanced Arm®-based 32-bit MCU. Rev 1 * RM0440 STM32G4 Series advanced Arm®-based 32-bit MCU. Rev 6 + * RM0434 Multiprotocol wireless 32-bit MCU Arm®-based Cortex®-M4 with + * FPU, Bluetooth® Low-Energy and 802.15.4 radio solution + * RM0453 STM32WL5x advanced Arm®-based 32-bit MCUswith sub-GHz radio solution * * */ @@ -63,6 +66,10 @@ static int stm32l4_flash_write(struct target_flash *f, #define L5_FLASH_OPTR_TZEN (1 << 31) +#define FLASH_OPTR_ESE (1 << 8) +#define PWR_CR4 0x5800040C +#define PWR_CR4_C2BOOT (1 << 15) + #define FLASH_CR_PG (1 << 0) #define FLASH_CR_PER (1 << 1) #define FLASH_CR_MER1 (1 << 2) @@ -309,7 +316,7 @@ static struct stm32l4_info const L4info[] = { .idcode = ID_STM32WLXX, .family = FAM_STM32WLxx, .designator = "STM32WLxx", - .sram1 = 64, + .sram1 = 32, .sram2 = 32, .flags = 2, .flash_regs_map = stm32wl_flash_regs_map, @@ -524,7 +531,24 @@ bool stm32l4_probe(target *t) if( !chip->idcode ) /* Not found */ return false; + t->driver = chip->designator; switch (idcode) { + case ID_STM32WLXX: + case ID_STM32WBXX: + if ((stm32l4_flash_read32(t, FLASH_OPTR)) & FLASH_OPTR_ESE) { + DEBUG_WARN("STM32W security enabled\n"); + t->driver = (idcode == ID_STM32WLXX) ? + "STM32WLxx(secure)" : "STM32WBxx(secure)"; + } + if (ap->apsel == 0) { + /* Enable CPU2 from CPU1. + * CPU2 does not boot after reset w/o C2BOOT set. + * RM0453/RM0434, 6.6.4. PWR control register 4 (PWR_CR4)*/ + uint32_t pwr_cr4 = target_mem_read32(t, PWR_CR4); + pwr_cr4 |= PWR_CR4_C2BOOT; + target_mem_write32(t, PWR_CR4, pwr_cr4); + } + break; case ID_STM32L55: if ((stm32l4_flash_read32(t, FLASH_OPTR)) & L5_FLASH_OPTR_TZEN) { DEBUG_WARN("STM32L5 Trust Zone enabled\n"); @@ -532,7 +556,6 @@ bool stm32l4_probe(target *t) break; } } - t->driver = chip->designator; t->attach = stm32l4_attach; t->detach = stm32l4_detach; target_add_commands(t, stm32l4_cmd_list, chip->designator); @@ -725,6 +748,10 @@ static bool stm32l4_cmd_option(target *t, int argc, char *argv[]) tc_printf(t, "STM32WBxx options not implemented!\n"); return false; } + if (t->idcode == ID_STM32WLXX) { + tc_printf(t, "STM32WLxx options not implemented!\n"); + return false; + } static const uint32_t g4_values[11] = { /* SEC_SIZE1 occupies 9 bits on G49/G4A (cat 4), * 8 bits on cat 3 and 7 bits on cat 2. diff --git a/src/target/target.c b/src/target/target.c index c8a10a5306f..dd4208a85f7 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -34,9 +34,9 @@ static bool nop_function(void) return true; } -static int null_function(void) +static bool false_function(void) { - return 0; + return false; } target *target_new(void) @@ -68,7 +68,7 @@ target *target_new(void) t->halt_request = (void*)nop_function; t->halt_poll = (void*)nop_function; t->halt_resume = (void*)nop_function; - t->check_error = (void*)null_function; + t->check_error = (void*)false_function; t->target_storage = NULL; @@ -84,13 +84,16 @@ int target_foreach(void (*cb)(int, target *t, void *context), void *context) return i; } -void target_mem_map_free(target *t) -{ + +void target_ram_map_free(target *t) { while (t->ram) { void * next = t->ram->next; free(t->ram); t->ram = next; } +} + +void target_flash_map_free(target *t) { while (t->flash) { void * next = t->flash->next; if (t->flash->buf) @@ -100,6 +103,12 @@ void target_mem_map_free(target *t) } } +void target_mem_map_free(target *t) +{ + target_ram_map_free(t); + target_flash_map_free(t); +} + void target_list_free(void) { struct target_command_s *tc; diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 17f9580064a..883d3b57313 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -136,6 +136,8 @@ struct target_s { void (*priv_free)(void *); }; +void target_ram_map_free(target *t); +void target_flash_map_free(target *t); void target_mem_map_free(target *t); void target_add_commands(target *t, const struct command_s *cmds, const char *name); void target_add_ram(target *t, target_addr start, uint32_t len); @@ -173,6 +175,7 @@ int tc_system(target *t, target_addr cmd, size_t cmdlen); /* Probe for various targets. * Actual functions implemented in their respective drivers. */ +bool ch32f1_probe(target *t); // will catch all the clones bool gd32f1_probe(target *t); bool stm32f1_probe(target *t); bool stm32f4_probe(target *t); @@ -187,6 +190,7 @@ bool lpc15xx_probe(target *t); bool lpc17xx_probe(target *t); bool lpc43xx_probe(target *t); bool lpc546xx_probe(target *t); +bool samx7x_probe(target *t); bool sam3x_probe(target *t); bool sam4l_probe(target *t); bool nrf51_probe(target *t); diff --git a/upgrade/main.c b/upgrade/main.c index 3e10b9a3115..912253a9c64 100644 --- a/upgrade/main.c +++ b/upgrade/main.c @@ -38,7 +38,7 @@ void banner(void) { puts("\nBlack Magic Probe -- Firmware Upgrade Utility -- Version " VERSION); - puts("Copyright (C) 2011 Black Sphere Technologies Ltd."); + puts("Copyright (C) 2022 Black Magic Debug Project"); puts("License GPLv3+: GNU GPL version 3 or later \n"); } @@ -74,12 +74,16 @@ struct usb_device * find_dev(void) if (((dev->descriptor.idProduct == 0x5740) || (dev->descriptor.idProduct == 0x6018)) && - !strcmp(man, "Black Sphere Technologies")) + (!strcmp(man, "Black Sphere Technologies") || + !strcmp(man, "Black Magic Debug") || + !strcmp(man, "1BitSquared"))) return dev; if (((dev->descriptor.idProduct == 0xDF11) || (dev->descriptor.idProduct == 0x6017)) && - !strcmp(man, "Black Sphere Technologies")) + (!strcmp(man, "Black Sphere Technologies") || + !strcmp(man, "Black Magic Debug") || + !strcmp(man, "1BitSquared"))) return dev; } }