Compare commits
No commits in common. "stable" and "production_01" have entirely different histories.
stable
...
production
40
.gitattributes
vendored
40
.gitattributes
vendored
@ -1,40 +0,0 @@
|
|||||||
# Text for humans
|
|
||||||
LICENSE text eol=lf
|
|
||||||
HACKING text eol=lf
|
|
||||||
COPYING text eol=lf
|
|
||||||
UsingSWO text eol=lf
|
|
||||||
README.* text eol=lf
|
|
||||||
|
|
||||||
# Source code
|
|
||||||
Makefile text eol=lf
|
|
||||||
*.mk text eol=lf
|
|
||||||
*.mak text eol=lf
|
|
||||||
*.inc text eol=lf
|
|
||||||
*.py text eol=lf
|
|
||||||
*.sh text eol=lf
|
|
||||||
*.c text eol=lf
|
|
||||||
*.S text eol=lf
|
|
||||||
*.s text eol=lf
|
|
||||||
*.h text eol=lf
|
|
||||||
*.ld text eol=lf
|
|
||||||
*.yml text eol=lf
|
|
||||||
*.rules text eol=lf
|
|
||||||
|
|
||||||
# Git control files
|
|
||||||
.gitattributes eol=lf
|
|
||||||
.gitignore eol=lf
|
|
||||||
.gitmodules eol=lf
|
|
||||||
|
|
||||||
# Windows source code uses CRLF
|
|
||||||
*.vcxproj text eol=crlf
|
|
||||||
*.props text eol=crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
*.ps1 text eol=crlf
|
|
||||||
*.inf text eol=crlf
|
|
||||||
|
|
||||||
# Other binary files
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.bin binary
|
|
||||||
*.elf binary
|
|
||||||
*.bin binary
|
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1,2 +0,0 @@
|
|||||||
github: [esden, dragonmux]
|
|
||||||
patreon: 1bitsquared
|
|
36
.github/workflows/build-and-upload.yml
vendored
36
.github/workflows/build-and-upload.yml
vendored
@ -1,36 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
# Runs a single command using the runners shell
|
|
||||||
- name: Build
|
|
||||||
run: make
|
|
||||||
|
|
||||||
- name: Archive firmware build artifacts as a zip
|
|
||||||
uses: actions/upload-artifact@v2.2.4
|
|
||||||
with:
|
|
||||||
name: blackmagic-firmware.zip
|
|
||||||
path: src/blackmagic*
|
|
||||||
if-no-files-found: error
|
|
38
.github/workflows/build-pr.yml
vendored
38
.github/workflows/build-pr.yml
vendored
@ -1,38 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
# Run some of the most common build types
|
|
||||||
- name: Build native fw
|
|
||||||
run: make
|
|
||||||
|
|
||||||
- name: Clean
|
|
||||||
run: make clean
|
|
||||||
|
|
||||||
- name: Install BMP PC hosted dependencies
|
|
||||||
run: sudo apt-get -y install libftdi1-dev libhidapi-dev
|
|
||||||
|
|
||||||
- name: Build PC hosted binary
|
|
||||||
run: make PROBE_HOST=hosted
|
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,10 +1,3 @@
|
|||||||
src/include/version.h
|
|
||||||
blackmagic
|
|
||||||
*.bin
|
|
||||||
*.hex
|
|
||||||
blackmagic_dfu
|
|
||||||
dfu_upgrade
|
|
||||||
mapfile
|
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
.*.swp
|
.*.swp
|
||||||
@ -12,14 +5,4 @@ mapfile
|
|||||||
*.pyc
|
*.pyc
|
||||||
tags
|
tags
|
||||||
.gdbinit
|
.gdbinit
|
||||||
*.s#*
|
|
||||||
*.b#*
|
|
||||||
blackmagic_upgrade
|
|
||||||
*.exe
|
|
||||||
.DS_Store
|
|
||||||
*.elf
|
|
||||||
.vscode
|
|
||||||
cscope.out
|
|
||||||
cscope.files
|
|
||||||
.gdb_history
|
|
||||||
src/artifacts/
|
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "libopencm3"]
|
|
||||||
path = libopencm3
|
|
||||||
url = https://github.com/flirc/libopencm3.git
|
|
27
.travis.yml
27
.travis.yml
@ -1,27 +0,0 @@
|
|||||||
dist: bionic
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
|
|
||||||
- sudo apt-get update -qq
|
|
||||||
- pip install --user intelhex
|
|
||||||
- sudo apt-get install -y build-essential libboost-all-dev gcc-arm-embedded libusb-1.0-0-dev libhidapi-dev libftdi1 libftdi1-dev
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make -C libopencm3 lib
|
|
||||||
- make -C src all_platforms
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
provider: s3
|
|
||||||
access_key_id: AKIAIDXRMRPEG5OCN5AQ
|
|
||||||
secret_access_key:
|
|
||||||
secure: AKGtHD71s7FEfmzSWkrEgW8DT3exDzKPROn4hI4kwuMcI6gYoOHqloEhzqZVi9ok8ynEkGfzzaAAcPYCNjlxOu2zmMJFgCiGegIUacrpJuzo9eknqW281pEpIlSXonrlttW9Hm0gJhl3yLKdMQYKoXvCfR7HgUkcN7QSAZxLgv297u/azMLq2z6rZdTCgZYpD2EIB3BlqChZkoepyczBYEtGOj0tMW4WF79qufIfVnUnQqWr8Lef2tQ9X9MEn7LIsHbBgGJ08TGb2yIiPTF+NyE+2ev3KmVmBdzDLdfAAV3koPtiTsTBdcstNAT51vvPsQhuIXB7gIr91xhquCel84DV285aZn2wOwgQ0KHhbFLVFXb2wv+r1saF1b4/rJTiobavDbIXIDVm/UfAs41AyM/zoclTeixAgyA8BpuhDEP+2dDYk3rehZ1Uhbaf8U9OKyQ6kYg1aFOQU5jYFjIbJmgSTlkJwntk9w3EX7lI8LSInNgdPhM8Ak6IjTCKg8NSKvtDH27X++UNVn1r2vX0eMjcusUNSPktjvilWIFMIvPw8bO2yLgj6bLDQ73WsEUC0LiiqL45r3A5KOMkMO8z1xp0TOz2f3Pxdyr1l2gXC0n/hxutoue7HaaPE2zA08giYdSCwZBxSedcoxxKA8V+E4vwfm0D/sWZS+U7vXm15LE=
|
|
||||||
bucket: builds.blacksphere.co.nz
|
|
||||||
region: us-west-2
|
|
||||||
local-dir: src/artifacts
|
|
||||||
upload-dir: blackmagic
|
|
||||||
acl: public_read
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
repo: blacksphere/blackmagic
|
|
||||||
branch: master
|
|
59
HACKING
59
HACKING
@ -1 +1,58 @@
|
|||||||
See https://github.com/blackmagic-debug/blackmagic/wiki/Hacking
|
========================================
|
||||||
|
The Black Magic Debug Project -- HACKING
|
||||||
|
========================================
|
||||||
|
|
||||||
|
The Black Magic Probe consists of both hardware and firmware components.
|
||||||
|
The hardware design resides in the 'hardware' directory and the firmware
|
||||||
|
resides in the 'src' directory.
|
||||||
|
|
||||||
|
|
||||||
|
Compiling for the native hardware
|
||||||
|
---------------------------------
|
||||||
|
To build the firmware for the standard hardware platform run 'make' in the
|
||||||
|
src directory. You will require a GCC cross compiler for ARM Cortex-M3
|
||||||
|
targets. You will also need to have the libopenstm32 library installed.
|
||||||
|
The default makefile assumes the target is arm-cortexm3-eabi, but
|
||||||
|
you can override this on the command line:
|
||||||
|
|
||||||
|
make CROSS_COMPILE=arm-none-eabi-
|
||||||
|
|
||||||
|
This will result in binary files:
|
||||||
|
blackmagic - ELF binary of the Black Magic debug probe.
|
||||||
|
blackmagic.bin - Flat binary of the Black Magic debug probe, load at 0x8002000.
|
||||||
|
blackmagic_dfu - ELF binary of the Black Magic DFU bootloader.
|
||||||
|
blackmagic_dfu.bin - Flat binary of the DFU bootloader, load at 0x8000000.
|
||||||
|
|
||||||
|
If you already have a JTAG/SWD debug probe that can be used to load these
|
||||||
|
binaries to your target hardware. If not the SystemMemory bootloader can
|
||||||
|
be used to load the DFU bootloader:
|
||||||
|
../scripts/bootprog.py blackmagic_dfu.bin
|
||||||
|
|
||||||
|
This requires an appropriate cable to connect the PC serial port to the probe.
|
||||||
|
See the schematic for more information.
|
||||||
|
|
||||||
|
Once the DFU bootloader is loaded, the Black Magic application can be loaded
|
||||||
|
over USB:
|
||||||
|
(First connect the probe and observe the flashing red led)
|
||||||
|
../scripts/stm32_mem.py blackmagic.bin
|
||||||
|
|
||||||
|
The device should reset and re-enumerate as a CDC-ACM device implementing
|
||||||
|
the GDB protocol.
|
||||||
|
|
||||||
|
|
||||||
|
Compiling as a Linux application using FT2232 hardware
|
||||||
|
------------------------------------------------------
|
||||||
|
The Black Magic application can also be compiled as a native Linux application
|
||||||
|
which will use an FT2232 device to implement the physical JTAG interface.
|
||||||
|
This is not the intended mode of operation, but is useful for debugging,
|
||||||
|
experimentation, and if you don't have the actual hardware.
|
||||||
|
|
||||||
|
First, get the VID/PID for your FT2232 device using 'lsusb'. Edit the file
|
||||||
|
'src/linux/platform.h' and change the VID/PID to match your hardware.
|
||||||
|
Compile the application with the command:
|
||||||
|
|
||||||
|
make PROBE_HOST=linux
|
||||||
|
|
||||||
|
Running the application 'blackmagic' will start a GDB server on TCP port 2000.
|
||||||
|
|
||||||
|
|
||||||
|
39
Makefile
39
Makefile
@ -1,39 +0,0 @@
|
|||||||
ifneq ($(V), 1)
|
|
||||||
MFLAGS += --no-print-dir
|
|
||||||
Q := @
|
|
||||||
endif
|
|
||||||
|
|
||||||
PC_HOSTED =
|
|
||||||
NO_LIBOPENCM3 =
|
|
||||||
ifeq ($(PROBE_HOST), hosted)
|
|
||||||
PC_HOSTED = true
|
|
||||||
NO_LIBOPENCM3 = true
|
|
||||||
endif
|
|
||||||
|
|
||||||
all:
|
|
||||||
ifndef NO_LIBOPENCM3
|
|
||||||
$(Q)if [ ! -f libopencm3/Makefile ]; then \
|
|
||||||
echo "Initialising git submodules..." ;\
|
|
||||||
git submodule init ;\
|
|
||||||
git submodule update ;\
|
|
||||||
fi
|
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/sam/d
|
|
||||||
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 $@
|
|
||||||
|
|
||||||
clang-tidy:
|
|
||||||
$(Q)scripts/run-clang-tidy.py -s "$(PWD)"
|
|
||||||
|
|
||||||
clang-format:
|
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
|
||||||
|
|
||||||
.PHONY: clean all_platforms clang-tidy clang-format
|
|
48
README
Normal file
48
README
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
=======================================
|
||||||
|
The Black Magic Debug Project -- README
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The Black Magic Debug Probe is gadget providing an in-application
|
||||||
|
debug capability for ARM Cortex-M3 and Cortex-M0 microcontrollers. It
|
||||||
|
interfaces with the GDB, the GNU source level debugger over the USB
|
||||||
|
bus, and communicates with the target microcontroller over either a
|
||||||
|
JTAG or Serial Wire debug port.
|
||||||
|
|
||||||
|
Currently supported microcontrollers:
|
||||||
|
|
||||||
|
- ST STM32 Family
|
||||||
|
- TI/LMI Stellaris Family
|
||||||
|
- NXP LPC11xx Family
|
||||||
|
|
||||||
|
|
||||||
|
Getting started
|
||||||
|
===============
|
||||||
|
The Black Magic Probe is available as a built-up product form Black
|
||||||
|
Sphere Technologies. Contact <sales@blacksphere.co.nz> should
|
||||||
|
you wish to order one. If you would like to work on the project see
|
||||||
|
the file HACKING for developer's info.
|
||||||
|
|
||||||
|
When connected via USB, the Black Magic probe will enumerate as a CDC-ACM
|
||||||
|
device which the OS should present as a tty device or serial port. The
|
||||||
|
GDB remote debugging protocol is implemented over this virtual character
|
||||||
|
stream. To connect your ARM GDB to the target device use the following
|
||||||
|
commands:
|
||||||
|
|
||||||
|
(gdb) target extended-remote /dev/ttyACM0
|
||||||
|
(gdb) mon jtag_scan
|
||||||
|
(gdb) attach 1
|
||||||
|
|
||||||
|
The command 'mon swdp_scan' may be used to use the Serial-Wire Debug Protocol
|
||||||
|
instead of JTAG to connect to the target.
|
||||||
|
|
||||||
|
Once attached, all the standard GDB commands may be used to start and control
|
||||||
|
the execution of the embedded application.
|
||||||
|
|
||||||
|
|
||||||
|
Project layout
|
||||||
|
==============
|
||||||
|
flashstub/ - Source code for flash programming stubs in target drivers.
|
||||||
|
hardware/ - Schematic (gschem) and PCB layout for the Black Magic Probe
|
||||||
|
scripts/ - Python scripts useful for programming devices.
|
||||||
|
src/ - Firmware source code for the Black Magic debug probe.
|
||||||
|
|
54
README.md
54
README.md
@ -1,54 +0,0 @@
|
|||||||
Jeff Probe
|
|
||||||
==========
|
|
||||||
|
|
||||||
This is a fork of the [original Black Magic Probe](https://github.com/blacksphere/blackmagic).
|
|
||||||
|
|
||||||
The original is arguably better, faster and wider supported. However, this
|
|
||||||
project was a way to offer an affordable version and I'll rely on community
|
|
||||||
support and pull requests.
|
|
||||||
|
|
||||||
One urguably better funncton is the ability to do DEBUG and Serial communication
|
|
||||||
over a single JTAG cable when paired with a device that uses single wire JTAG.
|
|
||||||
|
|
||||||
Normally, the serial header can be used on a target for the serial port, and
|
|
||||||
shows up as the second serial device on the system, however, we can dynamically
|
|
||||||
change the pins to use the ones on the JTAG cable with the following command:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
$ mon convert_tdio enable
|
|
||||||
```
|
|
||||||
|
|
||||||
Compilation
|
|
||||||
---
|
|
||||||
|
|
||||||
Newer toolchains can cause issues. I usually work 4_9-2014q4-20141203 found [here.](https://launchpad.net/gcc-arm-embedded/4.9/4.9-2014-q4-major/+download/gcc-arm-none-eabi-4_9-2014q4-20141203-mac.tar.bz2)
|
|
||||||
|
|
||||||
the versionfollowing version
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ make clean
|
|
||||||
$ make PROBE_HOST=jeff CUSTOM_SER=1
|
|
||||||
$ dfu-util --device ,1d50:6017 -s 0x00002000:leave -D src/blackmagic.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
CUSTOM OPTIONS
|
|
||||||
---
|
|
||||||
|
|
||||||
On mac, our device shows up with a serial number /dev/tty.cuJEFF123HDC
|
|
||||||
|
|
||||||
This can be annoy if we want to autocnnect with a gith script. We can override
|
|
||||||
the use of a serial number by doing a custom compliation such that our device
|
|
||||||
shows up with the following: /dev/cu.usbmodemJEFF1 and /dev/cu.usbmodemJEFF3
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ make PROBE_HOST=jeff CUSTOM_SER=1
|
|
||||||
```
|
|
||||||
|
|
||||||
More
|
|
||||||
---
|
|
||||||
|
|
||||||
More helpful information can be found on the black magic probe [readme](https://github.com/blacksphere/blackmagic/blob/master/README.md#black-magic-probe), which is relevant.
|
|
||||||
|
|
||||||
See online documentation at https://github.com/blacksphere/blackmagic/wiki
|
|
||||||
|
|
||||||
Binaries from the latest automated build can be found on the release page.
|
|
271
UsingRTT.md
271
UsingRTT.md
@ -1,271 +0,0 @@
|
|||||||
# 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)
|
|
260
UsingSWO
260
UsingSWO
@ -1,260 +0,0 @@
|
|||||||
SWO is a datastream that comes out of a single pin when the debug interface
|
|
||||||
is in SWD mode. It can be encoded either using NRZ (UART) or RZ (Manchester)
|
|
||||||
formats. The pin is a dedicated one that would be used for TDO when the
|
|
||||||
debug interface is in JTAG mode. On the STM32 it's port PB3.
|
|
||||||
|
|
||||||
When in NRZ mode the SWO data rate that comes out of the chip _must_ match
|
|
||||||
the rate that the debugger expects. By default on BMP the baudrate is
|
|
||||||
2.25MBps but that can be changed as an optional parameter to the monitor
|
|
||||||
traceswo command, like this;
|
|
||||||
|
|
||||||
monitor traceswo 115200
|
|
||||||
|
|
||||||
....would set the swo output at the low speed of 115kbps.
|
|
||||||
|
|
||||||
We are constrained on maximum input speed by both the capabilities of the
|
|
||||||
BMP STM32F103 USART and the ability to get the packets back out over the USB
|
|
||||||
link. The UART baudrate is set by b=(72x10^6)/d...with d >= 16 or
|
|
||||||
a maximum speed of 4.5Mbps UART1 and 2.25 Mbps on UART2.
|
|
||||||
For continious streaming that turns out to be_too_ fast for the USB
|
|
||||||
link, so the next available option is the 2.25Mbps that we use. ....
|
|
||||||
You can safely use the 4.5Mbps setting if your debug data
|
|
||||||
is bursty, or if you're using a different CPU to the STM32F103 as your BMP
|
|
||||||
host, but you potentially run the risk of losing packets if you have long
|
|
||||||
runs of sending which the usb cannot flush in time (there's a 12K buffer, so
|
|
||||||
the it is a pretty long run before it becomes a problem).
|
|
||||||
|
|
||||||
Note that the baudrate equation means there are only certain speeds
|
|
||||||
available. The highest:
|
|
||||||
BRR USART1(stlink) USART2(swlink)
|
|
||||||
16 4.50 Mbps 2.25 Mbps
|
|
||||||
17 4.235 Mbps 2.118 Mbps
|
|
||||||
18 4.000 Mbps 2.0 Mbps
|
|
||||||
19 3.789 Mbps 1.895 Mbps
|
|
||||||
20 3.600 Mbps 1.8 Mbps
|
|
||||||
...
|
|
||||||
24 3.0 Mbps 1.5 Mbps
|
|
||||||
...
|
|
||||||
36 2.0 Mbps 1.0 Mbps
|
|
||||||
|
|
||||||
...the USART will cope with some timing slip, but it's advisible to stay as
|
|
||||||
close to these values as you can. As the speed comes down the spread between
|
|
||||||
each valid value so mis-timing is less of an issue. The 'monitor traceswo
|
|
||||||
<x>' command will automatically find the closest divisor to the value you
|
|
||||||
set for the speed, so be aware the error could be significant.
|
|
||||||
|
|
||||||
Depending on what you're using to wake up SWO on the target side, you may
|
|
||||||
need code to get it into the correct mode and emitting data. You can do that
|
|
||||||
via gdb direct memory accesses, or from program code.
|
|
||||||
|
|
||||||
An example for a STM32F103 for the UART (NRZ) data format that we use;
|
|
||||||
|
|
||||||
/* STM32 specific configuration to enable the TRACESWO IO pin */
|
|
||||||
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
|
|
||||||
AFIO->MAPR |= (2 << 24); // Disable JTAG to release TRACESWO
|
|
||||||
DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; // Enable IO trace pins
|
|
||||||
|
|
||||||
TPI->ACPR = 31; // Output bits at 72000000/(31+1)=2.25MHz.
|
|
||||||
TPI->SPPR = 2; // Use Async mode (1 for RZ/Manchester)
|
|
||||||
TPI-FFCR = 0; // Disable formatter
|
|
||||||
|
|
||||||
/* Configure instrumentation trace macroblock */
|
|
||||||
ITM->LAR = 0xC5ACCE55;
|
|
||||||
ITM->TCR = 1 << ITM_TCR_TraceBusID_Pos | ITM_TCR_SYNCENA_Msk |
|
|
||||||
ITM_TCR_ITMENA_Msk;
|
|
||||||
ITM->TER = 0xFFFFFFFF; // Enable all stimulus ports
|
|
||||||
|
|
||||||
Code for the STM32L476 might look like:
|
|
||||||
#define BAUDRATE 115200
|
|
||||||
DBGMCU->CR |= DBGMCU_CR_TRACE_IOEN; /* Enable IO pins for Async trace */
|
|
||||||
uint32_t divisor, clk_frequency;
|
|
||||||
clk_frequency = NutGetCpuClock();
|
|
||||||
divisor = clk_frequency / BAUDRATE;
|
|
||||||
divisor--;
|
|
||||||
TPI->CSPSR = 1; /* port size = 1 bit */
|
|
||||||
TPI->ACPR = divisor;
|
|
||||||
TPI->SPPR = 2; /*Use Async mode pin protocol */
|
|
||||||
TPI->FFCR = 0x00; /* Bypass the TPIU formatter and send output directly*/
|
|
||||||
|
|
||||||
/* Configure Trace Port Interface Unit */
|
|
||||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable access to registers
|
|
||||||
DWT->CTRL = 0x400003FE; // DWT needs to provide sync for ITM
|
|
||||||
ITM->LAR = 0xC5ACCE55; // Allow access to the Control Register
|
|
||||||
ITM->TPR = 0x0000000F; // Trace access privilege from user level code, please
|
|
||||||
ITM->TCR = 0x0001000D; // ITM_TCR_TraceBusID_Msk | ITM_TCR_DWTENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk
|
|
||||||
ITM->TER = 1; // Only Enable stimulus port 1
|
|
||||||
|
|
||||||
while(1) {
|
|
||||||
for (uint32_t i = 'A'; i <= 'Z'; i++) {
|
|
||||||
ITM_SendChar(i);
|
|
||||||
NutSleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
If you're using RZ mode (e.g. on a genuine BMP) then you will need the trace
|
|
||||||
output speed to be quite a lot lower...in the order of 200kHz by means of
|
|
||||||
changing the divisor to something like 359. That's because the STM32F103
|
|
||||||
doesn't have a dedicated RZ decoder so it all has to be done in
|
|
||||||
software. The advantage of RZ is that the probe can adapt to the speed of
|
|
||||||
the target, so you don't have to set the speed on the probe in the monitor
|
|
||||||
traceswo command, and it will be tolerant of different speeds.
|
|
||||||
|
|
||||||
The SWO data appears on USB Interface 5, Endpoint 5.
|
|
||||||
|
|
||||||
SWOListen
|
|
||||||
=========
|
|
||||||
A program swolisten.c is found in ./scripts which will listen to this
|
|
||||||
endpoint, decode the datastream, and output it to a set of unix fifos which
|
|
||||||
can then be used as the input to other programs (e.g. cat, or something more
|
|
||||||
sophisticated like gnuplot, octave or whatever). This program doesn't care
|
|
||||||
if the data originates from a RZ or NRZ port, or at what speed.
|
|
||||||
|
|
||||||
Note that swolisten can be used with either BMP firmware, or with a
|
|
||||||
conventional TTL serial dongle. See at the bottom of this file for
|
|
||||||
information on how to use a dongle.
|
|
||||||
|
|
||||||
The command line to build the swolisten tool may look like:
|
|
||||||
|
|
||||||
E.g. for Ubuntu
|
|
||||||
gcc -I /usr/local/include/libusb-1.0 -L /usr/local/lib swolisten.c -o swolisten -lusb-1.0
|
|
||||||
|
|
||||||
E.g. For Opensuse:
|
|
||||||
gcc -I /usr/include/libusb-1.0 swolisten.c swolisten -std=gnu99 -g -Og -lusb-1.0
|
|
||||||
|
|
||||||
...you will obviously need to change the paths to your libusb files.
|
|
||||||
|
|
||||||
Attach to BMP to your PC:
|
|
||||||
Start gdb: "arm-none-eabi-gdb"
|
|
||||||
Choose bmp as target, like:
|
|
||||||
"target extended /dev/ttyACM0(*)"
|
|
||||||
Start SWO output: "mon traceswo"
|
|
||||||
If async SWO is used, give the baudrate your device sends
|
|
||||||
out as argument. 2.25 MBaud is the default, for the STM32L476 example above
|
|
||||||
the command would be: "mon traceswo 115200(*)".
|
|
||||||
Scan the SWD "mon swdp_scan"
|
|
||||||
Attach to the device: : "attach 1"
|
|
||||||
Start the program: "r".
|
|
||||||
(*) Your milage may vary
|
|
||||||
Now start swolisten without further options.
|
|
||||||
|
|
||||||
By default the tool will create fifos for the first 32 channels in a
|
|
||||||
directory swo (which you will need to create) as follows;
|
|
||||||
|
|
||||||
>ls swo/
|
|
||||||
chan00 chan02 chan04 chan06 chan08 chan0A chan0C chan0E chan10 chan12 chan14
|
|
||||||
chan16 chan18 chan1A chan1C chan1E chan01 chan03 chan05 chan07 chan09 chan0B
|
|
||||||
chan0D chan0F chan11 chan13 chan15 chan17 chan19 chan1B chan1D chan1F
|
|
||||||
|
|
||||||
>cat swo/channel0
|
|
||||||
<<OUTPUT FROM ITM Channel 0>>
|
|
||||||
|
|
||||||
With the F103 and L476 examples above, an endless stream of
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" should be seen. During reset of the target
|
|
||||||
device, no output will appear, but with release of reset output restarts.
|
|
||||||
|
|
||||||
Information about command line options can be found with the -h option.
|
|
||||||
swolisten is specifically designed to be 'hardy' to probe and target
|
|
||||||
disconnects and restarts (y'know, like you get in the real world). The
|
|
||||||
intention being to give you streams whenever it can get them. It does _not_
|
|
||||||
require gdb to be running. For the time being traceswo is not turned on by
|
|
||||||
default in the BMP to avoid possible interactions and making the overall
|
|
||||||
thing less reliable so You do need gdb to send the initial 'monitor
|
|
||||||
traceswo' to the probe, but beyond that there's no requirement for gdb to be
|
|
||||||
present.
|
|
||||||
|
|
||||||
Reliability
|
|
||||||
===========
|
|
||||||
|
|
||||||
A whole chunk of work has gone into making sure the dataflow over the SWO
|
|
||||||
link is reliable. The TL;DR is that the link _is_ reliable. There are
|
|
||||||
factors outside of our control (i.e. the USB bus you connect to) that could
|
|
||||||
potentially break the reliabilty but there's not too much we can do about
|
|
||||||
that since the SWO link is unidirectional (no opportunity for
|
|
||||||
re-transmits). The following section provides evidence for the claim that
|
|
||||||
the link is good;
|
|
||||||
|
|
||||||
A test 'mule' sends data flat out to the link at the maximum data rate of
|
|
||||||
2.25Mbps using a loop like the one below;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
for (uint32_t r=0; r<26; r++)
|
|
||||||
{
|
|
||||||
for (uint32_t g=0; g<31; g++)
|
|
||||||
{
|
|
||||||
ITM_SendChar('A'+r);
|
|
||||||
}
|
|
||||||
ITM_SendChar('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
100MB of data (more than 200MB of actual SWO packets, due to the encoding) was sent from the mule to the BMP where the
|
|
||||||
output from swolisten chan00 was cat'ted into a file;
|
|
||||||
|
|
||||||
>cat swo/chan00 > o
|
|
||||||
|
|
||||||
....this process was interrupted once the file had grown to 100MB. The first
|
|
||||||
and last lines were removed from it (these represent previously buffered
|
|
||||||
data and an incomplete packet at the point where the capture was
|
|
||||||
interrupted) and the resulting file analysed for consistency;
|
|
||||||
|
|
||||||
> sort o | uniq -c
|
|
||||||
|
|
||||||
The output was;
|
|
||||||
|
|
||||||
126462 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
126462 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
|
||||||
126462 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
|
||||||
126462 DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
|
||||||
126461 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
|
|
||||||
126461 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
|
||||||
126461 GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
|
|
||||||
126461 HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
|
|
||||||
126461 IIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
|
|
||||||
126461 JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
|
|
||||||
126461 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
|
|
||||||
126461 LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
|
|
||||||
126461 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
|
||||||
126461 NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
|
|
||||||
126461 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
|
||||||
126461 PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
|
|
||||||
126461 QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
|
|
||||||
126461 RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
|
||||||
126461 SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
|
|
||||||
126461 TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
|
|
||||||
126461 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
|
|
||||||
126461 VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
|
|
||||||
126461 WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
|
|
||||||
126461 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
||||||
126461 YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
|
|
||||||
126461 ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
|
|
||||||
|
|
||||||
(On inspection, the last line of recorded data was indeed a 'D' line).
|
|
||||||
|
|
||||||
Swolisten, using a TTL Serial Dongle
|
|
||||||
====================================
|
|
||||||
|
|
||||||
The NRZ data that comes out of the SWO is just UART formatted, but in a
|
|
||||||
frame. swolisten has been extended to accomodate TTL Serial Dongles that
|
|
||||||
can pick this up. Success has been had with CP2102 dongles at up to 921600
|
|
||||||
baud.
|
|
||||||
|
|
||||||
To use this mode just connect SWO to the RX pin of your dongle, and start
|
|
||||||
swolisten with parameters representing the speed and port. An example;
|
|
||||||
|
|
||||||
>./swolisten -p /dev/cu.SLAB_USBtoUART -v -b swo/ -s 921600
|
|
||||||
|
|
||||||
Any individual dongle will only support certain baudrates (Generally
|
|
||||||
multiples of 115200) so you may have to experiment to find the best
|
|
||||||
supported ones. For the CP2102 dongle 1.3824Mbps wasn't supported and
|
|
||||||
1.8432Mbps returned corrupted data.
|
|
||||||
|
|
||||||
Please email dave@marples.net with information about dongles you find work
|
|
||||||
well and at what speed.
|
|
||||||
|
|
||||||
Further information
|
|
||||||
===================
|
|
||||||
SWO is a wide field. Read e.g. the blogs around SWD on
|
|
||||||
http://shadetail.com/blog/swo-starting-the-steroids/
|
|
||||||
An open source program suite for SWO under active development is
|
|
||||||
https://github.com/mubes/orbuculum
|
|
@ -1,8 +0,0 @@
|
|||||||
# 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 /usr/sbin/udevadm control --reload-rules
|
|
||||||
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"
|
|
@ -13,17 +13,17 @@
|
|||||||
Signature="$Windows NT$"
|
Signature="$Windows NT$"
|
||||||
Class=Ports
|
Class=Ports
|
||||||
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
|
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
|
||||||
Provider=%BLACKMAGIC%
|
Provider=%BLACKSPHERE%
|
||||||
DriverVer=28/12/2011,0.0.1.1
|
DriverVer=28/12/2011,0.0.1.1
|
||||||
|
|
||||||
[Manufacturer]
|
[Manufacturer]
|
||||||
%VendorName%=DeviceList, NTamd64
|
%VendorName%=DeviceList, NTamd64
|
||||||
|
|
||||||
[Strings]
|
[Strings]
|
||||||
VendorName = "Black Magic Debug"
|
VendorName = "Black Sphere Technologies"
|
||||||
BLACKMAGICGDB = "Black Magic GDB Server"
|
BLACKMAGICGDB = "Black Magic GDB Server"
|
||||||
BLACKMAGICUART = "Black Magic UART Port"
|
BLACKMAGICUART = "Black Magic UART Port"
|
||||||
BLACKMAGIC_DISPLAY_NAME = "Black Magic Probe Driver"
|
BLACKSPHERE_DISPLAY_NAME = "Black Magic Probe Driver"
|
||||||
|
|
||||||
[DeviceList]
|
[DeviceList]
|
||||||
%BLACKMAGICGDB%=DriverInstall, USB\VID_1d50&PID_6018&Rev_0100&MI_00
|
%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
|
AddService = usbser,0x0002,DriverService.nt
|
||||||
|
|
||||||
[DriverService.nt]
|
[DriverService.nt]
|
||||||
DisplayName = %BLACKMAGIC_DISPLAY_NAME%
|
DisplayName = %BLACKSPHERE_DISPLAY_NAME%
|
||||||
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||||
StartType = 3 ; SERVICE_DEMAND_START
|
StartType = 3 ; SERVICE_DEMAND_START
|
||||||
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||||
@ -80,7 +80,7 @@ HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
|
|||||||
AddService = usbser,0x0002,DriverService.NTamd64
|
AddService = usbser,0x0002,DriverService.NTamd64
|
||||||
|
|
||||||
[DriverService.NTamd64]
|
[DriverService.NTamd64]
|
||||||
DisplayName = %BLACKMAGIC_DISPLAY_NAME%
|
DisplayName = %BLACKSPHERE_DISPLAY_NAME%
|
||||||
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||||
StartType = 3 ; SERVICE_DEMAND_START
|
StartType = 3 ; SERVICE_DEMAND_START
|
||||||
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
DeviceName = "Black Magic Firmware Upgrade"
|
DeviceName = "Black Magic Firmware Upgrade"
|
||||||
DeviceNameDFU = "Black Magic Probe (Upgrade)"
|
DeviceNameDFU = "Black Magic Probe (Upgrade)"
|
||||||
DeviceNameTPA = "Black Magic Trace Capture"
|
DeviceNameTPA = "Black Magic Trace Capture"
|
||||||
VendorName = "Black Magic Debug"
|
VendorName = "Black Sphere Technologies"
|
||||||
SourceName = "Black Magic Firmware Upgrade Install Disk"
|
SourceName = "Black Magic Firmware Upgrade Install Disk"
|
||||||
DeviceID = "VID_1d50&PID_6018&Rev_0100&MI_04"
|
DeviceID = "VID_1d50&PID_6018&Rev_0100&MI_04"
|
||||||
DeviceIDDFU= "VID_1d50&PID_6017&Rev_0100"
|
DeviceIDDFU= "VID_1d50&PID_6017&Rev_0100"
|
||||||
|
5
flashstub/README
Normal file
5
flashstub/README
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
These are the assembler routines for executing a flash write
|
||||||
|
on the supported targets. They are kept here for reference, but
|
||||||
|
are not used, as the compiled binary code is included in the
|
||||||
|
target drivers.
|
||||||
|
|
42
flashstub/lmi.s
Normal file
42
flashstub/lmi.s
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
_start:
|
||||||
|
ldr r0, _flashbase
|
||||||
|
ldr r1, _addr
|
||||||
|
mov r2, pc
|
||||||
|
add r2, #(_data - . - 2)
|
||||||
|
ldr r3, _size
|
||||||
|
ldr r5, _flash_write_cmd
|
||||||
|
_next:
|
||||||
|
cbz r3, _done
|
||||||
|
@ Write address to FMA
|
||||||
|
str r1, [r0]
|
||||||
|
@ Write data to FMD
|
||||||
|
ldr r4, [r2]
|
||||||
|
str r4, [r0, #4]
|
||||||
|
@ Write WRITE bit to FMC
|
||||||
|
str r5, [r0, #8]
|
||||||
|
_wait: @ Wait for WRITE bit to clear
|
||||||
|
ldr r4, [r0, #8]
|
||||||
|
mov r6, #1
|
||||||
|
tst r4, r6
|
||||||
|
bne _wait
|
||||||
|
|
||||||
|
sub r3, #1
|
||||||
|
add r1, #4
|
||||||
|
add r2, #4
|
||||||
|
b _next
|
||||||
|
_done:
|
||||||
|
bkpt
|
||||||
|
|
||||||
|
@.align 4
|
||||||
|
.org 0x28
|
||||||
|
_flashbase:
|
||||||
|
.word 0x400FD000
|
||||||
|
_flash_write_cmd:
|
||||||
|
.word 0xA4420001
|
||||||
|
_addr:
|
||||||
|
.word 0
|
||||||
|
_size:
|
||||||
|
.word 4
|
||||||
|
_data:
|
||||||
|
.string "Hello World!\n\0\0\0"
|
42
flashstub/stm32.s
Normal file
42
flashstub/stm32.s
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
ldr r0, _flashbase
|
||||||
|
ldr r1, _addr
|
||||||
|
mov r2, pc
|
||||||
|
add r2, #(_data - . - 2)
|
||||||
|
ldr r3, _size
|
||||||
|
mov r5, #1
|
||||||
|
_next:
|
||||||
|
cbz r3, _done
|
||||||
|
@ Write PG command to FLASH_CR
|
||||||
|
str r5, [r0, #0x10]
|
||||||
|
@ Write data to flash (half-word)
|
||||||
|
ldrh r4, [r2]
|
||||||
|
strh r4, [r1]
|
||||||
|
|
||||||
|
_wait: @ Wait for BSY bit to clear
|
||||||
|
ldr r4, [r0, #0x0C]
|
||||||
|
mov r6, #1
|
||||||
|
tst r4, r6
|
||||||
|
bne _wait
|
||||||
|
|
||||||
|
sub r3, #2
|
||||||
|
add r1, #2
|
||||||
|
add r2, #2
|
||||||
|
b _next
|
||||||
|
_done:
|
||||||
|
bkpt
|
||||||
|
|
||||||
|
@.align 4
|
||||||
|
.org 0x28
|
||||||
|
_flashbase:
|
||||||
|
.word 0x40022000
|
||||||
|
_addr:
|
||||||
|
.word 0
|
||||||
|
_size:
|
||||||
|
.word 12
|
||||||
|
_data:
|
||||||
|
.word 0xAAAAAAAA
|
||||||
|
.word 0xBBBBBBBB
|
||||||
|
.word 0xCCCCCCCC
|
44
flashstub/stm32f4.s
Normal file
44
flashstub/stm32f4.s
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
ldr r0, _flashbase
|
||||||
|
ldr r1, _addr
|
||||||
|
mov r2, pc
|
||||||
|
add r2, #(_data - . - 2)
|
||||||
|
ldr r3, _size
|
||||||
|
ldr r5, _cr
|
||||||
|
_next:
|
||||||
|
cbz r3, _done
|
||||||
|
@ Write PG command to FLASH_CR
|
||||||
|
str r5, [r0, #0x10]
|
||||||
|
@ Write data to flash (word)
|
||||||
|
ldr r4, [r2]
|
||||||
|
str r4, [r1]
|
||||||
|
|
||||||
|
_wait: @ Wait for BSY bit to clear
|
||||||
|
ldrh r4, [r0, #0x0E]
|
||||||
|
mov r6, #1
|
||||||
|
tst r4, r6
|
||||||
|
bne _wait
|
||||||
|
|
||||||
|
sub r3, #4
|
||||||
|
add r1, #4
|
||||||
|
add r2, #4
|
||||||
|
b _next
|
||||||
|
_done:
|
||||||
|
bkpt
|
||||||
|
|
||||||
|
@.align 4
|
||||||
|
.org 0x28
|
||||||
|
_cr:
|
||||||
|
.word 0x00000201
|
||||||
|
_flashbase:
|
||||||
|
.word 0x40023C00
|
||||||
|
_addr:
|
||||||
|
.word 0x0800bf78
|
||||||
|
_size:
|
||||||
|
.word 8
|
||||||
|
_data:
|
||||||
|
.word 0xAAAAAAAA
|
||||||
|
.word 0xBBBBBBBB
|
||||||
|
.word 0xCCCCCCCC
|
22
hardware/Makefile
Normal file
22
hardware/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
OUT = blackmagic_mini
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
all: $(OUT).gbr.zip $(OUT).xy
|
||||||
|
|
||||||
|
%.net: %.sch
|
||||||
|
gnetlist -g PCB -o $@ $<
|
||||||
|
|
||||||
|
%.xy %.bom: %.pcb
|
||||||
|
pcb -x bom $<
|
||||||
|
|
||||||
|
%.gbr: %.pcb
|
||||||
|
mkdir -p $@
|
||||||
|
pcb -x gerber --gerberfile $@/$(basename $@) $<
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
%.gbr.zip: %.gbr
|
||||||
|
zip -r $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -rf *.bom *.xy *.net *.gbr.zip $(OUT).gbr
|
||||||
|
|
1999
hardware/blackmagic.pcb
Normal file
1999
hardware/blackmagic.pcb
Normal file
File diff suppressed because it is too large
Load Diff
4161
hardware/blackmagic.sch
Normal file
4161
hardware/blackmagic.sch
Normal file
File diff suppressed because it is too large
Load Diff
1989
hardware/blackmagic_mini.pcb
Normal file
1989
hardware/blackmagic_mini.pcb
Normal file
File diff suppressed because it is too large
Load Diff
3078
hardware/blackmagic_mini.sch
Normal file
3078
hardware/blackmagic_mini.sch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
Subproject commit 63573143ef7e1b037d1f0c5baedc5264e12562b8
|
|
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/python
|
||||||
#
|
#
|
||||||
# bootprog.py: STM32 SystemMemory Production Programmer -- version 1.1
|
# bootprog.py: STM32 SystemMemory Production Programmer -- version 1.1
|
||||||
# Copyright (C) 2011 Black Sphere Technologies
|
# Copyright (C) 2011 Black Sphere Technologies
|
||||||
@ -28,100 +28,102 @@ class stm32_boot:
|
|||||||
|
|
||||||
# Turn on target device in SystemMemory boot mode
|
# Turn on target device in SystemMemory boot mode
|
||||||
self.serial.setDTR(1)
|
self.serial.setDTR(1)
|
||||||
sleep(0.1)
|
sleep(0.1);
|
||||||
|
|
||||||
self._sync()
|
self._sync()
|
||||||
|
|
||||||
def _sync(self):
|
def _sync(self):
|
||||||
# Send sync byte
|
# Send sync byte
|
||||||
#print("sending sync byte")
|
#print "sending sync byte"
|
||||||
self.serial.write(b"\x7f")
|
self.serial.write("\x7F")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
|
|
||||||
def _sendcmd(self, cmd):
|
def _sendcmd(self, cmd):
|
||||||
cmd = ord(cmd)
|
if type(cmd) == int:
|
||||||
cmd = bytes((cmd, cmd ^ 0xff))
|
cmd = chr(cmd)
|
||||||
#print("sendcmd:", repr(cmd))
|
cmd += chr(ord(cmd) ^ 0xff)
|
||||||
|
#print "sendcmd:", repr(cmd)
|
||||||
self.serial.write(cmd)
|
self.serial.write(cmd)
|
||||||
|
|
||||||
def _send(self, data):
|
def _send(self, data):
|
||||||
csum = 0
|
csum = 0
|
||||||
for c in data: csum ^= c
|
for c in data: csum ^= ord(c)
|
||||||
data = data + bytes((csum,))
|
data = data + chr(csum)
|
||||||
#print("sending:", repr(data))
|
#print "sending:", repr(data)
|
||||||
self.serial.write(data)
|
self.serial.write(data)
|
||||||
|
|
||||||
def _checkack(self):
|
def _checkack(self):
|
||||||
ACK = b"\x79"
|
ACK = "\x79"
|
||||||
b = self.serial.read(1)
|
b = self.serial.read(1)
|
||||||
if b != ACK: raise Exception("Invalid ack: %r" % b)
|
if b != ACK: raise Exception("Invalid ack: %r" % b)
|
||||||
#print("got ack!")
|
#print "got ack!"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
self._sendcmd(b"\x00")
|
self._sendcmd("\x00")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
num = self.serial.read(1)[0]
|
num = ord(self.serial.read(1))
|
||||||
data = self.serial.read(num+1)
|
data = self.serial.read(num+1)
|
||||||
self._checkack()
|
self._checkack()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def eraseall(self):
|
def eraseall(self):
|
||||||
# Send erase cmd
|
# Send erase cmd
|
||||||
self._sendcmd(b"\x43")
|
self._sendcmd("\x43")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Global erase
|
# Global erase
|
||||||
self._sendcmd(b"\xff")
|
self._sendcmd("\xff")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
|
|
||||||
def read(self, addr, len):
|
def read(self, addr, len):
|
||||||
# Send read cmd
|
# Send read cmd
|
||||||
self._sendcmd(b"\x11")
|
self._sendcmd("\x11")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Send address
|
# Send address
|
||||||
self._send(struct.pack(">L", addr))
|
self._send(struct.pack(">L", addr))
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Send length
|
# Send length
|
||||||
self._sendcmd(bytes((len-1,)))
|
self._sendcmd(chr(len-1))
|
||||||
self._checkack()
|
self._checkack()
|
||||||
return self.serial.read(len)
|
return self.serial.read(len)
|
||||||
|
|
||||||
def write(self, addr, data):
|
def write(self, addr, data):
|
||||||
# Send write cmd
|
# Send write cmd
|
||||||
self._sendcmd(b"\x31")
|
self._sendcmd("\x31")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Send address
|
# Send address
|
||||||
self._send(struct.pack(">L", addr))
|
self._send(struct.pack(">L", addr))
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Send data
|
# Send data
|
||||||
self._send(bytes((len(data)-1,)) + data)
|
self._send(chr(len(data)-1) + data)
|
||||||
self._checkack()
|
self._checkack()
|
||||||
|
|
||||||
|
|
||||||
def write_protect(self, sectors):
|
def write_protect(self, sectors):
|
||||||
# Send WP cmd
|
# Send WP cmd
|
||||||
self._sendcmd(b"\x63")
|
self._sendcmd("\x63")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Send sector list
|
# Send sector list
|
||||||
self._send(bytes((len(sectors)-1,)) + bytes(sectors))
|
self._send(chr(len(sectors)-1) + "".join(chr(i) for i in sectors))
|
||||||
self._checkack()
|
self._checkack()
|
||||||
# Resync after system reset
|
# Resync after system reset
|
||||||
self._sync()
|
self._sync()
|
||||||
|
|
||||||
def write_unprotect(self):
|
def write_unprotect(self):
|
||||||
self._sendcmd(b"\x73")
|
self._sendcmd("\x73")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._sync()
|
self._sync()
|
||||||
|
|
||||||
def read_protect(self):
|
def read_protect(self):
|
||||||
self._sendcmd(b"\x82")
|
self._sendcmd("\x82")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._sync()
|
self._sync()
|
||||||
|
|
||||||
def read_unprotect(self):
|
def read_unprotect(self):
|
||||||
self._sendcmd(b"\x92")
|
self._sendcmd("\x92")
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._checkack()
|
self._checkack()
|
||||||
self._sync()
|
self._sync()
|
||||||
@ -131,12 +133,12 @@ if __name__ == "__main__":
|
|||||||
from sys import stdout, argv, platform
|
from sys import stdout, argv, platform
|
||||||
from getopt import getopt
|
from getopt import getopt
|
||||||
|
|
||||||
if platform == "linux":
|
if platform == "linux2":
|
||||||
print("\x1b\x5b\x48\x1b\x5b\x32\x4a") # clear terminal screen
|
print "\x1b\x5b\x48\x1b\x5b\x32\x4a" # clear terminal screen
|
||||||
print("STM32 SystemMemory Production Programmer -- version 1.1")
|
print "STM32 SystemMemory Production Programmer -- version 1.1"
|
||||||
print("Copyright (C) 2011 Black Sphere Technologies")
|
print "Copyright (C) 2011 Black Sphere Technologies"
|
||||||
print("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>")
|
print "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>"
|
||||||
print()
|
print
|
||||||
|
|
||||||
dev = "COM1" if platform == "win32" else "/dev/ttyUSB0"
|
dev = "COM1" if platform == "win32" else "/dev/ttyUSB0"
|
||||||
baud = 115200
|
baud = 115200
|
||||||
@ -150,38 +152,38 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
progfile = args[0]
|
progfile = args[0]
|
||||||
except:
|
except:
|
||||||
print("Usage %s [-d <dev>] [-b <baudrate>] [-a <address>] <filename>" % argv[0])
|
print "Usage %s [-d <dev>] [-b <baudrate>] [-a <address>] <filename>" % argv[0]
|
||||||
print("\t-d : Use target on interface <dev> (default: %s)" % dev)
|
print "\t-d : Use target on interface <dev> (default: %s)" % dev
|
||||||
print("\t-b : Set device baudrate (default: %d)" % baud)
|
print "\t-b : Set device baudrate (default: %d)" % baud
|
||||||
print("\t-a : Set programming address (default: 0x%X)" % addr)
|
print "\t-a : Set programming address (default: 0x%X)" % addr
|
||||||
print()
|
print
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
prog = open(progfile, "rb").read()
|
prog = open(progfile, "rb").read()
|
||||||
boot = stm32_boot(dev, baud)
|
boot = stm32_boot(dev, baud)
|
||||||
|
|
||||||
cmds = boot.get()
|
cmds = boot.get()
|
||||||
print("Target bootloader version: %d.%d\n" % (cmds[0] >> 4, cmds[0] % 0xf))
|
print "Target bootloader version: %d.%d\n" % (ord(cmds[0]) >> 4, ord(cmds[0]) % 0xf)
|
||||||
|
|
||||||
print("Removing device protection...")
|
print "Removing device protection..."
|
||||||
boot.read_unprotect()
|
boot.read_unprotect()
|
||||||
boot.write_unprotect()
|
boot.write_unprotect()
|
||||||
print("Erasing target device...")
|
print "Erasing target device..."
|
||||||
boot.eraseall()
|
boot.eraseall()
|
||||||
addr = 0x8000000
|
addr = 0x8000000
|
||||||
while prog:
|
while prog:
|
||||||
print("Programming address 0x%08X..0x%08X...\r" % (addr, addr + min(len(prog), 255)), end=' ')
|
print ("Programming address 0x%08X..0x%08X...\r" % (addr, addr + min(len(prog), 255))),
|
||||||
stdout.flush()
|
stdout.flush();
|
||||||
boot.write(addr, prog[:256])
|
boot.write(addr, prog[:256])
|
||||||
addr += 256
|
addr += 256
|
||||||
prog = prog[256:]
|
prog = prog[256:]
|
||||||
|
|
||||||
print
|
print
|
||||||
print("Enabling device protection...")
|
print "Enabling device protection..."
|
||||||
boot.write_protect(range(0,2))
|
boot.write_protect(range(0,2))
|
||||||
#boot.read_protect()
|
#boot.read_protect()
|
||||||
|
|
||||||
print("All operations completed.")
|
print "All operations completed."
|
||||||
print
|
print
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Written by Antonio Galea - 2010/11/18
|
|
||||||
# Distributed under Gnu LGPL 3.0
|
|
||||||
# see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
|
|
||||||
import sys,struct,zlib,os
|
|
||||||
from optparse import OptionParser
|
|
||||||
from intelhex import IntelHex
|
|
||||||
|
|
||||||
DEFAULT_DEVICE="0x0483:0xdf11"
|
|
||||||
|
|
||||||
def named(tuple,names):
|
|
||||||
return dict(zip(names.split(),tuple))
|
|
||||||
def consume(fmt,data,names):
|
|
||||||
n = struct.calcsize(fmt)
|
|
||||||
return named(struct.unpack(fmt,data[:n]),names),data[n:]
|
|
||||||
def cstring(string):
|
|
||||||
return string.split(b'\0',1)[0]
|
|
||||||
def compute_crc(data):
|
|
||||||
return 0xFFFFFFFF & -zlib.crc32(data) -1
|
|
||||||
|
|
||||||
def parse(file,dump_images=False):
|
|
||||||
print('File: "%s"' % file)
|
|
||||||
data = open(file,'rb').read()
|
|
||||||
crc = compute_crc(data[:-4])
|
|
||||||
prefix, data = consume('<5sBIB',data,'signature version size targets')
|
|
||||||
print('%(signature)r v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix)
|
|
||||||
for t in range(prefix['targets']):
|
|
||||||
tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements')
|
|
||||||
tprefix['num'] = t
|
|
||||||
if tprefix['named']:
|
|
||||||
tprefix['name'] = cstring(tprefix['name'])
|
|
||||||
else:
|
|
||||||
tprefix['name'] = b''
|
|
||||||
print('%(signature)r %(num)d, alt setting: %(altsetting)r, name: %(name)r, size: %(size)d, elements: %(elements)d' % tprefix)
|
|
||||||
tsize = tprefix['size']
|
|
||||||
target, data = data[:tsize], data[tsize:]
|
|
||||||
for e in range(tprefix['elements']):
|
|
||||||
eprefix, target = consume('<2I',target,'address size')
|
|
||||||
eprefix['num'] = e
|
|
||||||
print(' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix)
|
|
||||||
esize = eprefix['size']
|
|
||||||
image, target = target[:esize], target[esize:]
|
|
||||||
if dump_images:
|
|
||||||
out = '%s.target%d.image%d.bin' % (file,t,e)
|
|
||||||
open(out,'wb').write(image)
|
|
||||||
print(' DUMPED IMAGE TO "%s"' % out)
|
|
||||||
if len(target):
|
|
||||||
print("target %d: PARSE ERROR" % t)
|
|
||||||
suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc')
|
|
||||||
print('usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)r, %(len)d, 0x%(crc)08x' % suffix)
|
|
||||||
if crc != suffix['crc']:
|
|
||||||
print("CRC ERROR: computed crc32 is 0x%08x" % crc)
|
|
||||||
data = data[16:]
|
|
||||||
if data:
|
|
||||||
print("PARSE ERROR")
|
|
||||||
|
|
||||||
def build(file,targets,device=DEFAULT_DEVICE):
|
|
||||||
data = b''
|
|
||||||
for t,target in enumerate(targets):
|
|
||||||
tdata = b''
|
|
||||||
for image in target:
|
|
||||||
tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data']
|
|
||||||
|
|
||||||
trgt = b'Target'
|
|
||||||
st = b'ST...'
|
|
||||||
tdata = struct.pack('<6sBI255s2I',trgt,0,1,st,len(tdata),len(target)) + tdata
|
|
||||||
data += tdata
|
|
||||||
|
|
||||||
dfu_se = b'DfuSe'
|
|
||||||
ufd = b'UFD'
|
|
||||||
data = struct.pack('<5sBIB',dfu_se,1,len(data)+11,len(targets)) + data
|
|
||||||
|
|
||||||
v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
|
|
||||||
data += struct.pack('<4H3sB',0,d,v,0x011a,ufd,16)
|
|
||||||
crc = compute_crc(data)
|
|
||||||
data += struct.pack('<I',crc)
|
|
||||||
open(file,'wb').write(data)
|
|
||||||
|
|
||||||
if __name__=="__main__":
|
|
||||||
usage = """
|
|
||||||
%prog [-d|--dump] infile.dfu
|
|
||||||
%prog {-b|--build} address:file.bin [-b address:file.bin ...] [{-D|--device}=vendor:device] outfile.dfu
|
|
||||||
%prog {-i|--ihex} file.hex [-i file.hex ...] [{-D|--device}=vendor:device] outfile.dfu"""
|
|
||||||
|
|
||||||
parser = OptionParser(usage=usage)
|
|
||||||
parser.add_option("-b", "--build", action="append", dest="binfiles",
|
|
||||||
help="build a DFU file from given BINFILES", metavar="BINFILES")
|
|
||||||
parser.add_option("-i", "--ihex", action="append", dest="hexfiles",
|
|
||||||
help="build a DFU file from given HEXFILES", metavar="HEXFILES")
|
|
||||||
parser.add_option("-D", "--device", action="store", dest="device",
|
|
||||||
help="build for DEVICE, defaults to %s" % DEFAULT_DEVICE, metavar="DEVICE")
|
|
||||||
parser.add_option("-d", "--dump", action="store_true", dest="dump_images",
|
|
||||||
default=False, help="dump contained images to current directory")
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if (options.binfiles or options.hexfiles) and len(args)==1:
|
|
||||||
target = []
|
|
||||||
|
|
||||||
if options.binfiles:
|
|
||||||
for arg in options.binfiles:
|
|
||||||
try:
|
|
||||||
address,binfile = arg.split(':',1)
|
|
||||||
except ValueError:
|
|
||||||
print("Address:file couple '%s' invalid." % arg)
|
|
||||||
sys.exit(1)
|
|
||||||
try:
|
|
||||||
address = int(address,0) & 0xFFFFFFFF
|
|
||||||
except ValueError:
|
|
||||||
print("Address %s invalid." % address)
|
|
||||||
sys.exit(1)
|
|
||||||
if not os.path.isfile(binfile):
|
|
||||||
print("Unreadable file '%s'." % binfile)
|
|
||||||
sys.exit(1)
|
|
||||||
target.append({ 'address': address, 'data': open(binfile,'rb').read() })
|
|
||||||
|
|
||||||
if options.hexfiles:
|
|
||||||
for hex in options.hexfiles:
|
|
||||||
ih = IntelHex(hex)
|
|
||||||
address = ih.minaddr()
|
|
||||||
data = ih.tobinstr()
|
|
||||||
try:
|
|
||||||
address = address & 0xFFFFFFFF
|
|
||||||
except ValueError:
|
|
||||||
print("Address %s invalid." % address)
|
|
||||||
sys.exit(1)
|
|
||||||
target.append({ 'address': address, 'data': data })
|
|
||||||
|
|
||||||
outfile = args[0]
|
|
||||||
device = DEFAULT_DEVICE
|
|
||||||
if options.device:
|
|
||||||
device=options.device
|
|
||||||
try:
|
|
||||||
v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1))
|
|
||||||
except:
|
|
||||||
print("Invalid device '%s'." % device)
|
|
||||||
sys.exit(1)
|
|
||||||
build(outfile,[target],device)
|
|
||||||
elif len(args)==1:
|
|
||||||
infile = args[0]
|
|
||||||
if not os.path.isfile(infile):
|
|
||||||
print("Unreadable file '%s'." % infile)
|
|
||||||
sys.exit(1)
|
|
||||||
parse(infile, dump_images=options.dump_images)
|
|
||||||
else:
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
# dfu.py: Access USB DFU class devices
|
# dfu.py: Access USB DFU class devices
|
||||||
# Copyright (C) 2009 Black Sphere Technologies
|
# Copyright (C) 2009 Black Sphere Technologies
|
||||||
@ -62,7 +62,7 @@ DFU_STATUS_ERROR_POR = 0x0d
|
|||||||
DFU_STATUS_ERROR_UNKNOWN = 0x0e
|
DFU_STATUS_ERROR_UNKNOWN = 0x0e
|
||||||
DFU_STATUS_ERROR_STALLEDPKT = 0x0f
|
DFU_STATUS_ERROR_STALLEDPKT = 0x0f
|
||||||
|
|
||||||
class dfu_status:
|
class dfu_status(object):
|
||||||
def __init__(self, buf):
|
def __init__(self, buf):
|
||||||
self.bStatus = buf[0]
|
self.bStatus = buf[0]
|
||||||
self.bwPollTimeout = buf[1] + (buf[2]<<8) + (buf[3]<<16)
|
self.bwPollTimeout = buf[1] + (buf[2]<<8) + (buf[3]<<16)
|
||||||
@ -70,7 +70,7 @@ class dfu_status:
|
|||||||
self.iString = buf[5]
|
self.iString = buf[5]
|
||||||
|
|
||||||
|
|
||||||
class dfu_device:
|
class dfu_device(object):
|
||||||
def __init__(self, dev, conf, iface):
|
def __init__(self, dev, conf, iface):
|
||||||
self.dev = dev
|
self.dev = dev
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
@ -84,8 +84,6 @@ class dfu_device:
|
|||||||
self.index = self.iface.interfaceNumber
|
self.index = self.iface.interfaceNumber
|
||||||
else: self.index = self.iface
|
else: self.index = self.iface
|
||||||
|
|
||||||
def release(self):
|
|
||||||
self.handle.releaseInterface()
|
|
||||||
def detach(self, wTimeout=255):
|
def detach(self, wTimeout=255):
|
||||||
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
|
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
|
||||||
usb.RECIP_INTERFACE, DFU_DETACH,
|
usb.RECIP_INTERFACE, DFU_DETACH,
|
||||||
@ -157,7 +155,7 @@ class dfu_device:
|
|||||||
|
|
||||||
if ((status.bState == STATE_APP_DETACH) or
|
if ((status.bState == STATE_APP_DETACH) or
|
||||||
(status.bState == STATE_DFU_MANIFEST_WAIT_RESET)):
|
(status.bState == STATE_DFU_MANIFEST_WAIT_RESET)):
|
||||||
usb.reset(self.handle)
|
usb.reset(self.handle)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
raise Exception
|
raise Exception
|
||||||
@ -178,16 +176,16 @@ def finddevs():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
devs = finddevs()
|
devs = finddevs()
|
||||||
if not devs:
|
if not devs:
|
||||||
print("No devices found!")
|
print "No devices found!"
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
for dfu in devs:
|
for dfu in devs:
|
||||||
handle = dfu[0].open()
|
handle = dfu[0].open()
|
||||||
man = handle.getString(dfu[0].iManufacturer, 30)
|
man = handle.getString(dfu[0].iManufacturer, 30)
|
||||||
product = handle.getString(dfu[0].iProduct, 30)
|
product = handle.getString(dfu[0].iProduct, 30)
|
||||||
print("Device %s: ID %04x:%04x %s - %s" % (dfu[0].filename,
|
print "Device %s: ID %04x:%04x %s - %s" % (dfu[0].filename,
|
||||||
dfu[0].idVendor, dfu[0].idProduct, man, product))
|
dfu[0].idVendor, dfu[0].idProduct, man, product)
|
||||||
print("%r, %r" % (dfu[1], dfu[2]))
|
print "%r, %r" % (dfu[1], dfu[2])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
241
scripts/gdb.py
241
scripts/gdb.py
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/python
|
||||||
#
|
#
|
||||||
# gdb.py: Python module for low level GDB protocol implementation
|
# gdb.py: Python module for low level GDB protocol implementation
|
||||||
# Copyright (C) 2009 Black Sphere Technologies
|
# Copyright (C) 2009 Black Sphere Technologies
|
||||||
@ -24,12 +24,18 @@ import struct
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
def hexify(s):
|
def hexify(s):
|
||||||
"""Convert a bytes object into hex bytes representation"""
|
"""Convert a binary string into hex representation"""
|
||||||
return s.hex().encode()
|
ret = ''
|
||||||
|
for c in s:
|
||||||
|
ret += "%02X" % ord(c)
|
||||||
|
return ret
|
||||||
|
|
||||||
def unhexify(s):
|
def unhexify(s):
|
||||||
"""Convert a hex-encoded bytes into bytes object"""
|
"""Convert a hex string into binary representation"""
|
||||||
return bytes.fromhex(s.decode())
|
ret = ''
|
||||||
|
for i in range(0, len(s), 2):
|
||||||
|
ret += chr(int(s[i:i+2], 16))
|
||||||
|
return ret
|
||||||
|
|
||||||
class FakeSocket:
|
class FakeSocket:
|
||||||
"""Emulate socket functions send and recv on a file object"""
|
"""Emulate socket functions send and recv on a file object"""
|
||||||
@ -49,183 +55,145 @@ class Target:
|
|||||||
else:
|
else:
|
||||||
self.sock = FakeSocket(sock)
|
self.sock = FakeSocket(sock)
|
||||||
|
|
||||||
self.PacketSize=0x100 # default
|
|
||||||
|
|
||||||
def getpacket(self):
|
def getpacket(self):
|
||||||
"""Return the first correctly received packet from GDB target"""
|
"""Return the first correctly received packet from GDB target"""
|
||||||
while True:
|
while True:
|
||||||
while self.sock.recv(1) != b'$':
|
while self.sock.recv(1) != '$': pass
|
||||||
pass
|
|
||||||
|
|
||||||
csum = 0
|
csum = 0
|
||||||
packet = [] # list-of-small-int
|
packet = ''
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
c, = self.sock.recv(1)
|
c = self.sock.recv(1)
|
||||||
if c == ord('#'):
|
if c == '#': break
|
||||||
break
|
|
||||||
|
|
||||||
if c == ord('$'):
|
if c == '$':
|
||||||
packet = []
|
packet = ''
|
||||||
csum = 0
|
csum = 0
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if c == ord('}'):
|
if c == '}':
|
||||||
c, = self.sock.recv(1)
|
c = self.sock.recv(1)
|
||||||
csum += c + ord('}')
|
csum += ord(c) + ord('}')
|
||||||
packet.append(c ^ 0x20)
|
packet += chr(ord(c) ^ 0x20)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
packet.append(c)
|
packet += c
|
||||||
csum += c
|
csum += ord(c)
|
||||||
|
|
||||||
if (csum & 0xFF) == int(self.sock.recv(2),16):
|
if (csum & 0xFF) == int(self.sock.recv(2),16): break
|
||||||
break
|
|
||||||
|
|
||||||
self.sock.send(b'-')
|
self.sock.send('-')
|
||||||
|
|
||||||
|
self.sock.send('+')
|
||||||
|
return packet
|
||||||
|
|
||||||
self.sock.send(b'+')
|
|
||||||
return bytes(packet)
|
|
||||||
|
|
||||||
def putpacket(self, packet):
|
def putpacket(self, packet):
|
||||||
"""Send packet to GDB target and wait for acknowledge
|
"""Send packet to GDB target and wait for acknowledge"""
|
||||||
packet is bytes or string"""
|
|
||||||
|
|
||||||
if type(packet) == str:
|
|
||||||
packet = packet.encode()
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
out = []
|
self.sock.send('$')
|
||||||
|
csum = 0
|
||||||
for c in packet:
|
for c in packet:
|
||||||
if (c in b'$#}'):
|
if (c == '$') or (c == '#') or (c == '}'):
|
||||||
out.append(ord('}'))
|
self.sock.send('}')
|
||||||
out.append(c ^ 0x20)
|
self.sock.send(chr(ord(c) ^ 0x20))
|
||||||
|
csum += (ord(c) ^ 0x20) + ord('}')
|
||||||
else:
|
else:
|
||||||
out.append(c)
|
self.sock.send(c)
|
||||||
|
csum += ord(c)
|
||||||
csum = sum(out)
|
self.sock.send('#')
|
||||||
outb = b'$'+bytes(out)+b'#%02X' % (csum & 0xff)
|
self.sock.send("%02X" % (csum & 0xFF))
|
||||||
|
if self.sock.recv(1) == '+': break
|
||||||
self.sock.send(outb)
|
|
||||||
if self.sock.recv(1) == b'+':
|
|
||||||
break
|
|
||||||
|
|
||||||
def monitor(self, cmd):
|
def monitor(self, cmd):
|
||||||
"""Send gdb "monitor" command to target"""
|
"""Send gdb "monitor" command to target"""
|
||||||
if type(cmd) == str:
|
|
||||||
cmd = cmd.encode()
|
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
self.putpacket(b"qRcmd," + hexify(cmd))
|
self.putpacket("qRcmd," + hexify(cmd))
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
s = self.getpacket()
|
s = self.getpacket()
|
||||||
|
if s == '': return None
|
||||||
if s == b'':
|
if s == 'OK': return ret
|
||||||
return None
|
if s.startswith('O'): ret.append(unhexify(s[1:]))
|
||||||
if s == b'OK':
|
|
||||||
return ret
|
|
||||||
if s.startswith(b'O'):
|
|
||||||
ret.append(unhexify(s[1:]))
|
|
||||||
else:
|
else:
|
||||||
raise Exception('Invalid GDB stub response %r'%s.decode())
|
raise Exception('Invalid GDB stub response')
|
||||||
|
|
||||||
def attach(self, pid):
|
def attach(self, pid):
|
||||||
"""Attach to target process (gdb "attach" command)"""
|
"""Attach to target process (gdb "attach" command)"""
|
||||||
self.putpacket(b"vAttach;%08X" % pid)
|
self.putpacket("vAttach;%08X" % pid)
|
||||||
reply = self.getpacket()
|
reply = self.getpacket()
|
||||||
if (reply == b'') or (reply[:1] == b'E'):
|
if (len(reply) == 0) or (reply[0] == 'E'):
|
||||||
raise Exception('Failed to attach to remote pid %d' % pid)
|
raise Exception('Failed to attach to remote pid %d' % pid)
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
"""Detach from target process (gdb "detach" command)"""
|
"""Detach from target process (gdb "detach" command)"""
|
||||||
self.putpacket(b"D")
|
self.putpacket("D")
|
||||||
if self.getpacket() != b'OK':
|
if self.getpacket() != 'OK':
|
||||||
raise Exception("Failed to detach from remote process")
|
raise Exception("Failed to detach from remote process")
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset the target system"""
|
"""Reset the target system"""
|
||||||
self.putpacket(b"r")
|
self.putpacket("r")
|
||||||
|
|
||||||
def read_mem(self, addr, length):
|
def read_mem(self, addr, length):
|
||||||
"""Read length bytes from target at address addr"""
|
"""Read length bytes from target at address addr"""
|
||||||
ret = b''
|
self.putpacket("m%08X,%08X" % (addr, length))
|
||||||
while length:
|
reply = self.getpacket()
|
||||||
# print("Read")
|
if (len(reply) == 0) or (reply[0] == 'E'):
|
||||||
packlen = min(length,self.PacketSize//2)
|
raise Exception('Error reading memory at 0x%08X' % addr)
|
||||||
self.putpacket(b"m%08X,%08X" % (addr, packlen))
|
try:
|
||||||
reply = self.getpacket()
|
data = unhexify(reply)
|
||||||
if (reply == b'') or (reply[:1] == b'E'):
|
except Excpetion:
|
||||||
raise Exception('Error reading memory at 0x%08X' % addr)
|
raise Exception('Invalid response to memory read packet: %r' % reply)
|
||||||
try:
|
return data
|
||||||
data = unhexify(reply)
|
|
||||||
except Exception:
|
|
||||||
raise Exception('Invalid response to memory read packet: %r' % reply)
|
|
||||||
ret += data
|
|
||||||
length -= packlen
|
|
||||||
addr += packlen
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def write_mem(self, addr, data):
|
def write_mem(self, addr, data):
|
||||||
"""Write data to target at address addr"""
|
"""Write data to target at address addr"""
|
||||||
data = bytes(data)
|
self.putpacket("X%08X,%08X:%s" % (addr, len(data), data))
|
||||||
|
if self.getpacket() != 'OK':
|
||||||
while data:
|
raise Exception('Error writing to memory at 0x%08X' % addr)
|
||||||
d = data[:self.PacketSize-44]
|
|
||||||
data = data[len(d):]
|
|
||||||
#print("Writing %d bytes at 0x%X" % (len(d), addr))
|
|
||||||
pack = b"X%08X,%08X:%s" % (addr, len(d), d)
|
|
||||||
self.putpacket(pack)
|
|
||||||
|
|
||||||
if self.getpacket() != b'OK':
|
|
||||||
raise Exception('Error writing to memory at 0x%08X' % addr)
|
|
||||||
addr += len(d)
|
|
||||||
|
|
||||||
def read_regs(self):
|
def read_regs(self):
|
||||||
"""Read target core registers"""
|
"""Read target core registers"""
|
||||||
self.putpacket(b"g")
|
self.putpacket("g")
|
||||||
reply = self.getpacket()
|
reply = self.getpacket()
|
||||||
if (reply == b'') or (reply[:1] == b'E'):
|
if (len(reply) == 0) or (reply[0] == 'E'):
|
||||||
raise Exception('Error reading target core registers')
|
raise Exception('Error reading memory at 0x%08X' % addr)
|
||||||
try:
|
try:
|
||||||
data = unhexify(reply)
|
data = unhexify(reply)
|
||||||
except Exception:
|
except Excpetion:
|
||||||
raise Exception('Invalid response to registers read packet: %r' % reply)
|
raise Exception('Invalid response to memory read packet: %r' % reply)
|
||||||
ret = array.array('I',data)
|
return struct.unpack("=20L", data)
|
||||||
return ret
|
|
||||||
|
|
||||||
def write_regs(self, *regs):
|
def write_regs(self, *regs):
|
||||||
"""Write target core registers"""
|
"""Write target core registers"""
|
||||||
data = struct.pack("=%dL" % len(regs), *regs)
|
data = struct.pack("=%dL" % len(regs), *regs)
|
||||||
self.putpacket(b"G" + hexify(data))
|
self.putpacket("G" + hexify(data))
|
||||||
if self.getpacket() != b'OK':
|
if self.getpacket() != 'OK':
|
||||||
raise Exception('Error writing to target core registers')
|
raise Exception('Error writing to target core registers')
|
||||||
|
|
||||||
def memmap_read(self):
|
def memmap_read(self):
|
||||||
"""Read the XML memory map from target"""
|
"""Read the XML memory map from target"""
|
||||||
offset = 0
|
offset = 0
|
||||||
ret = b''
|
ret = ''
|
||||||
while True:
|
while True:
|
||||||
self.putpacket(b"qXfer:memory-map:read::%08X,%08X" % (offset, 512))
|
self.putpacket("qXfer:memory-map:read::%08X,%08X" % (offset, 512))
|
||||||
reply = self.getpacket()
|
reply = self.getpacket()
|
||||||
if (reply[0] in b'ml'):
|
if (reply[0] == 'm') or (reply[0] == 'l'):
|
||||||
offset += len(reply) - 1
|
offset += len(reply) - 1
|
||||||
ret += reply[1:]
|
ret += reply[1:]
|
||||||
else:
|
else:
|
||||||
raise Exception('Invalid GDB stub response %r'%reply)
|
raise Exception("Invalid GDB stub response")
|
||||||
|
|
||||||
if reply[:1] == b'l':
|
if reply[0] == 'l': return ret
|
||||||
return ret
|
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
"""Resume target execution"""
|
"""Resume target execution"""
|
||||||
self.putpacket(b'c')
|
self.putpacket("c")
|
||||||
|
|
||||||
def interrupt(self):
|
def interrupt(self):
|
||||||
"""Interrupt target execution"""
|
"""Interrupt target execution"""
|
||||||
self.sock.send(b'\x03')
|
self.sock.send("\x03")
|
||||||
|
|
||||||
def run_stub(self, stub, address, *args):
|
def run_stub(self, stub, address, *args):
|
||||||
"""Execute a binary stub at address, passing args in core registers."""
|
"""Execute a binary stub at address, passing args in core registers."""
|
||||||
@ -237,19 +205,11 @@ class Target:
|
|||||||
regs[15] = address
|
regs[15] = address
|
||||||
self.write_regs(*regs)
|
self.write_regs(*regs)
|
||||||
self.resume()
|
self.resume()
|
||||||
reply = None
|
reply = self.getpacket()
|
||||||
while not reply:
|
while not reply:
|
||||||
reply = self.getpacket()
|
reply = self.getpacket()
|
||||||
if not reply.startswith(b"T05"):
|
if not reply.startswith("T05"):
|
||||||
message = "Invalid stop response: %r" % reply
|
raise Exception("Invalid stop response: %r" % reply)
|
||||||
try:
|
|
||||||
message += {'T02':' (SIGINT)',
|
|
||||||
'T05':' (SIGTRAP)',
|
|
||||||
'T0B':' (SIGSEGV)',
|
|
||||||
'T1D':' (SIGLOST)'}[reply]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
raise Exception(message)
|
|
||||||
|
|
||||||
class FlashMemory:
|
class FlashMemory:
|
||||||
def __init__(self, target, offset, length, blocksize):
|
def __init__(self, target, offset, length, blocksize):
|
||||||
@ -257,61 +217,57 @@ class Target:
|
|||||||
self.offset = offset
|
self.offset = offset
|
||||||
self.length = length
|
self.length = length
|
||||||
self.blocksize = blocksize
|
self.blocksize = blocksize
|
||||||
self.blocks = list(None for i in range(length // blocksize))
|
self.blocks = list(None for i in range(length / blocksize))
|
||||||
|
|
||||||
def prog(self, offset, data):
|
def prog(self, offset, data):
|
||||||
assert type(data)==bytes
|
|
||||||
|
|
||||||
assert ((offset >= self.offset) and
|
assert ((offset >= self.offset) and
|
||||||
(offset + len(data) <= self.offset + self.length))
|
(offset + len(data) <= self.offset + self.length))
|
||||||
|
|
||||||
while data:
|
while data:
|
||||||
index = (offset - self.offset) // self.blocksize
|
index = (offset - self.offset) / self.blocksize
|
||||||
bloffset = (offset - self.offset) % self.blocksize
|
bloffset = (offset - self.offset) % self.blocksize
|
||||||
bldata = data[:self.blocksize-bloffset]
|
bldata = data[:self.blocksize-bloffset]
|
||||||
data = data[len(bldata):]; offset += len(bldata)
|
data = data[len(bldata):]; offset += len(bldata)
|
||||||
if self.blocks[index] is None: # Initialize a clear block
|
if self.blocks[index] is None: # Initialize a clear block
|
||||||
self.blocks[index] = bytes(0xff for i in range(self.blocksize))
|
self.blocks[index] = "".join(chr(0xff) for i in range(self.blocksize))
|
||||||
self.blocks[index] = (self.blocks[index][:bloffset] + bldata +
|
self.blocks[index] = (self.blocks[index][:bloffset] + bldata +
|
||||||
self.blocks[index][bloffset+len(bldata):])
|
self.blocks[index][bloffset+len(bldata):])
|
||||||
|
|
||||||
def commit(self, progress_cb=None):
|
def commit(self, progress_cb=None):
|
||||||
totalblocks = 0
|
totalblocks = 0
|
||||||
for b in self.blocks:
|
for b in self.blocks:
|
||||||
if b is not None:
|
if b is not None: totalblocks += 1
|
||||||
totalblocks += 1
|
|
||||||
|
|
||||||
block = 0
|
block = 0
|
||||||
for i in range(len(self.blocks)):
|
for i in range(len(self.blocks)):
|
||||||
block += 1
|
block += 1
|
||||||
if callable(progress_cb) and totalblocks > 0:
|
if callable(progress_cb):
|
||||||
progress_cb(block*100/totalblocks)
|
progress_cb(block*100/totalblocks)
|
||||||
|
|
||||||
# Erase the block
|
# Erase the block
|
||||||
data = self.blocks[i]
|
data = self.blocks[i]
|
||||||
addr = self.offset + self.blocksize * i
|
addr = self.offset + self.blocksize * i
|
||||||
if data is None:
|
if data is None: continue
|
||||||
continue
|
#print "Erasing flash at 0x%X" % (self.offset + self.blocksize*i)
|
||||||
#print("Erasing flash at 0x%X" % (self.offset + self.blocksize*i))
|
self.target.putpacket("vFlashErase:%08X,%08X" %
|
||||||
self.target.putpacket(b"vFlashErase:%08X,%08X" %
|
|
||||||
(self.offset + self.blocksize*i, self.blocksize))
|
(self.offset + self.blocksize*i, self.blocksize))
|
||||||
if self.target.getpacket() != b'OK':
|
if self.target.getpacket() != 'OK':
|
||||||
raise Exception("Failed to erase flash")
|
raise Exception("Failed to erase flash")
|
||||||
|
|
||||||
while data:
|
while data:
|
||||||
d = data[0:980]
|
d = data[0:980]
|
||||||
data = data[len(d):]
|
data = data[len(d):]
|
||||||
#print("Writing %d bytes at 0x%X" % (len(d), addr))
|
#print "Writing %d bytes at 0x%X" % (len(d), addr)
|
||||||
self.target.putpacket(b"vFlashWrite:%08X:%s" % (addr, d))
|
self.target.putpacket("vFlashWrite:%08X:%s" % (addr, d))
|
||||||
addr += len(d)
|
addr += len(d)
|
||||||
if self.target.getpacket() != b'OK':
|
if self.target.getpacket() != 'OK':
|
||||||
raise Exception("Failed to write flash")
|
raise Exception("Failed to write flash")
|
||||||
|
|
||||||
self.target.putpacket(b"vFlashDone")
|
self.target.putpacket("vFlashDone")
|
||||||
if self.target.getpacket() != b'OK':
|
if self.target.getpacket() != 'OK':
|
||||||
raise Exception("Failed to commit")
|
raise Exception("Failed to commit")
|
||||||
|
|
||||||
self.blocks = list(None for i in range(self.length // self.blocksize))
|
self.blocks = list(None for i in range(self.length / self.blocksize))
|
||||||
|
|
||||||
|
|
||||||
def flash_probe(self):
|
def flash_probe(self):
|
||||||
@ -319,8 +275,7 @@ class Target:
|
|||||||
xmldom = parseString(self.memmap_read())
|
xmldom = parseString(self.memmap_read())
|
||||||
|
|
||||||
for memrange in xmldom.getElementsByTagName("memory"):
|
for memrange in xmldom.getElementsByTagName("memory"):
|
||||||
if memrange.getAttribute("type") != "flash":
|
if memrange.getAttribute("type") != "flash": continue
|
||||||
continue
|
|
||||||
offset = eval(memrange.getAttribute("start"))
|
offset = eval(memrange.getAttribute("start"))
|
||||||
length = eval(memrange.getAttribute("length"))
|
length = eval(memrange.getAttribute("length"))
|
||||||
for property in memrange.getElementsByTagName("property"):
|
for property in memrange.getElementsByTagName("property"):
|
||||||
@ -336,9 +291,11 @@ class Target:
|
|||||||
|
|
||||||
def flash_write_prepare(self, address, data):
|
def flash_write_prepare(self, address, data):
|
||||||
for m in self.mem:
|
for m in self.mem:
|
||||||
if (address >= m.offset) and (address + len(data) <= m.offset + m.length):
|
if (address >= m.offset) and (address + len(data) < m.offset + m.length):
|
||||||
m.prog(address, data)
|
m.prog(address, data)
|
||||||
|
|
||||||
def flash_commit(self, progress_cb=None):
|
def flash_commit(self, progress_cb=None):
|
||||||
for m in self.mem:
|
for m in self.mem:
|
||||||
m.commit(progress_cb)
|
m.commit(progress_cb)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""Pulls nRF51 IDs from openocd's nrf51.c in a form suitable for
|
|
||||||
pasting into blackmagic's nrf51.c
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
import io
|
|
||||||
|
|
||||||
cmd = 'git archive --remote=git://git.code.sf.net/p/openocd/code HEAD src/flash/nor/nrf5.c | tar -xO'
|
|
||||||
|
|
||||||
class Spec():
|
|
||||||
def __repr__(self):
|
|
||||||
return "0x%04X: /* %s %s %s */"%(self.hwid,self.comment, self.variant,self.build_code)
|
|
||||||
|
|
||||||
proc = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
|
|
||||||
|
|
||||||
specdict = {}
|
|
||||||
specs = []
|
|
||||||
spec = Spec()
|
|
||||||
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
|
|
||||||
m = re.search(r'/\*(.*)\*/',line)
|
|
||||||
if m:
|
|
||||||
lastcomment=m.group(1)
|
|
||||||
|
|
||||||
m = re.search(r'NRF51_DEVICE_DEF\((0x[0-9A-F]*),\s*"(.*)",\s*"(.*)",\s*"(.*)",\s*([0-9]*)\s*\),', line)
|
|
||||||
if m:
|
|
||||||
spec.hwid = int(m.group(1), base=0)
|
|
||||||
spec.variant = m.group(3)
|
|
||||||
spec.build_code = m.group(4)
|
|
||||||
spec.flash_size_kb = int(m.group(5), base=0)
|
|
||||||
ram, flash = {'AA':(16,256),
|
|
||||||
'AB':(16,128),
|
|
||||||
'AC':(32,256)}[spec.variant[-2:]]
|
|
||||||
assert flash == spec.flash_size_kb
|
|
||||||
spec.ram_size_kb = ram
|
|
||||||
nicecomment = lastcomment.strip().replace('IC ','').replace('Devices ','').replace('.','')
|
|
||||||
spec.comment = nicecomment
|
|
||||||
|
|
||||||
specdict.setdefault((ram,flash),[]).append(spec)
|
|
||||||
specs.append(spec)
|
|
||||||
spec=Spec()
|
|
||||||
|
|
||||||
for (ram,flash),specs in specdict.items():
|
|
||||||
specs.sort(key=lambda x:x.hwid)
|
|
||||||
for spec in specs:
|
|
||||||
print("\tcase",spec)
|
|
||||||
print('\t\tt->driver = "Nordic nRF51";')
|
|
||||||
print('\t\ttarget_add_ram(t, 0x20000000, 0x%X);'%(1024*ram))
|
|
||||||
print('\t\tnrf51_add_flash(t, 0x00000000, 0x%X, NRF51_PAGE_SIZE);'%(1024*flash))
|
|
||||||
print('\t\tnrf51_add_flash(t, NRF51_UICR, 0x100, 0x100);')
|
|
||||||
print('\t\ttarget_add_commands(t, nrf51_cmd_list, "nRF51");')
|
|
||||||
print('\t\treturn true;')
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/python
|
||||||
|
|
||||||
# hexprog.py: Python application to flash a target with an Intel hex file
|
# hexprog.py: Python application to flash a target with an Intel hex file
|
||||||
# Copyright (C) 2011 Black Sphere Technologies
|
# Copyright (C) 2011 Black Sphere Technologies
|
||||||
@ -18,6 +18,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import gdb
|
import gdb
|
||||||
|
import struct
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# Microcode sequence to erase option bytes
|
# Microcode sequence to erase option bytes
|
||||||
@ -30,16 +31,15 @@ def flash_write_hex(target, hexfile, progress_cb=None):
|
|||||||
f = open(hexfile)
|
f = open(hexfile)
|
||||||
addrhi = 0
|
addrhi = 0
|
||||||
for line in f:
|
for line in f:
|
||||||
if line[0] != ':':
|
if line[0] != ':': raise Exception("Error in hex file")
|
||||||
raise Exception("Error in hex file")
|
|
||||||
reclen = int(line[1:3], 16)
|
reclen = int(line[1:3], 16)
|
||||||
addrlo = int(line[3:7], 16)
|
addrlo = int(line[3:7], 16)
|
||||||
rectype = int(line[7:9], 16)
|
rectype = int(line[7:9], 16);
|
||||||
if sum(x for x in bytes.fromhex(line[1:11+reclen*2])) & 0xff != 0:
|
if sum(ord(x) for x in gdb.unhexify(line[1:11+reclen*2])) & 0xff != 0:
|
||||||
raise Exception("Checksum error in hex file")
|
raise Exception("Checksum error in hex file")
|
||||||
if rectype == 0: # Data record
|
if rectype == 0: # Data record
|
||||||
addr = (addrhi << 16) + addrlo
|
addr = (addrhi << 16) + addrlo
|
||||||
data = bytes.fromhex(line[9:9+reclen*2])
|
data = gdb.unhexify(line[9:9+reclen*2])
|
||||||
target.flash_write_prepare(addr, data)
|
target.flash_write_prepare(addr, data)
|
||||||
pass
|
pass
|
||||||
elif rectype == 4: # High address record
|
elif rectype == 4: # High address record
|
||||||
@ -55,7 +55,7 @@ def flash_write_hex(target, hexfile, progress_cb=None):
|
|||||||
try:
|
try:
|
||||||
target.flash_commit(progress_cb)
|
target.flash_commit(progress_cb)
|
||||||
except:
|
except:
|
||||||
print("Flash write failed! Is device protected?\n")
|
print "Flash write failed! Is device protected?\n"
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
@ -64,11 +64,11 @@ if __name__ == "__main__":
|
|||||||
from sys import argv, platform, stdout
|
from sys import argv, platform, stdout
|
||||||
from getopt import getopt
|
from getopt import getopt
|
||||||
|
|
||||||
if platform == "linux":
|
if platform == "linux2":
|
||||||
print("\x1b\x5b\x48\x1b\x5b\x32\x4a") # clear terminal screen
|
print ("\x1b\x5b\x48\x1b\x5b\x32\x4a") # clear terminal screen
|
||||||
print("Black Magic Probe -- Target Production Programming Tool -- version 1.0")
|
print("Black Magic Probe -- Target Production Programming Tool -- version 1.0")
|
||||||
print("Copyright (C) 2011 Black Sphere Technologies")
|
print "Copyright (C) 2011 Black Sphere Technologies"
|
||||||
print("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>")
|
print "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>"
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
dev = "COM1" if platform == "win32" else "/dev/ttyACM0"
|
dev = "COM1" if platform == "win32" else "/dev/ttyACM0"
|
||||||
@ -80,20 +80,13 @@ if __name__ == "__main__":
|
|||||||
try:
|
try:
|
||||||
opts, args = getopt(argv[1:], "sd:b:t:rR")
|
opts, args = getopt(argv[1:], "sd:b:t:rR")
|
||||||
for opt in opts:
|
for opt in opts:
|
||||||
if opt[0] == "-s":
|
if opt[0] == "-s": scan = "swdp_scan"
|
||||||
scan = "swdp_scan"
|
elif opt[0] == "-b": baud = int(opt[1])
|
||||||
elif opt[0] == "-b":
|
elif opt[0] == "-d": dev = opt[1]
|
||||||
baud = int(opt[1])
|
elif opt[0] == "-t": targetno = int(opt[1])
|
||||||
elif opt[0] == "-d":
|
elif opt[0] == "-r": unprot = True
|
||||||
dev = opt[1]
|
elif opt[0] == "-R": prot = True
|
||||||
elif opt[0] == "-t":
|
else: raise Exception()
|
||||||
targetno = int(opt[1])
|
|
||||||
elif opt[0] == "-r":
|
|
||||||
unprot = True
|
|
||||||
elif opt[0] == "-R":
|
|
||||||
prot = True
|
|
||||||
else:
|
|
||||||
raise Exception()
|
|
||||||
|
|
||||||
hexfile = args[0]
|
hexfile = args[0]
|
||||||
except:
|
except:
|
||||||
@ -108,44 +101,39 @@ if __name__ == "__main__":
|
|||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s = Serial(dev) #, baud, timeout=0.1)
|
s = Serial(dev, baud, timeout=3)
|
||||||
#s.setDTR(1)
|
s.setDTR(1)
|
||||||
#s.flushInput()
|
while s.read(1024):
|
||||||
|
pass
|
||||||
#while s.read(1024):
|
|
||||||
# pass
|
|
||||||
|
|
||||||
target = gdb.Target(s)
|
target = gdb.Target(s)
|
||||||
|
|
||||||
except SerialException as e:
|
except SerialException, e:
|
||||||
print("FATAL: Failed to open serial device!\n%s\n" % e[0])
|
print("FATAL: Failed to open serial device!\n%s\n" % e[0])
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = target.monitor("version")
|
r = target.monitor("version")
|
||||||
for s in r:
|
for s in r: print s,
|
||||||
print(s.decode(), end=' ')
|
except SerialException, e:
|
||||||
except SerialException as e:
|
|
||||||
print("FATAL: Serial communication failure!\n%s\n" % e[0])
|
print("FATAL: Serial communication failure!\n%s\n" % e[0])
|
||||||
exit(-1)
|
exit(-1)
|
||||||
#except: pass
|
except: pass
|
||||||
|
|
||||||
print("Target device scan:")
|
print "Target device scan:"
|
||||||
targetlist = None
|
targetlist = None
|
||||||
r = target.monitor(scan)
|
r = target.monitor(scan)
|
||||||
for s in r:
|
for s in r:
|
||||||
print(s.decode(), end=' ')
|
print s,
|
||||||
print()
|
print
|
||||||
|
|
||||||
r = target.monitor("targets")
|
r = target.monitor("targets")
|
||||||
for s in r:
|
for s in r:
|
||||||
if s.startswith(b"No. Att Driver"):
|
if s.startswith("No. Att Driver"): targetlist = []
|
||||||
targetlist = []
|
|
||||||
try:
|
try:
|
||||||
if type(targetlist) is list:
|
if type(targetlist) is list:
|
||||||
targetlist.append(int(s[:2]))
|
targetlist.append(int(s[:2]))
|
||||||
except:
|
except: pass
|
||||||
pass
|
|
||||||
|
|
||||||
#if not targetlist:
|
#if not targetlist:
|
||||||
# print("FATAL: No usable targets found!\n")
|
# print("FATAL: No usable targets found!\n")
|
||||||
@ -173,7 +161,7 @@ if __name__ == "__main__":
|
|||||||
print("FLASH memory -- Offset: 0x%X BlockSize:0x%X\n" % (m.offset, m.blocksize))
|
print("FLASH memory -- Offset: 0x%X BlockSize:0x%X\n" % (m.offset, m.blocksize))
|
||||||
|
|
||||||
def progress(percent):
|
def progress(percent):
|
||||||
print("Progress: %d%%\r" % percent, end=' ')
|
print ("Progress: %d%%\r" % percent),
|
||||||
stdout.flush()
|
stdout.flush()
|
||||||
|
|
||||||
print("Programming target")
|
print("Programming target")
|
||||||
@ -191,3 +179,4 @@ if __name__ == "__main__":
|
|||||||
target.detach()
|
target.detach()
|
||||||
|
|
||||||
print("\nAll operations complete!\n")
|
print("\nAll operations complete!\n")
|
||||||
|
|
||||||
|
82
scripts/setlocalversion
Executable file
82
scripts/setlocalversion
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# This scripts adds local version information from the version
|
||||||
|
# control systems git, mercurial (hg) and subversion (svn).
|
||||||
|
#
|
||||||
|
# If something goes wrong, send a mail the kernel build mailinglist
|
||||||
|
# (see MAINTAINERS) and CC Nico Schottelius
|
||||||
|
# <nico-linuxsetlocalversion -at- schottelius.org>.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [srctree]" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "${1:-.}" || usage
|
||||||
|
|
||||||
|
# Check for git and a git repo.
|
||||||
|
if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
|
||||||
|
|
||||||
|
# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore it,
|
||||||
|
# because this version is defined in the top level Makefile.
|
||||||
|
if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
|
||||||
|
|
||||||
|
# If we are past a tagged commit (like "v2.6.30-rc5-302-g72357d5"),
|
||||||
|
# we pretty print it.
|
||||||
|
if atag="`git describe 2>/dev/null`"; then
|
||||||
|
echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
|
||||||
|
|
||||||
|
# If we don't have a tag at all we print -g{commitish}.
|
||||||
|
else
|
||||||
|
printf '%s%s' -g $head
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Is this git on svn?
|
||||||
|
if git config --get svn-remote.svn.url >/dev/null; then
|
||||||
|
printf -- '-svn%s' "`git svn find-rev $head`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update index only on r/w media
|
||||||
|
[ -w . ] && git update-index --refresh --unmerged > /dev/null
|
||||||
|
|
||||||
|
# Check for uncommitted changes
|
||||||
|
if git diff-index --name-only HEAD | grep -v "^scripts/package" \
|
||||||
|
| read dummy; then
|
||||||
|
printf '%s' -dirty
|
||||||
|
fi
|
||||||
|
|
||||||
|
# All done with git
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for mercurial and a mercurial repo.
|
||||||
|
if hgid=`hg id 2>/dev/null`; then
|
||||||
|
tag=`printf '%s' "$hgid" | cut -d' ' -f2`
|
||||||
|
|
||||||
|
# Do we have an untagged version?
|
||||||
|
if [ -z "$tag" -o "$tag" = tip ]; then
|
||||||
|
id=`printf '%s' "$hgid" | sed 's/[+ ].*//'`
|
||||||
|
printf '%s%s' -hg "$id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Are there uncommitted changes?
|
||||||
|
# These are represented by + after the changeset id.
|
||||||
|
case "$hgid" in
|
||||||
|
*+|*+\ *) printf '%s' -dirty ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# All done with mercurial
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for svn and a svn repo.
|
||||||
|
if rev=`svn info 2>/dev/null | grep '^Last Changed Rev'`; then
|
||||||
|
rev=`echo $rev | awk '{print $NF}'`
|
||||||
|
printf -- '-svn%s' "$rev"
|
||||||
|
|
||||||
|
# All done with svn
|
||||||
|
exit
|
||||||
|
fi
|
@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/python
|
||||||
#
|
#
|
||||||
# stm32_mem.py: STM32 memory access using USB DFU class
|
# stm32_mem.py: STM32 memory access using USB DFU class
|
||||||
# Copyright (C) 2011 Black Sphere Technologies
|
# Copyright (C) 2011 Black Sphere Technologies
|
||||||
# Copyright (C) 2017, 2020 Uwe Bonnes (bon@elektron.ikp.physik.tu-darmstadt.de)
|
|
||||||
# Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
# Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
@ -20,15 +19,14 @@
|
|||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import struct
|
import struct
|
||||||
import os
|
from sys import stdout, argv
|
||||||
from sys import stdout
|
|
||||||
|
|
||||||
import argparse
|
import usb
|
||||||
import dfu
|
import dfu
|
||||||
|
|
||||||
CMD_GETCOMMANDS = 0x00
|
CMD_GETCOMMANDS = 0x00
|
||||||
CMD_SETADDRESSPOINTER = 0x21
|
CMD_SETADDRESSPOINTER = 0x21
|
||||||
CMD_ERASE = 0x41
|
CMD_ERASE = 0x41
|
||||||
|
|
||||||
def stm32_erase(dev, addr):
|
def stm32_erase(dev, addr):
|
||||||
erase_cmd = struct.pack("<BL", CMD_ERASE, addr)
|
erase_cmd = struct.pack("<BL", CMD_ERASE, addr)
|
||||||
@ -40,16 +38,6 @@ def stm32_erase(dev, addr):
|
|||||||
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
|
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
|
||||||
break
|
break
|
||||||
|
|
||||||
def stm32_set_address(dev, addr):
|
|
||||||
set_address_cmd = struct.pack("<BL", CMD_SETADDRESSPOINTER, addr)
|
|
||||||
dev.download(0, set_address_cmd)
|
|
||||||
while True:
|
|
||||||
status = dev.get_status()
|
|
||||||
if status.bState == dfu.STATE_DFU_DOWNLOAD_BUSY:
|
|
||||||
sleep(status.bwPollTimeout / 1000.0)
|
|
||||||
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
|
|
||||||
break
|
|
||||||
|
|
||||||
def stm32_write(dev, data):
|
def stm32_write(dev, data):
|
||||||
dev.download(2, data)
|
dev.download(2, data)
|
||||||
while True:
|
while True:
|
||||||
@ -59,18 +47,8 @@ def stm32_write(dev, data):
|
|||||||
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
|
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
|
||||||
break
|
break
|
||||||
|
|
||||||
def stm32_read(dev):
|
|
||||||
data = dev.upload(2, 1024)
|
|
||||||
while True:
|
|
||||||
status = dev.get_status()
|
|
||||||
if status.bState == dfu.STATE_DFU_DOWNLOAD_BUSY:
|
|
||||||
sleep(status.bwPollTimeout / 1000.0)
|
|
||||||
if status.bState == dfu.STATE_DFU_UPLOAD_IDLE:
|
|
||||||
break
|
|
||||||
return data
|
|
||||||
|
|
||||||
def stm32_manifest(dev):
|
def stm32_manifest(dev):
|
||||||
dev.download(0, b"")
|
dev.download(0, "")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
status = dev.get_status()
|
status = dev.get_status()
|
||||||
@ -80,182 +58,52 @@ def stm32_manifest(dev):
|
|||||||
if status.bState == dfu.STATE_DFU_MANIFEST:
|
if status.bState == dfu.STATE_DFU_MANIFEST:
|
||||||
break
|
break
|
||||||
|
|
||||||
def stm32_scan(args, test):
|
if __name__ == "__main__":
|
||||||
devs = dfu.finddevs()
|
print
|
||||||
bmp_devs = []
|
print "USB Device Firmware Upgrade - Host Utility -- version 1.1"
|
||||||
bmp = 0
|
print "Copyright (C) 2011 Black Sphere Technologies"
|
||||||
if not devs:
|
print "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>"
|
||||||
if test:
|
print
|
||||||
return
|
|
||||||
|
|
||||||
print("No DFU devices found!")
|
devs = dfu.finddevs()
|
||||||
|
if not devs:
|
||||||
|
print "No devices found!"
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
for dev in devs:
|
for dev in devs:
|
||||||
try:
|
|
||||||
dfudev = dfu.dfu_device(*dev)
|
|
||||||
except:
|
|
||||||
# Exceptions are raised when current user doesn't have permissions
|
|
||||||
# for the specified USB device, but the device scan needs to
|
|
||||||
# continue
|
|
||||||
continue
|
|
||||||
|
|
||||||
man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30)
|
|
||||||
if man == b"Black Sphere Technologies":
|
|
||||||
bmp = bmp + 1
|
|
||||||
bmp_devs.append(dev)
|
|
||||||
|
|
||||||
if bmp == 0:
|
|
||||||
if test:
|
|
||||||
return
|
|
||||||
|
|
||||||
print("No compatible device found\n")
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
if bmp > 1 and not args.serial_target:
|
|
||||||
if test:
|
|
||||||
return
|
|
||||||
|
|
||||||
print("Found multiple devices:\n")
|
|
||||||
for dev in bmp_devs:
|
|
||||||
dfudev = dfu.dfu_device(*dev)
|
|
||||||
man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30).decode('utf8')
|
|
||||||
product = dfudev.handle.getString(dfudev.dev.iProduct, 96).decode('utf8')
|
|
||||||
serial_no = dfudev.handle.getString(dfudev.dev.iSerialNumber, 30).decode('utf8')
|
|
||||||
print("Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct))
|
|
||||||
print("Manufacturer:\t %s" % man)
|
|
||||||
print("Product:\t %s" % product)
|
|
||||||
print("Serial:\t\t %s\n" % serial_no)
|
|
||||||
|
|
||||||
print("Select device with serial number!")
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
for dev in bmp_devs:
|
|
||||||
dfudev = dfu.dfu_device(*dev)
|
dfudev = dfu.dfu_device(*dev)
|
||||||
man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30).decode('utf8')
|
man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30)
|
||||||
product = dfudev.handle.getString(dfudev.dev.iProduct, 96).decode('utf8')
|
product = dfudev.handle.getString(dfudev.dev.iProduct, 30)
|
||||||
serial_no = dfudev.handle.getString(dfudev.dev.iSerialNumber, 30).decode('utf8')
|
if man == "Black Sphere Technologies": break
|
||||||
if args.serial_target:
|
if man == "STMicroelectronics": break
|
||||||
if man == "Black Sphere Technologies" and serial_no == args.serial_target:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if man == "Black Sphere Technologies":
|
|
||||||
break
|
|
||||||
|
|
||||||
print("Device ID:\t %04x:%04x" % (dfudev.dev.idVendor, dfudev.dev.idProduct))
|
print "Device %s: ID %04x:%04x %s - %s" % (dfudev.dev.filename,
|
||||||
print("Manufacturer:\t %s" % man)
|
dfudev.dev.idVendor, dfudev.dev.idProduct, man, product)
|
||||||
print("Product:\t %s" % product)
|
|
||||||
print("Serial:\t\t %s" % serial_no)
|
|
||||||
|
|
||||||
if args.serial_target and serial_no != args.serial_target:
|
|
||||||
print("Serial number doesn't match %s vs %s!\n" % (serial_no, args.serial_target))
|
|
||||||
exit(-2)
|
|
||||||
|
|
||||||
return dfudev
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("-")
|
|
||||||
print("USB Device Firmware Upgrade - Host Utility -- version 1.2")
|
|
||||||
print("Copyright (C) 2011 Black Sphere Technologies")
|
|
||||||
print("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>")
|
|
||||||
print("-")
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("progfile", help="Binary file to program")
|
|
||||||
parser.add_argument("-s", "--serial_target", help="Match Serial Number")
|
|
||||||
parser.add_argument("-a", "--address", help="Start address for firmware")
|
|
||||||
parser.add_argument("-m", "--manifest", help="Start application, if in DFU mode", action='store_true')
|
|
||||||
args = parser.parse_args()
|
|
||||||
dfudev = stm32_scan(args, False)
|
|
||||||
try:
|
try:
|
||||||
state = dfudev.get_state()
|
state = dfudev.get_state()
|
||||||
except:
|
except:
|
||||||
if args.manifest: exit(0)
|
print "Failed to read device state! Assuming APP_IDLE"
|
||||||
print("Failed to read device state! Assuming APP_IDLE")
|
|
||||||
state = dfu.STATE_APP_IDLE
|
state = dfu.STATE_APP_IDLE
|
||||||
if state == dfu.STATE_APP_IDLE:
|
if state == dfu.STATE_APP_IDLE:
|
||||||
try:
|
dfudev.detach()
|
||||||
dfudev.detach()
|
print "Run again to upgrade firmware."
|
||||||
except:
|
|
||||||
pass
|
|
||||||
dfudev.release()
|
|
||||||
print("Invoking DFU Device")
|
|
||||||
timeout = 0
|
|
||||||
while True:
|
|
||||||
sleep(1)
|
|
||||||
timeout = timeout + 0.5
|
|
||||||
dfudev = stm32_scan(args, True)
|
|
||||||
if dfudev: break
|
|
||||||
if timeout > 5:
|
|
||||||
print("Error: DFU device did not appear")
|
|
||||||
exit(-1)
|
|
||||||
if args.manifest:
|
|
||||||
stm32_manifest(dfudev)
|
|
||||||
print("Invoking Application Device")
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
dfudev.make_idle()
|
dfudev.make_idle()
|
||||||
file = open(args.progfile, "rb")
|
|
||||||
if (os.path.getsize(args.progfile) > 0x1f800):
|
|
||||||
print("File too large")
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
bin = file.read()
|
bin = open(argv[1], "rb").read()
|
||||||
|
|
||||||
product = dfudev.handle.getString(dfudev.dev.iProduct, 64).decode('utf8')
|
addr = 0x8002000
|
||||||
if args.address:
|
|
||||||
start = int(args.address, 0)
|
|
||||||
else:
|
|
||||||
if "F4" in product or "STLINK-V3" in product:
|
|
||||||
start = 0x8004000
|
|
||||||
else:
|
|
||||||
start = 0x8002000
|
|
||||||
addr = start
|
|
||||||
while bin:
|
while bin:
|
||||||
print("Programming memory at 0x%08X" % addr, end="\r")
|
print ("Programming memory at 0x%08X\r" % addr),
|
||||||
stdout.flush()
|
stdout.flush()
|
||||||
try:
|
stm32_erase(dfudev, addr)
|
||||||
# STM DFU bootloader erases always.
|
|
||||||
# BPM Bootloader only erases once per sector
|
|
||||||
# To support the STM DFU bootloader, the interface descriptor must
|
|
||||||
# get evaluated and erase called only once per sector!
|
|
||||||
stm32_erase(dfudev, addr)
|
|
||||||
except:
|
|
||||||
print("\nErase Timed out\n")
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
stm32_set_address(dfudev, addr)
|
|
||||||
except:
|
|
||||||
print("\nSet Address Timed out\n")
|
|
||||||
break
|
|
||||||
stm32_write(dfudev, bin[:1024])
|
stm32_write(dfudev, bin[:1024])
|
||||||
|
|
||||||
bin = bin[1024:]
|
bin = bin[1024:]
|
||||||
addr += 1024
|
addr += 1024
|
||||||
file.seek(0)
|
|
||||||
bin = file.read()
|
|
||||||
len = len(bin)
|
|
||||||
addr = start
|
|
||||||
print("\n-")
|
|
||||||
while bin:
|
|
||||||
try:
|
|
||||||
stm32_set_address(dfudev, addr)
|
|
||||||
data = stm32_read(dfudev)
|
|
||||||
except:
|
|
||||||
# Abort silent if bootloader does not support upload
|
|
||||||
break
|
|
||||||
print("Verifying memory at 0x%08X" % addr, end="\r")
|
|
||||||
stdout.flush()
|
|
||||||
if len > 1024:
|
|
||||||
size = 1024
|
|
||||||
else:
|
|
||||||
size = len
|
|
||||||
if bin[:size] != bytearray(data[:size]):
|
|
||||||
print("\nMismatch in block at 0x%08X" % addr)
|
|
||||||
break
|
|
||||||
bin = bin[1024:]
|
|
||||||
addr += 1024
|
|
||||||
len -= 1024
|
|
||||||
if len <= 0:
|
|
||||||
print("\nVerified!")
|
|
||||||
stm32_manifest(dfudev)
|
stm32_manifest(dfudev)
|
||||||
|
|
||||||
print("All operations complete!\n")
|
print "\nAll operations complete!\n"
|
||||||
|
@ -1,547 +0,0 @@
|
|||||||
/*
|
|
||||||
* SWO Splitter for Blackmagic Probe and others.
|
|
||||||
* =============================================
|
|
||||||
*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2017 Dave Marples <dave@marples.net>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <libusb.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#define VID (0x1d50)
|
|
||||||
#define PID (0x6018)
|
|
||||||
#define INTERFACE (5)
|
|
||||||
#define ENDPOINT (0x85)
|
|
||||||
|
|
||||||
#define TRANSFER_SIZE (64)
|
|
||||||
#define NUM_FIFOS 32
|
|
||||||
#define MAX_FIFOS 128
|
|
||||||
|
|
||||||
#define CHANNELNAME "chan"
|
|
||||||
|
|
||||||
#define BOOL char
|
|
||||||
#define FALSE (0)
|
|
||||||
#define TRUE (!FALSE)
|
|
||||||
|
|
||||||
// Record for options, either defaults or from command line
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
BOOL verbose;
|
|
||||||
BOOL dump;
|
|
||||||
int nChannels;
|
|
||||||
char *chanPath;
|
|
||||||
char *port;
|
|
||||||
int speed;
|
|
||||||
} options = {.nChannels=NUM_FIFOS, .chanPath="", .speed=115200};
|
|
||||||
|
|
||||||
// Runtime state
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
int fifo[MAX_FIFOS];
|
|
||||||
} _r;
|
|
||||||
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// Internals
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
static BOOL _runFifo(int portNo, int listenHandle, char *fifoName)
|
|
||||||
|
|
||||||
{
|
|
||||||
int pid,fifo;
|
|
||||||
int readDataLen, writeDataLen;
|
|
||||||
|
|
||||||
if (mkfifo(fifoName,0666)<0)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid=fork();
|
|
||||||
|
|
||||||
if (pid==0)
|
|
||||||
{
|
|
||||||
char rxdata[TRANSFER_SIZE];
|
|
||||||
int fifo;
|
|
||||||
|
|
||||||
/* Don't kill this sub-process when any reader or writer evaporates */
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* This is the child */
|
|
||||||
fifo=open(fifoName,O_WRONLY);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
readDataLen=read(listenHandle,rxdata,TRANSFER_SIZE);
|
|
||||||
if (readDataLen<=0)
|
|
||||||
{
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeDataLen=write(fifo,rxdata,readDataLen);
|
|
||||||
if (writeDataLen<=0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(fifo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pid<0)
|
|
||||||
{
|
|
||||||
/* The fork failed */
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
static BOOL _makeFifoTasks(void)
|
|
||||||
|
|
||||||
/* Create each sub-process that will handle a port */
|
|
||||||
|
|
||||||
{
|
|
||||||
char fifoName[PATH_MAX];
|
|
||||||
|
|
||||||
int f[2];
|
|
||||||
|
|
||||||
for (int t=0; t<options.nChannels; t++)
|
|
||||||
{
|
|
||||||
if (pipe(f)<0)
|
|
||||||
return FALSE;
|
|
||||||
fcntl(f[1],F_SETFL,O_NONBLOCK);
|
|
||||||
_r.fifo[t]=f[1];
|
|
||||||
sprintf(fifoName,"%s%s%02X",options.chanPath,CHANNELNAME,t);
|
|
||||||
if (!_runFifo(t,f[0],fifoName))
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
static void _removeFifoTasks(void)
|
|
||||||
|
|
||||||
/* Destroy the per-port sub-processes */
|
|
||||||
|
|
||||||
{
|
|
||||||
int statloc;
|
|
||||||
int remainingProcesses=0;
|
|
||||||
char fifoName[PATH_MAX];
|
|
||||||
|
|
||||||
for (int t=0; t<options.nChannels; t++)
|
|
||||||
{
|
|
||||||
if (_r.fifo[t]>0)
|
|
||||||
{
|
|
||||||
close(_r.fifo[t]);
|
|
||||||
sprintf(fifoName,"%s%s%02X",options.chanPath,CHANNELNAME,t);
|
|
||||||
unlink(fifoName);
|
|
||||||
remainingProcesses++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (remainingProcesses--)
|
|
||||||
{
|
|
||||||
waitpid(-1,&statloc,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// Handlers for each message type
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
void _handleSWIT(uint8_t addr, uint8_t length, uint8_t *d)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (addr<options.nChannels)
|
|
||||||
write(_r.fifo[addr],d,length);
|
|
||||||
|
|
||||||
// if (addr==0)
|
|
||||||
// fprintf(stdout,"%c",*d);
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
void _handleTS(uint8_t length, uint8_t *d)
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// Protocol pump for decoding messages
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
// ====================================================================================================
|
|
||||||
enum _protoState {ITM_IDLE, ITM_SYNCING, ITM_TS, ITM_SWIT};
|
|
||||||
|
|
||||||
#ifdef PRINT_TRANSITIONS
|
|
||||||
static char *_protoNames[]={"IDLE", "SYNCING","TS","SWIT"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void _protocolPump(uint8_t *c)
|
|
||||||
|
|
||||||
{
|
|
||||||
static enum _protoState p;
|
|
||||||
static int targetCount, currentCount, srcAddr;
|
|
||||||
static uint8_t rxPacket[5];
|
|
||||||
|
|
||||||
#ifdef PRINT_TRANSITIONS
|
|
||||||
printf("%02x %s --> ",*c,_protoNames[p]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (p)
|
|
||||||
{
|
|
||||||
// -----------------------------------------------------
|
|
||||||
case ITM_IDLE:
|
|
||||||
if (*c==0b01110000)
|
|
||||||
{
|
|
||||||
/* This is an overflow packet */
|
|
||||||
if (options.verbose)
|
|
||||||
fprintf(stderr,"Overflow!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// **********
|
|
||||||
if (*c==0)
|
|
||||||
{
|
|
||||||
/* This is a sync packet - expect to see 4 more 0's followed by 0x80 */
|
|
||||||
targetCount=4;
|
|
||||||
currentCount=0;
|
|
||||||
p=ITM_SYNCING;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// **********
|
|
||||||
if (!(*c&0x0F))
|
|
||||||
{
|
|
||||||
currentCount=1;
|
|
||||||
/* This is a timestamp packet */
|
|
||||||
rxPacket[0]=*c;
|
|
||||||
|
|
||||||
if (!(*c&0x80))
|
|
||||||
{
|
|
||||||
/* A one byte output */
|
|
||||||
_handleTS(currentCount,rxPacket);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p=ITM_TS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// **********
|
|
||||||
if ((*c&0x0F) == 0x04)
|
|
||||||
{
|
|
||||||
/* This is a reserved packet */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// **********
|
|
||||||
if (!(*c&0x04))
|
|
||||||
{
|
|
||||||
/* This is a SWIT packet */
|
|
||||||
if ((targetCount=*c&0x03)==3)
|
|
||||||
targetCount=4;
|
|
||||||
srcAddr=(*c&0xF8)>>3;
|
|
||||||
currentCount=0;
|
|
||||||
p=ITM_SWIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// **********
|
|
||||||
if (options.verbose)
|
|
||||||
fprintf(stderr,"Illegal packet start in IDLE state\n");
|
|
||||||
break;
|
|
||||||
// -----------------------------------------------------
|
|
||||||
case ITM_SWIT:
|
|
||||||
rxPacket[currentCount]=*c;
|
|
||||||
currentCount++;
|
|
||||||
|
|
||||||
if (currentCount>=targetCount)
|
|
||||||
{
|
|
||||||
p=ITM_IDLE;
|
|
||||||
_handleSWIT(srcAddr, targetCount, rxPacket);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// -----------------------------------------------------
|
|
||||||
case ITM_TS:
|
|
||||||
rxPacket[currentCount++]=*c;
|
|
||||||
if (!(*c&0x80))
|
|
||||||
{
|
|
||||||
/* We are done */
|
|
||||||
_handleTS(currentCount,rxPacket);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (currentCount>4)
|
|
||||||
{
|
|
||||||
/* Something went badly wrong */
|
|
||||||
p=ITM_IDLE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------
|
|
||||||
case ITM_SYNCING:
|
|
||||||
if ((*c==0) && (currentCount<targetCount))
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (*c==0x80)
|
|
||||||
{
|
|
||||||
p=ITM_IDLE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* This should really be an UNKNOWN state */
|
|
||||||
p=ITM_IDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// -----------------------------------------------------
|
|
||||||
}
|
|
||||||
#ifdef PRINT_TRANSITIONS
|
|
||||||
printf("%s\n",_protoNames[p]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
void intHandler(int dummy)
|
|
||||||
|
|
||||||
{
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
void _printHelp(char *progName)
|
|
||||||
|
|
||||||
{
|
|
||||||
printf("Useage: %s <dhnv> <b basedir> <p port> <s speed>\n",progName);
|
|
||||||
printf(" b: <basedir> for channels\n");
|
|
||||||
printf(" h: This help\n");
|
|
||||||
printf(" d: Dump received data without further processing\n");
|
|
||||||
printf(" n: <Number> of channels to populate\n");
|
|
||||||
printf(" p: <serialPort> to use\n");
|
|
||||||
printf(" s: <serialSpeed> to use\n");
|
|
||||||
printf(" v: Verbose mode\n");
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
int _processOptions(int argc, char *argv[])
|
|
||||||
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
while ((c = getopt (argc, argv, "vdn:b:hp:s:")) != -1)
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 'v':
|
|
||||||
options.verbose = 1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
options.dump = 1;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
options.port=optarg;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
options.speed=atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
_printHelp(argv[0]);
|
|
||||||
return FALSE;
|
|
||||||
case 'n':
|
|
||||||
options.nChannels=atoi(optarg);
|
|
||||||
if ((options.nChannels<1) || (options.nChannels>MAX_FIFOS))
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Number of channels out of range (1..%d)\n",MAX_FIFOS);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
options.chanPath = optarg;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
if (optopt == 'b')
|
|
||||||
fprintf (stderr, "Option '%c' requires an argument.\n", optopt);
|
|
||||||
else if (!isprint (optopt))
|
|
||||||
fprintf (stderr,"Unknown option character `\\x%x'.\n", optopt);
|
|
||||||
return FALSE;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.verbose)
|
|
||||||
{
|
|
||||||
fprintf(stdout,"Verbose: TRUE\nBasePath: %s\n",options.chanPath);
|
|
||||||
if (options.port)
|
|
||||||
{
|
|
||||||
fprintf(stdout,"Serial Port: %s\nSerial Speed: %d\n",options.port,options.speed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
int usbFeeder(void)
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
unsigned char cbw[TRANSFER_SIZE];
|
|
||||||
libusb_device_handle *handle;
|
|
||||||
libusb_device *dev;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
if (libusb_init(NULL) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Failed to initalise USB interface\n");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!(handle = libusb_open_device_with_vid_pid(NULL, VID, PID)))
|
|
||||||
{
|
|
||||||
usleep(500000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(dev = libusb_get_device(handle)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (libusb_claim_interface (handle, INTERFACE)<0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
while (0==libusb_bulk_transfer(handle, ENDPOINT, cbw, TRANSFER_SIZE, &size, 10))
|
|
||||||
{
|
|
||||||
unsigned char *c=cbw;
|
|
||||||
if (options.dump)
|
|
||||||
{
|
|
||||||
cbw[size] = 0;
|
|
||||||
printf("%s", (char*)cbw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
while (size--)
|
|
||||||
_protocolPump(c++);
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_close(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
int serialFeeder(void)
|
|
||||||
|
|
||||||
{
|
|
||||||
int f;
|
|
||||||
unsigned char cbw[TRANSFER_SIZE];
|
|
||||||
ssize_t t;
|
|
||||||
struct termios settings;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while ((f=open(options.port,O_RDONLY))<0)
|
|
||||||
{
|
|
||||||
if (options.verbose)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Can't open serial port\n");
|
|
||||||
}
|
|
||||||
usleep(500000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.verbose)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Port opened\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcgetattr(f, &settings) <0)
|
|
||||||
{
|
|
||||||
perror("tcgetattr");
|
|
||||||
return(-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfsetspeed(&settings, options.speed)<0)
|
|
||||||
{
|
|
||||||
perror("Setting input speed");
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
|
||||||
settings.c_cflag &= ~PARENB; /* no parity */
|
|
||||||
settings.c_cflag &= ~CSTOPB; /* 1 stop bit */
|
|
||||||
settings.c_cflag &= ~CSIZE;
|
|
||||||
settings.c_cflag |= CS8 | CLOCAL; /* 8 bits */
|
|
||||||
settings.c_oflag &= ~OPOST; /* raw output */
|
|
||||||
|
|
||||||
if (tcsetattr(f, TCSANOW, &settings)<0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Unsupported baudrate\n");
|
|
||||||
exit(-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
tcflush(f, TCOFLUSH);
|
|
||||||
|
|
||||||
while ((t=read(f,cbw,TRANSFER_SIZE))>0)
|
|
||||||
{
|
|
||||||
unsigned char *c=cbw;
|
|
||||||
while (t--)
|
|
||||||
_protocolPump(c++);
|
|
||||||
}
|
|
||||||
if (options.verbose)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Read failed\n");
|
|
||||||
}
|
|
||||||
close(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
|
|
||||||
{
|
|
||||||
if (!_processOptions(argc,argv))
|
|
||||||
{
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
atexit(_removeFifoTasks);
|
|
||||||
/* This ensures the atexit gets called */
|
|
||||||
signal(SIGINT, intHandler);
|
|
||||||
if (!_makeFifoTasks())
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Failed to make channel devices\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Using the exit construct rather than return ensures the atexit gets called */
|
|
||||||
if (!options.port)
|
|
||||||
exit(usbFeeder());
|
|
||||||
else
|
|
||||||
exit(serialFeeder());
|
|
||||||
fprintf(stderr,"Returned\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
// ====================================================================================================
|
|
217
src/Makefile
217
src/Makefile
@ -1,203 +1,48 @@
|
|||||||
PROBE_HOST ?= native
|
ifndef PROBE_HOST
|
||||||
PLATFORM_DIR = platforms/$(PROBE_HOST)
|
PROBE_HOST = stm32
|
||||||
VPATH += $(PLATFORM_DIR) target
|
|
||||||
ENABLE_DEBUG ?=
|
|
||||||
|
|
||||||
ifneq ($(V), 1)
|
|
||||||
MAKEFLAGS += --no-print-dir
|
|
||||||
Q := @
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts \
|
VPATH += $(PROBE_HOST)
|
||||||
-std=gnu99 -MD -I./target \
|
|
||||||
-I. -Iinclude -I$(PLATFORM_DIR)
|
|
||||||
|
|
||||||
ifeq ($(ENABLE_DEBUG), 1)
|
BUILDDATE := `date +"%Y%m%d"`
|
||||||
CFLAGS += -DENABLE_DEBUG
|
|
||||||
endif
|
|
||||||
|
|
||||||
SRC = \
|
CFLAGS += -Wall -Wextra -Wno-pointer-sign -Wno-char-subscripts\
|
||||||
adiv5.c \
|
-O2 -std=gnu99 -g3 -DBUILDDATE=\"$(BUILDDATE)\"\
|
||||||
adiv5_jtagdp.c \
|
-I. -Iinclude -I$(PROBE_HOST) \
|
||||||
adiv5_swdp.c \
|
-DVERSION_SUFFIX=\"`../scripts/setlocalversion`\" -MD
|
||||||
command.c \
|
|
||||||
cortexa.c \
|
SRC = gdb_if.c \
|
||||||
cortexm.c \
|
|
||||||
crc32.c \
|
|
||||||
efm32.c \
|
|
||||||
exception.c \
|
|
||||||
gdb_if.c \
|
|
||||||
gdb_main.c \
|
|
||||||
gdb_hostio.c \
|
|
||||||
gdb_packet.c \
|
gdb_packet.c \
|
||||||
|
gdb_main.c \
|
||||||
hex_utils.c \
|
hex_utils.c \
|
||||||
jtag_devs.c \
|
jtagtap.c \
|
||||||
jtag_scan.c \
|
swdptap.c \
|
||||||
lmi.c \
|
adiv5.c \
|
||||||
lpc_common.c \
|
adiv5_swdp.c \
|
||||||
lpc11xx.c \
|
cortexm3.c \
|
||||||
lpc17xx.c \
|
stm32_tgt.c \
|
||||||
lpc15xx.c \
|
nxp_tgt.c \
|
||||||
lpc43xx.c \
|
|
||||||
lpc546xx.c \
|
|
||||||
kinetis.c \
|
|
||||||
main.c \
|
main.c \
|
||||||
morse.c \
|
|
||||||
msp432.c \
|
|
||||||
nrf51.c \
|
|
||||||
nxpke04.c \
|
|
||||||
platform.c \
|
platform.c \
|
||||||
remote.c \
|
command.c \
|
||||||
rp.c \
|
jtag_scan.c \
|
||||||
sam3x.c \
|
adiv5_jtagdp.c \
|
||||||
sam4l.c \
|
lmi.c \
|
||||||
samd.c \
|
arm7tdmi.c \
|
||||||
samx5x.c \
|
|
||||||
stm32f1.c \
|
|
||||||
ch32f1.c \
|
|
||||||
stm32f4.c \
|
stm32f4.c \
|
||||||
stm32h7.c \
|
crc32.c \
|
||||||
stm32l0.c \
|
|
||||||
stm32l4.c \
|
|
||||||
stm32g0.c \
|
|
||||||
target.c \
|
|
||||||
|
|
||||||
|
include $(PROBE_HOST)/Makefile.inc
|
||||||
|
|
||||||
include $(PLATFORM_DIR)/Makefile.inc
|
OBJ = $(SRC:.c=.o)
|
||||||
|
|
||||||
OPT_FLAGS ?= -Os
|
blackmagic: $(OBJ)
|
||||||
CFLAGS += $(OPT_FLAGS)
|
$(CC) -o $@ $^ $(LDFLAGS)
|
||||||
LDFLAGS += $(OPT_FLAGS)
|
|
||||||
|
|
||||||
ifndef TARGET
|
.PHONY: clean host_clean
|
||||||
ifdef PC_HOSTED
|
|
||||||
TARGET = blackmagic
|
|
||||||
else
|
|
||||||
TARGET = blackmagic.elf
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef NO_OWN_LL
|
|
||||||
SRC += jtagtap_generic.c swdptap_generic.c
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef PC_HOSTED
|
|
||||||
CFLAGS += -DPC_HOSTED=1
|
|
||||||
else
|
|
||||||
SRC += swdptap.c jtagtap.c
|
|
||||||
CFLAGS += -DPC_HOSTED=0
|
|
||||||
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)))
|
|
||||||
|
|
||||||
OPTIMIZE := swdptap.o jtagtap.o \
|
|
||||||
adiv5_jtagdp.o adiv5_swdp.o adiv5.o \
|
|
||||||
cortexa.o cortexm.o \
|
|
||||||
gdb_if.o gdb_main.o gdb_hostio.o gdb_packet.o \
|
|
||||||
jtag_devs.o jtag_scan.o \
|
|
||||||
crc32.o main.o \
|
|
||||||
cdcacm.o jeff.o timing.o traceswo.o usbuart.o \
|
|
||||||
|
|
||||||
$(OPTIMIZE):: CFLAGS := $(filter-out -Os, $(CFLAGS))
|
|
||||||
$(OPTIMIZE):: CFLAGS += -O3
|
|
||||||
|
|
||||||
$(TARGET): include/version.h $(OBJ)
|
|
||||||
@echo " LD $@"
|
|
||||||
$(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
@echo " CC $<"
|
|
||||||
$(Q)$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
%.o: %.S
|
|
||||||
@echo " AS $<"
|
|
||||||
$(Q)$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
ifndef PC_HOSTED
|
|
||||||
%.bin: %.elf
|
|
||||||
@echo " OBJCOPY $@"
|
|
||||||
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@
|
|
||||||
|
|
||||||
%.hex: %.elf
|
|
||||||
@echo " OBJCOPY $@"
|
|
||||||
$(Q)$(OBJCOPY) -O ihex $^ $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: clean host_clean all_platforms clang-format FORCE
|
|
||||||
|
|
||||||
clean: host_clean
|
clean: host_clean
|
||||||
$(Q)echo " CLEAN"
|
$(RM) *.o *.d *~ blackmagic $(HOSTFILES)
|
||||||
-$(Q)$(RM) *.o *.d *.elf *~ $(TARGET) $(HOSTFILES)
|
|
||||||
-$(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 "<html><body><ul>" > artifacts/index.html ;\
|
|
||||||
$(MAKE) clean ;\
|
|
||||||
for i in platforms/*/Makefile.inc ; do \
|
|
||||||
export DIRNAME=`dirname $$i` ;\
|
|
||||||
export PROBE_HOST=`basename $$DIRNAME` ;\
|
|
||||||
export CFLAGS=-Werror ;\
|
|
||||||
echo "Building for hardware platform: $$PROBE_HOST" ;\
|
|
||||||
$(MAKE);\
|
|
||||||
if [ -f blackmagic ]; then \
|
|
||||||
mv blackmagic artifacts/blackmagic-$$PROBE_HOST ;\
|
|
||||||
echo "<li><a href='blackmagic-$$PROBE_HOST'>$$PROBE_HOST</a></li>"\
|
|
||||||
>> artifacts/index.html ;\
|
|
||||||
fi ;\
|
|
||||||
if [ -f blackmagic.bin ]; then \
|
|
||||||
mv blackmagic.bin artifacts/blackmagic-$$PROBE_HOST.bin ;\
|
|
||||||
echo "<li><a href='blackmagic-$$PROBE_HOST.bin'>$$PROBE_HOST</a></li>"\
|
|
||||||
>> artifacts/index.html ;\
|
|
||||||
fi ;\
|
|
||||||
if [ -f blackmagic_dfu.bin ]; then \
|
|
||||||
mv blackmagic_dfu.bin artifacts/blackmagic_dfu-$$PROBE_HOST.bin ;\
|
|
||||||
echo "<li><a href='blackmagic_dfu-$$PROBE_HOST.bin'>$$PROBE_HOST DFU</a></li>"\
|
|
||||||
>> artifacts/index.html ;\
|
|
||||||
fi ;\
|
|
||||||
$(MAKE) clean ;\
|
|
||||||
done ;\
|
|
||||||
echo "</ul></body></html>" >> artifacts/index.html ;\
|
|
||||||
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
|
|
||||||
@# 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
|
|
||||||
|
|
||||||
clang-format:
|
|
||||||
$(Q)clang-format -i *.c */*.c */*/*.c *.h */*.h */*/*.h
|
|
||||||
|
|
||||||
-include *.d
|
-include *.d
|
||||||
|
|
||||||
|
288
src/adiv5.c
Normal file
288
src/adiv5.c
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements the transport generic functions of the
|
||||||
|
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
|
||||||
|
*
|
||||||
|
* Issues:
|
||||||
|
* Currently doesn't use ROM table for introspection, just assumes
|
||||||
|
* the device is Cortex-M3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "gdb_packet.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
#include "cortexm3.h"
|
||||||
|
|
||||||
|
#ifndef DO_RESET_SEQ
|
||||||
|
#define DO_RESET_SEQ 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This belongs elsewhere... */
|
||||||
|
target *target_list = NULL;
|
||||||
|
target *cur_target = NULL;
|
||||||
|
target *last_target = NULL;
|
||||||
|
|
||||||
|
static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP";
|
||||||
|
|
||||||
|
ADIv5_DP_t *adiv5_dp_list;
|
||||||
|
ADIv5_AP_t adiv5_aps[5];
|
||||||
|
int adiv5_ap_count;
|
||||||
|
|
||||||
|
static int ap_check_error(struct target_s *target);
|
||||||
|
|
||||||
|
static int ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len);
|
||||||
|
static int ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len);
|
||||||
|
static int ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len);
|
||||||
|
static int ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len);
|
||||||
|
|
||||||
|
void adiv5_free_all(void)
|
||||||
|
{
|
||||||
|
ADIv5_DP_t *dp;
|
||||||
|
|
||||||
|
while(adiv5_dp_list) {
|
||||||
|
dp = adiv5_dp_list->next;
|
||||||
|
free(adiv5_dp_list);
|
||||||
|
adiv5_dp_list = dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
adiv5_ap_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void adiv5_dp_init(ADIv5_DP_t *dp)
|
||||||
|
{
|
||||||
|
uint32_t ctrlstat;
|
||||||
|
|
||||||
|
dp->next = adiv5_dp_list;
|
||||||
|
adiv5_dp_list = dp;
|
||||||
|
|
||||||
|
ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT);
|
||||||
|
|
||||||
|
/* Write request for system and debug power up */
|
||||||
|
adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT,
|
||||||
|
ctrlstat |= ADIV5_DP_CTRLSTAT_CSYSPWRUPREQ |
|
||||||
|
ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ);
|
||||||
|
/* Wait for acknowledge */
|
||||||
|
while(((ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT)) &
|
||||||
|
(ADIV5_DP_CTRLSTAT_CSYSPWRUPACK | ADIV5_DP_CTRLSTAT_CDBGPWRUPACK)) !=
|
||||||
|
(ADIV5_DP_CTRLSTAT_CSYSPWRUPACK | ADIV5_DP_CTRLSTAT_CDBGPWRUPACK));
|
||||||
|
|
||||||
|
if(DO_RESET_SEQ) {
|
||||||
|
/* This AP reset logic is described in ADIv5, but fails to work
|
||||||
|
* correctly on STM32. CDBGRSTACK is never asserted, and we
|
||||||
|
* just wait forever.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Write request for debug reset */
|
||||||
|
adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT,
|
||||||
|
ctrlstat |= ADIV5_DP_CTRLSTAT_CDBGRSTREQ);
|
||||||
|
/* Wait for acknowledge */
|
||||||
|
while(!((ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT)) &
|
||||||
|
ADIV5_DP_CTRLSTAT_CDBGRSTACK));
|
||||||
|
|
||||||
|
/* Write request for debug reset release */
|
||||||
|
adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT,
|
||||||
|
ctrlstat &= ~ADIV5_DP_CTRLSTAT_CDBGRSTREQ);
|
||||||
|
/* Wait for acknowledge */
|
||||||
|
while(adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT) &
|
||||||
|
ADIV5_DP_CTRLSTAT_CDBGRSTACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe for APs on this DP */
|
||||||
|
for(int i = 0; i < 256; i++) {
|
||||||
|
ADIv5_AP_t *ap = &adiv5_aps[adiv5_ap_count];
|
||||||
|
target *t;
|
||||||
|
|
||||||
|
/* Assume valid and try to read IDR */
|
||||||
|
ap->dp = dp;
|
||||||
|
ap->apsel = i;
|
||||||
|
ap->idr = adiv5_ap_read(ap, ADIV5_AP_IDR);
|
||||||
|
|
||||||
|
if(!ap->idr) /* IDR Invalid - Should we not continue here? */
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* We have a valid AP, adding to list */
|
||||||
|
ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG);
|
||||||
|
ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE);
|
||||||
|
/* Should probe further here... */
|
||||||
|
|
||||||
|
/* Prepend to target list... */
|
||||||
|
t = (void*)calloc(1, sizeof(struct target_ap_s));
|
||||||
|
t->next = target_list;
|
||||||
|
target_list = t;
|
||||||
|
((struct target_ap_s *)t)->ap = ap;
|
||||||
|
|
||||||
|
t->driver = adiv5_driver_str;
|
||||||
|
t->check_error = ap_check_error;
|
||||||
|
|
||||||
|
t->mem_read_words = ap_mem_read_words;
|
||||||
|
t->mem_write_words = ap_mem_write_words;
|
||||||
|
t->mem_read_bytes = ap_mem_read_bytes;
|
||||||
|
t->mem_write_bytes = ap_mem_write_bytes;
|
||||||
|
|
||||||
|
/* The rest sould only be added after checking ROM table */
|
||||||
|
cm3_probe(t);
|
||||||
|
|
||||||
|
adiv5_ap_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
adiv5_dp_low_access(dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr)
|
||||||
|
{
|
||||||
|
uint32_t ret;
|
||||||
|
|
||||||
|
adiv5_dp_low_access(dp, ADIV5_LOW_AP, ADIV5_LOW_READ, addr, 0);
|
||||||
|
ret = adiv5_dp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_DP_RDBUFF, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ap_check_error(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
return adiv5_dp_error(t->ap->dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ap_mem_read_words(struct target_s *target, uint32_t *dest, uint32_t src, int len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
len >>= 2;
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_TAR, src);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_AP_DRW, 0);
|
||||||
|
while(--len)
|
||||||
|
*dest++ = adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP,
|
||||||
|
ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
|
||||||
|
|
||||||
|
*dest++ = adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_DP_RDBUFF, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ap_mem_read_bytes(struct target_s *target, uint8_t *dest, uint32_t src, int len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t tmp = src;
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000050);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_TAR, src);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_AP_DRW, 0);
|
||||||
|
while(--len) {
|
||||||
|
tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, ADIV5_AP_DRW, 0);
|
||||||
|
*dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
|
||||||
|
}
|
||||||
|
tmp = adiv5_dp_low_access(t->ap->dp, 0, 1, ADIV5_DP_RDBUFF, 0);
|
||||||
|
*dest++ = (tmp >> ((src++ & 0x3) << 3) & 0xFF);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ap_mem_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
len >>= 2;
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_TAR, dest);
|
||||||
|
while(len--)
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_DRW, *src++);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ap_mem_write_bytes(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000050);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_TAR, dest);
|
||||||
|
while(len--) {
|
||||||
|
tmp = (uint32_t)*src++ << ((dest++ & 3) << 3);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, ADIV5_LOW_AP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_AP_DRW, tmp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr)
|
||||||
|
{
|
||||||
|
adiv5_ap_write(ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
|
||||||
|
return adiv5_ap_read(ap, ADIV5_AP_DRW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
adiv5_ap_write(ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
|
||||||
|
adiv5_ap_write(ap, ADIV5_AP_DRW, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
adiv5_dp_write(ap->dp, ADIV5_DP_SELECT,
|
||||||
|
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
|
||||||
|
adiv5_dp_write_ap(ap->dp, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr)
|
||||||
|
{
|
||||||
|
uint32_t ret;
|
||||||
|
adiv5_dp_write(ap->dp, ADIV5_DP_SELECT,
|
||||||
|
((uint32_t)ap->apsel << 24)|(addr & 0xF0));
|
||||||
|
ret = adiv5_dp_read_ap(ap->dp, addr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
107
src/adiv5_jtagdp.c
Normal file
107
src/adiv5_jtagdp.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements the JTAG-DP specific functions of the
|
||||||
|
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "jtagtap.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define JTAGDP_ACK_OK 0x02
|
||||||
|
#define JTAGDP_ACK_WAIT 0x01
|
||||||
|
|
||||||
|
/* 35-bit registers that control the ADIv5 DP */
|
||||||
|
#define IR_ABORT 0x8
|
||||||
|
#define IR_DPACC 0xA
|
||||||
|
#define IR_APACC 0xB
|
||||||
|
|
||||||
|
static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
|
||||||
|
static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr);
|
||||||
|
|
||||||
|
static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp);
|
||||||
|
|
||||||
|
static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
|
||||||
|
uint8_t addr, uint32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
void adiv5_jtag_dp_handler(jtag_dev_t *dev)
|
||||||
|
{
|
||||||
|
ADIv5_DP_t *dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
|
||||||
|
|
||||||
|
dp->dev = dev;
|
||||||
|
dp->idcode = dev->idcode;
|
||||||
|
|
||||||
|
dp->dp_write = adiv5_jtagdp_write;
|
||||||
|
dp->dp_read = adiv5_jtagdp_read;
|
||||||
|
dp->error = adiv5_jtagdp_error;
|
||||||
|
dp->low_access = adiv5_jtagdp_low_access;
|
||||||
|
|
||||||
|
adiv5_dp_init(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adiv5_jtagdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_WRITE, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_jtagdp_read(ADIv5_DP_t *dp, uint8_t addr)
|
||||||
|
{
|
||||||
|
adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ, addr, 0);
|
||||||
|
return adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_DP_RDBUFF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp)
|
||||||
|
{
|
||||||
|
adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ,
|
||||||
|
ADIV5_DP_CTRLSTAT, 0);
|
||||||
|
return adiv5_jtagdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_WRITE,
|
||||||
|
ADIV5_DP_CTRLSTAT, 0xF0000032) & 0x32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
|
||||||
|
uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
uint64_t request, response;
|
||||||
|
uint8_t ack;
|
||||||
|
|
||||||
|
request = ((uint64_t)value << 3) | ((addr >> 1) & 0x06) | (RnW?1:0);
|
||||||
|
|
||||||
|
jtag_dev_write_ir(dp->dev, APnDP?IR_APACC:IR_DPACC);
|
||||||
|
|
||||||
|
do {
|
||||||
|
jtag_dev_shift_dr(dp->dev, (uint8_t*)&response, (uint8_t*)&request, 35);
|
||||||
|
ack = response & 0x07;
|
||||||
|
} while(ack == JTAGDP_ACK_WAIT);
|
||||||
|
|
||||||
|
if((ack != JTAGDP_ACK_OK)) {
|
||||||
|
/* Fatal error if invalid ACK response */
|
||||||
|
PLATFORM_FATAL_ERROR(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint32_t)(response >> 3);
|
||||||
|
}
|
||||||
|
|
160
src/adiv5_swdp.c
Normal file
160
src/adiv5_swdp.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements the SW-DP specific functions of the
|
||||||
|
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
|
||||||
|
#include "swdptap.h"
|
||||||
|
|
||||||
|
#include "command.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define SWDP_ACK_OK 0x01
|
||||||
|
#define SWDP_ACK_WAIT 0x02
|
||||||
|
#define SWDP_ACK_FAULT 0x04
|
||||||
|
|
||||||
|
static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
|
||||||
|
static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr);
|
||||||
|
|
||||||
|
static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp);
|
||||||
|
|
||||||
|
static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
|
||||||
|
uint8_t addr, uint32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
int adiv5_swdp_scan(void)
|
||||||
|
{
|
||||||
|
ADIv5_DP_t *dp;
|
||||||
|
uint8_t ack;
|
||||||
|
|
||||||
|
TARGET_LIST_FREE();
|
||||||
|
#warning "These should be elsewhere!"
|
||||||
|
adiv5_free_all();
|
||||||
|
|
||||||
|
dp = (void*)calloc(1, sizeof(ADIv5_DP_t));
|
||||||
|
|
||||||
|
swdptap_init();
|
||||||
|
/* Read the SW-DP IDCODE register to syncronise */
|
||||||
|
/* This could be done with adiv_swdp_low_access(), but this doesn't
|
||||||
|
* allow the ack to be checked here. */
|
||||||
|
swdptap_seq_out(0xA5, 8);
|
||||||
|
ack = swdptap_seq_in(3);
|
||||||
|
if((ack != SWDP_ACK_OK) || swdptap_seq_in_parity(&dp->idcode, 32)) {
|
||||||
|
DEBUG("\n");
|
||||||
|
morse("NO TARGETS.", 1);
|
||||||
|
free(dp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dp->dp_write = adiv5_swdp_write;
|
||||||
|
dp->dp_read = adiv5_swdp_read;
|
||||||
|
dp->error = adiv5_swdp_error;
|
||||||
|
dp->low_access = adiv5_swdp_low_access;
|
||||||
|
|
||||||
|
adiv5_dp_init(dp);
|
||||||
|
|
||||||
|
if(!target_list) morse("NO TARGETS.", 1);
|
||||||
|
else morse(NULL, 0);
|
||||||
|
|
||||||
|
return target_list?1:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adiv5_swdp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
adiv5_swdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_WRITE, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_swdp_read(ADIv5_DP_t *dp, uint8_t addr)
|
||||||
|
{
|
||||||
|
return adiv5_swdp_low_access(dp, ADIV5_LOW_DP, ADIV5_LOW_READ, addr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp)
|
||||||
|
{
|
||||||
|
uint32_t err, clr = 0;
|
||||||
|
|
||||||
|
err = adiv5_swdp_read(dp, ADIV5_DP_CTRLSTAT) &
|
||||||
|
(ADIV5_DP_CTRLSTAT_STICKYORUN | ADIV5_DP_CTRLSTAT_STICKYCMP |
|
||||||
|
ADIV5_DP_CTRLSTAT_STICKYERR);
|
||||||
|
|
||||||
|
if(err & ADIV5_DP_CTRLSTAT_STICKYORUN)
|
||||||
|
clr |= ADIV5_DP_ABORT_ORUNERRCLR;
|
||||||
|
if(err & ADIV5_DP_CTRLSTAT_STICKYCMP)
|
||||||
|
clr |= ADIV5_DP_ABORT_STKCMPCLR;
|
||||||
|
if(err & ADIV5_DP_CTRLSTAT_STICKYERR)
|
||||||
|
clr |= ADIV5_DP_ABORT_STKERRCLR;
|
||||||
|
|
||||||
|
adiv5_swdp_write(dp, ADIV5_DP_ABORT, clr);
|
||||||
|
dp->fault = 0;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t APnDP, uint8_t RnW,
|
||||||
|
uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
uint8_t request = 0x81;
|
||||||
|
uint32_t response;
|
||||||
|
uint8_t ack;
|
||||||
|
|
||||||
|
if(APnDP && dp->fault) return 0;
|
||||||
|
|
||||||
|
if(APnDP) request ^= 0x22;
|
||||||
|
if(RnW) request ^= 0x24;
|
||||||
|
|
||||||
|
addr &= 0xC;
|
||||||
|
request |= (addr << 1) & 0x18;
|
||||||
|
if((addr == 4) || (addr == 8))
|
||||||
|
request ^= 0x20;
|
||||||
|
|
||||||
|
do {
|
||||||
|
swdptap_seq_out(request, 8);
|
||||||
|
ack = swdptap_seq_in(3);
|
||||||
|
} while(ack == SWDP_ACK_WAIT);
|
||||||
|
|
||||||
|
if(ack == SWDP_ACK_FAULT) {
|
||||||
|
dp->fault = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ack != SWDP_ACK_OK) {
|
||||||
|
/* Fatal error if invalid ACK response */
|
||||||
|
PLATFORM_FATAL_ERROR(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RnW) {
|
||||||
|
if(swdptap_seq_in_parity(&response, 32)) /* Give up on parity error */
|
||||||
|
PLATFORM_FATAL_ERROR(1);
|
||||||
|
} else {
|
||||||
|
swdptap_seq_out_parity(value, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REMOVE THIS */
|
||||||
|
swdptap_seq_out(0, 8);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
295
src/arm7tdmi.c
Normal file
295
src/arm7tdmi.c
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements the ARM7TDMI target support using the JTAG
|
||||||
|
* interface as described in ARM7TDMI Technical Reference Manual,
|
||||||
|
* ARM Document DDI 0210C
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "target.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "jtagtap.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* Skeleton target.
|
||||||
|
* EmbeddedICE registers, halt/resume target.
|
||||||
|
* Check target mode on halt, switch to ARM if needed.
|
||||||
|
* Read registers on halt, restore on resume. Give GDB cached copy.
|
||||||
|
* System speed access, read/write memory.
|
||||||
|
* Misaligned/byte memory access.
|
||||||
|
* Breakpoint support.
|
||||||
|
* Watchpoint support.
|
||||||
|
* Funnies: abort on breakpointed instruction, etc.
|
||||||
|
* Flash programming for STR73x and LPC2xxx.
|
||||||
|
*/
|
||||||
|
static const char arm7_driver_str[] = "ARM7TDMI";
|
||||||
|
|
||||||
|
/* ARM7 JTAG IR values */
|
||||||
|
#define ARM7_IR_EXTEST 0x0
|
||||||
|
#define ARM7_IR_SCAN_N 0x2
|
||||||
|
#define ARM7_IR_SAMPLE_PRELOAD 0x3
|
||||||
|
#define ARM7_IR_RESTART 0x4
|
||||||
|
#define ARM7_IR_CLAMP 0x5
|
||||||
|
#define ARM7_IR_HIGHZ 0x7
|
||||||
|
#define ARM7_IR_CLAMPZ 0x9
|
||||||
|
#define ARM7_IR_INTEST 0xC
|
||||||
|
#define ARM7_IR_IDCODE 0xE
|
||||||
|
#define ARM7_IR_BYPASS 0xF
|
||||||
|
|
||||||
|
/* ARM7 SCAN_N scan chain values */
|
||||||
|
#define ARM7_SCANN_BOUNDARY 0
|
||||||
|
#define ARM7_SCANN_DBUS 1
|
||||||
|
#define ARM7_SCANN_EICE 2
|
||||||
|
|
||||||
|
/* EmbeddedICE-RT Register addresses */
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL 0x00
|
||||||
|
#define ARM7_EICE_DEBUG_STAT 0x01
|
||||||
|
#define ARM7_EICE_ABORT_STAT 0x02
|
||||||
|
#define ARM7_EICE_COMMS_CTRL 0x04
|
||||||
|
#define ARM7_EICE_COMMS_DATA 0x05
|
||||||
|
#define ARM7_EICE_WATCH_ADDR(x) (0x08 + (8 * (x))
|
||||||
|
#define ARM7_EICE_WATCH_ADDR_MASK(x) (0x09 + (8 * (x))
|
||||||
|
#define ARM7_EICE_WATCH_DATA(x) (0x0A + (8 * (x))
|
||||||
|
#define ARM7_EICE_WATCH_DATA_MASK(x) (0x0B + (8 * (x))
|
||||||
|
#define ARM7_EICE_WATCH_CTRL(x) (0x0C + (8 * (x))
|
||||||
|
#define ARM7_EICE_WATCH_CTRL_MASK(x) (0x0D + (8 * (x))
|
||||||
|
|
||||||
|
/* Read/write bit in EmbeddedICE-RT scan chain */
|
||||||
|
#define ARM7_EICE_READ (0uLL << 37)
|
||||||
|
#define ARM7_EICE_WRITE (1uLL << 37)
|
||||||
|
|
||||||
|
/* Debug Control Register bits */
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL_EICE_DISABLE (1 << 5)
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL_MONITOR (1 << 4)
|
||||||
|
/* Bit 3 - Reserved */
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL_INTDIS (1 << 2)
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL_DBGRQ (1 << 1)
|
||||||
|
#define ARM7_EICE_DEBUG_CTRL_DBGACK (1 << 0)
|
||||||
|
|
||||||
|
/* Debug Status Register bits */
|
||||||
|
#define ARM7_EICE_DEBUG_STAT_TBIT (1 << 4)
|
||||||
|
#define ARM7_EICE_DEBUG_STAT_NMREQ (1 << 3)
|
||||||
|
#define ARM7_EICE_DEBUG_STAT_INTDIS (1 << 2)
|
||||||
|
#define ARM7_EICE_DEBUG_STAT_DBGRQ (1 << 1)
|
||||||
|
#define ARM7_EICE_DEBUG_STAT_DBGACK (1 << 0)
|
||||||
|
|
||||||
|
#define ARM7_OP_NOP 0xE1A00000
|
||||||
|
|
||||||
|
struct target_arm7_s {
|
||||||
|
target t;
|
||||||
|
jtag_dev_t *jtag;
|
||||||
|
uint32_t reg_cache[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* FIXME: Remove: */
|
||||||
|
static void do_nothing(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_attach(struct target_s *target);
|
||||||
|
static int arm7_regs_read(struct target_s *target, void *data);
|
||||||
|
static int arm7_regs_write(struct target_s *target, const void *data);
|
||||||
|
static void arm7_halt_request(struct target_s *target);
|
||||||
|
static int arm7_halt_wait(struct target_s *target);
|
||||||
|
static void arm7_halt_resume(struct target_s *target, uint8_t step);
|
||||||
|
|
||||||
|
void arm7tdmi_jtag_handler(jtag_dev_t *dev)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *tj = calloc(1, sizeof(*tj));
|
||||||
|
target *t = (target *)tj;
|
||||||
|
|
||||||
|
t->driver = arm7_driver_str;
|
||||||
|
tj->jtag = dev;
|
||||||
|
|
||||||
|
/* Setup mandatory virtual methods */
|
||||||
|
t->attach = arm7_attach;
|
||||||
|
t->detach = (void *)do_nothing;
|
||||||
|
t->check_error = (void *)do_nothing;
|
||||||
|
t->mem_read_words = (void *)do_nothing;
|
||||||
|
t->mem_write_words = (void *)do_nothing;
|
||||||
|
t->mem_read_bytes = (void *)do_nothing;
|
||||||
|
t->mem_write_bytes = (void *)do_nothing;
|
||||||
|
t->regs_size = 16 * 4;
|
||||||
|
t->regs_read = (void *)arm7_regs_read;
|
||||||
|
t->regs_write = (void *)arm7_regs_write;
|
||||||
|
t->pc_write = (void *)do_nothing;
|
||||||
|
t->reset = (void *)do_nothing;
|
||||||
|
t->halt_request = arm7_halt_request;
|
||||||
|
t->halt_wait = arm7_halt_wait;
|
||||||
|
t->halt_resume = arm7_halt_resume;
|
||||||
|
|
||||||
|
/* TODO: Breakpoint and watchpoint functions. */
|
||||||
|
/* TODO: Fault unwinder. */
|
||||||
|
/* TODO: Memory map / Flash programming. */
|
||||||
|
|
||||||
|
t->next = target_list;
|
||||||
|
target_list = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_select_scanchain(struct target_arm7_s *target, uint8_t chain)
|
||||||
|
{
|
||||||
|
jtag_dev_write_ir(target->jtag, ARM7_IR_SCAN_N);
|
||||||
|
jtag_dev_shift_dr(target->jtag, NULL, &chain, 4);
|
||||||
|
jtag_dev_write_ir(target->jtag, ARM7_IR_INTEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_eice_write(struct target_arm7_s *target,
|
||||||
|
uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
uint64_t val = ((uint64_t)addr << 32) | value | ARM7_EICE_WRITE;
|
||||||
|
|
||||||
|
arm7_select_scanchain(target, ARM7_SCANN_EICE);
|
||||||
|
jtag_dev_shift_dr(target->jtag, NULL, (uint8_t *)&val, 38);
|
||||||
|
DEBUG("eice_write(%d, 0x%08X)\n", addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t arm7_eice_read(struct target_arm7_s *target, uint8_t addr)
|
||||||
|
{
|
||||||
|
uint64_t val = ((uint64_t)addr << 32) | ARM7_EICE_READ;
|
||||||
|
|
||||||
|
arm7_select_scanchain(target, ARM7_SCANN_EICE);
|
||||||
|
jtag_dev_shift_dr(target->jtag, NULL, (uint8_t *)&val, 38);
|
||||||
|
jtag_dev_shift_dr(target->jtag, (uint8_t *)&val, (uint8_t *)&val, 38);
|
||||||
|
DEBUG("eice_read(%d, 0x%08X)\n", addr, (uint32_t)val);
|
||||||
|
|
||||||
|
return (uint32_t)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a single instruction at debug speed.
|
||||||
|
* Performs datalen data bus accesses after the op to capture data.
|
||||||
|
*/
|
||||||
|
static void arm7_op_debug(struct target_arm7_s *t, uint32_t op, uint32_t *data,
|
||||||
|
int datalen)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
/* FIXME: This routine is broken.
|
||||||
|
* This process isn't very well documented. Maybe NOPs need to
|
||||||
|
* be shifted into pipeline before data is read out.
|
||||||
|
*/
|
||||||
|
DEBUG("op_debug(0x%08X)\n", op);
|
||||||
|
arm7_select_scanchain(t, ARM7_SCANN_DBUS);
|
||||||
|
tmp = op;
|
||||||
|
jtag_dev_shift_dr(t->jtag, NULL, (const uint8_t*)&tmp, 33);
|
||||||
|
while(datalen--) {
|
||||||
|
tmp = *data;
|
||||||
|
jtag_dev_shift_dr(t->jtag, (uint8_t*)&tmp, (uint8_t*)&tmp, 33);
|
||||||
|
*data = (uint32_t)tmp;
|
||||||
|
DEBUG("\t0x%08X\n", *data);
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a single instruction at system speed. */
|
||||||
|
static void arm7_op_system(struct target_arm7_s *t, uint32_t op)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
arm7_select_scanchain(t, ARM7_SCANN_DBUS);
|
||||||
|
tmp = op | (1uLL << 32);
|
||||||
|
jtag_dev_shift_dr(t->jtag, NULL, (const uint8_t*)&tmp, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_halt_request(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *t = (struct target_arm7_s *)target;
|
||||||
|
|
||||||
|
arm7_eice_write(t, ARM7_EICE_DEBUG_CTRL, ARM7_EICE_DEBUG_CTRL_DBGRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int arm7_halt_wait(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *t = (struct target_arm7_s *)target;
|
||||||
|
int stat = arm7_eice_read(t, ARM7_EICE_DEBUG_STAT);
|
||||||
|
|
||||||
|
if(!(stat & ARM7_EICE_DEBUG_STAT_DBGACK))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* We are halted, so switch to ARM mode if needed. */
|
||||||
|
if(stat & ARM7_EICE_DEBUG_STAT_TBIT) {
|
||||||
|
/* This sequence switches to ARM mode:
|
||||||
|
* 6000 STR R0, [R0] ; Save R0 before use
|
||||||
|
* 4678 MOV R0, PC ; Copy PC into R0
|
||||||
|
* 6000 STR R0, [R0] ; Now save the PC in R0
|
||||||
|
* 4778 BX PC ; Jump into ARM state
|
||||||
|
* 46c0 MOV R8, R8 ; NOP
|
||||||
|
* 46c0 MOV R8, R8 ; NOP
|
||||||
|
*/
|
||||||
|
/* FIXME: Switch to ARM mode. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fetch core register values */
|
||||||
|
/* E880FFFF STM R0, {R0-R15} */
|
||||||
|
arm7_op_debug(t, 0xE880FFFF, t->reg_cache, 16);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_halt_resume(struct target_s *target, uint8_t step)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *t = (struct target_arm7_s *)target;
|
||||||
|
|
||||||
|
if(step) {
|
||||||
|
/* FIXME: Set breakpoint on any instruction to single step. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore core registers. */
|
||||||
|
/* E890FFFF LDM R0, {R0-R15} */
|
||||||
|
arm7_op_debug(t, 0xE890FFFF, t->reg_cache, 16);
|
||||||
|
|
||||||
|
/* Release DBGRQ */
|
||||||
|
arm7_eice_write(t, ARM7_EICE_DEBUG_CTRL, 0);
|
||||||
|
/* This sequence restores PC if no other instructions issued in
|
||||||
|
* debug mode...
|
||||||
|
* 0 E1A00000; MOV R0, R0
|
||||||
|
* 1 E1A00000; MOV R0, R0
|
||||||
|
* 0 EAFFFFFA; B -6
|
||||||
|
* FIXME: Add adjustment for other opcodes.
|
||||||
|
*/
|
||||||
|
arm7_op_debug(t, ARM7_OP_NOP, NULL, 0);
|
||||||
|
arm7_op_system(t, ARM7_OP_NOP);
|
||||||
|
arm7_op_debug(t, 0xEAFFFFF8, NULL, 0);
|
||||||
|
|
||||||
|
jtag_dev_write_ir(t->jtag, ARM7_IR_RESTART);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm7_attach(struct target_s *target)
|
||||||
|
{
|
||||||
|
target_halt_request(target);
|
||||||
|
while(!target_halt_wait(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int arm7_regs_read(struct target_s *target, void *data)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *t = (struct target_arm7_s *)target;
|
||||||
|
memcpy(data, t->reg_cache, target->regs_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int arm7_regs_write(struct target_s *target, const void *data)
|
||||||
|
{
|
||||||
|
struct target_arm7_s *t = (struct target_arm7_s *)target;
|
||||||
|
memcpy(t->reg_cache, data, target->regs_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
630
src/command.c
630
src/command.c
@ -3,8 +3,6 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
* Copyright (C) 2021 Uwe Bonnes
|
|
||||||
* (bon@elektron.ikp.physik.tu-darmstadt.de)
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -22,114 +20,53 @@
|
|||||||
|
|
||||||
/* This file implements a basic command interpreter for GDB 'monitor'
|
/* This file implements a basic command interpreter for GDB 'monitor'
|
||||||
* commands.
|
* commands.
|
||||||
|
*
|
||||||
|
* TODO: Add a mechanism for target driver so register new commands.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
#include "exception.h"
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "gdb_packet.h"
|
#include "gdb_packet.h"
|
||||||
|
|
||||||
|
#include "jtag_scan.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "target_internal.h"
|
|
||||||
#include "morse.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "serialno.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_RTT
|
#include "adiv5.h"
|
||||||
#include "rtt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
static void cmd_version(void);
|
||||||
|
static void cmd_help(void);
|
||||||
|
|
||||||
|
static void cmd_jtag_scan(void);
|
||||||
|
static void cmd_swdp_scan(void);
|
||||||
|
static void cmd_targets(void);
|
||||||
|
static void cmd_morse(void);
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
# include "traceswo.h"
|
static void cmd_traceswo(void);
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool cmd_version(target *t, int argc, char **argv);
|
|
||||||
#ifdef PLATFORM_HAS_PRINTSERIAL
|
|
||||||
static bool cmd_serial(target *t, int argc, char **argv);
|
|
||||||
#endif
|
|
||||||
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_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);
|
|
||||||
static bool cmd_halt_timeout(target *t, int argc, const char **argv);
|
|
||||||
static bool cmd_connect_srst(target *t, int argc, const char **argv);
|
|
||||||
static bool cmd_hard_srst(target *t, int argc, const char **argv);
|
|
||||||
#ifdef PLATFORM_HAS_POWER_SWITCH
|
|
||||||
static bool cmd_target_power(target *t, int argc, const char **argv);
|
|
||||||
#endif
|
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
|
||||||
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
|
|
||||||
#ifdef PLATFORM_HAS_UART_WHEN_SWDP
|
|
||||||
static bool cmd_convert_tdio(target *t, int argc, const char **argv);
|
|
||||||
static bool cmd_set_srst(target *t, int argc, const char **argv);
|
|
||||||
#endif
|
|
||||||
#ifdef PLATFORM_HAS_BOOTLOADER
|
|
||||||
static bool cmd_enter_bootldr(target *t, int argc, const char **argv);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const struct command_s cmd_list[] = {
|
const struct command_s cmd_list[] = {
|
||||||
{"version", (cmd_handler)cmd_version, "Display firmware version info"},
|
{"version", (cmd_handler)cmd_version, "Display firmware version info"},
|
||||||
#ifdef PLATFORM_HAS_PRINTSERIAL
|
|
||||||
{"serial", (cmd_handler)cmd_serial, "Display firmware serial number"},
|
|
||||||
#endif
|
|
||||||
{"help", (cmd_handler)cmd_help, "Display help for monitor commands"},
|
{"help", (cmd_handler)cmd_help, "Display help for monitor commands"},
|
||||||
{"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" },
|
{"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" },
|
||||||
{"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
|
{"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
|
||||||
{"frequency", (cmd_handler)cmd_frequency, "set minimum high and low times" },
|
|
||||||
{"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
|
{"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
|
||||||
{"morse", (cmd_handler)cmd_morse, "Display morse error message" },
|
{"morse", (cmd_handler)cmd_morse, "Display morse error message" },
|
||||||
{"halt_timeout", (cmd_handler)cmd_halt_timeout, "Timeout (ms) to wait until Cortex-M is halted: (Default 2000)" },
|
|
||||||
{"connect_srst", (cmd_handler)cmd_connect_srst, "Configure connect under SRST: (enable|disable)" },
|
|
||||||
{"hard_srst", (cmd_handler)cmd_hard_srst, "Force a pulse on the hard SRST line - disconnects target" },
|
|
||||||
#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
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture" },
|
||||||
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
|
||||||
#else
|
|
||||||
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, Manchester mode: (decode channel ...)" },
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
{"heapinfo", (cmd_handler)cmd_heapinfo, "Set semihosting heapinfo" },
|
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
|
||||||
{"debug_bmp", (cmd_handler)cmd_debug_bmp, "Output BMP \"debug\" strings to the second vcom: (enable|disable)"},
|
|
||||||
#endif
|
|
||||||
#ifdef PLATFORM_HAS_UART_WHEN_SWDP
|
|
||||||
{"convert_tdio", (cmd_handler)cmd_convert_tdio,"Switch TDI/O pins to UART TX/RX functions"},
|
|
||||||
{"set_srst", (cmd_handler)cmd_set_srst,"Set output state of SRST pin (enable|disable)"},
|
|
||||||
#endif
|
|
||||||
#ifdef PLATFORM_HAS_BOOTLOADER
|
|
||||||
{"enter_bootldr", (cmd_handler)cmd_enter_bootldr,"Force BMP into bootloader mode"},
|
|
||||||
#endif
|
#endif
|
||||||
{NULL, NULL, NULL}
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool connect_assert_srst;
|
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
|
||||||
bool debug_bmp;
|
|
||||||
#endif
|
|
||||||
unsigned cortexm_wait_timeout = 2000; /* Timeout to wait for Cortex to react on halt command. */
|
|
||||||
|
|
||||||
int command_process(target *t, char *cmd)
|
int command_process(char *cmd)
|
||||||
{
|
{
|
||||||
const struct command_s *c;
|
const struct command_s *c;
|
||||||
int argc = 1;
|
int argc = 0;
|
||||||
const char **argv;
|
const char **argv;
|
||||||
const char *part;
|
|
||||||
|
|
||||||
/* Initial estimate for argc */
|
/* Initial estimate for argc */
|
||||||
for(char *s = cmd; *s; s++)
|
for(char *s = cmd; *s; s++)
|
||||||
@ -138,532 +75,105 @@ int command_process(target *t, char *cmd)
|
|||||||
argv = alloca(sizeof(const char *) * argc);
|
argv = alloca(sizeof(const char *) * argc);
|
||||||
|
|
||||||
/* Tokenize cmd to find argv */
|
/* Tokenize cmd to find argv */
|
||||||
argc = 0;
|
for(argc = 0, argv[argc] = strtok(cmd, " \t");
|
||||||
for (part = strtok(cmd, " \t"); part; part = strtok(NULL, " \t"))
|
argv[argc]; argv[++argc] = strtok(NULL, " \t"));
|
||||||
argv[argc++] = part;
|
|
||||||
|
|
||||||
/* Look for match and call handler */
|
/* Look for match and call handler */
|
||||||
for(c = cmd_list; c->cmd; c++) {
|
for(c = cmd_list; c->cmd; c++) {
|
||||||
/* Accept a partial match as GDB does.
|
/* Accept a partial match as GDB does.
|
||||||
* So 'mon ver' will match 'monitor version'
|
* So 'mon ver' will match 'monitor version'
|
||||||
*/
|
*/
|
||||||
if ((argc == 0) || !strncmp(argv[0], c->cmd, strlen(argv[0])))
|
if(!strncmp(argv[0], c->cmd, strlen(argv[0]))) {
|
||||||
return !c->handler(t, argc, argv);
|
c->handler(argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!t)
|
return -1;
|
||||||
return -1;
|
|
||||||
|
|
||||||
return target_command(t, argc, argv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BOARD_IDENT "Black Magic Probe" PLATFORM_IDENT FIRMWARE_VERSION
|
void cmd_version(void)
|
||||||
|
|
||||||
bool cmd_version(target *t, int argc, char **argv)
|
|
||||||
{
|
{
|
||||||
(void)t;
|
gdb_out("Black Magic Probe (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")\n");
|
||||||
(void)argc;
|
gdb_out("Copyright (C) 2011 Black Sphere Technologies Ltd.\n");
|
||||||
(void)argv;
|
|
||||||
#if PC_HOSTED == 1
|
|
||||||
char ident[256];
|
|
||||||
gdb_ident(ident, sizeof(ident));
|
|
||||||
DEBUG_WARN("%s\n", ident);
|
|
||||||
#else
|
|
||||||
gdb_out(BOARD_IDENT);
|
|
||||||
gdb_outf(", Hardware Version %d\n", platform_hwversion());
|
|
||||||
gdb_out("Copyright (C) 2022 Black Magic Debug Project\n");
|
|
||||||
gdb_out("License GPLv3+: GNU GPL version 3 or later "
|
gdb_out("License GPLv3+: GNU GPL version 3 or later "
|
||||||
"<http://gnu.org/licenses/gpl.html>\n\n");
|
"<http://gnu.org/licenses/gpl.html>\n\n");
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_help(target *t, int argc, char **argv)
|
void cmd_help(void)
|
||||||
{
|
{
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
const struct command_s *c;
|
const struct command_s *c;
|
||||||
|
|
||||||
if (!t || t->tc->destroy_callback) {
|
for(c = cmd_list; c->cmd; c++)
|
||||||
gdb_out("General commands:\n");
|
gdb_outf("%s -- %s\n", c->cmd, c->help);
|
||||||
for(c = cmd_list; c->cmd; c++)
|
|
||||||
gdb_outf("\t%s -- %s\n", c->cmd, c->help);
|
|
||||||
}
|
|
||||||
if (!t)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
target_command_help(t);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_jtag_scan(target *t, int argc, char **argv)
|
void cmd_jtag_scan(void)
|
||||||
{
|
{
|
||||||
(void)t;
|
gdb_outf("Target voltage: %s\n", platform_target_voltage());
|
||||||
uint8_t irlens[argc];
|
|
||||||
|
|
||||||
if (platform_target_voltage())
|
int devs = jtag_scan();
|
||||||
gdb_outf("Target voltage: %s\n", platform_target_voltage());
|
|
||||||
|
|
||||||
if (argc > 1) {
|
if(devs < 0) {
|
||||||
/* Accept a list of IR lengths on command line */
|
|
||||||
for (int i = 1; i < argc; i++)
|
|
||||||
irlens[i-1] = atoi(argv[i]);
|
|
||||||
irlens[argc-1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(argc > 1 ? irlens : NULL);
|
|
||||||
#else
|
|
||||||
devs = jtag_scan(argc > 1 ? irlens : NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
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("JTAG device scan failed!\n");
|
gdb_out("JTAG device scan failed!\n");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
cmd_targets(NULL, 0, NULL);
|
if(devs == 0) {
|
||||||
morse(NULL, false);
|
gdb_out("JTAG scan found no devices!\n");
|
||||||
return true;
|
return;
|
||||||
|
}
|
||||||
|
gdb_outf("Device IR Len IDCODE Description\n");
|
||||||
|
for(int i = 0; i < jtag_dev_count; i++)
|
||||||
|
gdb_outf("%d\t%d\t0x%08lX %s\n", i,
|
||||||
|
jtag_devs[i].ir_len, jtag_devs[i].idcode,
|
||||||
|
jtag_devs[i].descr);
|
||||||
|
gdb_out("\n");
|
||||||
|
cmd_targets();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_swdp_scan(target *t, int argc, char **argv)
|
void cmd_swdp_scan(void)
|
||||||
{
|
{
|
||||||
(void)t;
|
gdb_outf("Target voltage: %s\n", platform_target_voltage());
|
||||||
volatile uint32_t targetid = 0;
|
|
||||||
if (argc > 1)
|
|
||||||
targetid = strtol(argv[1], NULL, 0);
|
|
||||||
if (platform_target_voltage())
|
|
||||||
gdb_outf("Target voltage: %s\n", platform_target_voltage());
|
|
||||||
|
|
||||||
if(connect_assert_srst)
|
if(adiv5_swdp_scan() < 0) {
|
||||||
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_adiv5_swdp_scan(targetid);
|
|
||||||
#else
|
|
||||||
devs = adiv5_swdp_scan(targetid);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
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("SW-DP scan failed!\n");
|
gdb_out("SW-DP scan failed!\n");
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_targets(NULL, 0, NULL);
|
gdb_outf("SW-DP detected IDCODE: 0x%08X\n", adiv5_dp_list->idcode);
|
||||||
morse(NULL, false);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
|
cmd_targets();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_frequency(target *t, int argc, char **argv)
|
void cmd_targets(void)
|
||||||
{
|
{
|
||||||
(void)t;
|
struct target_s *t;
|
||||||
if (argc == 2) {
|
int i;
|
||||||
char *p;
|
|
||||||
uint32_t frequency = strtol(argv[1], &p, 10);
|
if(!target_list) {
|
||||||
switch(*p) {
|
gdb_out("No usable targets found.\n");
|
||||||
case 'k':
|
return;
|
||||||
frequency *= 1000;
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
frequency *= 1000*1000;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
platform_max_frequency_set(frequency);
|
|
||||||
}
|
}
|
||||||
uint32_t freq = platform_max_frequency_get();
|
|
||||||
if (freq == FREQ_FIXED)
|
|
||||||
gdb_outf("SWJ freq fixed\n");
|
|
||||||
else
|
|
||||||
gdb_outf("Max SWJ freq %08" PRIx32 "\n", freq);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void display_target(int i, target *t, void *context)
|
|
||||||
{
|
|
||||||
(void)context;
|
|
||||||
if (!strcmp(target_driver_name(t), "ARM Cortex-M")) {
|
|
||||||
gdb_outf("***%2d%sUnknown %s Designer %3x Partno %3x %s\n",
|
|
||||||
i, target_attached(t)?" * ":" ",
|
|
||||||
target_driver_name(t),
|
|
||||||
target_designer(t),
|
|
||||||
target_idcode(t),
|
|
||||||
(target_core_name(t)) ? target_core_name(t): "");
|
|
||||||
} else {
|
|
||||||
gdb_outf("%2d %c %s %s\n", i, target_attached(t)?'*':' ',
|
|
||||||
target_driver_name(t),
|
|
||||||
(target_core_name(t)) ? target_core_name(t): "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cmd_targets(target *t, int argc, char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
gdb_out("Available Targets:\n");
|
gdb_out("Available Targets:\n");
|
||||||
gdb_out("No. Att Driver\n");
|
gdb_out("No. Att Driver\n");
|
||||||
if (!target_foreach(display_target, NULL)) {
|
for(t = target_list, i = 1; t; t = t->next, i++)
|
||||||
gdb_out("No usable targets found.\n");
|
gdb_outf("%2d %c %s\n", i, t==cur_target?'*':' ',
|
||||||
return false;
|
t->driver);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_morse(target *t, int argc, char **argv)
|
void cmd_morse(void)
|
||||||
{
|
{
|
||||||
(void)t;
|
if(morse_msg)
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
if(morse_msg) {
|
|
||||||
gdb_outf("%s\n", morse_msg);
|
gdb_outf("%s\n", morse_msg);
|
||||||
DEBUG_WARN("%s\n", morse_msg);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_enable_or_disable(const char *s, bool *out) {
|
|
||||||
if (strlen(s) == 0) {
|
|
||||||
gdb_outf("'enable' or 'disable' argument must be provided\n");
|
|
||||||
return false;
|
|
||||||
} else if (!strncmp(s, "enable", strlen(s))) {
|
|
||||||
*out = true;
|
|
||||||
return true;
|
|
||||||
} else if (!strncmp(s, "disable", strlen(s))) {
|
|
||||||
*out = false;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
gdb_outf("Argument '%s' not recognized as 'enable' or 'disable'\n", s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cmd_connect_srst(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
bool print_status = false;
|
|
||||||
if (argc == 1) {
|
|
||||||
print_status = true;
|
|
||||||
} else if (argc == 2) {
|
|
||||||
if (parse_enable_or_disable(argv[1], &connect_assert_srst)) {
|
|
||||||
print_status = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gdb_outf("Unrecognized command format\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_status) {
|
|
||||||
gdb_outf("Assert SRST during connect: %s\n",
|
|
||||||
connect_assert_srst ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cmd_halt_timeout(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
if (argc > 1)
|
|
||||||
cortexm_wait_timeout = atol(argv[1]);
|
|
||||||
gdb_outf("Cortex-M timeout to wait for device haltes: %d\n",
|
|
||||||
cortexm_wait_timeout);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cmd_hard_srst(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
target_list_free();
|
|
||||||
platform_srst_set_val(true);
|
|
||||||
platform_srst_set_val(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_POWER_SWITCH
|
|
||||||
static bool cmd_target_power(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
if (argc == 1) {
|
|
||||||
gdb_outf("Target Power: %s\n",
|
|
||||||
platform_target_get_power() ? "enabled" : "disabled");
|
|
||||||
} else if (argc == 2) {
|
|
||||||
bool want_enable = false;
|
|
||||||
if (parse_enable_or_disable(argv[1], &want_enable)) {
|
|
||||||
if (want_enable
|
|
||||||
&& !platform_target_get_power()
|
|
||||||
&& platform_target_voltage_sense() > POWER_CONFLICT_THRESHOLD) {
|
|
||||||
/* want to enable target power, but VREF > 0.5V sensed -> cancel */
|
|
||||||
gdb_outf("Target already powered (%s)\n", platform_target_voltage());
|
|
||||||
} else {
|
|
||||||
platform_target_set_power(want_enable);
|
|
||||||
gdb_outf("%s target power\n", want_enable ? "Enabling" : "Disabling");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gdb_outf("Unrecognized command format\n");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
static const char *on_or_off(const bool value)
|
|
||||||
{
|
|
||||||
return value ? "on" : "off";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cmd_rtt(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
const size_t command_len = strlen(argv[1]);
|
|
||||||
if (argc == 1 || (argc == 2 && !strncmp(argv[1], "enabled", command_len))) {
|
|
||||||
rtt_enabled = true;
|
|
||||||
rtt_found = false;
|
|
||||||
} else if ((argc == 2) && !strncmp(argv[1], "disabled", command_len)) {
|
|
||||||
rtt_enabled = false;
|
|
||||||
rtt_found = false;
|
|
||||||
} else if ((argc == 2) && !strncmp(argv[1], "status", command_len)) {
|
|
||||||
gdb_outf("rtt: %s found: %s ident: \"%s\"", on_or_off(rtt_enabled), rtt_found ? "yes" : "no",
|
|
||||||
rtt_ident[0] == '\0' ? "off" : rtt_ident);
|
|
||||||
gdb_outf(" halt: %s", on_or_off(target_no_background_memory_access(t)));
|
|
||||||
gdb_out(" channels: ");
|
|
||||||
if (rtt_auto_channel)
|
|
||||||
gdb_out("auto ");
|
|
||||||
for (size_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", command_len)) {
|
|
||||||
/* mon rtt channel switches to auto rtt channel selection
|
|
||||||
mon rtt channel number... selects channels given */
|
|
||||||
for (size_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 (size_t i = 2; i < (size_t)argc; ++i) {
|
|
||||||
const uint32_t channel = strtoul(argv[i], NULL, 0);
|
|
||||||
if (channel < MAX_RTT_CHAN)
|
|
||||||
rtt_channel[channel].is_enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (argc == 2 && !strncmp(argv[1], "ident", command_len))
|
|
||||||
rtt_ident[0] = '\0';
|
|
||||||
else if (argc == 2 && !strncmp(argv[1], "poll", command_len))
|
|
||||||
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", command_len)) {
|
|
||||||
gdb_outf("cbaddr: 0x%x\n", rtt_cbaddr);
|
|
||||||
gdb_out("ch ena cfg i/o buf@ size head@ tail@ flg\n");
|
|
||||||
for (size_t i = 0; i < MAX_RTT_CHAN; ++i) {
|
|
||||||
gdb_outf("%2zu %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", command_len)) {
|
|
||||||
strncpy(rtt_ident, argv[2], sizeof(rtt_ident));
|
|
||||||
rtt_ident[sizeof(rtt_ident) - 1] = '\0';
|
|
||||||
for (size_t i = 0; i < sizeof(rtt_ident); i++) {
|
|
||||||
if (rtt_ident[i] == '_')
|
|
||||||
rtt_ident[i] = ' ';
|
|
||||||
}
|
|
||||||
} else if (argc == 5 && !strncmp(argv[1], "poll", command_len)) {
|
|
||||||
/* set polling params */
|
|
||||||
rtt_max_poll_ms = strtoul(argv[2], NULL, 0);
|
|
||||||
rtt_min_poll_ms = strtoul(argv[3], NULL, 0);
|
|
||||||
rtt_max_poll_errs = strtoul(argv[4], NULL, 0);
|
|
||||||
} else
|
|
||||||
gdb_out("what?\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
static bool cmd_traceswo(target *t, int argc, const char **argv)
|
static void cmd_traceswo(void)
|
||||||
{
|
{
|
||||||
char serial_no[DFU_SERIAL_LENGTH];
|
extern char serial_no[9];
|
||||||
(void)t;
|
traceswo_init();
|
||||||
#if TRACESWO_PROTOCOL == 2
|
|
||||||
uint32_t baudrate = SWO_DEFAULT_BAUD;
|
|
||||||
#endif
|
|
||||||
uint32_t swo_channelmask = 0; /* swo decoding off */
|
|
||||||
uint8_t decode_arg = 1;
|
|
||||||
#if TRACESWO_PROTOCOL == 2
|
|
||||||
/* argument: optional baud rate for async mode */
|
|
||||||
if ((argc > 1) && (*argv[1] >= '0') && (*argv[1] <= '9')) {
|
|
||||||
baudrate = atoi(argv[1]);
|
|
||||||
if (baudrate == 0) baudrate = SWO_DEFAULT_BAUD;
|
|
||||||
decode_arg = 2;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* argument: 'decode' literal */
|
|
||||||
if((argc > decode_arg) && !strncmp(argv[decode_arg], "decode", strlen(argv[decode_arg]))) {
|
|
||||||
swo_channelmask = 0xFFFFFFFF; /* decoding all channels */
|
|
||||||
/* arguments: channels to decode */
|
|
||||||
if (argc > decode_arg + 1) {
|
|
||||||
swo_channelmask = 0x0;
|
|
||||||
for (int i = decode_arg+1; i < argc; i++) { /* create bitmask of channels to decode */
|
|
||||||
int channel = atoi(argv[i]);
|
|
||||||
if ((channel >= 0) && (channel <= 31))
|
|
||||||
swo_channelmask |= (uint32_t)0x1 << channel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0) && defined(ENABLE_DEBUG)
|
|
||||||
if (debug_bmp) {
|
|
||||||
#if TRACESWO_PROTOCOL == 2
|
|
||||||
gdb_outf("baudrate: %lu ", baudrate);
|
|
||||||
#endif
|
|
||||||
gdb_outf("channel mask: ");
|
|
||||||
for (int8_t i=31;i>=0;i--) {
|
|
||||||
uint8_t bit = (swo_channelmask >> i) & 0x1;
|
|
||||||
gdb_outf("%u", bit);
|
|
||||||
}
|
|
||||||
gdb_outf("\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if TRACESWO_PROTOCOL == 2
|
|
||||||
traceswo_init(baudrate, swo_channelmask);
|
|
||||||
#else
|
|
||||||
traceswo_init(swo_channelmask);
|
|
||||||
#endif
|
|
||||||
serial_no_read(serial_no);
|
|
||||||
gdb_outf("%s:%02X:%02X\n", serial_no, 5, 0x85);
|
gdb_outf("%s:%02X:%02X\n", serial_no, 5, 0x85);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
|
||||||
static bool cmd_debug_bmp(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
bool print_status = false;
|
|
||||||
if (argc == 1) {
|
|
||||||
print_status = true;
|
|
||||||
} else if (argc == 2) {
|
|
||||||
if (parse_enable_or_disable(argv[1], &debug_bmp)) {
|
|
||||||
print_status = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gdb_outf("Unrecognized command format\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_status) {
|
|
||||||
gdb_outf("Debug mode is %s\n",
|
|
||||||
debug_bmp ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_UART_WHEN_SWDP
|
|
||||||
static bool cmd_convert_tdio(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void)t;
|
|
||||||
|
|
||||||
uint8_t val;
|
|
||||||
if (argc > 1) {
|
|
||||||
val = (!strcmp(argv[1], "enable")) ? true : false;
|
|
||||||
usbuart_convert_tdio(val);
|
|
||||||
} else {
|
|
||||||
gdb_outf("Convert_tdio: %s\n",(usbuart_convert_tdio_enabled()) ?
|
|
||||||
"enabled" : "disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cmd_set_srst(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void) t;
|
|
||||||
|
|
||||||
uint8_t val;
|
|
||||||
if (argc > 1) {
|
|
||||||
val = (!strcmp(argv[1], "enable")) ? true : false;
|
|
||||||
platform_srst_set_val(val);
|
|
||||||
} else {
|
|
||||||
gdb_outf("SRST: %s\n",(platform_srst_get_val()) ?
|
|
||||||
"enabled" : "disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_BOOTLOADER
|
|
||||||
static bool cmd_enter_bootldr(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
(void) t;
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
|
|
||||||
scb_reset_system();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_PRINTSERIAL
|
|
||||||
bool cmd_serial(target *t, int argc, char **argv)
|
|
||||||
{
|
|
||||||
(void) t;
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
|
|
||||||
print_serial();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool cmd_heapinfo(target *t, int argc, const char **argv)
|
|
||||||
{
|
|
||||||
if (t == NULL) gdb_out("not attached\n");
|
|
||||||
else if (argc == 5) {
|
|
||||||
target_addr heap_base = strtoul(argv[1], NULL, 16);
|
|
||||||
target_addr heap_limit = strtoul(argv[2], NULL, 16);
|
|
||||||
target_addr stack_base = strtoul(argv[3], NULL, 16);
|
|
||||||
target_addr stack_limit = strtoul(argv[4], NULL, 16);
|
|
||||||
gdb_outf("heapinfo heap_base: %p heap_limit: %p stack_base: %p stack_limit: %p\n",
|
|
||||||
heap_base, heap_limit, stack_base, stack_limit);
|
|
||||||
target_set_heapinfo(t, heap_base, heap_limit, stack_base, stack_limit);
|
|
||||||
} else gdb_outf("heapinfo heap_base heap_limit stack_base stack_limit\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
735
src/cortexm3.c
Normal file
735
src/cortexm3.c
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements debugging functionality specific to ARM
|
||||||
|
* the Cortex-M3 core. This should be generic to ARMv7-M as it is
|
||||||
|
* implemented according to the "ARMv7-M Architectue Reference Manual",
|
||||||
|
* ARM doc DDI0403C.
|
||||||
|
*
|
||||||
|
* Also supports Cortex-M0 / ARMv6-M
|
||||||
|
*
|
||||||
|
* Issues:
|
||||||
|
* There are way too many magic numbers used here.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "jtagtap.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
#include "cortexm3.h"
|
||||||
|
#include "lmi.h"
|
||||||
|
#include "stm32_tgt.h"
|
||||||
|
#include "nxp_tgt.h"
|
||||||
|
|
||||||
|
static char cm3_driver_str[] = "ARM Cortex-M3";
|
||||||
|
|
||||||
|
/* Private peripheral bus base address */
|
||||||
|
#define CM3_PPB_BASE 0xE0000000
|
||||||
|
|
||||||
|
#define CM3_SCS_BASE (CM3_PPB_BASE + 0xE000)
|
||||||
|
|
||||||
|
#define CM3_AIRCR (CM3_SCS_BASE + 0xD0C)
|
||||||
|
#define CM3_CFSR (CM3_SCS_BASE + 0xD28)
|
||||||
|
#define CM3_HFSR (CM3_SCS_BASE + 0xD2C)
|
||||||
|
#define CM3_DFSR (CM3_SCS_BASE + 0xD30)
|
||||||
|
#define CM3_CPACR (CM3_SCS_BASE + 0xD88)
|
||||||
|
#define CM3_DHCSR (CM3_SCS_BASE + 0xDF0)
|
||||||
|
#define CM3_DCRSR (CM3_SCS_BASE + 0xDF4)
|
||||||
|
#define CM3_DCRDR (CM3_SCS_BASE + 0xDF8)
|
||||||
|
#define CM3_DEMCR (CM3_SCS_BASE + 0xDFC)
|
||||||
|
|
||||||
|
#define CM3_FPB_BASE (CM3_PPB_BASE + 0x2000)
|
||||||
|
|
||||||
|
/* ARM Literature uses FP_*, we use CM3_FPB_* consistently */
|
||||||
|
#define CM3_FPB_CTRL (CM3_FPB_BASE + 0x000)
|
||||||
|
#define CM3_FPB_REMAP (CM3_FPB_BASE + 0x004)
|
||||||
|
#define CM3_FPB_COMP(i) (CM3_FPB_BASE + 0x008 + (4*(i)))
|
||||||
|
|
||||||
|
#define CM3_DWT_BASE (CM3_PPB_BASE + 0x1000)
|
||||||
|
|
||||||
|
#define CM3_DWT_CTRL (CM3_DWT_BASE + 0x000)
|
||||||
|
#define CM3_DWT_COMP(i) (CM3_DWT_BASE + 0x020 + (0x10*(i)))
|
||||||
|
#define CM3_DWT_MASK(i) (CM3_DWT_BASE + 0x024 + (0x10*(i)))
|
||||||
|
#define CM3_DWT_FUNC(i) (CM3_DWT_BASE + 0x028 + (0x10*(i)))
|
||||||
|
|
||||||
|
/* Application Interrupt and Reset Control Register (AIRCR) */
|
||||||
|
#define CM3_AIRCR_VECTKEY (0x05FA << 16)
|
||||||
|
/* Bits 31:16 - Read as VECTKETSTAT, 0xFA05 */
|
||||||
|
#define CM3_AIRCR_ENDIANESS (1 << 15)
|
||||||
|
/* Bits 15:11 - Unused, reserved */
|
||||||
|
#define CM3_AIRCR_PRIGROUP (7 << 8)
|
||||||
|
/* Bits 7:3 - Unused, reserved */
|
||||||
|
#define CM3_AIRCR_SYSRESETREQ (1 << 2)
|
||||||
|
#define CM3_AIRCR_VECTCLRACTIVE (1 << 1)
|
||||||
|
#define CM3_AIRCR_VECTRESET (1 << 0)
|
||||||
|
|
||||||
|
/* HardFault Status Register (HFSR) */
|
||||||
|
#define CM3_HFSR_DEBUGEVT (1 << 31)
|
||||||
|
#define CM3_HFSR_FORCED (1 << 30)
|
||||||
|
/* Bits 29:2 - Not specified */
|
||||||
|
#define CM3_HFSR_VECTTBL (1 << 1)
|
||||||
|
/* Bits 0 - Reserved */
|
||||||
|
|
||||||
|
/* Debug Fault Status Register (DFSR) */
|
||||||
|
/* Bits 31:5 - Reserved */
|
||||||
|
#define CM3_DFSR_RESETALL 0x1F
|
||||||
|
#define CM3_DFSR_EXTERNAL (1 << 4)
|
||||||
|
#define CM3_DFSR_VCATCH (1 << 3)
|
||||||
|
#define CM3_DFSR_DWTTRAP (1 << 2)
|
||||||
|
#define CM3_DFSR_BKPT (1 << 1)
|
||||||
|
#define CM3_DFSR_HALTED (1 << 0)
|
||||||
|
|
||||||
|
/* Debug Halting Control and Status Register (DHCSR) */
|
||||||
|
/* This key must be written to bits 31:16 for write to take effect */
|
||||||
|
#define CM3_DHCSR_DBGKEY 0xA05F0000
|
||||||
|
/* Bits 31:26 - Reserved */
|
||||||
|
#define CM3_DHCSR_S_RESET_ST (1 << 25)
|
||||||
|
#define CM3_DHCSR_S_RETIRE_ST (1 << 24)
|
||||||
|
/* Bits 23:20 - Reserved */
|
||||||
|
#define CM3_DHCSR_S_LOCKUP (1 << 19)
|
||||||
|
#define CM3_DHCSR_S_SLEEP (1 << 18)
|
||||||
|
#define CM3_DHCSR_S_HALT (1 << 17)
|
||||||
|
#define CM3_DHCSR_S_REGRDY (1 << 16)
|
||||||
|
/* Bits 15:6 - Reserved */
|
||||||
|
#define CM3_DHCSR_C_SNAPSTALL (1 << 5) /* v7m only */
|
||||||
|
/* Bit 4 - Reserved */
|
||||||
|
#define CM3_DHCSR_C_MASKINTS (1 << 3)
|
||||||
|
#define CM3_DHCSR_C_STEP (1 << 2)
|
||||||
|
#define CM3_DHCSR_C_HALT (1 << 1)
|
||||||
|
#define CM3_DHCSR_C_DEBUGEN (1 << 0)
|
||||||
|
|
||||||
|
/* Debug Core Register Selector Register (DCRSR) */
|
||||||
|
#define CM3_DCRSR_REGSEL_MASK 0x0000001F
|
||||||
|
#define CM3_DCRSR_REGSEL_XPSR 0x00000010
|
||||||
|
#define CM3_DCRSR_REGSEL_MSP 0x00000011
|
||||||
|
#define CM3_DCRSR_REGSEL_PSP 0x00000012
|
||||||
|
|
||||||
|
/* Debug Exception and Monitor Control Register (DEMCR) */
|
||||||
|
/* Bits 31:25 - Reserved */
|
||||||
|
#define CM3_DEMCR_TRCENA (1 << 24)
|
||||||
|
/* Bits 23:20 - Reserved */
|
||||||
|
#define CM3_DEMCR_MON_REQ (1 << 19) /* v7m only */
|
||||||
|
#define CM3_DEMCR_MON_STEP (1 << 18) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_MON_PEND (1 << 17) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_MON_EN (1 << 16) /* v7m only */
|
||||||
|
/* Bits 15:11 - Reserved */
|
||||||
|
#define CM3_DEMCR_VC_HARDERR (1 << 10)
|
||||||
|
#define CM3_DEMCR_VC_INTERR (1 << 9) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_BUSERR (1 << 8) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_STATERR (1 << 7) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_CHKERR (1 << 6) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_NOCPERR (1 << 5) /* v7m only */
|
||||||
|
#define CM3_DEMCR_VC_MMERR (1 << 4) /* v7m only */
|
||||||
|
/* Bits 3:1 - Reserved */
|
||||||
|
#define CM3_DEMCR_VC_CORERESET (1 << 0)
|
||||||
|
|
||||||
|
/* Flash Patch and Breakpoint Control Register (FP_CTRL) */
|
||||||
|
/* Bits 32:15 - Reserved */
|
||||||
|
/* Bits 14:12 - NUM_CODE2 */ /* v7m only */
|
||||||
|
/* Bits 11:8 - NUM_LIT */ /* v7m only */
|
||||||
|
/* Bits 7:4 - NUM_CODE1 */
|
||||||
|
/* Bits 3:2 - Unspecified */
|
||||||
|
#define CM3_FPB_CTRL_KEY (1 << 1)
|
||||||
|
#define CM3_FPB_CTRL_ENABLE (1 << 0)
|
||||||
|
|
||||||
|
/* Data Watchpoint and Trace Mask Register (DWT_MASKx) */
|
||||||
|
#define CM3_DWT_MASK_BYTE (0 << 0)
|
||||||
|
#define CM3_DWT_MASK_HALFWORD (1 << 0)
|
||||||
|
#define CM3_DWT_MASK_WORD (3 << 0)
|
||||||
|
|
||||||
|
/* Data Watchpoint and Trace Function Register (DWT_FUNCTIONx) */
|
||||||
|
#define CM3_DWT_FUNC_MATCHED (1 << 24)
|
||||||
|
#define CM3_DWT_FUNC_DATAVSIZE_WORD (2 << 10) /* v7m only */
|
||||||
|
#define CM3_DWT_FUNC_FUNC_READ (5 << 0)
|
||||||
|
#define CM3_DWT_FUNC_FUNC_WRITE (6 << 0)
|
||||||
|
#define CM3_DWT_FUNC_FUNC_ACCESS (7 << 0)
|
||||||
|
|
||||||
|
static void cm3_attach(struct target_s *target);
|
||||||
|
static void cm3_detach(struct target_s *target);
|
||||||
|
|
||||||
|
static int cm3_regs_read(struct target_s *target, void *data);
|
||||||
|
static int cm3_regs_write(struct target_s *target, const void *data);
|
||||||
|
static int cm3_pc_write(struct target_s *target, const uint32_t val);
|
||||||
|
|
||||||
|
static void cm3_reset(struct target_s *target);
|
||||||
|
static void cm3_halt_resume(struct target_s *target, uint8_t step);
|
||||||
|
static int cm3_halt_wait(struct target_s *target);
|
||||||
|
static void cm3_halt_request(struct target_s *target);
|
||||||
|
static int cm3_fault_unwind(struct target_s *target);
|
||||||
|
|
||||||
|
static int cm3_set_hw_bp(struct target_s *target, uint32_t addr);
|
||||||
|
static int cm3_clear_hw_bp(struct target_s *target, uint32_t addr);
|
||||||
|
|
||||||
|
static int cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
|
||||||
|
static int cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
|
||||||
|
|
||||||
|
static int cm3_check_hw_wp(struct target_s *target, uint32_t *addr);
|
||||||
|
|
||||||
|
/* Watchpoint unit status */
|
||||||
|
#define CM3_MAX_WATCHPOINTS 4 /* architecture says up to 15, no implementation has > 4 */
|
||||||
|
static struct wp_unit_s {
|
||||||
|
uint32_t addr;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t size;
|
||||||
|
} hw_watchpoint[CM3_MAX_WATCHPOINTS];
|
||||||
|
static unsigned hw_watchpoint_max;
|
||||||
|
|
||||||
|
/* Breakpoint unit status */
|
||||||
|
#define CM3_MAX_BREAKPOINTS 6 /* architecture says up to 127, no implementation has > 6 */
|
||||||
|
static uint32_t hw_breakpoint[CM3_MAX_BREAKPOINTS];
|
||||||
|
static unsigned hw_breakpoint_max;
|
||||||
|
|
||||||
|
/* Register number tables */
|
||||||
|
static uint32_t regnum_cortex_m[] = {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* standard r0-r15 */
|
||||||
|
0x10, /* xpsr */
|
||||||
|
0x11, /* msp */
|
||||||
|
0x12, /* psp */
|
||||||
|
0x14 /* special */
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t regnum_cortex_mf[] = {
|
||||||
|
0x21, /* fpscr */
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* s0-s7 */
|
||||||
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* s8-s15 */
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* s16-s23 */
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* s24-s31 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char tdesc_cortex_m[] =
|
||||||
|
"<?xml version=\"1.0\"?>"
|
||||||
|
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
||||||
|
"<target>"
|
||||||
|
" <architecture>arm</architecture>"
|
||||||
|
" <feature name=\"org.gnu.gdb.arm.m-profile\">"
|
||||||
|
" <reg name=\"r0\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r1\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r2\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r3\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r4\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r5\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r6\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r7\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r8\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r9\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r10\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r11\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r12\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"lr\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
" <reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
" <reg name=\"xpsr\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"msp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"psp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"special\" bitsize=\"32\" save-restore=\"no\"/>"
|
||||||
|
" </feature>"
|
||||||
|
"</target>";
|
||||||
|
|
||||||
|
static const char tdesc_cortex_mf[] =
|
||||||
|
"<?xml version=\"1.0\"?>"
|
||||||
|
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
||||||
|
"<target>"
|
||||||
|
" <architecture>arm</architecture>"
|
||||||
|
" <feature name=\"org.gnu.gdb.arm.m-profile\">"
|
||||||
|
" <reg name=\"r0\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r1\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r2\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r3\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r4\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r5\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r6\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r7\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r8\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r9\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r10\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r11\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"r12\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"lr\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
" <reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
|
" <reg name=\"xpsr\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"msp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"psp\" bitsize=\"32\" save-restore=\"no\" type=\"data_ptr\"/>"
|
||||||
|
" <reg name=\"special\" bitsize=\"32\" save-restore=\"no\"/>"
|
||||||
|
" </feature>"
|
||||||
|
" <feature name=\"org.gnu.gdb.arm.vfp\">"
|
||||||
|
" <reg name=\"fpscr\" bitsize=\"32\"/>"
|
||||||
|
" <reg name=\"d0\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d1\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d2\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d3\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d4\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d5\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d6\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d7\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d8\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d9\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d10\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d11\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d12\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d13\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d14\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" <reg name=\"d15\" bitsize=\"64\" type=\"float\"/>"
|
||||||
|
" </feature>"
|
||||||
|
"</target>";
|
||||||
|
|
||||||
|
int
|
||||||
|
cm3_probe(struct target_s *target)
|
||||||
|
{
|
||||||
|
target->driver = cm3_driver_str;
|
||||||
|
|
||||||
|
target->attach = cm3_attach;
|
||||||
|
target->detach = cm3_detach;
|
||||||
|
|
||||||
|
/* Should probe here to make sure it's Cortex-M3 */
|
||||||
|
target->tdesc = tdesc_cortex_m;
|
||||||
|
target->regs_read = cm3_regs_read;
|
||||||
|
target->regs_write = cm3_regs_write;
|
||||||
|
target->pc_write = cm3_pc_write;
|
||||||
|
|
||||||
|
target->reset = cm3_reset;
|
||||||
|
target->halt_request = cm3_halt_request;
|
||||||
|
target->halt_wait = cm3_halt_wait;
|
||||||
|
target->halt_resume = cm3_halt_resume;
|
||||||
|
target->fault_unwind = cm3_fault_unwind;
|
||||||
|
target->regs_size = sizeof(regnum_cortex_m); /* XXX: detect FP extension */
|
||||||
|
|
||||||
|
/* Probe for FP extension */
|
||||||
|
struct target_ap_s *t = (void*)target;
|
||||||
|
uint32_t cpacr = adiv5_ap_mem_read(t->ap, CM3_CPACR);
|
||||||
|
cpacr |= 0x00F00000; /* CP10 = 0b11, CP11 = 0b11 */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_CPACR, cpacr);
|
||||||
|
if (adiv5_ap_mem_read(t->ap, CM3_CPACR) == cpacr) {
|
||||||
|
target->target_options |= TOPT_FLAVOUR_V7MF;
|
||||||
|
target->regs_size += sizeof(regnum_cortex_mf);
|
||||||
|
target->tdesc = tdesc_cortex_mf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(stm32_probe(target) == 0) return 0;
|
||||||
|
if(stm32f4_probe(target) == 0) return 0;
|
||||||
|
if(lpc11xx_probe(target) == 0) return 0;
|
||||||
|
/* if not STM32 try LMI which I don't know how to detect reliably */
|
||||||
|
lmi_probe(target);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cm3_attach(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
uint32_t r;
|
||||||
|
|
||||||
|
target_halt_request(target);
|
||||||
|
while(!target_halt_wait(target));
|
||||||
|
|
||||||
|
/* Request halt on reset */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DEMCR,
|
||||||
|
CM3_DEMCR_TRCENA | CM3_DEMCR_VC_HARDERR |
|
||||||
|
CM3_DEMCR_VC_CORERESET);
|
||||||
|
|
||||||
|
/* Reset DFSR flags */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DFSR, CM3_DFSR_RESETALL);
|
||||||
|
|
||||||
|
/* size the break/watchpoint units */
|
||||||
|
hw_breakpoint_max = CM3_MAX_BREAKPOINTS;
|
||||||
|
r = adiv5_ap_mem_read(t->ap, CM3_FPB_CTRL);
|
||||||
|
if (((r >> 4) & 0xf) < hw_breakpoint_max) /* only look at NUM_COMP1 */
|
||||||
|
hw_breakpoint_max = (r >> 4) & 0xf;
|
||||||
|
hw_watchpoint_max = CM3_MAX_WATCHPOINTS;
|
||||||
|
r = adiv5_ap_mem_read(t->ap, CM3_DWT_CTRL);
|
||||||
|
if ((r >> 28) > hw_watchpoint_max)
|
||||||
|
hw_watchpoint_max = r >> 28;
|
||||||
|
|
||||||
|
/* Clear any stale breakpoints */
|
||||||
|
for(i = 0; i < hw_breakpoint_max; i++) {
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
|
||||||
|
hw_breakpoint[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear any stale watchpoints */
|
||||||
|
for(i = 0; i < hw_watchpoint_max; i++) {
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
|
||||||
|
hw_watchpoint[i].type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flash Patch Control Register: set ENABLE */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_FPB_CTRL,
|
||||||
|
CM3_FPB_CTRL_KEY | CM3_FPB_CTRL_ENABLE);
|
||||||
|
target->set_hw_bp = cm3_set_hw_bp;
|
||||||
|
target->clear_hw_bp = cm3_clear_hw_bp;
|
||||||
|
|
||||||
|
/* Data Watchpoint and Trace */
|
||||||
|
target->set_hw_wp = cm3_set_hw_wp;
|
||||||
|
target->clear_hw_wp = cm3_clear_hw_wp;
|
||||||
|
target->check_hw_wp = cm3_check_hw_wp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cm3_detach(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
/* Clear any stale breakpoints */
|
||||||
|
for(i = 0; i < hw_breakpoint_max; i++)
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
|
||||||
|
|
||||||
|
/* Clear any stale watchpoints */
|
||||||
|
for(i = 0; i < hw_watchpoint_max; i++)
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
|
||||||
|
|
||||||
|
/* Disable debug */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DHCSR, CM3_DHCSR_DBGKEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_regs_read(struct target_s *target, void *data)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t *regs = data;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
/* FIXME: Describe what's really going on here */
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
|
||||||
|
/* Map the banked data registers (0x10-0x1c) to the
|
||||||
|
* debug registers DHCSR, DCRSR, DCRDR and DEMCR respectively */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_TAR, CM3_DHCSR);
|
||||||
|
|
||||||
|
/* Walk the regnum_cortex_m array, reading the registers it
|
||||||
|
* calls out. */
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_DB(1), regnum_cortex_m[0]); /* Required to switch banks */
|
||||||
|
*regs++ = adiv5_dp_read_ap(t->ap->dp, ADIV5_AP_DB(2));
|
||||||
|
for(i = 1; i < sizeof(regnum_cortex_m) / 4; i++) {
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1), regnum_cortex_m[i]);
|
||||||
|
*regs++ = adiv5_dp_read_ap(t->ap->dp, ADIV5_AP_DB(2));
|
||||||
|
}
|
||||||
|
if (target->target_options & TOPT_FLAVOUR_V7MF)
|
||||||
|
for(i = 0; i < sizeof(regnum_cortex_mf) / 4; i++) {
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1), regnum_cortex_mf[i]);
|
||||||
|
*regs++ = adiv5_dp_read_ap(t->ap->dp, ADIV5_AP_DB(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_regs_write(struct target_s *target, const void *data)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
const uint32_t *regs = data;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
/* FIXME: Describe what's really going on here */
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
|
||||||
|
/* Map the banked data registers (0x10-0x1c) to the
|
||||||
|
* debug registers DHCSR, DCRSR, DCRDR and DEMCR respectively */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_TAR, CM3_DHCSR);
|
||||||
|
|
||||||
|
/* Walk the regnum_cortex_m array, writing the registers it
|
||||||
|
* calls out. */
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_DB(2), *regs++); /* Required to switch banks */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1), 0x10000 | regnum_cortex_m[0]);
|
||||||
|
for(i = 1; i < sizeof(regnum_cortex_m) / 4; i++) {
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(2), *regs++);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1),
|
||||||
|
0x10000 | regnum_cortex_m[i]);
|
||||||
|
}
|
||||||
|
if (target->target_options & TOPT_FLAVOUR_V7MF)
|
||||||
|
for(i = 0; i < sizeof(regnum_cortex_mf) / 4; i++) {
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(2), *regs++);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1),
|
||||||
|
0x10000 | regnum_cortex_mf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_pc_write(struct target_s *target, const uint32_t val)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_CSW, 0xA2000052);
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_TAR, CM3_DHCSR);
|
||||||
|
|
||||||
|
adiv5_ap_write(t->ap, ADIV5_AP_DB(2), val); /* Required to switch banks */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, ADIV5_AP_DB(1), 0x1000F);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following three routines implement target halt/resume
|
||||||
|
* using the core debug registers in the NVIC. */
|
||||||
|
static void
|
||||||
|
cm3_reset(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
jtagtap_srst();
|
||||||
|
|
||||||
|
/* Request system reset from NVIC: SRST doesn't work correctly */
|
||||||
|
/* This could be VECTRESET: 0x05FA0001 (reset only core)
|
||||||
|
* or SYSRESETREQ: 0x05FA0004 (system reset)
|
||||||
|
*/
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_AIRCR,
|
||||||
|
CM3_AIRCR_VECTKEY | CM3_AIRCR_SYSRESETREQ);
|
||||||
|
|
||||||
|
/* Poll for release from reset */
|
||||||
|
while(adiv5_ap_mem_read(t->ap, CM3_AIRCR) &
|
||||||
|
(CM3_AIRCR_VECTRESET | CM3_AIRCR_SYSRESETREQ));
|
||||||
|
|
||||||
|
/* Reset DFSR flags */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DFSR, CM3_DFSR_RESETALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cm3_halt_request(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DHCSR,
|
||||||
|
CM3_DHCSR_DBGKEY | CM3_DHCSR_C_HALT | CM3_DHCSR_C_DEBUGEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_halt_wait(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
|
||||||
|
return adiv5_ap_mem_read(t->ap, CM3_DHCSR) & CM3_DHCSR_S_HALT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cm3_halt_resume(struct target_s *target, uint8_t step)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
static uint8_t old_step = 0;
|
||||||
|
uint32_t dhcsr = CM3_DHCSR_DBGKEY | CM3_DHCSR_C_DEBUGEN;
|
||||||
|
|
||||||
|
if(step) dhcsr |= CM3_DHCSR_C_STEP | CM3_DHCSR_C_MASKINTS;
|
||||||
|
|
||||||
|
/* Disable interrupts while single stepping... */
|
||||||
|
if(step != old_step) {
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DHCSR, dhcsr | CM3_DHCSR_C_HALT);
|
||||||
|
old_step = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DHCSR, dhcsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cm3_fault_unwind(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t dfsr = adiv5_ap_mem_read(t->ap, CM3_DFSR);
|
||||||
|
uint32_t hfsr = adiv5_ap_mem_read(t->ap, CM3_HFSR);
|
||||||
|
uint32_t cfsr = adiv5_ap_mem_read(t->ap, CM3_CFSR);
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DFSR, dfsr);/* write back to reset */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_HFSR, hfsr);/* write back to reset */
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_CFSR, cfsr);/* write back to reset */
|
||||||
|
/* We check for FORCED in the HardFault Status Register or
|
||||||
|
* for a configurable fault to avoid catching core resets */
|
||||||
|
if((dfsr & CM3_DFSR_VCATCH) && ((hfsr & CM3_HFSR_FORCED) || cfsr)) {
|
||||||
|
/* Unwind exception */
|
||||||
|
uint32_t regs[target->regs_size];
|
||||||
|
uint32_t stack[8];
|
||||||
|
uint32_t retcode, framesize;
|
||||||
|
/* Read registers for post-exception stack pointer */
|
||||||
|
target_regs_read(target, regs);
|
||||||
|
/* save retcode currently in lr */
|
||||||
|
retcode = regs[14];
|
||||||
|
/* Read stack for pre-exception registers */
|
||||||
|
target_mem_read_words(target, stack, regs[13], sizeof(stack));
|
||||||
|
regs[14] = stack[5]; /* restore LR to pre-exception state */
|
||||||
|
regs[15] = stack[6]; /* restore PC to pre-exception state */
|
||||||
|
|
||||||
|
/* adjust stack to pop exception state */
|
||||||
|
framesize = (retcode & (1<<4)) ? 0x68 : 0x20; /* check for basic vs. extended frame */
|
||||||
|
if (stack[7] & (1<<9)) /* check for stack alignment fixup */
|
||||||
|
framesize += 4;
|
||||||
|
regs[13] += framesize;
|
||||||
|
|
||||||
|
/* FIXME: stack[7] contains xPSR when this is supported */
|
||||||
|
/* although, if we caught the exception it will be unchanged */
|
||||||
|
|
||||||
|
/* Reset exception state to allow resuming from restored
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_AIRCR,
|
||||||
|
CM3_AIRCR_VECTKEY | CM3_AIRCR_VECTCLRACTIVE);
|
||||||
|
|
||||||
|
/* Write pre-exception registers back to core */
|
||||||
|
target_regs_write(target, regs);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following routines implement hardware breakpoints.
|
||||||
|
* The Flash Patch and Breakpoint (FPB) system is used. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_set_hw_bp(struct target_s *target, uint32_t addr)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t val = addr & 0x1FFFFFFC;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
val |= (addr & 2)?0x80000000:0x40000000;
|
||||||
|
val |= 1;
|
||||||
|
|
||||||
|
for(i = 0; i < hw_breakpoint_max; i++)
|
||||||
|
if((hw_breakpoint[i] & 1) == 0) break;
|
||||||
|
|
||||||
|
if(i == hw_breakpoint_max) return -1;
|
||||||
|
|
||||||
|
hw_breakpoint[i] = addr | 1;
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_clear_hw_bp(struct target_s *target, uint32_t addr)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i = 0; i < hw_breakpoint_max; i++)
|
||||||
|
if((hw_breakpoint[i] & ~1) == addr) break;
|
||||||
|
|
||||||
|
if(i == hw_breakpoint_max) return -1;
|
||||||
|
|
||||||
|
hw_breakpoint[i] = 0;
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The following routines implement hardware watchpoints.
|
||||||
|
* The Data Watch and Trace (DWT) system is used. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
switch(len) { /* Convert bytes size to mask size */
|
||||||
|
case 1: len = CM3_DWT_MASK_BYTE; break;
|
||||||
|
case 2: len = CM3_DWT_MASK_HALFWORD; break;
|
||||||
|
case 4: len = CM3_DWT_MASK_WORD; break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(type) { /* Convert gdb type to function type */
|
||||||
|
case 2: type = CM3_DWT_FUNC_FUNC_WRITE; break;
|
||||||
|
case 3: type = CM3_DWT_FUNC_FUNC_READ; break;
|
||||||
|
case 4: type = CM3_DWT_FUNC_FUNC_ACCESS; break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < hw_watchpoint_max; i++)
|
||||||
|
if((hw_watchpoint[i].type == 0) &&
|
||||||
|
((adiv5_ap_mem_read(t->ap, CM3_DWT_FUNC(i)) & 0xF) == 0))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(i == hw_watchpoint_max) return -2;
|
||||||
|
|
||||||
|
hw_watchpoint[i].type = type;
|
||||||
|
hw_watchpoint[i].addr = addr;
|
||||||
|
hw_watchpoint[i].size = len;
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_COMP(i), addr);
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_MASK(i), len);
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), type |
|
||||||
|
((target->target_options & TOPT_FLAVOUR_V6M) ? 0: CM3_DWT_FUNC_DATAVSIZE_WORD));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
switch(len) {
|
||||||
|
case 1: len = CM3_DWT_MASK_BYTE; break;
|
||||||
|
case 2: len = CM3_DWT_MASK_HALFWORD; break;
|
||||||
|
case 4: len = CM3_DWT_MASK_WORD; break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case 2: type = CM3_DWT_FUNC_FUNC_WRITE; break;
|
||||||
|
case 3: type = CM3_DWT_FUNC_FUNC_READ; break;
|
||||||
|
case 4: type = CM3_DWT_FUNC_FUNC_ACCESS; break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < hw_watchpoint_max; i++)
|
||||||
|
if((hw_watchpoint[i].addr == addr) &&
|
||||||
|
(hw_watchpoint[i].type == type) &&
|
||||||
|
(hw_watchpoint[i].size == len)) break;
|
||||||
|
|
||||||
|
if(i == hw_watchpoint_max) return -2;
|
||||||
|
|
||||||
|
hw_watchpoint[i].type = 0;
|
||||||
|
|
||||||
|
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cm3_check_hw_wp(struct target_s *target, uint32_t *addr)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for(i = 0; i < hw_watchpoint_max; i++)
|
||||||
|
/* if SET and MATCHED then break */
|
||||||
|
if(hw_watchpoint[i].type &&
|
||||||
|
(adiv5_ap_mem_read(t->ap, CM3_DWT_FUNC(i)) &
|
||||||
|
CM3_DWT_FUNC_MATCHED))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(i == hw_watchpoint_max) return 0;
|
||||||
|
|
||||||
|
*addr = hw_watchpoint[i].addr;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
104
src/crc32.c
104
src/crc32.c
@ -18,14 +18,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "general.h"
|
#include "platform.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "gdb_if.h"
|
|
||||||
|
|
||||||
#if !defined(STM32F0) && !defined(STM32F1) && !defined(STM32F2) && \
|
|
||||||
!defined(STM32F3) && !defined(STM32F4) && !defined(STM32F7) && \
|
|
||||||
!defined(STM32L0) && !defined(STM32L1) && !defined(STM32F4) && \
|
|
||||||
!defined(STM32G0) && !defined(STM32G4)
|
|
||||||
static const uint32_t crc32_table[] = {
|
static const uint32_t crc32_table[] = {
|
||||||
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
|
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
|
||||||
0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
|
0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
|
||||||
@ -93,98 +88,23 @@ static const uint32_t crc32_table[] = {
|
|||||||
0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4,
|
0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4,
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t crc32_calc(uint32_t crc, uint8_t data)
|
uint32_t crc32_calc(uint32_t crc, uint8_t data)
|
||||||
{
|
{
|
||||||
return (crc << 8) ^ crc32_table[((crc >> 24) ^ data) & 255];
|
return (crc << 8) ^ crc32_table[((crc >> 24) ^ data) & 255];
|
||||||
}
|
}
|
||||||
|
|
||||||
int generic_crc32(target *t, uint32_t *crc_res, uint32_t base, size_t len)
|
uint32_t generic_crc32(struct target_s *target, uint32_t base, int len)
|
||||||
{
|
{
|
||||||
uint32_t crc = -1;
|
uint32_t crc = -1;
|
||||||
#if PC_HOSTED == 1
|
uint8_t byte;
|
||||||
/* Reading a 2 MByte on a H743 takes about 80 s@128, 28s @ 1k,
|
|
||||||
* 22 s @ 4k and 21 s @ 64k
|
|
||||||
*/
|
|
||||||
uint8_t bytes[0x1000];
|
|
||||||
#else
|
|
||||||
uint8_t bytes[128];
|
|
||||||
#endif
|
|
||||||
#if defined(ENABLE_DEBUG)
|
|
||||||
uint32_t start_time = platform_time_ms();
|
|
||||||
#endif
|
|
||||||
uint32_t last_time = platform_time_ms();
|
|
||||||
while (len) {
|
|
||||||
uint32_t actual_time = platform_time_ms();
|
|
||||||
if ( actual_time > last_time + 1000) {
|
|
||||||
last_time = actual_time;
|
|
||||||
gdb_if_putchar(0, true);
|
|
||||||
}
|
|
||||||
size_t read_len = MIN(sizeof(bytes), len);
|
|
||||||
if (target_mem_read(t, bytes, base, read_len)) {
|
|
||||||
DEBUG_WARN("generic_crc32 error around address 0x%08" PRIx32 "\n",
|
|
||||||
base);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < read_len; i++)
|
|
||||||
crc = crc32_calc(crc, bytes[i]);
|
|
||||||
|
|
||||||
base += read_len;
|
|
||||||
len -= read_len;
|
|
||||||
}
|
|
||||||
DEBUG_WARN("%" PRIu32 " ms\n", platform_time_ms() - start_time);
|
|
||||||
*crc_res = crc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#include <libopencm3/stm32/crc.h>
|
|
||||||
int generic_crc32(target *t, uint32_t *crc_res, uint32_t base, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t bytes[128];
|
|
||||||
uint32_t crc;
|
|
||||||
|
|
||||||
CRC_CR |= CRC_CR_RESET;
|
|
||||||
|
|
||||||
uint32_t last_time = platform_time_ms();
|
|
||||||
while (len > 3) {
|
|
||||||
uint32_t actual_time = platform_time_ms();
|
|
||||||
if ( actual_time > last_time + 1000) {
|
|
||||||
last_time = actual_time;
|
|
||||||
gdb_if_putchar(0, true);
|
|
||||||
}
|
|
||||||
size_t read_len = MIN(sizeof(bytes), len) & ~3;
|
|
||||||
if (target_mem_read(t, bytes, base, read_len)) {
|
|
||||||
DEBUG_WARN("generic_crc32 error around address 0x%08" PRIx32 "\n",
|
|
||||||
base);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < read_len; i += 4)
|
|
||||||
CRC_DR = __builtin_bswap32(*(uint32_t*)(bytes+i));
|
|
||||||
|
|
||||||
base += read_len;
|
|
||||||
len -= read_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
crc = CRC_DR;
|
|
||||||
|
|
||||||
if (target_mem_read(t, bytes, base, len)) {
|
|
||||||
DEBUG_WARN("generic_crc32 error around address 0x%08" PRIx32 "\n",
|
|
||||||
base);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
uint8_t *data = bytes;
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
crc ^= *data++ << 24;
|
if (target_mem_read_bytes(target, &byte, base, 1) != 0)
|
||||||
for (int i = 0; i < 8; i++) {
|
return -1;
|
||||||
if (crc & 0x80000000)
|
|
||||||
crc = (crc << 1) ^ 0x4C11DB7;
|
crc = crc32_calc(crc, byte);
|
||||||
else
|
base++;
|
||||||
crc <<= 1;
|
}
|
||||||
}
|
return crc;
|
||||||
}
|
}
|
||||||
*crc_res = crc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "exception.h"
|
|
||||||
|
|
||||||
struct exception *innermost_exception;
|
|
||||||
|
|
||||||
void raise_exception(uint32_t type, const char *msg)
|
|
||||||
{
|
|
||||||
struct exception *e;
|
|
||||||
for (e = innermost_exception; e; e = e->outer) {
|
|
||||||
if (e->mask & type) {
|
|
||||||
e->type = type;
|
|
||||||
e->msg = msg;
|
|
||||||
innermost_exception = e->outer;
|
|
||||||
longjmp(e->jmpbuf, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG_WARN("Unhandled exception: %s\n", msg);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
132
src/gdb_hostio.c
132
src/gdb_hostio.c
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2016 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "target.h"
|
|
||||||
#include "gdb_main.h"
|
|
||||||
#include "gdb_hostio.h"
|
|
||||||
#include "gdb_packet.h"
|
|
||||||
|
|
||||||
int gdb_main_loop(struct target_controller *, bool in_syscall);
|
|
||||||
|
|
||||||
int hostio_reply(struct target_controller *tc, char *pbuf, int len)
|
|
||||||
{
|
|
||||||
(void)len;
|
|
||||||
int retcode, items, errno_;
|
|
||||||
char c, *p;
|
|
||||||
if (pbuf[1] == '-')
|
|
||||||
p = &pbuf[2];
|
|
||||||
else
|
|
||||||
p = &pbuf[1];
|
|
||||||
items = sscanf(p, "%x,%x,%c", &retcode, &errno_, &c);
|
|
||||||
if (pbuf[1] == '-')
|
|
||||||
retcode = -retcode;
|
|
||||||
|
|
||||||
/* if break is requested */
|
|
||||||
tc->interrupted = items == 3 && c == 'C';
|
|
||||||
tc->errno_ = errno_;
|
|
||||||
|
|
||||||
return retcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Interface to host system calls */
|
|
||||||
int hostio_open(struct target_controller *tc,
|
|
||||||
target_addr path, size_t path_len,
|
|
||||||
enum target_open_flags flags, mode_t mode)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fopen,%08X/%X,%08X,%08X", path, path_len, flags, mode);;;;
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_close(struct target_controller *tc, int fd)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fclose,%08X", fd);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_read(struct target_controller *tc,
|
|
||||||
int fd, target_addr buf, unsigned int count)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fread,%08X,%08X,%08X", fd, buf, count);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_write(struct target_controller *tc,
|
|
||||||
int fd, target_addr buf, unsigned int count)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fwrite,%08X,%08X,%08X", fd, buf, count);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
long hostio_lseek(struct target_controller *tc,
|
|
||||||
int fd, long offset, enum target_seek_flag flag)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Flseek,%08X,%08X,%08X", fd, offset, flag);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_rename(struct target_controller *tc,
|
|
||||||
target_addr oldpath, size_t old_len,
|
|
||||||
target_addr newpath, size_t new_len)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Frename,%08X/%X,%08X/%X",
|
|
||||||
oldpath, old_len, newpath, new_len);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_unlink(struct target_controller *tc,
|
|
||||||
target_addr path, size_t path_len)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Funlink,%08X/%X", path, path_len);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_stat(struct target_controller *tc,
|
|
||||||
target_addr path, size_t path_len, target_addr buf)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fstat,%08X/%X,%08X", path, path_len, buf);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_fstat(struct target_controller *tc, int fd, target_addr buf)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Ffstat,%X,%08X", fd, buf);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_gettimeofday(struct target_controller *tc,
|
|
||||||
target_addr tv, target_addr tz)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fgettimeofday,%08X,%08X", tv, tz);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_isatty(struct target_controller *tc, int fd)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fisatty,%08X", fd);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hostio_system(struct target_controller *tc,
|
|
||||||
target_addr cmd, size_t cmd_len)
|
|
||||||
{
|
|
||||||
gdb_putpacket_f("Fsystem,%08X/%X", cmd, cmd_len);
|
|
||||||
return gdb_main_loop(tc, true);
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2016 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef __GDB_SYSCALLS_H
|
|
||||||
#define __GDB_SYSCALLS_H
|
|
||||||
|
|
||||||
#include "target.h"
|
|
||||||
|
|
||||||
int hostio_reply(struct target_controller *tc, char *packet, int len);
|
|
||||||
|
|
||||||
/* Interface to host system calls */
|
|
||||||
int hostio_open(struct target_controller *,
|
|
||||||
target_addr path, size_t path_len,
|
|
||||||
enum target_open_flags flags, mode_t mode);
|
|
||||||
int hostio_close(struct target_controller *, int fd);
|
|
||||||
int hostio_read(struct target_controller *,
|
|
||||||
int fd, target_addr buf, unsigned int count);
|
|
||||||
int hostio_write(struct target_controller *,
|
|
||||||
int fd, target_addr buf, unsigned int count);
|
|
||||||
long hostio_lseek(struct target_controller *,
|
|
||||||
int fd, long offset, enum target_seek_flag flag);
|
|
||||||
int hostio_rename(struct target_controller *,
|
|
||||||
target_addr oldpath, size_t old_len,
|
|
||||||
target_addr newpath, size_t new_len);
|
|
||||||
int hostio_unlink(struct target_controller *,
|
|
||||||
target_addr path, size_t path_len);
|
|
||||||
int hostio_stat(struct target_controller *,
|
|
||||||
target_addr path, size_t path_len, target_addr buf);
|
|
||||||
int hostio_fstat(struct target_controller *, int fd, target_addr buf);
|
|
||||||
int hostio_gettimeofday(struct target_controller *,
|
|
||||||
target_addr tv, target_addr tz);
|
|
||||||
int hostio_isatty(struct target_controller *, int fd);
|
|
||||||
int hostio_system(struct target_controller *,
|
|
||||||
target_addr cmd, size_t cmd_len);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
803
src/gdb_main.c
803
src/gdb_main.c
@ -24,668 +24,419 @@
|
|||||||
* Originally written for GDB 6.8, updated and tested with GDB 7.2.
|
* Originally written for GDB 6.8, updated and tested with GDB 7.2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
#include "ctype.h"
|
|
||||||
#include "hex_utils.h"
|
#include "hex_utils.h"
|
||||||
#include "gdb_if.h"
|
#include "gdb_if.h"
|
||||||
#include "gdb_packet.h"
|
#include "gdb_packet.h"
|
||||||
#include "gdb_main.h"
|
#include "gdb_main.h"
|
||||||
#include "gdb_hostio.h"
|
|
||||||
|
#include "jtagtap.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
|
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
#include "morse.h"
|
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
#include "rtt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum gdb_signal {
|
#define BUF_SIZE 1024
|
||||||
GDB_SIGINT = 2,
|
|
||||||
GDB_SIGTRAP = 5,
|
|
||||||
GDB_SIGSEGV = 11,
|
|
||||||
GDB_SIGLOST = 29,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define BUF_SIZE 1024U
|
|
||||||
|
|
||||||
#define ERROR_IF_NO_TARGET() \
|
#define ERROR_IF_NO_TARGET() \
|
||||||
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
||||||
|
|
||||||
typedef struct
|
static unsigned char pbuf[BUF_SIZE];
|
||||||
|
|
||||||
|
static void handle_q_packet(char *packet, int len);
|
||||||
|
static void handle_v_packet(char *packet, int len);
|
||||||
|
|
||||||
|
void
|
||||||
|
gdb_main(void)
|
||||||
{
|
{
|
||||||
const char *cmd_prefix;
|
int size;
|
||||||
void (*func)(const char *packet, size_t len);
|
static uint8_t single_step = 0;
|
||||||
} cmd_executer;
|
|
||||||
|
|
||||||
static char pbuf[BUF_SIZE + 1U];
|
|
||||||
|
|
||||||
static target *cur_target;
|
|
||||||
static target *last_target;
|
|
||||||
static bool gdb_needs_detach_notify = false;
|
|
||||||
|
|
||||||
static void handle_q_packet(char *packet, size_t len);
|
|
||||||
static void handle_v_packet(char *packet, size_t len);
|
|
||||||
static void handle_z_packet(char *packet, size_t len);
|
|
||||||
static void handle_kill_target(void);
|
|
||||||
|
|
||||||
static void gdb_target_destroy_callback(struct target_controller *tc, target *t)
|
|
||||||
{
|
|
||||||
(void)tc;
|
|
||||||
if (cur_target == t) {
|
|
||||||
gdb_put_notificationz("%Stop:W00");
|
|
||||||
gdb_out("You are now detached from the previous target.\n");
|
|
||||||
cur_target = NULL;
|
|
||||||
gdb_needs_detach_notify = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_target == t)
|
|
||||||
last_target = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gdb_target_printf(struct target_controller *tc,
|
|
||||||
const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
(void)tc;
|
|
||||||
gdb_voutf(fmt, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct target_controller gdb_controller = {
|
|
||||||
.destroy_callback = gdb_target_destroy_callback,
|
|
||||||
.printf = gdb_target_printf,
|
|
||||||
|
|
||||||
.open = hostio_open,
|
|
||||||
.close = hostio_close,
|
|
||||||
.read = hostio_read,
|
|
||||||
.write = hostio_write,
|
|
||||||
.lseek = hostio_lseek,
|
|
||||||
.rename = hostio_rename,
|
|
||||||
.unlink = hostio_unlink,
|
|
||||||
.stat = hostio_stat,
|
|
||||||
.fstat = hostio_fstat,
|
|
||||||
.gettimeofday = hostio_gettimeofday,
|
|
||||||
.isatty = hostio_isatty,
|
|
||||||
.system = hostio_system,
|
|
||||||
};
|
|
||||||
|
|
||||||
int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|
||||||
{
|
|
||||||
bool single_step = false;
|
|
||||||
|
|
||||||
|
DEBUG("Entring GDB protocol main loop\n");
|
||||||
/* GDB protocol main loop */
|
/* GDB protocol main loop */
|
||||||
while (1) {
|
while(1) {
|
||||||
SET_IDLE_STATE(1);
|
SET_IDLE_STATE(1);
|
||||||
size_t size = gdb_getpacket(pbuf, BUF_SIZE);
|
size = gdb_getpacket(pbuf, BUF_SIZE);
|
||||||
SET_IDLE_STATE(0);
|
SET_IDLE_STATE(0);
|
||||||
switch (pbuf[0]) {
|
switch(pbuf[0]) {
|
||||||
/* Implementation of these is mandatory! */
|
/* Implementation of these is mandatory! */
|
||||||
case 'g': { /* 'g': Read general registers */
|
case 'g': { /* 'g': Read general registers */
|
||||||
|
uint32_t arm_regs[cur_target->regs_size];
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
target_regs_read(cur_target, (void*)arm_regs);
|
||||||
target_regs_read(cur_target, gp_regs);
|
gdb_putpacket(hexify(pbuf, (void*)arm_regs, cur_target->regs_size), cur_target->regs_size * 2);
|
||||||
gdb_putpacket(hexify(pbuf, gp_regs, sizeof(gp_regs)), sizeof(gp_regs) * 2U);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
||||||
uint32_t addr, len;
|
unsigned long addr, len;
|
||||||
|
char *mem;
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
sscanf(pbuf, "m%" SCNx32 ",%" SCNx32, &addr, &len);
|
sscanf(pbuf, "m%08lX,%08lX", &addr, &len);
|
||||||
if (len > sizeof(pbuf) / 2) {
|
DEBUG("m packet: addr = %08lX, len = %08lX\n", addr, len);
|
||||||
gdb_putpacketz("E02");
|
mem = malloc(len);
|
||||||
break;
|
if(!mem) break;
|
||||||
}
|
if(((addr & 3) == 0) && ((len & 3) == 0))
|
||||||
DEBUG_GDB("m packet: addr = %" PRIx32 ", len = %" PRIx32 "\n",
|
target_mem_read_words(cur_target, (void*)mem, addr, len);
|
||||||
addr, len);
|
|
||||||
uint8_t mem[len];
|
|
||||||
if (target_mem_read(cur_target, mem, addr, len))
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
else
|
else
|
||||||
gdb_putpacket(hexify(pbuf, mem, len), len * 2U);
|
target_mem_read_bytes(cur_target, (void*)mem, addr, len);
|
||||||
|
if(target_check_error(cur_target))
|
||||||
|
gdb_putpacket("E01", 3);
|
||||||
|
else
|
||||||
|
gdb_putpacket(hexify(pbuf, mem, len), len*2);
|
||||||
|
free(mem);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'G': { /* 'G XX': Write general registers */
|
case 'G': { /* 'G XX': Write general registers */
|
||||||
|
uint32_t arm_regs[cur_target->regs_size];
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
unhexify((void*)arm_regs, &pbuf[1], cur_target->regs_size);
|
||||||
unhexify(gp_regs, &pbuf[1], sizeof(gp_regs));
|
target_regs_write(cur_target, arm_regs);
|
||||||
target_regs_write(cur_target, gp_regs);
|
gdb_putpacket("OK", 2);
|
||||||
gdb_putpacketz("OK");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
|
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
|
||||||
uint32_t addr = 0;
|
unsigned long addr, len;
|
||||||
uint32_t len = 0;
|
|
||||||
int hex;
|
int hex;
|
||||||
|
char *mem;
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
|
sscanf(pbuf, "M%08lX,%08lX:%n", &addr, &len, &hex);
|
||||||
if (len > (unsigned)(size - hex) / 2) {
|
DEBUG("M packet: addr = %08lX, len = %08lX\n", addr, len);
|
||||||
gdb_putpacketz("E02");
|
mem = malloc(len);
|
||||||
break;
|
|
||||||
}
|
|
||||||
DEBUG_GDB("M packet: addr = %" PRIx32 ", len = %" PRIx32 "\n",
|
|
||||||
addr, len);
|
|
||||||
uint8_t mem[len];
|
|
||||||
unhexify(mem, pbuf + hex, len);
|
unhexify(mem, pbuf + hex, len);
|
||||||
if (target_mem_write(cur_target, addr, mem, len))
|
if(((addr & 3) == 0) && ((len & 3) == 0))
|
||||||
gdb_putpacketz("E01");
|
target_mem_write_words(cur_target, addr, (void*)mem, len);
|
||||||
else
|
else
|
||||||
gdb_putpacketz("OK");
|
target_mem_write_bytes(cur_target, addr, (void*)mem, len);
|
||||||
break;
|
if(target_check_error(cur_target))
|
||||||
}
|
gdb_putpacket("E01", 3);
|
||||||
/* '[m|M|g|G|c][thread-id]' : Set the thread ID for the given subsequent operation
|
|
||||||
* (we don't actually care which as we only care about the TID for whether to send OK or an error)
|
|
||||||
*/
|
|
||||||
case 'H': {
|
|
||||||
char operation = 0;
|
|
||||||
uint32_t thread_id = 0;
|
|
||||||
sscanf(pbuf, "H%c%" SCNx32, &operation, &thread_id);
|
|
||||||
if (thread_id <= 1)
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
else
|
else
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacket("OK", 2);
|
||||||
|
free(mem);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 's': /* 's [addr]': Single step [start at addr] */
|
case 's': /* 's [addr]': Single step [start at addr] */
|
||||||
single_step = true;
|
single_step = 1;
|
||||||
/* fall through */
|
// Fall through to resume target
|
||||||
case 'c': /* 'c [addr]': Continue [at addr] */
|
case 'c': /* 'c [addr]': Continue [at addr] */
|
||||||
if (!cur_target) {
|
if(!cur_target) {
|
||||||
gdb_putpacketz("X1D");
|
gdb_putpacketz("X1D");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_halt_resume(cur_target, single_step);
|
target_halt_resume(cur_target, single_step);
|
||||||
SET_RUN_STATE(1);
|
SET_RUN_STATE(1);
|
||||||
single_step = false;
|
single_step = 0;
|
||||||
/* fall through */
|
// Fall through to wait for target halt
|
||||||
case '?': { /* '?': Request reason for target halt */
|
case '?': { /* '?': Request reason for target halt */
|
||||||
/* This packet isn't documented as being mandatory,
|
/* This packet isn't documented as being mandatory,
|
||||||
* but GDB doesn't work without it. */
|
* but GDB doesn't work without it. */
|
||||||
target_addr watch;
|
int sent_int = 0;
|
||||||
enum target_halt_reason reason;
|
uint32_t watch_addr;
|
||||||
|
|
||||||
if (!cur_target) {
|
if(!cur_target) {
|
||||||
/* Report "target exited" if no target */
|
/* Report "target exited" if no target */
|
||||||
gdb_putpacketz("W00");
|
gdb_putpacketz("W00");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for target halt */
|
/* Wait for target halt */
|
||||||
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
while(!target_halt_wait(cur_target)) {
|
||||||
char c = (char)gdb_if_getchar_to(0);
|
unsigned char c = gdb_if_getchar_to(0);
|
||||||
if(c == '\x03' || c == '\x04') {
|
if((c == '\x03') || (c == '\x04')) {
|
||||||
target_halt_request(cur_target);
|
target_halt_request(cur_target);
|
||||||
|
sent_int = 1;
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
if (rtt_enabled) poll_rtt(cur_target);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SET_RUN_STATE(0);
|
SET_RUN_STATE(0);
|
||||||
|
/* Report reason for halt */
|
||||||
/* Translate reason to GDB signal */
|
if(target_check_hw_wp(cur_target, &watch_addr)) {
|
||||||
switch (reason) {
|
/* Watchpoint hit */
|
||||||
case TARGET_HALT_ERROR:
|
gdb_putpacket_f("T05watch:%08X;", watch_addr);
|
||||||
gdb_putpacket_f("X%02X", GDB_SIGLOST);
|
} else if(target_fault_unwind(cur_target)) {
|
||||||
morse("TARGET LOST.", true);
|
gdb_putpacketz("T0b");
|
||||||
break;
|
} else if(sent_int) {
|
||||||
case TARGET_HALT_REQUEST:
|
/* Target interrupted */
|
||||||
gdb_putpacket_f("T%02X", GDB_SIGINT);
|
gdb_putpacketz("T02");
|
||||||
break;
|
} else {
|
||||||
case TARGET_HALT_WATCHPOINT:
|
gdb_putpacketz("T05");
|
||||||
gdb_putpacket_f("T%02Xwatch:%08X;", GDB_SIGTRAP, watch);
|
|
||||||
break;
|
|
||||||
case TARGET_HALT_FAULT:
|
|
||||||
gdb_putpacket_f("T%02X", GDB_SIGSEGV);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
gdb_putpacket_f("T%02X", GDB_SIGTRAP);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optional GDB packet support */
|
/* Optional GDB packet support */
|
||||||
case 'p': { /* Read single register */
|
case '!': /* Enable Extended GDB Protocol. */
|
||||||
ERROR_IF_NO_TARGET();
|
|
||||||
uint32_t reg;
|
|
||||||
sscanf(pbuf, "p%" SCNx32, ®);
|
|
||||||
uint8_t val[8];
|
|
||||||
size_t s = target_reg_read(cur_target, reg, val, sizeof(val));
|
|
||||||
if (s > 0)
|
|
||||||
gdb_putpacket(hexify(pbuf, val, s), s * 2);
|
|
||||||
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);
|
|
||||||
// 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)
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
else
|
|
||||||
gdb_putpacketz("EFF");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'F': /* Semihosting call finished */
|
|
||||||
if (in_syscall)
|
|
||||||
return hostio_reply(tc, pbuf, size);
|
|
||||||
else {
|
|
||||||
DEBUG_GDB("*** F packet when not in syscall! '%s'\n", pbuf);
|
|
||||||
gdb_putpacketz("");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '!': /* Enable Extended GDB Protocol. */
|
|
||||||
/* This doesn't do anything, we support the extended
|
/* This doesn't do anything, we support the extended
|
||||||
* protocol anyway, but GDB will never send us a 'R'
|
* protocol anyway, but GDB will never send us a 'R'
|
||||||
* packet unless we answer 'OK' here.
|
* packet unless we answer 'OK' here.
|
||||||
*/
|
*/
|
||||||
gdb_putpacketz("OK");
|
gdb_putpacket("OK", 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x04:
|
case 0x04:
|
||||||
case 'D': /* GDB 'detach' command. */
|
case 'D': /* GDB 'detach' command. */
|
||||||
if(cur_target) {
|
if(cur_target)
|
||||||
SET_RUN_STATE(1);
|
|
||||||
target_detach(cur_target);
|
target_detach(cur_target);
|
||||||
}
|
|
||||||
last_target = cur_target;
|
last_target = cur_target;
|
||||||
cur_target = NULL;
|
cur_target = NULL;
|
||||||
gdb_putpacketz("OK");
|
gdb_putpacket("OK", 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'k': /* Kill the target */
|
case 'k': /* Kill the target */
|
||||||
handle_kill_target();
|
if(cur_target) {
|
||||||
|
target_reset(cur_target);
|
||||||
|
target_detach(cur_target);
|
||||||
|
last_target = cur_target;
|
||||||
|
cur_target = NULL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r': /* Reset the target system */
|
case 'r': /* Reset the target system */
|
||||||
case 'R': /* Restart the target program */
|
case 'R': /* Restart the target program */
|
||||||
if(cur_target)
|
if(cur_target)
|
||||||
target_reset(cur_target);
|
target_reset(cur_target);
|
||||||
else if(last_target) {
|
else if(last_target) {
|
||||||
cur_target = target_attach(last_target,
|
cur_target = last_target;
|
||||||
&gdb_controller);
|
target_attach(cur_target);
|
||||||
if(cur_target)
|
|
||||||
morse(NULL, false);
|
|
||||||
target_reset(cur_target);
|
target_reset(cur_target);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'X': { /* 'X addr,len:XX': Write binary data to addr */
|
case 'X': { /* 'X addr,len:XX': Write binary data to addr */
|
||||||
uint32_t addr, len;
|
unsigned long addr, len;
|
||||||
int bin;
|
int bin;
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
sscanf(pbuf, "X%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &bin);
|
sscanf(pbuf, "X%08lX,%08lX:%n", &addr, &len, &bin);
|
||||||
if (len > (unsigned)(size - bin)) {
|
DEBUG("X packet: addr = %08lX, len = %08lX\n", addr, len);
|
||||||
gdb_putpacketz("E02");
|
if(((addr & 3) == 0) && ((len & 3) == 0))
|
||||||
break;
|
target_mem_write_words(cur_target, addr, (void*)pbuf+bin, len);
|
||||||
}
|
|
||||||
DEBUG_GDB("X packet: addr = %" PRIx32 ", len = %" PRIx32 "\n",
|
|
||||||
addr, len);
|
|
||||||
if (target_mem_write(cur_target, addr, pbuf + bin, len))
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
else
|
else
|
||||||
gdb_putpacketz("OK");
|
target_mem_write_bytes(cur_target, addr, (void*)pbuf+bin, len);
|
||||||
|
if(target_check_error(cur_target))
|
||||||
|
gdb_putpacket("E01", 3);
|
||||||
|
else
|
||||||
|
gdb_putpacket("OK", 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'q': /* General query packet */
|
case 'q': /* General query packet */
|
||||||
handle_q_packet(pbuf, size);
|
handle_q_packet(pbuf, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v': /* Verbose command packet */
|
case 'v': /* General query packet */
|
||||||
handle_v_packet(pbuf, size);
|
handle_v_packet(pbuf, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* These packet implement hardware break-/watchpoints */
|
/* These packet implement hardware break-/watchpoints */
|
||||||
case 'Z': /* Z type,addr,len: Set breakpoint packet */
|
case 'Z': /* Z type,addr,len: Set breakpoint packet */
|
||||||
case 'z': /* z type,addr,len: Clear breakpoint packet */
|
case 'z': { /* z type,addr,len: Clear breakpoint packet */
|
||||||
|
uint8_t set = (pbuf[0]=='Z')?1:0;
|
||||||
|
int type, len;
|
||||||
|
unsigned long addr;
|
||||||
|
int ret;
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
handle_z_packet(pbuf, size);
|
/* I have no idea why this doesn't work. Seems to work
|
||||||
|
* with real sscanf() though... */
|
||||||
|
//sscanf(pbuf, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
|
||||||
|
type = pbuf[1] - '0';
|
||||||
|
sscanf(pbuf + 2, ",%08lX,%d", &addr, &len);
|
||||||
|
switch(type) {
|
||||||
|
case 1: /* Hardware breakpoint */
|
||||||
|
if(!cur_target->set_hw_bp) { /* Not supported */
|
||||||
|
gdb_putpacket("", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(set) ret = target_set_hw_bp(cur_target, addr);
|
||||||
|
else ret = target_clear_hw_bp(cur_target, addr);
|
||||||
|
|
||||||
|
if(!ret) gdb_putpacket("OK", 2);
|
||||||
|
else gdb_putpacket("E01", 3);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
if(!cur_target->set_hw_wp) { /* Not supported */
|
||||||
|
gdb_putpacket("", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(set) ret = target_set_hw_wp(cur_target, type, addr, len);
|
||||||
|
else ret = target_clear_hw_wp(cur_target, type, addr, len);
|
||||||
|
|
||||||
|
if(!ret) gdb_putpacket("OK", 2);
|
||||||
|
else gdb_putpacket("E01", 3);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gdb_putpacket("", 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: /* Packet not implemented */
|
default: /* Packet not implemented */
|
||||||
DEBUG_GDB("*** Unsupported packet: %s\n", pbuf);
|
DEBUG("*** Unsupported packet: %s\n", pbuf);
|
||||||
gdb_putpacketz("");
|
gdb_putpacket("", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool exec_command(char *packet, const size_t length, const cmd_executer *exec)
|
static void
|
||||||
|
handle_q_string_reply(const char *str, const char *param)
|
||||||
{
|
{
|
||||||
while (exec->cmd_prefix) {
|
unsigned long addr, len;
|
||||||
const size_t prefix_length = strlen(exec->cmd_prefix);
|
|
||||||
if (!strncmp(packet, exec->cmd_prefix, prefix_length)) {
|
|
||||||
exec->func(packet + prefix_length, length - prefix_length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
++exec;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exec_q_rcmd(const char *packet, const size_t length)
|
if (sscanf(param, "%08lX,%08lX", &addr, &len) != 2) {
|
||||||
{
|
|
||||||
/* calculate size and allocate buffer for command */
|
|
||||||
const size_t datalen = length / 2U;
|
|
||||||
char *data = alloca(datalen + 1);
|
|
||||||
/* dehexify command */
|
|
||||||
unhexify(data, packet, datalen);
|
|
||||||
data[datalen] = 0; /* add terminating null */
|
|
||||||
|
|
||||||
const 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 *reply, const char *param)
|
|
||||||
{
|
|
||||||
const size_t reply_length = strlen(reply);
|
|
||||||
uint32_t addr = 0;
|
|
||||||
uint32_t len = 0;
|
|
||||||
|
|
||||||
if (sscanf(param, "%08" PRIx32 ",%08" PRIx32, &addr, &len) != 2) {
|
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (addr > reply_length) {
|
if (addr < strlen (str)) {
|
||||||
gdb_putpacketz("E01");
|
uint8_t reply[len+2];
|
||||||
return;
|
reply[0] = 'm';
|
||||||
}
|
strncpy (reply + 1, &str[addr], len);
|
||||||
if (addr == reply_length) {
|
if(len > strlen(&str[addr]))
|
||||||
|
len = strlen(&str[addr]);
|
||||||
|
gdb_putpacket(reply, len + 1);
|
||||||
|
} else if (addr == strlen (str)) {
|
||||||
gdb_putpacketz("l");
|
gdb_putpacketz("l");
|
||||||
return;
|
} else
|
||||||
}
|
|
||||||
size_t output_len = reply_length - addr;
|
|
||||||
if (output_len > len)
|
|
||||||
output_len = len;
|
|
||||||
gdb_putpacket2("m", 1U, reply + addr, output_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exec_q_supported(const char *packet, const size_t length)
|
|
||||||
{
|
|
||||||
(void)packet;
|
|
||||||
(void)length;
|
|
||||||
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exec_q_memory_map(const char *packet, const size_t length)
|
|
||||||
{
|
|
||||||
(void)packet;
|
|
||||||
(void)length;
|
|
||||||
/* 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");
|
gdb_putpacketz("E01");
|
||||||
return;
|
|
||||||
}
|
|
||||||
char buf[1024];
|
|
||||||
target_mem_map(cur_target, buf, sizeof(buf)); /* Fixme: Check size!*/
|
|
||||||
handle_q_string_reply(buf, packet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_q_feature_read(const char *packet, const size_t length)
|
static void
|
||||||
|
handle_q_packet(char *packet, int len)
|
||||||
{
|
{
|
||||||
(void)length;
|
uint32_t addr, alen;
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exec_q_crc(const char *packet, const size_t length)
|
if(!strncmp(packet, "qRcmd,", 6)) {
|
||||||
{
|
unsigned char *data;
|
||||||
(void)length;
|
int datalen;
|
||||||
uint32_t addr;
|
|
||||||
uint32_t addr_length;
|
/* calculate size and allocate buffer for command */
|
||||||
if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &addr_length) == 2) {
|
datalen = (len - 6) / 2;
|
||||||
if (!cur_target) {
|
data = alloca(datalen+1);
|
||||||
|
/* dehexify command */
|
||||||
|
unhexify(data, packet+6, datalen);
|
||||||
|
data[datalen] = 0; /* add terminating null */
|
||||||
|
|
||||||
|
if(command_process(data) < 0)
|
||||||
|
gdb_putpacket("", 0);
|
||||||
|
else gdb_putpacket("OK", 2);
|
||||||
|
|
||||||
|
} else if (!strncmp (packet, "qSupported", 10)) {
|
||||||
|
/* Query supported protocol features */
|
||||||
|
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE);
|
||||||
|
|
||||||
|
} 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 = last_target;
|
||||||
|
target_attach(cur_target);
|
||||||
|
}
|
||||||
|
if((!cur_target) || (!cur_target->xml_mem_map)) {
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t crc;
|
handle_q_string_reply(cur_target->xml_mem_map, packet + 23);
|
||||||
if (generic_crc32(cur_target, &crc, addr, addr_length))
|
|
||||||
gdb_putpacketz("E03");
|
} else if (strncmp (packet, "qXfer:features:read:target.xml:", 31) == 0) {
|
||||||
else
|
/* Read target description */
|
||||||
gdb_putpacket_f("C%lx", crc);
|
if((!cur_target) && last_target) {
|
||||||
}
|
/* Attach to last target if detached. */
|
||||||
|
cur_target = last_target;
|
||||||
|
target_attach(cur_target);
|
||||||
|
}
|
||||||
|
if((!cur_target) || (!cur_target->tdesc)) {
|
||||||
|
gdb_putpacketz("E01");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handle_q_string_reply(cur_target->tdesc, packet + 31);
|
||||||
|
} else if (sscanf(packet, "qCRC:%08lX,%08lX", &addr, &alen) == 2) {
|
||||||
|
if(!cur_target) {
|
||||||
|
gdb_putpacketz("E01");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gdb_putpacket_f("C%lx", generic_crc32(cur_target, addr, alen));
|
||||||
|
|
||||||
|
} else gdb_putpacket("", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void
|
||||||
* qC queries are for the current thread. We don't support threads but GDB 11 and 12 require this,
|
handle_v_packet(char *packet, int plen)
|
||||||
* so we always answer that the current thread is thread 1.
|
|
||||||
*/
|
|
||||||
static void exec_q_c(const char *packet, const size_t length)
|
|
||||||
{
|
{
|
||||||
(void)packet;
|
unsigned long addr, len;
|
||||||
(void)length;
|
|
||||||
gdb_putpacketz("QC1");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* qfThreadInfo queries are required in GDB 11 and 12 as these GDBs require the server to support
|
|
||||||
* threading even when there's only the possiblity for one thread to exist. In this instance,
|
|
||||||
* we have to tell GDB that there is a single active thread so it doesn't think the "thread" died.
|
|
||||||
* qsThreadInfo will always follow qfThreadInfo when we reply as we have to specify 'l' at the
|
|
||||||
* end to terminate the list.. GDB doesn't like this not happening.
|
|
||||||
*/
|
|
||||||
static void exec_q_thread_info(const char *packet, const size_t length)
|
|
||||||
{
|
|
||||||
(void)length;
|
|
||||||
if (packet[-11] == 'f')
|
|
||||||
gdb_putpacketz("m1");
|
|
||||||
else
|
|
||||||
gdb_putpacketz("l");
|
|
||||||
}
|
|
||||||
|
|
||||||
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},
|
|
||||||
{"qC", exec_q_c},
|
|
||||||
{"qfThreadInfo", exec_q_thread_info},
|
|
||||||
{"qsThreadInfo", exec_q_thread_info},
|
|
||||||
{NULL, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_kill_target(void)
|
|
||||||
{
|
|
||||||
if (cur_target) {
|
|
||||||
target_reset(cur_target);
|
|
||||||
target_detach(cur_target);
|
|
||||||
last_target = cur_target;
|
|
||||||
cur_target = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_q_packet(char *packet, const size_t length)
|
|
||||||
{
|
|
||||||
if (exec_command(packet, length, q_commands))
|
|
||||||
return;
|
|
||||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
|
||||||
gdb_putpacket("", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_v_packet(char *packet, const size_t plen)
|
|
||||||
{
|
|
||||||
uint32_t addr = 0;
|
|
||||||
uint32_t len = 0;
|
|
||||||
int bin;
|
int bin;
|
||||||
static uint8_t flash_mode = 0;
|
static uint8_t flash_mode = 0;
|
||||||
|
|
||||||
if (sscanf(packet, "vAttach;%08" PRIx32, &addr) == 1) {
|
if (sscanf(packet, "vAttach;%08lX", &addr) == 1) {
|
||||||
/* Attach to remote target processor */
|
/* Attach to remote target processor */
|
||||||
cur_target = target_attach_n(addr, &gdb_controller);
|
target *t;
|
||||||
if(cur_target) {
|
uint32_t i;
|
||||||
morse(NULL, false);
|
for(t = target_list, i = 1; t; t = t->next, i++)
|
||||||
/*
|
if(i == addr) {
|
||||||
* We don't actually support threads, but GDB 11 and 12 can't work without
|
cur_target = t;
|
||||||
* us saying we attached to thread 1.. see the following for the low-down of this:
|
target_attach(t);
|
||||||
* https://sourceware.org/bugzilla/show_bug.cgi?id=28405
|
gdb_putpacketz("T05");
|
||||||
* https://sourceware.org/bugzilla/show_bug.cgi?id=28874
|
break;
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2021-December/184171.html
|
}
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2022-April/188058.html
|
if(!cur_target) /* Failed to attach */
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2022-July/190869.html
|
|
||||||
*/
|
|
||||||
gdb_putpacketz("T05thread:1;");
|
|
||||||
} else
|
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
|
|
||||||
} else if (!strncmp(packet, "vKill;", 6)) {
|
} else if (!strcmp(packet, "vRun;")) {
|
||||||
/* Kill the target - we don't actually care about the PID that follows "vKill;" */
|
|
||||||
handle_kill_target();
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
|
|
||||||
} else if (!strncmp(packet, "vRun", 4)) {
|
|
||||||
/* Parse command line for get_cmdline semihosting call */
|
|
||||||
char cmdline[83];
|
|
||||||
char *pcmdline = cmdline;
|
|
||||||
char *tok = packet + 4;
|
|
||||||
if (*tok == ';')
|
|
||||||
++tok;
|
|
||||||
cmdline[0] = '\0';
|
|
||||||
while(*tok != '\0') {
|
|
||||||
if (strlen(cmdline) + 3 >= sizeof(cmdline))
|
|
||||||
break;
|
|
||||||
if (*tok == ';') {
|
|
||||||
*pcmdline++ = ' ';
|
|
||||||
pcmdline[0] = '\0';
|
|
||||||
tok++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isxdigit(tok[0]) && isxdigit(tok[1])) {
|
|
||||||
unhexify(pcmdline, tok, 2);
|
|
||||||
if ((*pcmdline == ' ') || (*pcmdline == '\\')) {
|
|
||||||
pcmdline[1] = *pcmdline;
|
|
||||||
*pcmdline++ = '\\';
|
|
||||||
}
|
|
||||||
pcmdline++;
|
|
||||||
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. */
|
/* 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);
|
target_reset(cur_target);
|
||||||
gdb_putpacketz("T05");
|
gdb_putpacketz("T05");
|
||||||
} else if (last_target) {
|
} else if(last_target) {
|
||||||
cur_target = target_attach(last_target,
|
cur_target = last_target;
|
||||||
&gdb_controller);
|
target_attach(cur_target);
|
||||||
|
target_reset(cur_target);
|
||||||
|
gdb_putpacketz("T05");
|
||||||
|
} else gdb_putpacketz("E01");
|
||||||
|
|
||||||
/* If we were able to attach to the target again */
|
} else if (sscanf(packet, "vFlashErase:%08lX,%08lX", &addr, &len) == 2) {
|
||||||
if (cur_target) {
|
|
||||||
target_set_cmdline(cur_target, cmdline);
|
|
||||||
target_reset(cur_target);
|
|
||||||
morse(NULL, false);
|
|
||||||
gdb_putpacketz("T05");
|
|
||||||
} else
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
|
|
||||||
} else
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
|
|
||||||
} else if (sscanf(packet, "vFlashErase:%08" PRIx32 ",%08" PRIx32, &addr, &len) == 2) {
|
|
||||||
/* Erase Flash Memory */
|
/* Erase Flash Memory */
|
||||||
DEBUG_GDB("Flash Erase %08" PRIX32 " %08" PRIX32 "\n", addr, len);
|
DEBUG("Flash Erase %08lX %08lX\n", addr, len);
|
||||||
if (!cur_target) {
|
if(!cur_target) { gdb_putpacketz("EFF"); return; }
|
||||||
gdb_putpacketz("EFF");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!flash_mode) {
|
if(!flash_mode) {
|
||||||
/* Reset target if first flash command! */
|
/* Reset target if first flash command! */
|
||||||
/* This saves us if we're interrupted in IRQ context */
|
/* This saves us if we're interrupted in IRQ context */
|
||||||
target_reset(cur_target);
|
target_reset(cur_target);
|
||||||
flash_mode = 1;
|
flash_mode = 1;
|
||||||
}
|
}
|
||||||
if (target_flash_erase(cur_target, addr, len) == 0)
|
if(target_flash_erase(cur_target, addr, len) == 0)
|
||||||
gdb_putpacketz("OK");
|
gdb_putpacketz("OK");
|
||||||
else {
|
else
|
||||||
flash_mode = 0;
|
|
||||||
gdb_putpacketz("EFF");
|
gdb_putpacketz("EFF");
|
||||||
}
|
|
||||||
|
|
||||||
} else if (sscanf(packet, "vFlashWrite:%08" PRIx32 ":%n", &addr, &bin) == 1) {
|
} else if (sscanf(packet, "vFlashWrite:%08lX:%n", &addr, &bin) == 1) {
|
||||||
/* Write Flash Memory */
|
/* Write Flash Memory */
|
||||||
const uint32_t count = plen - bin;
|
len = plen - bin;
|
||||||
DEBUG_GDB("Flash Write %08" PRIX32 " %08" PRIX32 "\n", addr, count);
|
DEBUG("Flash Write %08lX %08lX\n", addr, len);
|
||||||
if (cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, count) == 0)
|
if(cur_target && target_flash_write_words(cur_target, addr, (void*)packet + bin, len) == 0)
|
||||||
gdb_putpacketz("OK");
|
gdb_putpacketz("OK");
|
||||||
else {
|
else
|
||||||
flash_mode = 0;
|
|
||||||
gdb_putpacketz("EFF");
|
gdb_putpacketz("EFF");
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!strcmp(packet, "vFlashDone")) {
|
} else if (!strcmp(packet, "vFlashDone")) {
|
||||||
/* Commit flash operations. */
|
/* Commit flash operations. */
|
||||||
gdb_putpacketz(target_flash_done(cur_target) ? "EFF" : "OK");
|
gdb_putpacketz("OK");
|
||||||
flash_mode = 0;
|
flash_mode = 0;
|
||||||
|
|
||||||
} else if (!strcmp(packet, "vStopped")) {
|
} else
|
||||||
if (gdb_needs_detach_notify) {
|
|
||||||
gdb_putpacketz("W00");
|
|
||||||
gdb_needs_detach_notify = false;
|
|
||||||
} else
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
|
||||||
gdb_putpacket("", 0);
|
gdb_putpacket("", 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_z_packet(char *packet, const size_t plen)
|
|
||||||
{
|
|
||||||
(void)plen;
|
|
||||||
|
|
||||||
uint32_t type;
|
|
||||||
uint32_t len;
|
|
||||||
uint32_t addr;
|
|
||||||
sscanf(packet, "%*[zZ]%" PRIu32 ",%08" PRIx32 ",%" PRIu32, &type, &addr, &len);
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
if (packet[0] == 'Z')
|
|
||||||
ret = target_breakwatch_set(cur_target, type, addr, len);
|
|
||||||
else
|
|
||||||
ret = target_breakwatch_clear(cur_target, type, addr, len);
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
else if (ret > 0)
|
|
||||||
gdb_putpacketz("");
|
|
||||||
else
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_main(void)
|
|
||||||
{
|
|
||||||
gdb_main_loop(&gdb_controller, false);
|
|
||||||
}
|
|
||||||
|
251
src/gdb_packet.c
251
src/gdb_packet.c
@ -22,207 +22,120 @@
|
|||||||
* reception and transmission as well as some convenience functions.
|
* reception and transmission as well as some convenience functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
#include "gdb_if.h"
|
#include "gdb_if.h"
|
||||||
#include "gdb_packet.h"
|
#include "gdb_packet.h"
|
||||||
#include "hex_utils.h"
|
#include "hex_utils.h"
|
||||||
#include "remote.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
int
|
||||||
|
gdb_getpacket(unsigned char *packet, int size)
|
||||||
size_t gdb_getpacket(char *packet, size_t size)
|
|
||||||
{
|
{
|
||||||
|
unsigned char c;
|
||||||
unsigned char csum;
|
unsigned char csum;
|
||||||
char recv_csum[3];
|
char recv_csum[3];
|
||||||
size_t offset = 0;
|
int i;
|
||||||
|
|
||||||
while (1) {
|
while(1) {
|
||||||
/* Wait for packet start */
|
/* Wait for packet start */
|
||||||
do {
|
while((packet[0] = gdb_if_getchar()) != '$')
|
||||||
/* Spin waiting for a start of packet character - either a gdb
|
if(packet[0] == 0x04) return 1;
|
||||||
* start ('$') or a BMP remote packet start ('!').
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
/* Smells like bad code */
|
|
||||||
packet[0] = (char)gdb_if_getchar();
|
|
||||||
if (packet[0] == 0x04)
|
|
||||||
return 1;
|
|
||||||
} while ((packet[0] != '$') && (packet[0] != REMOTE_SOM));
|
|
||||||
#if PC_HOSTED == 0
|
|
||||||
if (packet[0] == REMOTE_SOM) {
|
|
||||||
/* This is probably a remote control packet
|
|
||||||
* - get and handle it */
|
|
||||||
offset = 0;
|
|
||||||
bool gettingRemotePacket = true;
|
|
||||||
while (gettingRemotePacket) {
|
|
||||||
/* Smells like bad code */
|
|
||||||
const char c = (char)gdb_if_getchar();
|
|
||||||
switch (c) {
|
|
||||||
case REMOTE_SOM: /* Oh dear, packet restarts */
|
|
||||||
offset = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REMOTE_EOM: /* Complete packet for processing */
|
i = 0; csum = 0;
|
||||||
packet[offset] = 0;
|
|
||||||
remotePacketProcess(offset, packet);
|
|
||||||
gettingRemotePacket = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '$': /* A 'real' gdb packet, best stop squatting now */
|
|
||||||
packet[0] = '$';
|
|
||||||
gettingRemotePacket = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (offset < size) {
|
|
||||||
packet[offset++] = c;
|
|
||||||
} else {
|
|
||||||
/* Who knows what is going on...return to normality */
|
|
||||||
gettingRemotePacket = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Reset the packet buffer start character to zero, because function
|
|
||||||
* 'remotePacketProcess()' above overwrites this buffer, and
|
|
||||||
* an arbitrary character may have been placed there. If this is a '$'
|
|
||||||
* character, this will cause this loop to be terminated, which is wrong.
|
|
||||||
*/
|
|
||||||
packet[0] = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} while (packet[0] != '$');
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
csum = 0;
|
|
||||||
char c;
|
|
||||||
/* Capture packet data into buffer */
|
/* Capture packet data into buffer */
|
||||||
while ((c = (char)gdb_if_getchar()) != '#') {
|
while((c = gdb_if_getchar()) != '#') {
|
||||||
|
|
||||||
/* If we run out of buffer space, exit early */
|
if(i == size) break; /* Oh shit */
|
||||||
if (offset == size)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (c == '$') { /* Restart capture */
|
if(c == '$') { /* Restart capture */
|
||||||
offset = 0;
|
i = 0;
|
||||||
csum = 0;
|
csum = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c == '}') { /* escaped char */
|
if(c == '}') { /* escaped char */
|
||||||
c = gdb_if_getchar();
|
c = gdb_if_getchar();
|
||||||
csum += c + '}';
|
csum += c + '}';
|
||||||
packet[offset++] = c ^ 0x20;
|
packet[i++] = c ^ 0x20;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
csum += c;
|
csum += c;
|
||||||
packet[offset++] = c;
|
packet[i++] = c;
|
||||||
}
|
}
|
||||||
recv_csum[0] = (char)gdb_if_getchar();
|
recv_csum[0] = gdb_if_getchar();
|
||||||
recv_csum[1] = (char)gdb_if_getchar();
|
recv_csum[1] = gdb_if_getchar();
|
||||||
recv_csum[2] = 0;
|
recv_csum[2] = 0;
|
||||||
|
|
||||||
/* return packet if checksum matches */
|
/* return packet if checksum matches */
|
||||||
if (csum == strtol(recv_csum, NULL, 16))
|
if(csum == strtol(recv_csum, NULL, 16)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
/* get here if checksum fails */
|
/* get here if checksum fails */
|
||||||
gdb_if_putchar('-', 1); /* send nack */
|
gdb_if_putchar('-', 1); /* send nack */
|
||||||
}
|
}
|
||||||
gdb_if_putchar('+', 1); /* send ack */
|
gdb_if_putchar('+', 1); /* send ack */
|
||||||
packet[offset] = 0;
|
packet[i] = 0;
|
||||||
|
|
||||||
#if PC_HOSTED == 1
|
#ifdef DEBUG_GDBPACKET
|
||||||
DEBUG_GDB_WIRE("%s : ", __func__);
|
DEBUG("%s : ", __func__);
|
||||||
for (size_t j = 0; j < offset; j++) {
|
for(int j = 0; j < i; j++) {
|
||||||
const char c = packet[j];
|
c = packet[j];
|
||||||
if (c >= ' ' && c < 0x7F)
|
if ((c >= 32) && (c < 127))
|
||||||
DEBUG_GDB_WIRE("%c", c);
|
DEBUG("%c", c);
|
||||||
else
|
else
|
||||||
DEBUG_GDB_WIRE("\\x%02X", c);
|
DEBUG("\\x%02X", c);
|
||||||
}
|
}
|
||||||
DEBUG_GDB_WIRE("\n");
|
DEBUG("\n");
|
||||||
#endif
|
#endif
|
||||||
return offset;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdb_next_char(char c, unsigned char *csum)
|
void gdb_putpacket(unsigned char *packet, int size)
|
||||||
{
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2)
|
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char csum;
|
||||||
|
unsigned char c;
|
||||||
char xmit_csum[3];
|
char xmit_csum[3];
|
||||||
size_t tries = 0;
|
int tries = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
#ifdef DEBUG_GDBPACKET
|
||||||
unsigned char csum = 0;
|
DEBUG("%s : ", __func__);
|
||||||
|
#endif
|
||||||
|
csum = 0;
|
||||||
gdb_if_putchar('$', 0);
|
gdb_if_putchar('$', 0);
|
||||||
|
for(i = 0; i < size; i++) {
|
||||||
for (size_t i = 0; i < size1; ++i)
|
c = packet[i];
|
||||||
gdb_next_char(packet1[i], &csum);
|
#ifdef DEBUG_GDBPACKET
|
||||||
for (size_t i = 0; i < size2; ++i)
|
if ((c >= 32) && (c < 127))
|
||||||
gdb_next_char(packet2[i], &csum);
|
DEBUG("%c", c);
|
||||||
|
else
|
||||||
|
DEBUG("\\x%02X", c);
|
||||||
|
#endif
|
||||||
|
if((c == '$') || (c == '#') || (c == '}')) {
|
||||||
|
gdb_if_putchar('}', 0);
|
||||||
|
gdb_if_putchar(c ^ 0x20, 0);
|
||||||
|
csum += '}' + (c ^ 0x20);
|
||||||
|
} else {
|
||||||
|
gdb_if_putchar(c, 0);
|
||||||
|
csum += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
gdb_if_putchar('#', 0);
|
gdb_if_putchar('#', 0);
|
||||||
snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum);
|
sprintf(xmit_csum, "%02X", csum);
|
||||||
gdb_if_putchar(xmit_csum[0], 0);
|
gdb_if_putchar(xmit_csum[0], 0);
|
||||||
gdb_if_putchar(xmit_csum[1], 1);
|
gdb_if_putchar(xmit_csum[1], 1);
|
||||||
DEBUG_GDB_WIRE("\n");
|
#ifdef DEBUG_GDBPACKET
|
||||||
} while (gdb_if_getchar_to(2000) != '+' && tries++ < 3);
|
DEBUG("\n");
|
||||||
|
#endif
|
||||||
|
} while((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdb_putpacket(const char *packet, size_t size)
|
void gdb_putpacket_f(const unsigned char *fmt, ...)
|
||||||
{
|
|
||||||
char xmit_csum[3];
|
|
||||||
size_t tries = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
|
||||||
unsigned char csum = 0;
|
|
||||||
gdb_if_putchar('$', 0);
|
|
||||||
for (size_t 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_put_notification(const char *const packet, const size_t size)
|
|
||||||
{
|
|
||||||
char xmit_csum[3];
|
|
||||||
|
|
||||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
|
||||||
uint8_t csum = 0;
|
|
||||||
gdb_if_putchar('%', 0);
|
|
||||||
for (size_t 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_putpacket_f(const char *fmt, ...)
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *buf;
|
char *buf;
|
||||||
@ -237,30 +150,24 @@ void gdb_putpacket_f(const char *fmt, ...)
|
|||||||
|
|
||||||
void gdb_out(const char *buf)
|
void gdb_out(const char *buf)
|
||||||
{
|
{
|
||||||
int l = strlen(buf);
|
char *hexdata;
|
||||||
char *hexdata = calloc(1, 2 * l + 1);
|
int i;
|
||||||
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)
|
hexdata = alloca((i = strlen(buf)*2 + 1) + 1);
|
||||||
{
|
hexdata[0] = 'O';
|
||||||
char *buf;
|
hexify(hexdata+1, buf, strlen(buf));
|
||||||
|
gdb_putpacket(hexdata, i);
|
||||||
if (vasprintf(&buf, fmt, ap) < 0)
|
|
||||||
return;
|
|
||||||
gdb_out(buf);
|
|
||||||
free(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdb_outf(const char *fmt, ...)
|
void gdb_outf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
gdb_voutf(fmt, ap);
|
vasprintf(&buf, fmt, ap);
|
||||||
|
gdb_out(buf);
|
||||||
|
free(buf);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,21 +21,22 @@
|
|||||||
/* Convenience function to convert to/from ascii strings of hex digits.
|
/* Convenience function to convert to/from ascii strings of hex digits.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "general.h"
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "hex_utils.h"
|
#include "hex_utils.h"
|
||||||
|
|
||||||
static const char hexdigits[] = "0123456789abcdef";
|
static char hexdigits[] = "0123456789abcdef";
|
||||||
|
|
||||||
char *hexify(char *hex, const void *buf, const size_t size)
|
char * hexify(char *hex, const unsigned char *buf, int size)
|
||||||
{
|
{
|
||||||
char *dst = hex;
|
char *tmp = hex;
|
||||||
const uint8_t *const src = buf;
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < size; ++idx) {
|
while(size--) {
|
||||||
*dst++ = hexdigits[src[idx] >> 4];
|
*tmp++ = hexdigits[*buf >> 4];
|
||||||
*dst++ = hexdigits[src[idx] & 0xF];
|
*tmp++ = hexdigits[*buf++ & 0xF];
|
||||||
}
|
}
|
||||||
*dst++ = 0;
|
*tmp++ = 0;
|
||||||
|
|
||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
@ -43,18 +44,19 @@ char *hexify(char *hex, const void *buf, const size_t size)
|
|||||||
static uint8_t unhex_digit(char hex)
|
static uint8_t unhex_digit(char hex)
|
||||||
{
|
{
|
||||||
uint8_t tmp = hex - '0';
|
uint8_t tmp = hex - '0';
|
||||||
if (tmp > 9)
|
if(tmp > 9)
|
||||||
tmp -= 'A' - '0' - 10;
|
tmp -= 'A' - '0' - 10;
|
||||||
if (tmp > 16)
|
if(tmp > 16)
|
||||||
tmp -= 'a' - 'A';
|
tmp -= 'a' - 'A';
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *unhexify(void *buf, const char *hex, const size_t size)
|
char * unhexify(unsigned char *buf, const char *hex, int size)
|
||||||
{
|
{
|
||||||
uint8_t *const dst = buf;
|
while(size--) {
|
||||||
for (size_t idx = 0; idx < size; ++idx, hex += 2) {
|
*buf = unhex_digit(*hex++) << 4;
|
||||||
dst[idx] = (unhex_digit(hex[0]) << 4) | unhex_digit(hex[1]);
|
*buf++ |= unhex_digit(*hex++);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
178
src/include/adiv5.h
Normal file
178
src/include/adiv5.h
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ADIV5_H
|
||||||
|
#define __ADIV5_H
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
/* ADIv5 DP Register addresses */
|
||||||
|
#define ADIV5_DP_IDCODE 0x0
|
||||||
|
#define ADIV5_DP_ABORT 0x0
|
||||||
|
#define ADIV5_DP_CTRLSTAT 0x4
|
||||||
|
#define ADIV5_DP_SELECT 0x8
|
||||||
|
#define ADIV5_DP_RDBUFF 0xC
|
||||||
|
|
||||||
|
/* AP Abort Register (ABORT) */
|
||||||
|
/* Bits 31:5 - Reserved */
|
||||||
|
#define ADIV5_DP_ABORT_ORUNERRCLR (1 << 4)
|
||||||
|
#define ADIV5_DP_ABORT_WDERRCLR (1 << 3)
|
||||||
|
#define ADIV5_DP_ABORT_STKERRCLR (1 << 2)
|
||||||
|
#define ADIV5_DP_ABORT_STKCMPCLR (1 << 1)
|
||||||
|
/* Bits 5:1 - SW-DP only, reserved in JTAG-DP */
|
||||||
|
#define ADIV5_DP_ABORT_DAPABORT (1 << 0)
|
||||||
|
|
||||||
|
/* Control/Status Register (CTRLSTAT) */
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CSYSPWRUPACK (1u << 31)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CSYSPWRUPREQ (1u << 30)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CDBGPWRUPACK (1u << 29)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CDBGPWRUPREQ (1u << 28)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CDBGRSTACK (1u << 27)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_CDBGRSTREQ (1u << 26)
|
||||||
|
/* Bits 25:24 - Reserved */
|
||||||
|
/* Bits 23:12 - TRNCNT */
|
||||||
|
#define ADIV5_DP_CTRLSTAT_TRNCNT
|
||||||
|
/* Bits 11:8 - MASKLANE */
|
||||||
|
#define ADIV5_DP_CTRLSTAT_MASKLANE
|
||||||
|
/* Bits 7:6 - Reserved in JTAG-DP */
|
||||||
|
#define ADIV5_DP_CTRLSTAT_WDATAERR (1u << 7)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_READOK (1u << 6)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_STICKYERR (1u << 5)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_STICKYCMP (1u << 4)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_TRNMODE_MASK (3u << 2)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_STICKYORUN (1u << 1)
|
||||||
|
#define ADIV5_DP_CTRLSTAT_ORUNDETECT (1u << 0)
|
||||||
|
|
||||||
|
|
||||||
|
/* ADIv5 MEM-AP Registers */
|
||||||
|
#define ADIV5_AP_CSW 0x00
|
||||||
|
#define ADIV5_AP_TAR 0x04
|
||||||
|
/* 0x08 - Reserved */
|
||||||
|
#define ADIV5_AP_DRW 0x0C
|
||||||
|
#define ADIV5_AP_DB(x) (0x10 + (4*(x)))
|
||||||
|
/* 0x20:0xF0 - Reserved */
|
||||||
|
#define ADIV5_AP_CFG 0xF4
|
||||||
|
#define ADIV5_AP_BASE 0xF8
|
||||||
|
#define ADIV5_AP_IDR 0xFC
|
||||||
|
|
||||||
|
/* AP Control and Status Word (CSW) */
|
||||||
|
#define ADIV5_AP_CSW_DBGSWENABLE (1u << 31)
|
||||||
|
/* Bits 30:24 - Prot, Implementation defined, for Cortex-M3: */
|
||||||
|
#define ADIV5_AP_CSW_MASTERTYPE_DEBUG (1u << 29)
|
||||||
|
#define ADIV5_AP_CSW_HPROT1 (1u << 25)
|
||||||
|
#define ADIV5_AP_CSW_SPIDEN (1u << 23)
|
||||||
|
/* Bits 22:12 - Reserved */
|
||||||
|
/* Bits 11:8 - Mode, must be zero */
|
||||||
|
#define ADIV5_AP_CSW_TRINPROG (1u << 7)
|
||||||
|
#define ADIV5_AP_CSW_DEVICEEN (1u << 6)
|
||||||
|
#define ADIV5_AP_CSW_ADDRINC_NONE (0u << 4)
|
||||||
|
#define ADIV5_AP_CSW_ADDRINC_SINGLE (1u << 4)
|
||||||
|
#define ADIV5_AP_CSW_ADDRINC_PACKED (2u << 4)
|
||||||
|
#define ADIV5_AP_CSW_ADDRINC_MASK (3u << 4)
|
||||||
|
/* Bit 3 - Reserved */
|
||||||
|
#define ADIV5_AP_CSW_SIZE_BYTE (0u << 0)
|
||||||
|
#define ADIV5_AP_CSW_SIZE_HALFWORD (1u << 0)
|
||||||
|
#define ADIV5_AP_CSW_SIZE_WORD (2u << 0)
|
||||||
|
#define ADIV5_AP_CSW_SIZE_MASK (7u << 0)
|
||||||
|
|
||||||
|
/* Constants to make RnW and APnDP parameters more clear in code */
|
||||||
|
#define ADIV5_LOW_WRITE 0
|
||||||
|
#define ADIV5_LOW_READ 1
|
||||||
|
#define ADIV5_LOW_DP 0
|
||||||
|
#define ADIV5_LOW_AP 1
|
||||||
|
|
||||||
|
/* Try to keep this somewhat absract for later adding SW-DP */
|
||||||
|
typedef struct ADIv5_DP_s {
|
||||||
|
struct ADIv5_DP_s *next;
|
||||||
|
uint32_t idcode;
|
||||||
|
|
||||||
|
void (*dp_write)(struct ADIv5_DP_s *dp, uint8_t addr, uint32_t value);
|
||||||
|
uint32_t (*dp_read)(struct ADIv5_DP_s *dp, uint8_t addr);
|
||||||
|
|
||||||
|
uint32_t (*error)(struct ADIv5_DP_s *dp);
|
||||||
|
|
||||||
|
uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t APnDP, uint8_t RnW,
|
||||||
|
uint8_t addr, uint32_t value);
|
||||||
|
|
||||||
|
union {
|
||||||
|
jtag_dev_t *dev;
|
||||||
|
uint8_t fault;
|
||||||
|
};
|
||||||
|
} ADIv5_DP_t;
|
||||||
|
|
||||||
|
static inline void adiv5_dp_write(ADIv5_DP_t *dp, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
dp->dp_write(dp, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t adiv5_dp_read(ADIv5_DP_t *dp, uint8_t addr)
|
||||||
|
{
|
||||||
|
return dp->dp_read(dp, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t adiv5_dp_error(ADIv5_DP_t *dp)
|
||||||
|
{
|
||||||
|
return dp->error(dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t APnDP,
|
||||||
|
uint8_t RnW, uint8_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
return dp->low_access(dp, APnDP, RnW, addr, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern ADIv5_DP_t *adiv5_dp_list;
|
||||||
|
|
||||||
|
typedef struct ADIv5_AP_s {
|
||||||
|
ADIv5_DP_t *dp;
|
||||||
|
uint8_t apsel;
|
||||||
|
|
||||||
|
uint32_t idr;
|
||||||
|
uint32_t cfg;
|
||||||
|
uint32_t base;
|
||||||
|
} ADIv5_AP_t;
|
||||||
|
|
||||||
|
struct target_ap_s {
|
||||||
|
target t;
|
||||||
|
ADIv5_AP_t *ap;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ADIv5_AP_t adiv5_aps[5];
|
||||||
|
extern int adiv5_ap_count;
|
||||||
|
|
||||||
|
void adiv5_free_all(void);
|
||||||
|
void adiv5_dp_init(ADIv5_DP_t *dp);
|
||||||
|
|
||||||
|
void adiv5_dp_write_ap(ADIv5_DP_t *dp, uint8_t addr, uint32_t value);
|
||||||
|
uint32_t adiv5_dp_read_ap(ADIv5_DP_t *dp, uint8_t addr);
|
||||||
|
|
||||||
|
uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr);
|
||||||
|
void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value);
|
||||||
|
|
||||||
|
void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value);
|
||||||
|
uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr);
|
||||||
|
|
||||||
|
void adiv5_jtag_dp_handler(jtag_dev_t *dev);
|
||||||
|
int adiv5_swdp_scan(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -18,13 +18,13 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __MORSE_H
|
#ifndef __ARM7TDMI_H
|
||||||
#define __MORSE_H
|
#define __ARM7TDMI_H
|
||||||
|
|
||||||
extern const char *morse_msg;
|
#include "general.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
|
||||||
void morse(const char *msg, char repeat);
|
void arm7tdmi_jtag_handler(jtag_dev_t *dev);
|
||||||
bool morse_update(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -21,19 +21,17 @@
|
|||||||
#ifndef __COMMAND_H
|
#ifndef __COMMAND_H
|
||||||
#define __COMMAND_H
|
#define __COMMAND_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include "general.h"
|
||||||
|
|
||||||
#include "target.h"
|
int command_process(char *cmd);
|
||||||
|
typedef void (*cmd_handler)(int argc, const char **argv);
|
||||||
|
|
||||||
int command_process(target *t, char *cmd);
|
struct command_s {
|
||||||
|
const char *cmd;
|
||||||
|
cmd_handler handler;
|
||||||
|
|
||||||
/*
|
const char *help;
|
||||||
* Attempts to parse a string as either being "enable" or "disable".
|
};
|
||||||
* If the parse is successful, returns true and sets the out param to
|
|
||||||
* indicate what was parsed. If not successful, emits a warning to the
|
|
||||||
* gdb port, returns false and leaves out untouched.
|
|
||||||
*/
|
|
||||||
bool parse_enable_or_disable(const char *s, bool *out);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
33
src/include/cortexm3.h
Normal file
33
src/include/cortexm3.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CORTEXM3_H
|
||||||
|
#define __CORTEXM3_H
|
||||||
|
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
/* target options recognised by the Cortex-M target */
|
||||||
|
#define TOPT_FLAVOUR_V6M (1<<0) /* if not set, target is assumed to be v7m */
|
||||||
|
#define TOPT_FLAVOUR_V7MF (1<<1) /* if set, floating-point enabled. */
|
||||||
|
|
||||||
|
int cm3_probe(struct target_s *target);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -21,6 +21,9 @@
|
|||||||
#ifndef __CRC32_H
|
#ifndef __CRC32_H
|
||||||
#define __CRC32_H
|
#define __CRC32_H
|
||||||
|
|
||||||
int generic_crc32(target *t, uint32_t *crc, uint32_t base, int len);
|
#include "platform.h"
|
||||||
|
|
||||||
|
uint32_t crc32_calc(uint32_t crc, uint8_t data);
|
||||||
|
uint32_t generic_crc32(struct target_s *target, uint32_t base, int len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Exception handling to escape deep nesting.
|
|
||||||
* Used for the case of communicaiton failure and timeouts.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Example usage:
|
|
||||||
*
|
|
||||||
* volatile struct exception e;
|
|
||||||
* TRY_CATCH (e, EXCEPTION_TIMEOUT) {
|
|
||||||
* ...
|
|
||||||
* raise_exception(EXCEPTION_TIMEOUT, "Timeout occurred");
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* if (e.type == EXCEPTION_TIMEOUT) {
|
|
||||||
* printf("timeout: %s\n", e.msg);
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Limitations:
|
|
||||||
* Can't use break, return, goto, etc from inside the TRY_CATCH block.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __EXCEPTION_H
|
|
||||||
#define __EXCEPTION_H
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define EXCEPTION_ERROR 0x01
|
|
||||||
#define EXCEPTION_TIMEOUT 0x02
|
|
||||||
#define EXCEPTION_ALL -1
|
|
||||||
|
|
||||||
struct exception {
|
|
||||||
uint32_t type;
|
|
||||||
const char *msg;
|
|
||||||
/* private */
|
|
||||||
uint32_t mask;
|
|
||||||
jmp_buf jmpbuf;
|
|
||||||
struct exception *outer;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct exception *innermost_exception;
|
|
||||||
|
|
||||||
#define TRY_CATCH(e, type_mask) \
|
|
||||||
(e).type = 0; \
|
|
||||||
(e).mask = (type_mask); \
|
|
||||||
(e).outer = innermost_exception; \
|
|
||||||
innermost_exception = (void*)&(e); \
|
|
||||||
if (setjmp(innermost_exception->jmpbuf) == 0) \
|
|
||||||
for (;innermost_exception == &(e); innermost_exception = (e).outer)
|
|
||||||
|
|
||||||
void raise_exception(uint32_t type, const char *msg);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -21,16 +21,9 @@
|
|||||||
#ifndef __GDB_IF_H
|
#ifndef __GDB_IF_H
|
||||||
#define __GDB_IF_H
|
#define __GDB_IF_H
|
||||||
|
|
||||||
#if PC_HOSTED == 0
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
void gdb_usb_out_cb(usbd_device *dev, uint8_t ep);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int gdb_if_init(void);
|
int gdb_if_init(void);
|
||||||
unsigned char gdb_if_getchar(void);
|
unsigned char gdb_if_getchar(void);
|
||||||
unsigned char gdb_if_getchar_to(int timeout);
|
unsigned char gdb_if_getchar_to(int timeout);
|
||||||
|
|
||||||
/* sending gdb_if_putchar(0, true) seems to work as keep alive */
|
|
||||||
void gdb_if_putchar(unsigned char c, int flush);
|
void gdb_if_putchar(unsigned char c, int flush);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,19 +21,16 @@
|
|||||||
#ifndef __GDB_PACKET_H
|
#ifndef __GDB_PACKET_H
|
||||||
#define __GDB_PACKET_H
|
#define __GDB_PACKET_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
size_t gdb_getpacket(char *packet, size_t size);
|
int gdb_getpacket(unsigned char *packet, int size);
|
||||||
void gdb_putpacket(const char *packet, size_t size);
|
void gdb_putpacket(unsigned char *packet, int size);
|
||||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2);
|
|
||||||
#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
|
#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
|
||||||
void gdb_putpacket_f(const char *packet, ...);
|
void gdb_putpacket_f(const unsigned char *packet, ...);
|
||||||
void gdb_put_notification(const char *packet, size_t size);
|
|
||||||
#define gdb_put_notificationz(packet) gdb_put_notification((packet), strlen(packet))
|
|
||||||
|
|
||||||
void gdb_out(const char *buf);
|
void gdb_out(const char *buf);
|
||||||
void gdb_voutf(const char *fmt, va_list);
|
|
||||||
void gdb_outf(const char *fmt, ...);
|
void gdb_outf(const char *fmt, ...);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,154 +21,14 @@
|
|||||||
#ifndef __GENERAL_H
|
#ifndef __GENERAL_H
|
||||||
#define __GENERAL_H
|
#define __GENERAL_H
|
||||||
|
|
||||||
#if !defined(_GNU_SOURCE)
|
|
||||||
# define _GNU_SOURCE
|
|
||||||
#endif
|
|
||||||
#if !defined(__USE_MINGW_ANSI_STDIO)
|
|
||||||
# define __USE_MINGW_ANSI_STDIO 1
|
|
||||||
#endif
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "platform_support.h"
|
|
||||||
|
|
||||||
extern uint32_t delay_cnt;
|
#ifndef DEBUG
|
||||||
|
#include <stdio.h>
|
||||||
enum BMP_DEBUG {
|
#define DEBUG printf
|
||||||
BMP_DEBUG_NONE = 0,
|
|
||||||
BMP_DEBUG_INFO = 1,
|
|
||||||
BMP_DEBUG_GDB = 2,
|
|
||||||
BMP_DEBUG_TARGET = 4,
|
|
||||||
BMP_DEBUG_PROBE = 8,
|
|
||||||
BMP_DEBUG_WIRE = 0x10,
|
|
||||||
BMP_DEBUG_MAX = 0x20,
|
|
||||||
BMP_DEBUG_STDOUT = 0x8000,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FREQ_FIXED 0xffffffff
|
|
||||||
|
|
||||||
#if PC_HOSTED == 0
|
|
||||||
/* For BMP debug output on a firmware BMP platform, using
|
|
||||||
* BMP PC-Hosted is the preferred way. Printing DEBUG_WARN
|
|
||||||
* and DEBUG_INFO is kept for comptibiluty.
|
|
||||||
*/
|
|
||||||
# if !defined(PLATFORM_PRINTF)
|
|
||||||
# define PLATFORM_PRINTF printf
|
|
||||||
# endif
|
|
||||||
# if defined(ENABLE_DEBUG)
|
|
||||||
# define DEBUG_WARN PLATFORM_PRINTF
|
|
||||||
# define DEBUG_INFO PLATFORM_PRINTF
|
|
||||||
# else
|
|
||||||
# define DEBUG_WARN(...) do {} while(0)
|
|
||||||
# define DEBUG_INFO(...) do {} while(0)
|
|
||||||
# endif
|
|
||||||
# define DEBUG_GDB(...) do {} while(0)
|
|
||||||
# define DEBUG_TARGET(...) do {} while(0)
|
|
||||||
# define DEBUG_PROBE(...) do {} while(0)
|
|
||||||
# define DEBUG_WIRE(...) do {} while(0)
|
|
||||||
# define DEBUG_GDB_WIRE(...) do {} while(0)
|
|
||||||
#else
|
|
||||||
# include <stdarg.h>
|
|
||||||
extern int cl_debuglevel;
|
|
||||||
|
|
||||||
static inline void DEBUG_WARN(const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_INFO(const char *format, ...)
|
|
||||||
{
|
|
||||||
if (~cl_debuglevel & BMP_DEBUG_INFO)
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
if (cl_debuglevel & BMP_DEBUG_STDOUT)
|
|
||||||
vfprintf(stdout, format, ap);
|
|
||||||
else
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_GDB(const char *format, ...)
|
|
||||||
{
|
|
||||||
if (~cl_debuglevel & BMP_DEBUG_GDB)
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_GDB_WIRE(const char *format, ...)
|
|
||||||
{
|
|
||||||
if ((cl_debuglevel & (BMP_DEBUG_GDB | BMP_DEBUG_WIRE)) !=
|
|
||||||
(BMP_DEBUG_GDB | BMP_DEBUG_WIRE))
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_TARGET(const char *format, ...)
|
|
||||||
{
|
|
||||||
if (~cl_debuglevel & BMP_DEBUG_TARGET)
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_PROBE(const char *format, ...)
|
|
||||||
{
|
|
||||||
if (~cl_debuglevel & BMP_DEBUG_PROBE)
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void DEBUG_WIRE(const char *format, ...)
|
|
||||||
{
|
|
||||||
if (~cl_debuglevel & BMP_DEBUG_WIRE)
|
|
||||||
return;
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
vfprintf(stderr, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ALIGN(x, n) (((x) + (n) - 1) & ~((n) - 1))
|
#include <stdint.h>
|
||||||
#undef MIN
|
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
||||||
#undef MAX
|
|
||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
||||||
|
|
||||||
#if !defined(SYSTICKHZ)
|
|
||||||
# define SYSTICKHZ 100
|
|
||||||
#endif
|
|
||||||
#define SYSTICKMS (1000 / SYSTICKHZ)
|
|
||||||
#define MORSECNT ((SYSTICKHZ / 10) - 1)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
#ifndef __HEX_UTILS_H
|
#ifndef __HEX_UTILS_H
|
||||||
#define __HEX_UTILS_H
|
#define __HEX_UTILS_H
|
||||||
|
|
||||||
char * hexify(char *hex, const void *buf, size_t size);
|
char * hexify(char *hex, const unsigned char *buf, int size);
|
||||||
char * unhexify(void *buf, const char *hex, size_t size);
|
|
||||||
|
char * unhexify(unsigned char *buf, const char *hex, int size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -17,17 +17,18 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <jtagtap.h>
|
|
||||||
|
|
||||||
#ifndef __JTAG_SCAN_H
|
#ifndef __JTAG_SCAN_H
|
||||||
#define __JTAG_SCAN_H
|
#define __JTAG_SCAN_H
|
||||||
|
|
||||||
#define JTAG_MAX_DEVS 32
|
#include "general.h"
|
||||||
|
|
||||||
|
#define JTAG_MAX_DEVS 5
|
||||||
#define JTAG_MAX_IR_LEN 16
|
#define JTAG_MAX_IR_LEN 16
|
||||||
|
|
||||||
typedef struct jtag_dev_s {
|
typedef struct jtag_dev_s {
|
||||||
union {
|
union {
|
||||||
uint8_t jd_dev;
|
uint8_t dev;
|
||||||
uint8_t dr_prescan;
|
uint8_t dr_prescan;
|
||||||
};
|
};
|
||||||
uint8_t dr_postscan;
|
uint8_t dr_postscan;
|
||||||
@ -35,16 +36,21 @@ typedef struct jtag_dev_s {
|
|||||||
uint8_t ir_len;
|
uint8_t ir_len;
|
||||||
uint8_t ir_prescan;
|
uint8_t ir_prescan;
|
||||||
uint8_t ir_postscan;
|
uint8_t ir_postscan;
|
||||||
uint32_t jd_idcode;
|
|
||||||
const char *jd_descr;
|
uint32_t idcode;
|
||||||
|
char *descr;
|
||||||
|
|
||||||
uint32_t current_ir;
|
uint32_t current_ir;
|
||||||
|
|
||||||
} jtag_dev_t;
|
} jtag_dev_t;
|
||||||
|
|
||||||
extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
|
extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
|
||||||
extern int jtag_dev_count;
|
extern int jtag_dev_count;
|
||||||
|
|
||||||
void jtag_dev_write_ir(jtag_proc_t *jp, uint8_t jd_index, uint32_t ir);
|
int jtag_scan(void);
|
||||||
void jtag_dev_shift_dr(jtag_proc_t *jp, uint8_t jd_index, uint8_t *dout, const uint8_t *din, int ticks);
|
|
||||||
void jtag_add_device(const int dev_index, const jtag_dev_t *jtag_dev);
|
void jtag_dev_write_ir(jtag_dev_t *dev, uint32_t ir);
|
||||||
|
void jtag_dev_shift_dr(jtag_dev_t *dev, uint8_t *dout, const uint8_t *din, int ticks);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -21,12 +21,17 @@
|
|||||||
#ifndef __JTAGTAP_H
|
#ifndef __JTAGTAP_H
|
||||||
#define __JTAGTAP_H
|
#define __JTAGTAP_H
|
||||||
|
|
||||||
typedef struct jtag_proc_s {
|
#include "general.h"
|
||||||
|
|
||||||
/* Note: Signal names are as for the device under test. */
|
/* Note: Signal names are as for the device under test. */
|
||||||
|
|
||||||
void (*jtagtap_reset)(void);
|
int jtagtap_init(void);
|
||||||
|
|
||||||
uint8_t (*jtagtap_next)(const uint8_t TMS, const uint8_t TDI);
|
void jtagtap_reset(void);
|
||||||
|
|
||||||
|
void jtagtap_srst(void);
|
||||||
|
|
||||||
|
uint8_t jtagtap_next(const uint8_t TMS, const uint8_t TDI);
|
||||||
/* tap_next executes one state transision in the JTAG TAP state machine:
|
/* tap_next executes one state transision in the JTAG TAP state machine:
|
||||||
* - Ensure TCK is low
|
* - Ensure TCK is low
|
||||||
* - Assert the values of TMS and TDI
|
* - Assert the values of TMS and TDI
|
||||||
@ -35,39 +40,30 @@ typedef struct jtag_proc_s {
|
|||||||
* - Release TCK.
|
* - Release TCK.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void (*jtagtap_tms_seq)(uint32_t MS, int ticks);
|
void jtagtap_tms_seq(uint32_t MS, int ticks);
|
||||||
void (*jtagtap_tdi_tdo_seq)
|
void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks);
|
||||||
(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks);
|
void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks);
|
||||||
/* Shift out a sequence on MS and DI, capture data to DO.
|
/* Shift out a sequence on MS and DI, capture data to DO.
|
||||||
* - This is not endian safe: First byte will always be first shifted out.
|
* - This is not endian safe: First byte will always be first shifted out.
|
||||||
* - DO may be NULL to ignore captured data.
|
* - DO may be NULL to ignore captured data.
|
||||||
* - DO may be point to the same address as DI.
|
* - DO may be point to the same address as DI.
|
||||||
*/
|
*/
|
||||||
void (*jtagtap_tdi_seq)
|
|
||||||
(const uint8_t final_tms, const uint8_t *DI, int ticks);
|
|
||||||
} jtag_proc_t;
|
|
||||||
extern jtag_proc_t jtag_proc;
|
|
||||||
|
|
||||||
/* generic soft reset: 1, 1, 1, 1, 1, 0 */
|
/* generic soft reset: 1, 1, 1, 1, 1, 0 */
|
||||||
#define jtagtap_soft_reset() \
|
#define jtagtap_soft_reset() \
|
||||||
jtag_proc.jtagtap_tms_seq(0x1F, 6)
|
jtagtap_tms_seq(0x1F, 6)
|
||||||
|
|
||||||
/* Goto Shift-IR: 1, 1, 0, 0 */
|
/* Goto Shift-IR: 1, 1, 0, 0 */
|
||||||
#define jtagtap_shift_ir() \
|
#define jtagtap_shift_ir() \
|
||||||
jtag_proc.jtagtap_tms_seq(0x03, 4)
|
jtagtap_tms_seq(0x03, 4)
|
||||||
|
|
||||||
/* Goto Shift-DR: 1, 0, 0 */
|
/* Goto Shift-DR: 1, 0, 0 */
|
||||||
#define jtagtap_shift_dr() \
|
#define jtagtap_shift_dr() \
|
||||||
jtag_proc.jtagtap_tms_seq(0x01, 3)
|
jtagtap_tms_seq(0x01, 3)
|
||||||
|
|
||||||
/* Goto Run-test/Idle: 1, 1, 0 */
|
/* Goto Run-test/Idle: 1, 1, 0 */
|
||||||
#define jtagtap_return_idle() \
|
#define jtagtap_return_idle() \
|
||||||
jtag_proc.jtagtap_tms_seq(0x01, 2)
|
jtagtap_tms_seq(0x01, 2)
|
||||||
|
|
||||||
# if PC_HOSTED == 1
|
|
||||||
int platform_jtagtap_init(void);
|
|
||||||
# else
|
|
||||||
int jtagtap_init(void);
|
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -17,10 +17,13 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef __SERIALNO_H
|
|
||||||
#define __SERIALNO_H
|
|
||||||
|
|
||||||
char *serial_no_read(char *s);
|
#ifndef __LMI_H
|
||||||
|
#define __LMI_H
|
||||||
|
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
int lmi_probe(struct target_s *target);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -17,14 +17,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef __STUB_H
|
|
||||||
#define __STUB_H
|
|
||||||
|
|
||||||
static inline void __attribute__((always_inline))
|
#ifndef __NXP_TGT_H
|
||||||
stub_exit(const int code)
|
#define __NXP_TGT_H
|
||||||
{
|
|
||||||
asm("bkpt %0"::"i"(code));
|
#include "target.h"
|
||||||
}
|
|
||||||
|
int lpc11xx_probe(struct target_s *target);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __PLATFORM_SUPPORT_H
|
|
||||||
#define __PLATFORM_SUPPORT_H
|
|
||||||
|
|
||||||
#ifndef __GENERAL_H
|
|
||||||
# error "Include 'general.h' instead"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "target.h"
|
|
||||||
|
|
||||||
#if PC_HOSTED == 1
|
|
||||||
void platform_init(int argc, char **argv);
|
|
||||||
#else
|
|
||||||
void platform_init(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct platform_timeout platform_timeout;
|
|
||||||
void platform_timeout_set(platform_timeout *t, uint32_t ms);
|
|
||||||
bool platform_timeout_is_expired(platform_timeout *t);
|
|
||||||
void platform_delay(uint32_t ms);
|
|
||||||
|
|
||||||
#define POWER_CONFLICT_THRESHOLD 5 /* in 0.1V, so 5 stands for 0.5V */
|
|
||||||
extern bool connect_assert_srst;
|
|
||||||
uint32_t platform_target_voltage_sense(void);
|
|
||||||
const char *platform_target_voltage(void);
|
|
||||||
int platform_hwversion(void);
|
|
||||||
void platform_srst_set_val(bool assert);
|
|
||||||
bool platform_srst_get_val(void);
|
|
||||||
bool platform_target_get_power(void);
|
|
||||||
void platform_target_set_power(bool power);
|
|
||||||
void platform_request_boot(void);
|
|
||||||
void platform_max_frequency_set(uint32_t frequency);
|
|
||||||
uint32_t platform_max_frequency_get(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2018 Robert C. Curtis. 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.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY ROBERT C. CURTIS ``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 ROBERT C. CURTIS 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.
|
|
||||||
*
|
|
||||||
* The views and conclusions contained in the software and documentation
|
|
||||||
* are those of the authors and should not be interpreted as representing
|
|
||||||
* official policies, either expressed or implied, of Robert C. Curtis.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file util/queue.h
|
|
||||||
* Static queue primitives.
|
|
||||||
*
|
|
||||||
* This file contains a set of static queue primitives that are common among
|
|
||||||
* all of the low level queue implementations. The primitives are inline
|
|
||||||
* functions, and will be optimal if size is a constant power of 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef I__QUEUE_H__
|
|
||||||
#define I__QUEUE_H__
|
|
||||||
|
|
||||||
/** Increment a queue index.
|
|
||||||
* @param[in] idx Queue index
|
|
||||||
* @param[in] size Queue size
|
|
||||||
* @returns The new queue index value
|
|
||||||
*/
|
|
||||||
static inline size_t qinc(size_t idx, size_t size)
|
|
||||||
{
|
|
||||||
return ((idx + 1) % size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Decrement a queue index.
|
|
||||||
* @param[in] idx Queue index
|
|
||||||
* @param[in] size Queue size
|
|
||||||
* @returns The new queue index value
|
|
||||||
*/
|
|
||||||
static inline size_t qdec(size_t idx, size_t size)
|
|
||||||
{
|
|
||||||
return ((idx - 1) % size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tests if a queue is full.
|
|
||||||
* @param[in] head Head index
|
|
||||||
* @param[in] tail Tail index
|
|
||||||
* @param[in] size Queue size
|
|
||||||
*/
|
|
||||||
static inline int qfull(size_t head, size_t tail, size_t size)
|
|
||||||
{
|
|
||||||
size_t next_head = qinc(head, size);
|
|
||||||
return (next_head == tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tests if a queue is empty.
|
|
||||||
* @param[in] head Head index
|
|
||||||
* @param[in] tail Tail index
|
|
||||||
*/
|
|
||||||
static inline int qempty(size_t head, size_t tail)
|
|
||||||
{
|
|
||||||
return (head == tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* I__QUEUE_H__ */
|
|
@ -1,34 +0,0 @@
|
|||||||
#ifndef RTT_H
|
|
||||||
#define RTT_H
|
|
||||||
#include <target.h>
|
|
||||||
|
|
||||||
#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
|
|
@ -1,36 +0,0 @@
|
|||||||
#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
|
|
@ -18,11 +18,13 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef const struct jtag_dev_descr_s {
|
#ifndef __STM32_TGT_H
|
||||||
const uint32_t idcode;
|
#define __STM32_TGT_H
|
||||||
const uint32_t idmask;
|
|
||||||
const char * const descr;
|
#include "target.h"
|
||||||
void (*const handler)(uint8_t jd_index);
|
|
||||||
} jtag_dev_descr_t;
|
int stm32_probe(struct target_s *target);
|
||||||
extern jtag_dev_descr_t dev_descr[];
|
int stm32f4_probe(struct target_s *target);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -17,16 +17,19 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef __SWDPTAP_H
|
||||||
|
#define __SWDPTAP_H
|
||||||
|
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
|
|
||||||
void platform_timeout_set(platform_timeout *t, uint32_t ms)
|
int swdptap_init(void);
|
||||||
{
|
void swdptap_reset(void);
|
||||||
if (ms <= SYSTICKMS)
|
|
||||||
ms = SYSTICKMS;
|
uint32_t swdptap_seq_in(int ticks);
|
||||||
t->time = platform_time_ms() + ms;
|
uint8_t swdptap_seq_in_parity(uint32_t *data, int ticks);
|
||||||
}
|
void swdptap_seq_out(uint32_t MS, int ticks);
|
||||||
|
void swdptap_seq_out_parity(uint32_t MS, int ticks);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
bool platform_timeout_is_expired(platform_timeout *t)
|
|
||||||
{
|
|
||||||
return platform_time_ms() > t->time;
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -25,158 +25,156 @@
|
|||||||
#ifndef __TARGET_H
|
#ifndef __TARGET_H
|
||||||
#define __TARGET_H
|
#define __TARGET_H
|
||||||
|
|
||||||
#include <stdarg.h>
|
/* Halt/resume functions */
|
||||||
#include <stdbool.h>
|
#define target_attach(target) \
|
||||||
#include <stdint.h>
|
(target)->attach(target)
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
typedef struct target_s target;
|
#define target_detach(target) \
|
||||||
typedef uint32_t target_addr;
|
(target)->detach(target)
|
||||||
struct target_controller;
|
|
||||||
|
|
||||||
#if PC_HOSTED == 1
|
#define target_check_error(target) \
|
||||||
int platform_adiv5_swdp_scan(uint32_t targetid);
|
(target)->check_error(target)
|
||||||
int platform_jtag_scan(const uint8_t *lrlens);
|
|
||||||
#endif
|
|
||||||
int adiv5_swdp_scan(uint32_t targetid);
|
|
||||||
int jtag_scan(const uint8_t *lrlens);
|
|
||||||
|
|
||||||
int target_foreach(void (*cb)(int i, target *t, void *context), void *context);
|
|
||||||
void target_list_free(void);
|
|
||||||
|
|
||||||
/* Attach/detach functions */
|
|
||||||
target *target_attach(target *t, struct target_controller *);
|
|
||||||
target *target_attach_n(int n, struct target_controller *);
|
|
||||||
void target_detach(target *t);
|
|
||||||
bool target_attached(target *t);
|
|
||||||
const char *target_driver_name(target *t);
|
|
||||||
const char *target_core_name(target *t);
|
|
||||||
unsigned int target_designer(target *t);
|
|
||||||
unsigned int target_idcode(target *t);
|
|
||||||
|
|
||||||
/* Memory access functions */
|
/* Memory access functions */
|
||||||
bool target_mem_map(target *t, char *buf, size_t len);
|
#define target_mem_read_words(target, dest, src, len) \
|
||||||
int target_mem_read(target *t, void *dest, target_addr src, size_t len);
|
(target)->mem_read_words((target), (dest), (src), (len))
|
||||||
int target_mem_write(target *t, target_addr dest, const void *src, size_t len);
|
|
||||||
/* Flash memory access functions */
|
#define target_mem_write_words(target, dest, src, len) \
|
||||||
int target_flash_erase(target *t, target_addr addr, size_t len);
|
(target)->mem_write_words((target), (dest), (src), (len))
|
||||||
int target_flash_write(target *t, target_addr dest, const void *src, size_t len);
|
|
||||||
int target_flash_done(target *t);
|
#define target_mem_read_bytes(target, dest, src, len) \
|
||||||
|
(target)->mem_read_bytes((target), (dest), (src), (len))
|
||||||
|
|
||||||
|
#define target_mem_write_bytes(target, dest, src, len) \
|
||||||
|
(target)->mem_write_bytes((target), (dest), (src), (len))
|
||||||
|
|
||||||
|
|
||||||
/* Register access functions */
|
/* Register access functions */
|
||||||
size_t target_regs_size(target *t);
|
#define target_regs_read(target, data) \
|
||||||
const char *target_tdesc(target *t);
|
(target)->regs_read((target), (data))
|
||||||
void target_regs_read(target *t, void *data);
|
|
||||||
void target_regs_write(target *t, const void *data);
|
#define target_regs_write(target, data) \
|
||||||
ssize_t target_reg_read(target *t, int reg, void *data, size_t max);
|
(target)->regs_write((target), (data))
|
||||||
ssize_t target_reg_write(target *t, int reg, const void *data, size_t size);
|
|
||||||
|
#define target_pc_read(target) \
|
||||||
|
(target)->pc_read((target))
|
||||||
|
|
||||||
|
#define target_pc_write(target, val) \
|
||||||
|
(target)->pc_write((target), (val))
|
||||||
|
|
||||||
|
|
||||||
/* Halt/resume functions */
|
/* Halt/resume functions */
|
||||||
enum target_halt_reason {
|
#define target_reset(target) \
|
||||||
TARGET_HALT_RUNNING = 0, /* Target not halted */
|
(target)->reset(target)
|
||||||
TARGET_HALT_ERROR, /* Failed to read target status */
|
|
||||||
TARGET_HALT_REQUEST,
|
|
||||||
TARGET_HALT_STEPPING,
|
|
||||||
TARGET_HALT_BREAKPOINT,
|
|
||||||
TARGET_HALT_WATCHPOINT,
|
|
||||||
TARGET_HALT_FAULT,
|
|
||||||
};
|
|
||||||
|
|
||||||
void target_reset(target *t);
|
#define target_halt_request(target) \
|
||||||
void target_halt_request(target *t);
|
(target)->halt_request(target)
|
||||||
enum target_halt_reason target_halt_poll(target *t, target_addr *watch);
|
|
||||||
void target_halt_resume(target *t, bool step);
|
#define target_halt_wait(target) \
|
||||||
void target_set_cmdline(target *t, char *cmdline);
|
(target)->halt_wait(target)
|
||||||
void target_set_heapinfo(target *t, target_addr heap_base, target_addr heap_limit,
|
|
||||||
target_addr stack_base, target_addr stack_limit);
|
#define target_halt_resume(target, step) \
|
||||||
|
(target)->halt_resume((target), (step))
|
||||||
|
|
||||||
|
#define target_fault_unwind(target) \
|
||||||
|
((target)->fault_unwind?(target)->fault_unwind((target)):0)
|
||||||
|
|
||||||
/* Break-/watchpoint functions */
|
/* Break-/watchpoint functions */
|
||||||
enum target_breakwatch {
|
#define target_set_hw_bp(target, addr) \
|
||||||
TARGET_BREAK_SOFT,
|
(target)->set_hw_bp((target), (addr))
|
||||||
TARGET_BREAK_HARD,
|
|
||||||
TARGET_WATCH_WRITE,
|
|
||||||
TARGET_WATCH_READ,
|
|
||||||
TARGET_WATCH_ACCESS,
|
|
||||||
};
|
|
||||||
int target_breakwatch_set(target *t, enum target_breakwatch, target_addr, size_t);
|
|
||||||
int target_breakwatch_clear(target *t, enum target_breakwatch, target_addr, size_t);
|
|
||||||
|
|
||||||
/* Command interpreter */
|
#define target_clear_hw_bp(target, addr) \
|
||||||
void target_command_help(target *t);
|
(target)->clear_hw_bp((target), (addr))
|
||||||
int target_command(target *t, int argc, const char *argv[]);
|
|
||||||
|
|
||||||
/* keep target_errno in sync with errno values in gdb/include/gdb/fileio.h */
|
|
||||||
enum target_errno {
|
|
||||||
TARGET_EPERM = 1,
|
|
||||||
TARGET_ENOENT = 2,
|
|
||||||
TARGET_EINTR = 4,
|
|
||||||
TARGET_EIO = 5,
|
|
||||||
TARGET_EBADF = 9,
|
|
||||||
TARGET_EACCES = 13,
|
|
||||||
TARGET_EFAULT = 14,
|
|
||||||
TARGET_EBUSY = 16,
|
|
||||||
TARGET_EEXIST = 17,
|
|
||||||
TARGET_ENODEV = 19,
|
|
||||||
TARGET_ENOTDIR = 20,
|
|
||||||
TARGET_EISDIR = 21,
|
|
||||||
TARGET_EINVAL = 22,
|
|
||||||
TARGET_ENFILE = 23,
|
|
||||||
TARGET_EMFILE = 24,
|
|
||||||
TARGET_EFBIG = 27,
|
|
||||||
TARGET_ENOSPC = 28,
|
|
||||||
TARGET_ESPIPE = 29,
|
|
||||||
TARGET_EROFS = 30,
|
|
||||||
TARGET_ENOSYS = 88,
|
|
||||||
TARGET_ENAMETOOLONG = 91,
|
|
||||||
TARGET_EUNKNOWN = 9999,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum target_open_flags {
|
#define target_set_hw_wp(target, type, addr, len) \
|
||||||
TARGET_O_RDONLY = 0,
|
(target)->set_hw_wp((target), (type), (addr), (len))
|
||||||
TARGET_O_WRONLY = 1,
|
|
||||||
TARGET_O_RDWR = 2,
|
|
||||||
TARGET_O_APPEND = 0x008,
|
|
||||||
TARGET_O_CREAT = 0x200,
|
|
||||||
TARGET_O_TRUNC = 0x400,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum target_seek_flag {
|
#define target_clear_hw_wp(target, type, addr, len) \
|
||||||
TARGET_SEEK_SET = 0,
|
(target)->clear_hw_wp((target), (type), (addr), (len))
|
||||||
TARGET_SEEK_CUR = 1,
|
|
||||||
TARGET_SEEK_END = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct target_controller {
|
|
||||||
void (*destroy_callback)(struct target_controller *, target *t);
|
|
||||||
void (*printf)(struct target_controller *, const char *fmt, va_list);
|
|
||||||
|
|
||||||
/* Interface to host system calls */
|
#define target_check_hw_wp(target, addr) \
|
||||||
int (*open)(struct target_controller *,
|
((target)->check_hw_wp?(target)->check_hw_wp((target), (addr)):0)
|
||||||
target_addr path, size_t path_len,
|
|
||||||
enum target_open_flags flags, mode_t mode);
|
|
||||||
int (*close)(struct target_controller *, int fd);
|
/* Flash memory access functions */
|
||||||
int (*read)(struct target_controller *,
|
#define target_flash_erase(target, addr, len) \
|
||||||
int fd, target_addr buf, unsigned int count);
|
(target)->flash_erase((target), (addr), (len))
|
||||||
int (*write)(struct target_controller *,
|
|
||||||
int fd, target_addr buf, unsigned int count);
|
#define target_flash_write_words(target, dest, src, len) \
|
||||||
long (*lseek)(struct target_controller *,
|
(target)->flash_write_words((target), (dest), (src), (len))
|
||||||
int fd, long offset, enum target_seek_flag flag);
|
|
||||||
int (*rename)(struct target_controller *,
|
|
||||||
target_addr oldpath, size_t old_len,
|
#define TARGET_LIST_FREE() { \
|
||||||
target_addr newpath, size_t new_len);
|
while(target_list) { \
|
||||||
int (*unlink)(struct target_controller *,
|
target *t = target_list->next; \
|
||||||
target_addr path, size_t path_len);
|
free(target_list); \
|
||||||
int (*stat)(struct target_controller *,
|
target_list = t; \
|
||||||
target_addr path, size_t path_len, target_addr buf);
|
} \
|
||||||
int (*fstat)(struct target_controller *, int fd, target_addr buf);
|
last_target = cur_target = NULL; \
|
||||||
int (*gettimeofday)(struct target_controller *,
|
}
|
||||||
target_addr tv, target_addr tz);
|
|
||||||
int (*isatty)(struct target_controller *, int fd);
|
|
||||||
int (*system)(struct target_controller *,
|
typedef struct target_s {
|
||||||
target_addr cmd, size_t cmd_len);
|
/* Attach/Detach funcitons */
|
||||||
enum target_errno errno_;
|
void (*attach)(struct target_s *target);
|
||||||
bool interrupted;
|
void (*detach)(struct target_s *target);
|
||||||
};
|
int (*check_error)(struct target_s *target);
|
||||||
|
|
||||||
|
/* Memory access functions */
|
||||||
|
int (*mem_read_words)(struct target_s *target, uint32_t *dest, uint32_t src,
|
||||||
|
int len);
|
||||||
|
int (*mem_write_words)(struct target_s *target, uint32_t dest,
|
||||||
|
const uint32_t *src, int len);
|
||||||
|
|
||||||
|
int (*mem_read_bytes)(struct target_s *target, uint8_t *dest, uint32_t src,
|
||||||
|
int len);
|
||||||
|
int (*mem_write_bytes)(struct target_s *target, uint32_t dest,
|
||||||
|
const uint8_t *src, int len);
|
||||||
|
|
||||||
|
/* Register access functions */
|
||||||
|
int regs_size;
|
||||||
|
const char *tdesc;
|
||||||
|
int (*regs_read)(struct target_s *target, void *data);
|
||||||
|
int (*regs_write)(struct target_s *target, const void *data);
|
||||||
|
|
||||||
|
uint32_t (*pc_read)(struct target_s *target);
|
||||||
|
int (*pc_write)(struct target_s *target, const uint32_t val);
|
||||||
|
|
||||||
|
/* Halt/resume functions */
|
||||||
|
void (*reset)(struct target_s *target);
|
||||||
|
void (*halt_request)(struct target_s *target);
|
||||||
|
int (*halt_wait)(struct target_s *target);
|
||||||
|
void (*halt_resume)(struct target_s *target, uint8_t step);
|
||||||
|
int (*fault_unwind)(struct target_s *target);
|
||||||
|
|
||||||
|
/* Break-/watchpoint functions */
|
||||||
|
int (*set_hw_bp)(struct target_s *target, uint32_t addr);
|
||||||
|
int (*clear_hw_bp)(struct target_s *target, uint32_t addr);
|
||||||
|
|
||||||
|
int (*set_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
|
||||||
|
int (*clear_hw_wp)(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
|
||||||
|
|
||||||
|
int (*check_hw_wp)(struct target_s *target, uint32_t *addr);
|
||||||
|
|
||||||
|
/* target-defined options */
|
||||||
|
unsigned target_options;
|
||||||
|
|
||||||
|
/* Flash memory access functions */
|
||||||
|
const char *xml_mem_map;
|
||||||
|
int (*flash_erase)(struct target_s *target, uint32_t addr, int len);
|
||||||
|
int (*flash_write_words)(struct target_s *target, uint32_t dest,
|
||||||
|
const uint32_t *src, int len);
|
||||||
|
|
||||||
|
const char *driver;
|
||||||
|
|
||||||
|
int size;
|
||||||
|
struct target_s *next;
|
||||||
|
} target;
|
||||||
|
|
||||||
|
extern target *target_list, *cur_target, *last_target;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
240
src/jtag_scan.c
Normal file
240
src/jtag_scan.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements JTAG protocol support. Provides functionality
|
||||||
|
* to detect devices on the scan chain and read their IDCODEs.
|
||||||
|
* It depends on the low-level function provided by the platform's jtagtap.c.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "jtagtap.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
|
||||||
|
#include "gdb_packet.h"
|
||||||
|
|
||||||
|
#include "adiv5.h"
|
||||||
|
#include "arm7tdmi.h"
|
||||||
|
|
||||||
|
struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
|
||||||
|
int jtag_dev_count;
|
||||||
|
|
||||||
|
static struct jtag_dev_descr_s {
|
||||||
|
uint32_t idcode;
|
||||||
|
uint32_t idmask;
|
||||||
|
char *descr;
|
||||||
|
void (*handler)(jtag_dev_t *dev);
|
||||||
|
} dev_descr[] = {
|
||||||
|
{.idcode = 0x0BA00477, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ARM Limited: ADIv5 JTAG-DP port.",
|
||||||
|
.handler = adiv5_jtag_dp_handler},
|
||||||
|
{.idcode = 0x3F0F0F0F, .idmask = 0xFFFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STR730",
|
||||||
|
.handler = arm7tdmi_jtag_handler},
|
||||||
|
{.idcode = 0x06410041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, Medium density."},
|
||||||
|
{.idcode = 0x06412041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, Low density."},
|
||||||
|
{.idcode = 0x06414041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, High density."},
|
||||||
|
{.idcode = 0x06418041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, Connectivity Line."},
|
||||||
|
{.idcode = 0x06420041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, Value Line."},
|
||||||
|
{.idcode = 0x06428041, .idmask = 0x0FFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32, Value Line, High density."},
|
||||||
|
{.idcode = 0x06411041, .idmask = 0xFFFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32F2xx."},
|
||||||
|
{.idcode = 0x06413041 , .idmask = 0xFFFFFFFF,
|
||||||
|
.descr = "ST Microelectronics: STM32F4xx."},
|
||||||
|
{.idcode = 0x0BB11477 , .idmask = 0xFFFFFFFF,
|
||||||
|
.descr = "NPX: LPC11C24."},
|
||||||
|
/* 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 = 0, .idmask = 0, .descr = "Unknown"},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* bucket of ones for don't care TDI */
|
||||||
|
static const char ones[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t j;
|
||||||
|
|
||||||
|
TARGET_LIST_FREE();
|
||||||
|
|
||||||
|
jtag_dev_count = 0;
|
||||||
|
memset(&jtag_devs, 0, sizeof(jtag_devs));
|
||||||
|
|
||||||
|
#warning "These should be elsewhere!"
|
||||||
|
adiv5_free_all();
|
||||||
|
|
||||||
|
/* Run throught the SWD to JTAG sequence for the case where an attached SWJ-DP is
|
||||||
|
* in SW-DP mode.
|
||||||
|
*/
|
||||||
|
DEBUG("Resetting TAP\n");
|
||||||
|
jtagtap_init();
|
||||||
|
jtagtap_reset();
|
||||||
|
|
||||||
|
DEBUG("Change state to Shift-IR\n");
|
||||||
|
jtagtap_shift_ir();
|
||||||
|
|
||||||
|
DEBUG("Scanning out IRs\n");
|
||||||
|
if(!jtagtap_next(0, 1)) {
|
||||||
|
DEBUG("jtag_scan: Sanity check failed: IR[0] shifted out as 0\n");
|
||||||
|
jtag_dev_count = -1;
|
||||||
|
return -1; /* must be 1 */
|
||||||
|
}
|
||||||
|
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(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].dev = jtag_dev_count;
|
||||||
|
} else jtag_devs[jtag_dev_count].ir_len++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if(jtag_dev_count > JTAG_MAX_DEVS) {
|
||||||
|
DEBUG("jtag_scan: Maximum device count exceeded\n");
|
||||||
|
jtag_dev_count = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(jtag_devs[jtag_dev_count].ir_len > JTAG_MAX_IR_LEN) {
|
||||||
|
DEBUG("jtag_scan: Maximum IR length exceeded\n");
|
||||||
|
jtag_dev_count = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Return to Run-Test/Idle\n");
|
||||||
|
jtagtap_next(1, 1);
|
||||||
|
jtagtap_return_idle();
|
||||||
|
|
||||||
|
/* All devices should be in BYPASS now */
|
||||||
|
|
||||||
|
/* Count device on chain */
|
||||||
|
DEBUG("Change state to Shift-DR\n");
|
||||||
|
jtagtap_shift_dr();
|
||||||
|
for(i = 0; (jtagtap_next(0, 1) == 0) && (i <= jtag_dev_count); i++)
|
||||||
|
jtag_devs[i].dr_postscan = jtag_dev_count - i - 1;
|
||||||
|
|
||||||
|
if(i != jtag_dev_count) {
|
||||||
|
DEBUG("jtag_scan: Sanity check failed: "
|
||||||
|
"BYPASS dev count doesn't match IR scan\n");
|
||||||
|
jtag_dev_count = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG("Return to Run-Test/Idle\n");
|
||||||
|
jtagtap_next(1, 1);
|
||||||
|
jtagtap_return_idle();
|
||||||
|
if(!jtag_dev_count) {
|
||||||
|
morse("NO TARGETS.", 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the ir_postscan fields */
|
||||||
|
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 */
|
||||||
|
jtagtap_reset();
|
||||||
|
jtagtap_shift_dr();
|
||||||
|
for(i = 0; i < jtag_dev_count; i++) {
|
||||||
|
if(!jtagtap_next(0, 1)) continue;
|
||||||
|
jtag_devs[i].idcode = 1;
|
||||||
|
for(j = 2; j; j <<= 1)
|
||||||
|
if(jtagtap_next(0, 1)) jtag_devs[i].idcode |= j;
|
||||||
|
|
||||||
|
}
|
||||||
|
DEBUG("Return to Run-Test/Idle\n");
|
||||||
|
jtagtap_next(1, 1);
|
||||||
|
jtagtap_return_idle();
|
||||||
|
|
||||||
|
/* Check for known devices and handle accordingly */
|
||||||
|
for(i = 0; i < jtag_dev_count; i++)
|
||||||
|
for(j = 0; dev_descr[j].idcode; j++)
|
||||||
|
if((jtag_devs[i].idcode & dev_descr[j].idmask) ==
|
||||||
|
dev_descr[j].idcode) {
|
||||||
|
jtag_devs[i].current_ir = -1;
|
||||||
|
/* Save description in table */
|
||||||
|
jtag_devs[i].descr = dev_descr[j].descr;
|
||||||
|
/* Call handler to initialise/probe device further */
|
||||||
|
if(dev_descr[j].handler)
|
||||||
|
dev_descr[j].handler(&jtag_devs[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!target_list) morse("NO TARGETS.", 1);
|
||||||
|
else morse(NULL, 0);
|
||||||
|
|
||||||
|
return jtag_dev_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtag_dev_write_ir(jtag_dev_t *d, uint32_t ir)
|
||||||
|
{
|
||||||
|
if(ir == d->current_ir) return;
|
||||||
|
d->current_ir = ir;
|
||||||
|
|
||||||
|
jtagtap_shift_ir();
|
||||||
|
jtagtap_tdi_seq(0, ones, d->ir_prescan);
|
||||||
|
jtagtap_tdi_seq(d->ir_postscan?0:1, (void*)&ir, d->ir_len);
|
||||||
|
jtagtap_tdi_seq(1, ones, d->ir_postscan);
|
||||||
|
jtagtap_return_idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtag_dev_shift_dr(jtag_dev_t *d, uint8_t *dout, const uint8_t *din, int ticks)
|
||||||
|
{
|
||||||
|
jtagtap_shift_dr();
|
||||||
|
jtagtap_tdi_seq(0, ones, d->dr_prescan);
|
||||||
|
if(dout)
|
||||||
|
jtagtap_tdi_tdo_seq((void*)dout, d->dr_postscan?0:1, (void*)din, ticks);
|
||||||
|
else
|
||||||
|
jtagtap_tdi_seq(d->dr_postscan?0:1, (void*)din, ticks);
|
||||||
|
jtagtap_tdi_seq(1, ones, d->dr_postscan);
|
||||||
|
jtagtap_return_idle();
|
||||||
|
}
|
||||||
|
|
@ -21,18 +21,22 @@
|
|||||||
/* This file provides generic forms of the low-level jtagtap functions
|
/* This file provides generic forms of the low-level jtagtap functions
|
||||||
* for platforms that don't require optimised forms.
|
* for platforms that don't require optimised forms.
|
||||||
*/
|
*/
|
||||||
#include "general.h"
|
|
||||||
#include "jtagtap.h"
|
|
||||||
|
|
||||||
void jtagtap_tms_seq(uint32_t MS, int ticks)
|
#ifdef PROVIDE_GENERIC_JTAGTAP_TMS_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tms_seq(uint32_t MS, int ticks)
|
||||||
{
|
{
|
||||||
while(ticks--) {
|
while(ticks--) {
|
||||||
jtagtap_next(MS & 1, 1);
|
jtagtap_next(MS & 1, 1);
|
||||||
MS >>= 1;
|
MS >>= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
|
|
||||||
|
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
|
||||||
{
|
{
|
||||||
uint8_t index = 1;
|
uint8_t index = 1;
|
||||||
while(ticks--) {
|
while(ticks--) {
|
||||||
@ -47,8 +51,12 @@ void jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
|
|
||||||
|
#ifdef PROVIDE_GENERIC_JTAGTAP_TDI_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
|
||||||
{
|
{
|
||||||
uint8_t index = 1;
|
uint8_t index = 1;
|
||||||
while(ticks--) {
|
while(ticks--) {
|
||||||
@ -59,4 +67,6 @@ void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
1
src/linux/Makefile.inc
Normal file
1
src/linux/Makefile.inc
Normal file
@ -0,0 +1 @@
|
|||||||
|
LDFLAGS += -lftdi -lusb
|
114
src/linux/gdb_if.c
Normal file
114
src/linux/gdb_if.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements a transparent channel over which the GDB Remote
|
||||||
|
* Serial Debugging protocol is implemented. This implementation for Linux
|
||||||
|
* uses a TCP server on port 2000.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
#else
|
||||||
|
# include <windows.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "gdb_if.h"
|
||||||
|
|
||||||
|
static int gdb_if_serv, gdb_if_conn;
|
||||||
|
|
||||||
|
int gdb_if_init(void)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
WSADATA wsaData;
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
#endif
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(2000);
|
||||||
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
|
||||||
|
assert((gdb_if_serv = socket(PF_INET, SOCK_STREAM, 0)) != -1);
|
||||||
|
opt = 1;
|
||||||
|
assert(setsockopt(gdb_if_serv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != -1);
|
||||||
|
|
||||||
|
assert(bind(gdb_if_serv, (void*)&addr, sizeof(addr)) != -1);
|
||||||
|
assert(listen(gdb_if_serv, 1) != -1);
|
||||||
|
|
||||||
|
DEBUG("Listening on TCP:2000\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char gdb_if_getchar(void)
|
||||||
|
{
|
||||||
|
unsigned char ret;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while(i <= 0) {
|
||||||
|
if(gdb_if_conn <= 0) {
|
||||||
|
gdb_if_conn = accept(gdb_if_serv, NULL, NULL);
|
||||||
|
DEBUG("Got connection\n");
|
||||||
|
}
|
||||||
|
i = recv(gdb_if_conn, &ret, 1, 0);
|
||||||
|
if(i <= 0) {
|
||||||
|
gdb_if_conn = -1;
|
||||||
|
DEBUG("Dropped broken connection\n");
|
||||||
|
/* Return '+' in case we were waiting for an ACK */
|
||||||
|
return '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char gdb_if_getchar_to(int timeout)
|
||||||
|
{
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if(gdb_if_conn == -1) return -1;
|
||||||
|
|
||||||
|
tv.tv_sec = timeout / 1000;
|
||||||
|
tv.tv_usec = (timeout % 1000) * 1000;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(gdb_if_conn, &fds);
|
||||||
|
|
||||||
|
if(select(gdb_if_conn+1, &fds, NULL, NULL, &tv) > 0)
|
||||||
|
return gdb_if_getchar();
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gdb_if_putchar(unsigned char c, int flush)
|
||||||
|
{
|
||||||
|
if(gdb_if_conn > 0)
|
||||||
|
send(gdb_if_conn, &c, 1, 0);
|
||||||
|
}
|
||||||
|
|
221
src/linux/jtagtap.c
Normal file
221
src/linux/jtagtap.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Low level JTAG implementation using FT2232 with libftdi.
|
||||||
|
*
|
||||||
|
* Issues:
|
||||||
|
* This code is old, rotten and unsupported.
|
||||||
|
* Magic numbers everywhere.
|
||||||
|
* Should share interface with swdptap.c or at least clean up...
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <ftdi.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
|
||||||
|
#define PROVIDE_GENERIC_TAP_SEQ
|
||||||
|
//#define PROVIDE_GENERIC_TAP_TMS_SEQ
|
||||||
|
//#define PROVIDE_GENERIC_TAP_TDI_TDO_SEQ
|
||||||
|
//#define PROVIDE_GENERIC_TAP_TDI_SEQ
|
||||||
|
#include "jtagtap.h"
|
||||||
|
|
||||||
|
#define ALL_ZERO 0xA0
|
||||||
|
#define TCK 0x01
|
||||||
|
#define TDI 0x02
|
||||||
|
#define TDO 0x04
|
||||||
|
#define TMS 0x08
|
||||||
|
#define nSRST 0x20
|
||||||
|
|
||||||
|
int jtagtap_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
assert(ftdic != NULL);
|
||||||
|
|
||||||
|
if((err = ftdi_set_bitmode(ftdic, 0xAB, BITMODE_MPSSE)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_bitmode: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ftdi_write_data(ftdic, "\x86\x00\x00\x80\xA8\xAB", 6) == 6);
|
||||||
|
|
||||||
|
/* Go to JTAG mode for SWJ-DP */
|
||||||
|
for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-DP */
|
||||||
|
jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */
|
||||||
|
jtagtap_soft_reset();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtagtap_reset(void)
|
||||||
|
{
|
||||||
|
jtagtap_soft_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void jtagtap_srst(void)
|
||||||
|
{
|
||||||
|
platform_buffer_flush();
|
||||||
|
//ftdi_write_data(ftdic, "\x80\x88\xAB", 3);
|
||||||
|
//usleep(1000);
|
||||||
|
//ftdi_write_data(ftdic, "\x80\xA8\xAB", 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PROVIDE_GENERIC_TAP_TMS_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tms_seq(uint32_t MS, int ticks)
|
||||||
|
{
|
||||||
|
uint8_t tmp[3] = "\x4B";
|
||||||
|
while(ticks >= 0) {
|
||||||
|
//jtagtap_next(MS & 1, 1);
|
||||||
|
tmp[1] = ticks<7?ticks-1:6;
|
||||||
|
tmp[2] = 0x80 | (MS & 0x7F);
|
||||||
|
|
||||||
|
// assert(ftdi_write_data(ftdic, tmp, 3) == 3);
|
||||||
|
platform_buffer_write(tmp, 3);
|
||||||
|
MS >>= 7; ticks -= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PROVIDE_GENERIC_TAP_TDI_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
|
||||||
|
{
|
||||||
|
char *tmp;
|
||||||
|
int index = 0;
|
||||||
|
int rticks;
|
||||||
|
|
||||||
|
if(!ticks) return;
|
||||||
|
|
||||||
|
if(final_tms) ticks--;
|
||||||
|
rticks = ticks & 7;
|
||||||
|
ticks >>= 3;
|
||||||
|
tmp = alloca(ticks + 9);
|
||||||
|
|
||||||
|
if(ticks) {
|
||||||
|
tmp[index++] = 0x19;
|
||||||
|
tmp[index++] = ticks - 1;
|
||||||
|
tmp[index++] = 0;
|
||||||
|
while(ticks--) tmp[index++] = *DI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rticks) {
|
||||||
|
tmp[index++] = 0x1B;
|
||||||
|
tmp[index++] = rticks - 1;
|
||||||
|
tmp[index++] = *DI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(final_tms) {
|
||||||
|
tmp[index++] = 0x4B;
|
||||||
|
tmp[index++] = 0;
|
||||||
|
tmp[index++] = (*DI)>>rticks?0x81:0x01;
|
||||||
|
}
|
||||||
|
// assert(ftdi_write_data(ftdic, tmp, index) == index);
|
||||||
|
platform_buffer_write(tmp, index);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PROVIDE_GENERIC_TAP_TDI_TDO_SEQ
|
||||||
|
void
|
||||||
|
jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
|
||||||
|
{
|
||||||
|
uint8_t *tmp;
|
||||||
|
int index = 0, rsize;
|
||||||
|
int rticks;
|
||||||
|
|
||||||
|
if(!ticks) return;
|
||||||
|
|
||||||
|
// printf("ticks: %d\n", ticks);
|
||||||
|
if(final_tms) ticks--;
|
||||||
|
rticks = ticks & 7;
|
||||||
|
ticks >>= 3;
|
||||||
|
tmp = alloca(ticks + 9);
|
||||||
|
rsize = ticks;
|
||||||
|
if(ticks) {
|
||||||
|
tmp[index++] = 0x39;
|
||||||
|
tmp[index++] = ticks - 1;
|
||||||
|
tmp[index++] = 0;
|
||||||
|
while(ticks--) tmp[index++] = *DI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rticks) {
|
||||||
|
rsize++;
|
||||||
|
tmp[index++] = 0x3B;
|
||||||
|
tmp[index++] = rticks - 1;
|
||||||
|
tmp[index++] = *DI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(final_tms) {
|
||||||
|
rsize++;
|
||||||
|
tmp[index++] = 0x6B;
|
||||||
|
tmp[index++] = 0;
|
||||||
|
tmp[index++] = (*DI)>>rticks?0x81:0x01;
|
||||||
|
}
|
||||||
|
// assert(ftdi_write_data(ftdic, tmp, index) == index);
|
||||||
|
platform_buffer_write(tmp, index);
|
||||||
|
// index = 0;
|
||||||
|
// while((index += ftdi_read_data(ftdic, tmp + index, rsize-index)) != rsize);
|
||||||
|
platform_buffer_read(tmp, rsize);
|
||||||
|
/*for(index = 0; index < rsize; index++)
|
||||||
|
printf("%02X ", tmp[index]);
|
||||||
|
printf("\n");*/
|
||||||
|
index = 0;
|
||||||
|
if(final_tms) rsize--;
|
||||||
|
|
||||||
|
while(rsize--) {
|
||||||
|
/*if(rsize) printf("%02X ", tmp[index]);*/
|
||||||
|
*DO++ = tmp[index++];
|
||||||
|
}
|
||||||
|
if(final_tms) {
|
||||||
|
rticks++;
|
||||||
|
*(--DO) >>= 1;
|
||||||
|
*DO |= tmp[index] & 0x80;
|
||||||
|
} else DO--;
|
||||||
|
if(rticks) {
|
||||||
|
*DO >>= (8-rticks);
|
||||||
|
}
|
||||||
|
/*printf("%02X\n", *DO);*/
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO)
|
||||||
|
{
|
||||||
|
uint8_t ret;
|
||||||
|
uint8_t tmp[3] = "\x6B\x00\x00";
|
||||||
|
tmp[2] = (dTDO?0x80:0) | (dTMS?0x01:0);
|
||||||
|
// assert(ftdi_write_data(ftdic, tmp, 3) == 3);
|
||||||
|
// while(ftdi_read_data(ftdic, &ret, 1) != 1);
|
||||||
|
platform_buffer_write(tmp, 3);
|
||||||
|
platform_buffer_read(&ret, 1);
|
||||||
|
|
||||||
|
ret &= 0x80;
|
||||||
|
|
||||||
|
// DEBUG("jtagtap_next(TMS = %d, TDO = %d) = %02X\n", dTMS, dTDO, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
128
src/linux/platform.c
Normal file
128
src/linux/platform.c
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include "gdb_if.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct ftdi_context *ftdic;
|
||||||
|
|
||||||
|
#define BUF_SIZE 4096
|
||||||
|
static uint8_t outbuf[BUF_SIZE];
|
||||||
|
static uint16_t bufptr = 0;
|
||||||
|
|
||||||
|
int platform_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if(ftdic) {
|
||||||
|
ftdi_usb_close(ftdic);
|
||||||
|
ftdi_free(ftdic);
|
||||||
|
ftdic = NULL;
|
||||||
|
}
|
||||||
|
if((ftdic = ftdi_new()) == NULL) {
|
||||||
|
fprintf(stderr, "ftdi_new: %s\n",
|
||||||
|
ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if((err = ftdi_set_interface(ftdic, INTERFACE_A)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_interface: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if((err = ftdi_usb_open(ftdic, FT2232_VID, FT2232_PID)) != 0) {
|
||||||
|
fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = ftdi_set_latency_timer(ftdic, 1)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_latency_timer: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if((err = ftdi_set_baudrate(ftdic, 1000000)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if((err = ftdi_usb_purge_buffers(ftdic)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_baudrate: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if((err = ftdi_write_data_set_chunksize(ftdic, BUF_SIZE)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_write_data_set_chunksize: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(gdb_if_init() == 0);
|
||||||
|
|
||||||
|
jtag_scan();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void platform_buffer_flush(void)
|
||||||
|
{
|
||||||
|
assert(ftdi_write_data(ftdic, outbuf, bufptr) == bufptr);
|
||||||
|
// printf("FT2232 platform_buffer flush: %d bytes\n", bufptr);
|
||||||
|
bufptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int platform_buffer_write(const uint8_t *data, int size)
|
||||||
|
{
|
||||||
|
if((bufptr + size) / BUF_SIZE > 0) platform_buffer_flush();
|
||||||
|
memcpy(outbuf + bufptr, data, size);
|
||||||
|
bufptr += size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int platform_buffer_read(uint8_t *data, int size)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
platform_buffer_flush();
|
||||||
|
while((index += ftdi_read_data(ftdic, data + index, size-index)) != size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#warning "This vasprintf() is dubious!"
|
||||||
|
int vasprintf(char **strp, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
int size = 128, ret = 0;
|
||||||
|
|
||||||
|
*strp = malloc(size);
|
||||||
|
while(*strp && ((ret = vsnprintf(*strp, size, fmt, ap)) == size))
|
||||||
|
*strp = realloc(*strp, size <<= 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *platform_target_voltage(void)
|
||||||
|
{
|
||||||
|
return "not supported";
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the Black Magic Debug project.
|
* This file is part of the Black Magic Debug project.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Black Sphere Technologies Ltd.
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -17,26 +17,40 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef __TRACESWO_H
|
|
||||||
#define __TRACESWO_H
|
|
||||||
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
#ifndef __PLATFORM_H
|
||||||
|
#define __PLATFORM_H
|
||||||
|
|
||||||
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
#include <stdint.h>
|
||||||
/* Default line rate, used as default for a request without baudrate */
|
#include <ftdi.h>
|
||||||
#define SWO_DEFAULT_BAUD (2250000)
|
|
||||||
void traceswo_init(uint32_t baudrate, uint32_t swo_chan_bitmask);
|
#ifndef WIN32
|
||||||
|
# include <alloca.h>
|
||||||
#else
|
#else
|
||||||
void traceswo_init(uint32_t swo_chan_bitmask);
|
# define alloca __builtin_alloca
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void trace_buf_drain(usbd_device *dev, uint8_t ep);
|
#define FT2232_VID 0x0403
|
||||||
|
#define FT2232_PID 0x6010
|
||||||
|
|
||||||
/* set bitmask of swo channels to be decoded */
|
#define SET_RUN_STATE(state)
|
||||||
void traceswo_setmask(uint32_t mask);
|
#define SET_IDLE_STATE(state)
|
||||||
|
#define SET_ERROR_STATE(state)
|
||||||
|
|
||||||
/* print decoded swo packet on usb serial */
|
#define PLATFORM_FATAL_ERROR(error) abort()
|
||||||
uint16_t traceswo_decode(usbd_device *usbd_dev, uint8_t addr,
|
#define PLATFORM_SET_FATAL_ERROR_RECOVERY()
|
||||||
const void *buf, uint16_t len);
|
|
||||||
|
#define morse(x, y) do {} while(0)
|
||||||
|
#define morse_msg 0
|
||||||
|
|
||||||
|
extern struct ftdi_context *ftdic;
|
||||||
|
|
||||||
|
int platform_init(void);
|
||||||
|
const char *platform_target_voltage(void);
|
||||||
|
|
||||||
|
void platform_buffer_flush(void);
|
||||||
|
int platform_buffer_write(const uint8_t *data, int size);
|
||||||
|
int platform_buffer_read(uint8_t *data, int size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
176
src/linux/swdptap.c
Normal file
176
src/linux/swdptap.c
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Quick hack for bit-banging SW-DP interface over FT2232.
|
||||||
|
* Intended as proof of concept, not for production.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ftdi.h>
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
#include "swdptap.h"
|
||||||
|
|
||||||
|
static void swdptap_turnaround(uint8_t dir);
|
||||||
|
static uint8_t swdptap_bit_in(void);
|
||||||
|
static void swdptap_bit_out(uint8_t val);
|
||||||
|
|
||||||
|
int swdptap_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
assert(ftdic != NULL);
|
||||||
|
|
||||||
|
if((err = ftdi_set_bitmode(ftdic, 0xAB, BITMODE_BITBANG)) != 0) {
|
||||||
|
fprintf(stderr, "ftdi_set_bitmode: %d: %s\n",
|
||||||
|
err, ftdi_get_error_string(ftdic));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ftdi_write_data(ftdic, "\xAB\xA8", 2) == 2);
|
||||||
|
|
||||||
|
/* This must be investigated in more detail.
|
||||||
|
* As described in STM32 Reference Manual... */
|
||||||
|
swdptap_seq_out(0xFFFF, 16);
|
||||||
|
swdptap_reset();
|
||||||
|
swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */
|
||||||
|
swdptap_reset();
|
||||||
|
swdptap_seq_out(0, 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swdptap_reset(void)
|
||||||
|
{
|
||||||
|
swdptap_turnaround(0);
|
||||||
|
/* 50 clocks with TMS high */
|
||||||
|
for(int i = 0; i < 50; i++) swdptap_bit_out(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swdptap_turnaround(uint8_t dir)
|
||||||
|
{
|
||||||
|
static uint8_t olddir = 0;
|
||||||
|
|
||||||
|
//DEBUG("%s", dir ? "\n-> ":"\n<- ");
|
||||||
|
platform_buffer_flush();
|
||||||
|
|
||||||
|
if(dir == olddir) return;
|
||||||
|
olddir = dir;
|
||||||
|
|
||||||
|
if(dir) /* SWDIO goes to input */
|
||||||
|
assert(ftdi_set_bitmode(ftdic, 0xA3, BITMODE_BITBANG) == 0);
|
||||||
|
|
||||||
|
/* One clock cycle */
|
||||||
|
ftdi_write_data(ftdic, "\xAB\xA8", 2);
|
||||||
|
|
||||||
|
if(!dir) /* SWDIO goes to output */
|
||||||
|
assert(ftdi_set_bitmode(ftdic, 0xAB, BITMODE_BITBANG) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t swdptap_bit_in(void)
|
||||||
|
{
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
//ftdi_read_data(ftdic, &ret, 1);
|
||||||
|
ftdi_read_pins(ftdic, &ret);
|
||||||
|
ret &= 0x08;
|
||||||
|
ftdi_write_data(ftdic, "\xA1\xA0", 2);
|
||||||
|
|
||||||
|
//DEBUG("%d", ret?1:0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swdptap_bit_out(uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t buf[3] = "\xA0\xA1\xA0";
|
||||||
|
|
||||||
|
//DEBUG("%d", val);
|
||||||
|
|
||||||
|
if(val) {
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
buf[i] |= 0x08;
|
||||||
|
}
|
||||||
|
//ftdi_write_data(ftdic, buf, 3);
|
||||||
|
platform_buffer_write(buf, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t swdptap_seq_in(int ticks)
|
||||||
|
{
|
||||||
|
uint32_t index = 1;
|
||||||
|
uint32_t ret = 0;
|
||||||
|
|
||||||
|
swdptap_turnaround(1);
|
||||||
|
|
||||||
|
while (ticks--) {
|
||||||
|
if (swdptap_bit_in())
|
||||||
|
ret |= index;
|
||||||
|
index <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t swdptap_seq_in_parity(uint32_t *ret, int ticks)
|
||||||
|
{
|
||||||
|
uint32_t index = 1;
|
||||||
|
uint8_t parity = 0;
|
||||||
|
*ret = 0;
|
||||||
|
|
||||||
|
swdptap_turnaround(1);
|
||||||
|
|
||||||
|
while (ticks--) {
|
||||||
|
if (swdptap_bit_in()) {
|
||||||
|
*ret |= index;
|
||||||
|
parity ^= 1;
|
||||||
|
}
|
||||||
|
index <<= 1;
|
||||||
|
}
|
||||||
|
if (swdptap_bit_in())
|
||||||
|
parity ^= 1;
|
||||||
|
|
||||||
|
return parity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swdptap_seq_out(uint32_t MS, int ticks)
|
||||||
|
{
|
||||||
|
swdptap_turnaround(0);
|
||||||
|
|
||||||
|
while (ticks--) {
|
||||||
|
swdptap_bit_out(MS & 1);
|
||||||
|
MS >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void swdptap_seq_out_parity(uint32_t MS, int ticks)
|
||||||
|
{
|
||||||
|
uint8_t parity = 0;
|
||||||
|
|
||||||
|
swdptap_turnaround(0);
|
||||||
|
|
||||||
|
while (ticks--) {
|
||||||
|
swdptap_bit_out(MS & 1);
|
||||||
|
parity ^= MS;
|
||||||
|
MS >>= 1;
|
||||||
|
}
|
||||||
|
swdptap_bit_out(parity & 1);
|
||||||
|
}
|
||||||
|
|
153
src/lmi.c
Normal file
153
src/lmi.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Black Magic Debug project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||||
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This file implements TI/LMI LM3S target specific functions providing
|
||||||
|
* the XML memory map and Flash memory programming.
|
||||||
|
*
|
||||||
|
* Issues:
|
||||||
|
* No detection of the target device.
|
||||||
|
* Add reference to documentation.
|
||||||
|
* Flash erase is very slow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
#include "target.h"
|
||||||
|
|
||||||
|
static int lmi_flash_erase(struct target_s *target, uint32_t addr, int len);
|
||||||
|
static int lmi_flash_write_words(struct target_s *target, uint32_t dest,
|
||||||
|
const uint32_t *src, int len);
|
||||||
|
|
||||||
|
static const char lmi_driver_str[] = "LuminaryMicro Stellaris";
|
||||||
|
|
||||||
|
static const char lmi_xml_memory_map[] = "<?xml version=\"1.0\"?>"
|
||||||
|
/* "<!DOCTYPE memory-map "
|
||||||
|
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
|
||||||
|
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
|
||||||
|
"<memory-map>"
|
||||||
|
" <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
|
||||||
|
" <property name=\"blocksize\">0x400</property>"
|
||||||
|
" </memory>"
|
||||||
|
" <memory type=\"ram\" start=\"0x20000000\" length=\"0x10000\"/>"
|
||||||
|
"</memory-map>";
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t lmi_flash_write_stub[] = {
|
||||||
|
// _start:
|
||||||
|
0x4809, // ldr r0, [pc, #36] // _flashbase
|
||||||
|
0x490b, // ldr r1, [pc, #44] // _addr
|
||||||
|
0x467a, // mov r2, pc
|
||||||
|
0x3230, // adds r2, #48
|
||||||
|
0x4b0a, // ldr r3, [pc, #40] // _size
|
||||||
|
0x4d08, // ldr r5, [pc, #32] // _flash_write_cmd
|
||||||
|
// _next:
|
||||||
|
0xb15b, // cbz r3, _done
|
||||||
|
0x6001, // str r1, [r0, #0]
|
||||||
|
0x6814, // ldr r4, [r2]
|
||||||
|
0x6044, // str r4, [r0, #4]
|
||||||
|
0x6085, // str r5, [r0, #8]
|
||||||
|
// _wait:
|
||||||
|
0x6884, // ldr r4, [r0, #8]
|
||||||
|
0x2601, // movs r6, #1
|
||||||
|
0x4234, // tst r4, r6
|
||||||
|
0xd1fb, // bne _wait
|
||||||
|
|
||||||
|
0x3b01, // subs r3, #1
|
||||||
|
0x3104, // adds r1, #4
|
||||||
|
0x3204, // adds r2, #4
|
||||||
|
0xe7f2, // b _next
|
||||||
|
// _done:
|
||||||
|
0xbe00, // bkpt
|
||||||
|
// _flashbase:
|
||||||
|
0xd000, 0x400f, // .word 0x400fd000
|
||||||
|
// _flash_write_cmd:
|
||||||
|
0x0001, 0xa442, // .word 0xa4420001
|
||||||
|
// _addr:
|
||||||
|
// 0x0000, 0x0000,
|
||||||
|
// _size:
|
||||||
|
// 0x0000, 0x0000,
|
||||||
|
// _data:
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
int lmi_probe(struct target_s *target)
|
||||||
|
{
|
||||||
|
/* How do we really probe the LMI device??? */
|
||||||
|
target->driver = lmi_driver_str;
|
||||||
|
target->xml_mem_map = lmi_xml_memory_map;
|
||||||
|
target->flash_erase = lmi_flash_erase;
|
||||||
|
target->flash_write_words = lmi_flash_write_words;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lmi_flash_erase(struct target_s *target, uint32_t addr, int len)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
addr &= 0xFFFFFC00;
|
||||||
|
len &= 0xFFFFFC00;
|
||||||
|
|
||||||
|
/* setup word access */
|
||||||
|
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
|
||||||
|
|
||||||
|
/* select Flash Control */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0x400FD000);
|
||||||
|
|
||||||
|
while(len) {
|
||||||
|
/* write address to FMA */
|
||||||
|
adiv5_ap_write(t->ap, 0x10, addr); /* Required to switch banks */
|
||||||
|
/* set ERASE bit in FMC */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x08, 0xA4420002);
|
||||||
|
/* Read FMC to poll for ERASE bit */
|
||||||
|
adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
|
||||||
|
do {
|
||||||
|
tmp = adiv5_dp_low_access(t->ap->dp, 1, 1, 0x08, 0);
|
||||||
|
} while (tmp & 2);
|
||||||
|
|
||||||
|
len -= 0x400;
|
||||||
|
addr += 0x400;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lmi_flash_write_words(struct target_s *target, uint32_t dest,
|
||||||
|
const uint32_t *src, int len)
|
||||||
|
{
|
||||||
|
uint32_t data[(len>>2)+2];
|
||||||
|
data[0] = dest;
|
||||||
|
data[1] = len >> 2;
|
||||||
|
memcpy(&data[2], src, len);
|
||||||
|
DEBUG("Sending stub\n");
|
||||||
|
target_mem_write_words(target, 0x20000000, (void*)lmi_flash_write_stub, 0x30);
|
||||||
|
DEBUG("Sending data\n");
|
||||||
|
target_mem_write_words(target, 0x20000030, data, len + 8);
|
||||||
|
DEBUG("Running stub\n");
|
||||||
|
target_pc_write(target, 0x20000000);
|
||||||
|
target_halt_resume(target, 0);
|
||||||
|
DEBUG("Waiting for halt\n");
|
||||||
|
while(!target_halt_wait(target));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
35
src/main.c
35
src/main.c
@ -22,36 +22,25 @@
|
|||||||
* protocol loop.
|
* protocol loop.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "general.h"
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "gdb_if.h"
|
#include "gdb_if.h"
|
||||||
#include "gdb_main.h"
|
#include "gdb_main.h"
|
||||||
|
#include "jtagtap.h"
|
||||||
|
#include "jtag_scan.h"
|
||||||
|
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "exception.h"
|
|
||||||
#include "gdb_packet.h"
|
|
||||||
#include "morse.h"
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(void)
|
||||||
{
|
{
|
||||||
#if PC_HOSTED == 1
|
assert(platform_init() == 0);
|
||||||
platform_init(argc, argv);
|
|
||||||
#else
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
platform_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (true) {
|
PLATFORM_SET_FATAL_ERROR_RECOVERY();
|
||||||
volatile struct exception e;
|
|
||||||
TRY_CATCH(e, EXCEPTION_ALL) {
|
gdb_main();
|
||||||
gdb_main();
|
|
||||||
}
|
|
||||||
if (e.type) {
|
|
||||||
gdb_putpacketz("EFF");
|
|
||||||
target_list_free();
|
|
||||||
morse("TARGET LOST.", 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Should never get here */
|
/* Should never get here */
|
||||||
return 0;
|
return 0;
|
||||||
|
106
src/morse.c
106
src/morse.c
@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include "general.h"
|
|
||||||
#include "morse.h"
|
|
||||||
|
|
||||||
/* Morse code patterns and lengths */
|
|
||||||
static const struct {
|
|
||||||
uint16_t code;
|
|
||||||
uint8_t bits;
|
|
||||||
} morse_letter[] = {
|
|
||||||
{ 0b00011101, 8}, // 'A' .-
|
|
||||||
{ 0b000101010111, 12}, // 'B' -...
|
|
||||||
{ 0b00010111010111, 14}, // 'C' -.-.
|
|
||||||
{ 0b0001010111, 10}, // 'D' -..
|
|
||||||
{ 0b0001, 4}, // 'E' .
|
|
||||||
{ 0b000101110101, 12}, // 'F' ..-.
|
|
||||||
{ 0b000101110111, 12}, // 'G' --.
|
|
||||||
{ 0b0001010101, 10}, // 'H' ....
|
|
||||||
{ 0b000101, 6}, // 'I' ..
|
|
||||||
{0b0001110111011101, 16}, // 'J' .---
|
|
||||||
{ 0b000111010111, 12}, // 'K' -.-
|
|
||||||
{ 0b000101011101, 12}, // 'L' .-..
|
|
||||||
{ 0b0001110111, 10}, // 'M' --
|
|
||||||
{ 0b00010111, 8}, // 'N' -.
|
|
||||||
{ 0b00011101110111, 14}, // 'O' ---
|
|
||||||
{ 0b00010111011101, 14}, // 'P' .--.
|
|
||||||
{0b0001110101110111, 16}, // 'Q' --.-
|
|
||||||
{ 0b0001011101, 10}, // 'R' .-.
|
|
||||||
{ 0b00010101, 8}, // 'S' ...
|
|
||||||
{ 0b000111, 6}, // 'T' -
|
|
||||||
{ 0b0001110101, 10}, // 'U' ..-
|
|
||||||
{ 0b000111010101, 12}, // 'V' ...-
|
|
||||||
{ 0b000111011101, 12}, // 'W' .--
|
|
||||||
{ 0b00011101010111, 14}, // 'X' -..-
|
|
||||||
{0b0001110111010111, 16}, // 'Y' -.--
|
|
||||||
{ 0b00010101110111, 14}, // 'Z' --..
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *morse_msg;
|
|
||||||
static const char * volatile morse_ptr;
|
|
||||||
static char morse_repeat;
|
|
||||||
|
|
||||||
void morse(const char *msg, char repeat)
|
|
||||||
{
|
|
||||||
#if PC_HOSTED == 1
|
|
||||||
if (msg)
|
|
||||||
DEBUG_WARN("%s\n", msg);
|
|
||||||
(void) repeat;
|
|
||||||
#else
|
|
||||||
morse_msg = morse_ptr = msg;
|
|
||||||
morse_repeat = repeat;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool morse_update(void)
|
|
||||||
{
|
|
||||||
static uint16_t code;
|
|
||||||
static uint8_t bits;
|
|
||||||
|
|
||||||
if (!morse_ptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!bits) {
|
|
||||||
char c = *morse_ptr++;
|
|
||||||
if (!c) {
|
|
||||||
if(morse_repeat) {
|
|
||||||
morse_ptr = morse_msg;
|
|
||||||
c = *morse_ptr++;
|
|
||||||
} else {
|
|
||||||
morse_ptr = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((c >= 'A') && (c <= 'Z')) {
|
|
||||||
c -= 'A';
|
|
||||||
code = morse_letter[c].code;
|
|
||||||
bits = morse_letter[c].bits;
|
|
||||||
} else {
|
|
||||||
code = 0; bits = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = code & 1;
|
|
||||||
code >>= 1;
|
|
||||||
bits--;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
253
src/nxp_tgt.c
Normal file
253
src/nxp_tgt.c
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "general.h"
|
||||||
|
#include "adiv5.h"
|
||||||
|
#include "target.h"
|
||||||
|
#include "nxp_tgt.h"
|
||||||
|
|
||||||
|
#define IAP_PGM_CHUNKSIZE 256 /* should fit in RAM on any device */
|
||||||
|
|
||||||
|
struct flash_param {
|
||||||
|
uint16_t opcodes[2]; /* two opcodes to return to after calling the ROM */
|
||||||
|
uint32_t command[5]; /* command operands */
|
||||||
|
uint32_t result[4]; /* result data */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct flash_program {
|
||||||
|
struct flash_param p;
|
||||||
|
uint8_t data[IAP_PGM_CHUNKSIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct flash_program flash_pgm;
|
||||||
|
|
||||||
|
#define IAP_ENTRYPOINT 0x1fff1ff1
|
||||||
|
#define IAP_RAM_BASE 0x10000000
|
||||||
|
|
||||||
|
|
||||||
|
#define IAP_CMD_PREPARE 50
|
||||||
|
#define IAP_CMD_PROGRAM 51
|
||||||
|
#define IAP_CMD_ERASE 52
|
||||||
|
#define IAP_CMD_BLANKCHECK 53
|
||||||
|
|
||||||
|
#define IAP_STATUS_CMD_SUCCESS 0
|
||||||
|
#define IAP_STATUS_INVALID_COMMAND 1
|
||||||
|
#define IAP_STATUS_SRC_ADDR_ERROR 2
|
||||||
|
#define IAP_STATUS_DST_ADDR_ERROR 3
|
||||||
|
#define IAP_STATUS_SRC_ADDR_NOT_MAPPED 4
|
||||||
|
#define IAP_STATUS_DST_ADDR_NOT_MAPPED 5
|
||||||
|
#define IAP_STATUS_COUNT_ERROR 6
|
||||||
|
#define IAP_STATUS_INVALID_SECTOR 7
|
||||||
|
#define IAP_STATUS_SECTOR_NOT_BLANK 8
|
||||||
|
#define IAP_STATUS_SECTOR_NOT_PREPARED 9
|
||||||
|
#define IAP_STATUS_COMPARE_ERROR 10
|
||||||
|
#define IAP_STATUS_BUSY 11
|
||||||
|
|
||||||
|
static void lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len);
|
||||||
|
static int lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len);
|
||||||
|
static int lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len);
|
||||||
|
static int lpc11xx_flash_write_words(struct target_s *target, uint32_t dest, const uint32_t *src,
|
||||||
|
int len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that this memory map is actually for the largest of the lpc11xx devices;
|
||||||
|
* There seems to be no good way to decode the part number to determine the RAM
|
||||||
|
* and flash sizes.
|
||||||
|
*/
|
||||||
|
static const char lpc11xx_xml_memory_map[] = "<?xml version=\"1.0\"?>"
|
||||||
|
/* "<!DOCTYPE memory-map "
|
||||||
|
" PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
|
||||||
|
" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
|
||||||
|
"<memory-map>"
|
||||||
|
" <memory type=\"flash\" start=\"0x00000000\" length=\"0x8000\">"
|
||||||
|
" <property name=\"blocksize\">0x1000</property>"
|
||||||
|
" </memory>"
|
||||||
|
" <memory type=\"ram\" start=\"0x10000000\" length=\"0x2000\"/>"
|
||||||
|
"</memory-map>";
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
lpc11xx_probe(struct target_s *target)
|
||||||
|
{
|
||||||
|
struct target_ap_s *t = (void *)target;
|
||||||
|
uint32_t idcode;
|
||||||
|
|
||||||
|
/* read the device ID register */
|
||||||
|
idcode = adiv5_ap_mem_read(t->ap, 0x400483F4);
|
||||||
|
|
||||||
|
switch (idcode) {
|
||||||
|
|
||||||
|
case 0x041E502B:
|
||||||
|
case 0x2516D02B:
|
||||||
|
case 0x0416502B:
|
||||||
|
case 0x2516902B: /* lpc1111 */
|
||||||
|
case 0x2524D02B:
|
||||||
|
case 0x0425502B:
|
||||||
|
case 0x2524902B:
|
||||||
|
case 0x1421102B: /* lpc1112 */
|
||||||
|
case 0x0434502B:
|
||||||
|
case 0x2532902B:
|
||||||
|
case 0x0434102B:
|
||||||
|
case 0x2532102B: /* lpc1113 */
|
||||||
|
case 0x0444502B:
|
||||||
|
case 0x2540902B:
|
||||||
|
case 0x0444102B:
|
||||||
|
case 0x2540102B:
|
||||||
|
case 0x1440102B: /* lpc1114 */
|
||||||
|
case 0x1431102B: /* lpc11c22 */
|
||||||
|
case 0x1430102B: /* lpc11c24 */
|
||||||
|
target->driver = "lpc11xx";
|
||||||
|
target->xml_mem_map = lpc11xx_xml_memory_map;
|
||||||
|
target->flash_erase = lpc11xx_flash_erase;
|
||||||
|
target->flash_write_words = lpc11xx_flash_write_words;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len)
|
||||||
|
{
|
||||||
|
uint32_t regs[target->regs_size];
|
||||||
|
|
||||||
|
/* fill out the remainder of the parameters and copy the structure to RAM */
|
||||||
|
param->opcodes[0] = 0xbe00;
|
||||||
|
param->opcodes[1] = 0x0000;
|
||||||
|
target_mem_write_words(target, IAP_RAM_BASE, (void *)param, param_len);
|
||||||
|
|
||||||
|
/* set up for the call to the IAP ROM */
|
||||||
|
target_regs_read(target, regs);
|
||||||
|
regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
|
||||||
|
regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);
|
||||||
|
|
||||||
|
regs[14] = IAP_RAM_BASE | 1;
|
||||||
|
regs[15] = IAP_ENTRYPOINT;
|
||||||
|
target_regs_write(target, regs);
|
||||||
|
|
||||||
|
/* start the target and wait for it to halt again */
|
||||||
|
target_halt_resume(target, 0);
|
||||||
|
while (!target_halt_wait(target));
|
||||||
|
|
||||||
|
/* copy back just the parameters structure */
|
||||||
|
target_mem_read_words(target, (void *)param, IAP_RAM_BASE, sizeof(struct flash_param));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len)
|
||||||
|
{
|
||||||
|
/* prepare the sector(s) to be erased */
|
||||||
|
memset(&flash_pgm.p, 0, sizeof(flash_pgm.p));
|
||||||
|
flash_pgm.p.command[0] = IAP_CMD_PREPARE;
|
||||||
|
flash_pgm.p.command[1] = addr / 4096;
|
||||||
|
flash_pgm.p.command[2] = flash_pgm.p.command[1] + ((len + 4095) / 4096) - 1;
|
||||||
|
|
||||||
|
lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
|
||||||
|
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (addr % 4096)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* prepare... */
|
||||||
|
if (lpc11xx_flash_prepare(target, addr, len))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* and now erase them */
|
||||||
|
flash_pgm.p.command[0] = IAP_CMD_ERASE;
|
||||||
|
flash_pgm.p.command[1] = addr / 4096;
|
||||||
|
flash_pgm.p.command[2] = flash_pgm.p.command[1] + ((len + 4095) / 4096) - 1;
|
||||||
|
flash_pgm.p.command[3] = 12000; /* XXX safe to assume this? */
|
||||||
|
lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
|
||||||
|
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
flash_pgm.p.command[0] = IAP_CMD_BLANKCHECK;
|
||||||
|
lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
|
||||||
|
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lpc11xx_flash_write_words(struct target_s *target, uint32_t dest, const uint32_t *src, int len)
|
||||||
|
{
|
||||||
|
const uint8_t *s = (const uint8_t *)src;
|
||||||
|
unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
|
||||||
|
unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
|
||||||
|
unsigned chunk_offset = dest % IAP_PGM_CHUNKSIZE;
|
||||||
|
unsigned chunk;
|
||||||
|
|
||||||
|
for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
|
||||||
|
|
||||||
|
printf("chunk %u len %d\n", chunk, len);
|
||||||
|
/* first and last chunk may require special handling */
|
||||||
|
if ((chunk == first_chunk) || (chunk == last_chunk)) {
|
||||||
|
|
||||||
|
/* fill with all ff to avoid sector rewrite corrupting other writes */
|
||||||
|
memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
|
||||||
|
|
||||||
|
/* copy as much as fits */
|
||||||
|
int copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
|
||||||
|
if (copylen > len)
|
||||||
|
copylen = len;
|
||||||
|
memcpy(&flash_pgm.data[chunk_offset], s, copylen);
|
||||||
|
|
||||||
|
/* update to suit */
|
||||||
|
len -= copylen;
|
||||||
|
s += copylen;
|
||||||
|
chunk_offset = 0;
|
||||||
|
|
||||||
|
/* if we are programming the vectors, calculate the magic number */
|
||||||
|
if (chunk == 0) {
|
||||||
|
uint32_t *w = (uint32_t *)(&flash_pgm.data[0]);
|
||||||
|
uint32_t sum = 0;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 7; i++)
|
||||||
|
sum += w[i];
|
||||||
|
w[7] = 0 - sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* interior chunk, must be aligned and full-sized */
|
||||||
|
memcpy(flash_pgm.data, s, IAP_PGM_CHUNKSIZE);
|
||||||
|
len -= IAP_PGM_CHUNKSIZE;
|
||||||
|
s += IAP_PGM_CHUNKSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare... */
|
||||||
|
if (lpc11xx_flash_prepare(target, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* set the destination address and program */
|
||||||
|
flash_pgm.p.command[0] = IAP_CMD_PROGRAM;
|
||||||
|
flash_pgm.p.command[1] = chunk * IAP_PGM_CHUNKSIZE;
|
||||||
|
flash_pgm.p.command[2] = IAP_RAM_BASE + offsetof(struct flash_program, data);
|
||||||
|
flash_pgm.p.command[3] = IAP_PGM_CHUNKSIZE;
|
||||||
|
flash_pgm.p.command[4] = 12000; /* XXX safe to presume this? */
|
||||||
|
lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm));
|
||||||
|
if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
# Platforms and platform support files
|
|
||||||
|
|
||||||
This directory contains the implementation of platforms and support file
|
|
||||||
used by (multiple) platforms.
|
|
||||||
|
|
||||||
## Implementation directories
|
|
||||||
|
|
||||||
native : Firmware for original Black Magic Probe<br>
|
|
||||||
stlink : Firmware for STLINK-V2 and V21<br>
|
|
||||||
swlink : Firmware for STLINK-V1 and Bluepill<br>
|
|
||||||
hydrabus : Firmware https://hydrabus.com/ <br>
|
|
||||||
f4discovery : Firmware for STM32F407DISCO<br>
|
|
||||||
launchpad-icdi :<br>
|
|
||||||
tm4c: <br>
|
|
||||||
hosted: PC-hosted BMP running as PC application talking to firmware BMPs,
|
|
||||||
STLINK-V2/21/3, FTDI MPSSE probes, CMSIS-DAP and JLINK
|
|
||||||
|
|
||||||
|
|
||||||
## Support directories
|
|
||||||
|
|
||||||
common: libopencm3 based support for firmware BMPs<br>
|
|
||||||
stm32: STM32 specific libopencm3 based support for firmware BMPs<br>
|
|
||||||
pc: Support for PC-hosted BMPs.<br>
|
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This file implements a the USB Communications Device Class - Abstract
|
|
||||||
* Control Model (CDC-ACM) as defined in CDC PSTN subclass 1.2.
|
|
||||||
* A Device Firmware Upgrade (DFU 1.1) class interface is provided for
|
|
||||||
* field firmware upgrade.
|
|
||||||
*
|
|
||||||
* The device's unique id is used as the USB serial number string.
|
|
||||||
*/
|
|
||||||
#ifndef __CDCACM_H
|
|
||||||
#define __CDCACM_H
|
|
||||||
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
|
|
||||||
#define CDCACM_PACKET_SIZE 64
|
|
||||||
|
|
||||||
#define CDCACM_GDB_ENDPOINT 1
|
|
||||||
#define CDCACM_UART_ENDPOINT 3
|
|
||||||
#define TRACE_ENDPOINT 5
|
|
||||||
|
|
||||||
extern usbd_device *usbdev;
|
|
||||||
|
|
||||||
void cdcacm_init(void);
|
|
||||||
/* Returns current usb configuration, or 0 if not configured. */
|
|
||||||
int cdcacm_get_config(void);
|
|
||||||
int cdcacm_get_dtr(void);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This file implements the low-level JTAG TAP interface. */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "jtagtap.h"
|
|
||||||
#include "gdb_packet.h"
|
|
||||||
|
|
||||||
jtag_proc_t jtag_proc;
|
|
||||||
|
|
||||||
static void jtagtap_reset(void);
|
|
||||||
static void jtagtap_tms_seq(uint32_t MS, int ticks);
|
|
||||||
static void jtagtap_tdi_tdo_seq(
|
|
||||||
uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks);
|
|
||||||
static void jtagtap_tdi_seq(
|
|
||||||
const uint8_t final_tms, const uint8_t *DI, int ticks);
|
|
||||||
static uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI);
|
|
||||||
|
|
||||||
int jtagtap_init()
|
|
||||||
{
|
|
||||||
TMS_SET_MODE();
|
|
||||||
|
|
||||||
jtag_proc.jtagtap_reset = jtagtap_reset;
|
|
||||||
jtag_proc.jtagtap_next =jtagtap_next;
|
|
||||||
jtag_proc.jtagtap_tms_seq = jtagtap_tms_seq;
|
|
||||||
jtag_proc.jtagtap_tdi_tdo_seq = jtagtap_tdi_tdo_seq;
|
|
||||||
jtag_proc.jtagtap_tdi_seq = jtagtap_tdi_seq;
|
|
||||||
|
|
||||||
/* Go to JTAG mode for SWJ-DP */
|
|
||||||
for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-DP */
|
|
||||||
jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */
|
|
||||||
jtagtap_soft_reset();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jtagtap_reset(void)
|
|
||||||
{
|
|
||||||
#ifdef TRST_PORT
|
|
||||||
if (platform_hwversion() == 0) {
|
|
||||||
volatile int i;
|
|
||||||
gpio_clear(TRST_PORT, TRST_PIN);
|
|
||||||
for(i = 0; i < 10000; i++) asm("nop");
|
|
||||||
gpio_set(TRST_PORT, TRST_PIN);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
jtagtap_soft_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDI)
|
|
||||||
{
|
|
||||||
uint16_t ret;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, dTMS);
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, dTDI);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
ret = gpio_get(TDO_PORT, TDO_PIN);
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt - 2; cnt > 0; cnt--);
|
|
||||||
|
|
||||||
//DEBUG("jtagtap_next(TMS = %d, TDI = %d) = %d\n", dTMS, dTDI, ret);
|
|
||||||
|
|
||||||
return ret != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jtagtap_tms_seq(uint32_t MS, int ticks)
|
|
||||||
{
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, 1);
|
|
||||||
int data = MS & 1;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while(ticks) {
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, data);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
MS >>= 1;
|
|
||||||
data = MS & 1;
|
|
||||||
ticks--;
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(ticks) {
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, data);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
MS >>= 1;
|
|
||||||
data = MS & 1;
|
|
||||||
ticks--;
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jtagtap_tdi_tdo_seq(
|
|
||||||
uint8_t *DO, const uint8_t final_tms, const uint8_t *DI, int ticks)
|
|
||||||
{
|
|
||||||
uint8_t index = 1;
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, 0);
|
|
||||||
uint8_t res = 0;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while(ticks > 1) {
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, *DI & index);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
if (gpio_get(TDO_PORT, TDO_PIN)) {
|
|
||||||
res |= index;
|
|
||||||
}
|
|
||||||
if(!(index <<= 1)) {
|
|
||||||
*DO = res;
|
|
||||||
res = 0;
|
|
||||||
index = 1;
|
|
||||||
DI++; DO++;
|
|
||||||
}
|
|
||||||
ticks--;
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(ticks > 1) {
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, *DI & index);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
if (gpio_get(TDO_PORT, TDO_PIN)) {
|
|
||||||
res |= index;
|
|
||||||
}
|
|
||||||
if(!(index <<= 1)) {
|
|
||||||
*DO = res;
|
|
||||||
res = 0;
|
|
||||||
index = 1;
|
|
||||||
DI++; DO++;
|
|
||||||
}
|
|
||||||
ticks--;
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, final_tms);
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, *DI & index);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
if (gpio_get(TDO_PORT, TDO_PIN)) {
|
|
||||||
res |= index;
|
|
||||||
}
|
|
||||||
*DO = res;
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jtagtap_tdi_seq(const uint8_t final_tms, const uint8_t *DI, int ticks)
|
|
||||||
{
|
|
||||||
uint8_t index = 1;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while(ticks--) {
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, ticks? 0 : final_tms);
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, *DI & index);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
if(!(index <<= 1)) {
|
|
||||||
index = 1;
|
|
||||||
DI++;
|
|
||||||
}
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt -2 ; cnt > 0; cnt--);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while(ticks--) {
|
|
||||||
gpio_set_val(TMS_PORT, TMS_PIN, ticks? 0 : final_tms);
|
|
||||||
gpio_set_val(TDI_PORT, TDI_PIN, *DI & index);
|
|
||||||
gpio_set(TCK_PORT, TCK_PIN);
|
|
||||||
if(!(index <<= 1)) {
|
|
||||||
index = 1;
|
|
||||||
DI++;
|
|
||||||
}
|
|
||||||
gpio_clear(TCK_PORT, TCK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This file implements the SW-DP interface. */
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "timing.h"
|
|
||||||
#include "adiv5.h"
|
|
||||||
|
|
||||||
enum {
|
|
||||||
SWDIO_STATUS_FLOAT = 0,
|
|
||||||
SWDIO_STATUS_DRIVE
|
|
||||||
};
|
|
||||||
static void swdptap_turnaround(int dir) __attribute__ ((optimize(3)));
|
|
||||||
static uint32_t swdptap_seq_in(int ticks) __attribute__ ((optimize(3)));
|
|
||||||
static bool swdptap_seq_in_parity(uint32_t *ret, int ticks)
|
|
||||||
__attribute__ ((optimize(3)));
|
|
||||||
static void swdptap_seq_out(uint32_t MS, int ticks)
|
|
||||||
__attribute__ ((optimize(3)));
|
|
||||||
static void swdptap_seq_out_parity(uint32_t MS, int ticks)
|
|
||||||
__attribute__ ((optimize(3)));
|
|
||||||
|
|
||||||
static void swdptap_turnaround(int dir)
|
|
||||||
{
|
|
||||||
static int olddir = SWDIO_STATUS_FLOAT;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
|
|
||||||
/* Don't turnaround if direction not changing */
|
|
||||||
if(dir == olddir) return;
|
|
||||||
olddir = dir;
|
|
||||||
|
|
||||||
#ifdef DEBUG_SWD_BITS
|
|
||||||
DEBUG("%s", dir ? "\n-> ":"\n<- ");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(dir == SWDIO_STATUS_FLOAT)
|
|
||||||
SWDIO_MODE_FLOAT();
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
if(dir == SWDIO_STATUS_DRIVE)
|
|
||||||
SWDIO_MODE_DRIVE();
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t swdptap_seq_in(int ticks)
|
|
||||||
{
|
|
||||||
uint32_t index = 1;
|
|
||||||
uint32_t ret = 0;
|
|
||||||
int len = ticks;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
|
|
||||||
swdptap_turnaround(SWDIO_STATUS_FLOAT);
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while (len--) {
|
|
||||||
int res;
|
|
||||||
res = gpio_get(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
ret |= (res) ? index : 0;
|
|
||||||
index <<= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
volatile int res;
|
|
||||||
while (len--) {
|
|
||||||
res = gpio_get(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
ret |= (res) ? index : 0;
|
|
||||||
index <<= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_SWD_BITS
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
DEBUG("%d", (ret & (1 << i)) ? 1 : 0);
|
|
||||||
#endif
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool swdptap_seq_in_parity(uint32_t *ret, int ticks)
|
|
||||||
{
|
|
||||||
uint32_t index = 1;
|
|
||||||
uint32_t res = 0;
|
|
||||||
bool bit;
|
|
||||||
int len = ticks;
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
|
|
||||||
swdptap_turnaround(SWDIO_STATUS_FLOAT);
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while (len--) {
|
|
||||||
bit = gpio_get(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
res |= (bit) ? index : 0;
|
|
||||||
index <<= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (len--) {
|
|
||||||
bit = gpio_get(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
res |= (bit) ? index : 0;
|
|
||||||
index <<= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int parity = __builtin_popcount(res);
|
|
||||||
bit = gpio_get(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
parity += (bit) ? 1 : 0;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
#ifdef DEBUG_SWD_BITS
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
DEBUG("%d", (res & (1 << i)) ? 1 : 0);
|
|
||||||
#endif
|
|
||||||
*ret = res;
|
|
||||||
/* Terminate the read cycle now */
|
|
||||||
swdptap_turnaround(SWDIO_STATUS_DRIVE);
|
|
||||||
return (parity & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void swdptap_seq_out(uint32_t MS, int ticks)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_SWD_BITS
|
|
||||||
for (int i = 0; i < ticks; i++)
|
|
||||||
DEBUG("%d", (MS & (1 << i)) ? 1 : 0);
|
|
||||||
#endif
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
swdptap_turnaround(SWDIO_STATUS_DRIVE);
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while (ticks--) {
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
MS >>= 1;
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (ticks--) {
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
MS >>= 1;
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void swdptap_seq_out_parity(uint32_t MS, int ticks)
|
|
||||||
{
|
|
||||||
int parity = __builtin_popcount(MS);
|
|
||||||
#ifdef DEBUG_SWD_BITS
|
|
||||||
for (int i = 0; i < ticks; i++)
|
|
||||||
DEBUG("%d", (MS & (1 << i)) ? 1 : 0);
|
|
||||||
#endif
|
|
||||||
register volatile int32_t cnt;
|
|
||||||
swdptap_turnaround(SWDIO_STATUS_DRIVE);
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
MS >>= 1;
|
|
||||||
if (swd_delay_cnt) {
|
|
||||||
while (ticks--) {
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
MS >>= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (ticks--) {
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, MS & 1);
|
|
||||||
MS >>= 1;
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gpio_set_val(SWDIO_PORT, SWDIO_PIN, parity & 1);
|
|
||||||
gpio_set(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
for(cnt = swd_delay_cnt; --cnt > 0;);
|
|
||||||
}
|
|
||||||
|
|
||||||
int swdptap_init(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
dp->seq_in = swdptap_seq_in;
|
|
||||||
dp->seq_in_parity = swdptap_seq_in_parity;
|
|
||||||
dp->seq_out = swdptap_seq_out;
|
|
||||||
dp->seq_out_parity = swdptap_seq_out_parity;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
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 \
|
|
||||||
-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
|
|
@ -1,65 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
## Connections:
|
|
||||||
|
|
||||||
PC2: TDI<br>
|
|
||||||
PC4: TMS/SWDIO<br>
|
|
||||||
PC5: TCK/SWCLK<br>
|
|
||||||
PC6: TDO/TRACESWO<br>
|
|
||||||
|
|
||||||
PC1: TRST<br>
|
|
||||||
PC8: SRST<br>
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
* 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
|
|
@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This file implements the platform specific functions for the STM32
|
|
||||||
* implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "cdcacm.h"
|
|
||||||
#include "usbuart.h"
|
|
||||||
#include "morse.h"
|
|
||||||
|
|
||||||
#include <libopencm3/stm32/rcc.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
#include <libopencm3/cm3/nvic.h>
|
|
||||||
#include <libopencm3/stm32/exti.h>
|
|
||||||
#include <libopencm3/stm32/usart.h>
|
|
||||||
#include <libopencm3/stm32/syscfg.h>
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
#include <libopencm3/cm3/systick.h>
|
|
||||||
#include <libopencm3/cm3/cortex.h>
|
|
||||||
|
|
||||||
#ifdef BLACKPILL
|
|
||||||
#include <libopencm3/usb/dwc/otg_fs.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
#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)))
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
#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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
#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,
|
|
||||||
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();
|
|
||||||
#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; }
|
|
||||||
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
|
|
@ -1,264 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* 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 <setjmp.h>
|
|
||||||
|
|
||||||
#define PLATFORM_HAS_TRACESWO
|
|
||||||
#ifdef BLACKPILL
|
|
||||||
#define PLATFORM_IDENT "(F4Discovery/BlackPillV2) "
|
|
||||||
/* Important pin mappings for STM32 implementation:
|
|
||||||
* JTAG/SWD
|
|
||||||
* PA1: TDI<br>
|
|
||||||
* PA13: TMS/SWDIO<br>
|
|
||||||
* PA14: TCK/SWCLK<br>
|
|
||||||
* PB3: TDO/TRACESWO<br>
|
|
||||||
* PB5: TRST<br>
|
|
||||||
* PB4: SRST<br>
|
|
||||||
* 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:
|
|
||||||
*
|
|
||||||
* LED0 = PD12 (Green LED : Running)
|
|
||||||
* LED1 = PD13 (Orange LED : Idle)
|
|
||||||
* LED2 = PD12 (Red LED : Error)
|
|
||||||
* LED3 = PD15 (Blue LED : Bootloader active)
|
|
||||||
*
|
|
||||||
* nTRST = PC1
|
|
||||||
* SRST_OUT = PC8
|
|
||||||
* TDI = PC2
|
|
||||||
* TMS = PC4 (input for SWDP)
|
|
||||||
* TCK = PC5/SWCLK
|
|
||||||
* TDO = PC6 (input for TRACESWO
|
|
||||||
* nSRST =
|
|
||||||
*
|
|
||||||
* Force DFU mode button: PA0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Hardware definitions... */
|
|
||||||
#define JTAG_PORT GPIOC
|
|
||||||
#define TDI_PORT JTAG_PORT
|
|
||||||
#define TMS_PORT JTAG_PORT
|
|
||||||
#define TCK_PORT JTAG_PORT
|
|
||||||
#define TDO_PORT GPIOC
|
|
||||||
#define TDI_PIN GPIO2
|
|
||||||
#define TMS_PIN GPIO4
|
|
||||||
#define TCK_PIN GPIO5
|
|
||||||
#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 TRST_PORT GPIOC
|
|
||||||
#define TRST_PIN GPIO1
|
|
||||||
#define SRST_PORT GPIOC
|
|
||||||
#define SRST_PIN GPIO8
|
|
||||||
|
|
||||||
#define LED_PORT GPIOD
|
|
||||||
#define LED_PORT_UART GPIOD
|
|
||||||
#define LED_UART GPIO12
|
|
||||||
#define LED_IDLE_RUN GPIO13
|
|
||||||
#define LED_ERROR GPIO14
|
|
||||||
#define LED_BOOTLOADER GPIO15
|
|
||||||
|
|
||||||
#define USBUSART USART3
|
|
||||||
#define USBUSART_CR1 USART3_CR1
|
|
||||||
#define USBUSART_DR USART3_DR
|
|
||||||
#define USBUSART_IRQ NVIC_USART3_IRQ
|
|
||||||
#define USBUSART_CLK RCC_USART3
|
|
||||||
#define USBUSART_PORT GPIOD
|
|
||||||
#define USBUSART_TX_PIN GPIO8
|
|
||||||
#define USBUSART_RX_PIN GPIO9
|
|
||||||
#define USBUSART_ISR(x) usart3_isr(x)
|
|
||||||
#define USBUSART_DMA_BUS DMA1
|
|
||||||
#define USBUSART_DMA_CLK RCC_DMA1
|
|
||||||
#define USBUSART_DMA_TX_CHAN DMA_STREAM3
|
|
||||||
#define USBUSART_DMA_TX_IRQ NVIC_DMA1_STREAM3_IRQ
|
|
||||||
#define USBUSART_DMA_TX_ISR(x) dma1_stream3_isr(x)
|
|
||||||
#define USBUSART_DMA_RX_CHAN DMA_STREAM1
|
|
||||||
#define USBUSART_DMA_RX_IRQ NVIC_DMA1_STREAM1_IRQ
|
|
||||||
#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
|
|
||||||
|
|
||||||
#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
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013 Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <libopencm3/cm3/systick.h>
|
|
||||||
#include <libopencm3/stm32/rcc.h>
|
|
||||||
#include <libopencm3/stm32/gpio.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
|||||||
CC ?= gcc
|
|
||||||
SYS = $(shell $(CC) -dumpmachine)
|
|
||||||
CFLAGS += -DENABLE_DEBUG -DPLATFORM_HAS_DEBUG
|
|
||||||
CFLAGS +=-I ./target -I./platforms/pc
|
|
||||||
|
|
||||||
# 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
|
|
||||||
# == 1 on Windows makes linking against the libftdi and libusb libraries
|
|
||||||
# unnecessary.
|
|
||||||
# This can be useful to minimize external dependencies, and make building on
|
|
||||||
# windows systems easier and is default now.
|
|
||||||
ifneq (, $(findstring linux, $(SYS)))
|
|
||||||
HOSTED_BMP_ONLY ?= 0
|
|
||||||
else
|
|
||||||
HOSTED_BMP_ONLY ?= 1
|
|
||||||
endif
|
|
||||||
CFLAGS += -DHOSTED_BMP_ONLY=$(HOSTED_BMP_ONLY)
|
|
||||||
|
|
||||||
ifneq (, $(findstring linux, $(SYS)))
|
|
||||||
SRC += serial_unix.c
|
|
||||||
HIDAPILIB = hidapi-hidraw
|
|
||||||
ifeq ($(ASAN), 1)
|
|
||||||
CFLAGS += -fsanitize=address -Wno-format-truncation
|
|
||||||
LDFLAGS += -lasan
|
|
||||||
endif
|
|
||||||
else ifneq (, $(findstring mingw, $(SYS)))
|
|
||||||
# Build for windows versions Vista, and above, where the
|
|
||||||
# 'SetupDiGetDevicePropertyW()' function is available
|
|
||||||
CFLAGS += -D_WIN32_WINNT=0x600
|
|
||||||
SRC += serial_win.c
|
|
||||||
LDFLAGS += -lws2_32
|
|
||||||
LDFLAGS += -lsetupapi
|
|
||||||
else ifneq (, $(findstring cygwin, $(SYS)))
|
|
||||||
# Build for windows versions Vista, and above, where the
|
|
||||||
# 'SetupDiGetDevicePropertyW()' function is available
|
|
||||||
CFLAGS += -D_WIN32_WINNT=0x600
|
|
||||||
SRC += serial_win.c
|
|
||||||
LDFLAGS += -lws2_32
|
|
||||||
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 += -framework CoreFoundation
|
|
||||||
CFLAGS += -Ihidapi/hidapi
|
|
||||||
HIDAPILIB = hidapi
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(HOSTED_BMP_ONLY), 1)
|
|
||||||
$(shell pkg-config --exists libftdi1)
|
|
||||||
ifneq ($(.SHELLSTATUS), 0)
|
|
||||||
$(error Please install libftdi1 dependency or set HOSTED_BMP_ONLY to 1)
|
|
||||||
endif
|
|
||||||
LDFLAGS += -lusb-1.0
|
|
||||||
CFLAGS += $(shell pkg-config --cflags libftdi1)
|
|
||||||
LDFLAGS += $(shell pkg-config --libs libftdi1)
|
|
||||||
CFLAGS += -Wno-missing-field-initializers
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq ($(HOSTED_BMP_ONLY), 1)
|
|
||||||
CFLAGS += -DCMSIS_DAP
|
|
||||||
SRC += cmsis_dap.c dap.c
|
|
||||||
ifneq (, $(findstring mingw, $(SYS)))
|
|
||||||
SRC += hid.c
|
|
||||||
else
|
|
||||||
$(shell pkg-config --exists $(HIDAPILIB))
|
|
||||||
ifneq ($(.SHELLSTATUS), 0)
|
|
||||||
$(error Please install $(HIDAPILIB) dependency or set HOSTED_BMP_ONLY to 1)
|
|
||||||
endif
|
|
||||||
CFLAGS += $(shell pkg-config --cflags $(HIDAPILIB))
|
|
||||||
LDFLAGS += $(shell pkg-config --libs $(HIDAPILIB))
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
VPATH += platforms/pc
|
|
||||||
SRC += timing.c cl_utils.c utils.c
|
|
||||||
SRC += bmp_remote.c remote_swdptap.c remote_jtagtap.c
|
|
||||||
ifneq ($(HOSTED_BMP_ONLY), 1)
|
|
||||||
SRC += bmp_libusb.c stlinkv2.c
|
|
||||||
SRC += ftdi_bmp.c libftdi_swdptap.c libftdi_jtagtap.c
|
|
||||||
SRC += jlink.c jlink_adiv5_swdp.c jlink_jtagtap.c
|
|
||||||
else
|
|
||||||
SRC += bmp_serial.c
|
|
||||||
endif
|
|
||||||
PC_HOSTED = 1
|
|
@ -1,177 +0,0 @@
|
|||||||
# PC-Hosted BMP
|
|
||||||
Compile in src with "make PROBE_HOST=hosted". This needs minimal external
|
|
||||||
support. "make PROBE_HOST=hosted HOSTED_BMP_ONLY=0" will compile support for FTDI,
|
|
||||||
STLink, CMSIS-DAP and JLINK probes, but requires external libraries.
|
|
||||||
|
|
||||||
## Description
|
|
||||||
PC-hosted BMP run on the PC and compiles as "blackmagic". When started,
|
|
||||||
it either presents a list of available probes or starts the BMP process
|
|
||||||
if either only one probe is attached to the PC or enough information is
|
|
||||||
given on the command line to select one of several probes.
|
|
||||||
|
|
||||||
When started without any other argument beside the probe selection, a
|
|
||||||
GDB server is started on port 2000 and up. Connect to the server as you would
|
|
||||||
connect to the BMP with the CDCACM GDB serial server. GDB functionality
|
|
||||||
is the same, monitor option may vary.
|
|
||||||
|
|
||||||
More arguments allow to
|
|
||||||
### Print information on the connected target
|
|
||||||
```
|
|
||||||
blackmagic -t
|
|
||||||
```
|
|
||||||
### Directly flash a binary file at lowest flash address
|
|
||||||
```
|
|
||||||
blackmagic <file.bin>
|
|
||||||
```
|
|
||||||
or with the -S argument at some other address
|
|
||||||
```
|
|
||||||
blackmagic -S 0x08002000 <file.bin>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read flash to binary file
|
|
||||||
```
|
|
||||||
blackmagic -r <file>.bin
|
|
||||||
```
|
|
||||||
### Verify flash against binary file
|
|
||||||
```
|
|
||||||
blackmagic -V <file>.bin
|
|
||||||
```
|
|
||||||
### Show more options
|
|
||||||
```
|
|
||||||
blackmagic -h
|
|
||||||
```
|
|
||||||
### Show available monitor commands
|
|
||||||
```
|
|
||||||
blackmagic -M help
|
|
||||||
```
|
|
||||||
### Show available monitor commands on second target
|
|
||||||
```
|
|
||||||
blackmagic -n 2 -M help
|
|
||||||
```
|
|
||||||
### Monitor commands with multiple arguments, e.g.Stm32F1:
|
|
||||||
```
|
|
||||||
blackmagic -M "option help"
|
|
||||||
```
|
|
||||||
## Used shared libraries:
|
|
||||||
### libusb
|
|
||||||
### libftdi, for FTDI support
|
|
||||||
|
|
||||||
## Other used libraries:
|
|
||||||
### hidapi-libusb, for CMSIS-DAP support
|
|
||||||
|
|
||||||
## Compiling on windows
|
|
||||||
|
|
||||||
You can crosscompile blackmagic for windows with mingw or on windows
|
|
||||||
with cygwin. For suppport of other probes beside BMP, headers for libftdi1 and
|
|
||||||
libusb-1.0 are needed. For running, libftdi1.dll and libusb-1.0.dll are needed
|
|
||||||
and the executable must be able to find them. Mingw on cygwin does not provide
|
|
||||||
a libftdi package yet.
|
|
||||||
|
|
||||||
PC-hosted BMP for windows can also be built with [MSYS2](https://www.msys2.org/),
|
|
||||||
in windows. Make sure to use the `mingw64` shell from msys2, otherwise,
|
|
||||||
you may get compilation errors. You will need to install the libusb
|
|
||||||
and libftdi libraries, and have the correct mingw compiler.
|
|
||||||
You can use these commands to install dependencies, and build PC-hosted BMP
|
|
||||||
from a mingw64 shell, from within the `src` directory:
|
|
||||||
```
|
|
||||||
pacman -S mingw-w64-x86_64-libusb --needed
|
|
||||||
pacman -S mingw-w64-x86_64-libftdi --needed
|
|
||||||
pacman -S mingw-w64-x86_64-gcc --needed
|
|
||||||
PROBE_HOST=hosted make
|
|
||||||
```
|
|
||||||
|
|
||||||
For suppport of other probes beside BMP, libusb access is needed. To prepare
|
|
||||||
libusb access to the ftdi/stlink/jlink/cmsis-dap devices, run zadig
|
|
||||||
https://zadig.akeo.ie/. Choose WinUSB(libusb-1.0).
|
|
||||||
|
|
||||||
Running cygwin/blackmagic in a cygwin console, the program does not react
|
|
||||||
on ^C. In another console, run "ps ax" to find the WINPID of the process
|
|
||||||
and then "taskkill /F ?PID (WINPID)".
|
|
||||||
|
|
||||||
## Supported debuggers
|
|
||||||
REMOTE_BMP is a "normal" BMP usb connected
|
|
||||||
|
|
||||||
| Debugger | Speed | Remarks
|
|
||||||
| ------------ | ----- | ------
|
|
||||||
| REMOTE_BMP | +++ | Requires recent firmware for decent speed
|
|
||||||
Probes below only when compiled with HOSTED_BMP_ONLY=0
|
|
||||||
| ST-Link V3 | ++++ | Requires recent firmware, Only STM32 devices supported!
|
|
||||||
| ST-Link V2 | +++ | Requires recent firmware, No CDCACM uart! Cortex only!
|
|
||||||
| ST-Link V2/1 | +++ | Requires recent firmware, Cortex only!
|
|
||||||
| CMSIS-DAP | +++ | Speed varies with MCU implementing CMSIS-DAP
|
|
||||||
| FTDI MPSSE | ++ | Requires a device descrition
|
|
||||||
| JLINK | - | Usefull to add BMP support for MCUs with built-in JLINK
|
|
||||||
|
|
||||||
## Device matching
|
|
||||||
As other USB dongles already connected to the host PC may use FTDI chips,
|
|
||||||
cable descriptions must be provided to match with the dongle.
|
|
||||||
To match the dongle, at least USB VID/PID that must match.
|
|
||||||
If a description is given, the USB device must provide that string. If a
|
|
||||||
serial number string is given on the command line, that number must match
|
|
||||||
with serial number in the USB descriptor of the device.
|
|
||||||
|
|
||||||
## FTDI connection possibilities:
|
|
||||||
|
|
||||||
| Direct Connection |
|
|
||||||
| ----------------------|
|
|
||||||
| MPSSE_SK --> JTAG_TCK |
|
|
||||||
| MPSSE_DO --> JTAG_TDI |
|
|
||||||
| MPSSE_DI <-- JTAG_TDO |
|
|
||||||
| MPSSE_CS <-> JTAG_TMS |
|
|
||||||
|
|
||||||
\+ JTAG and bitbanging SWD is possible<br>
|
|
||||||
\- No level translation, no buffering, no isolation<br>
|
|
||||||
Example: [Flossjtag](https://randomprojects.org/wiki/Floss-JTAG).
|
|
||||||
|
|
||||||
| Resistor SWD |
|
|
||||||
|------------------------|
|
|
||||||
| MPSSE_SK ---> JTAG_TCK |
|
|
||||||
| MPSSE_DO -R-> JTAG_TMS |
|
|
||||||
| MPSSE_DI <--- JTAG_TMS |
|
|
||||||
|
|
||||||
BMP would allow direct MPSSE_DO ->JTAG_TMS connections as BMP tristates DO
|
|
||||||
when reading. Resistor defeats contentions anyways. R is typical choosen
|
|
||||||
in the range of 470R
|
|
||||||
|
|
||||||
\+ MPSSE SWD possible<br>
|
|
||||||
\- No Jtag, no level translation, no buffering, no isolation<br>
|
|
||||||
|
|
||||||
|Direct buffered Connection|
|
|
||||||
|--------------------------|
|
|
||||||
| MPSSE_SK -B-> JTAG_TCK |
|
|
||||||
| MPSSE_DO -B-> JTAG_TDI |
|
|
||||||
| MPSSE_DI <-B- JTAG_TDO |
|
|
||||||
| MPSSE_CS -B-> JTAG_TMS |
|
|
||||||
|
|
||||||
\+ Only Jtag, buffered, possible level translation and isolation<br>
|
|
||||||
\- No SWD<br>
|
|
||||||
Example: [Turtelizer]http://www.ethernut.de/en/hardware/turtelizer/index.html)
|
|
||||||
[schematics](http://www.ethernut.de/pdf/turtelizer20c-schematic.pdf)
|
|
||||||
|
|
||||||
The 'r' command line arguments allows to specify an external SWD
|
|
||||||
resistor connection added to some existing cable. Jtag is not possible
|
|
||||||
together with the 'r' argument.
|
|
||||||
|
|
||||||
### Many variants possible
|
|
||||||
As the FTDI has more pins, these pins may be used to control
|
|
||||||
enables of buffers and multiplexer selection in many variants.
|
|
||||||
|
|
||||||
### FTDI SWD speed
|
|
||||||
SWD read needs two USB round trip, one for the acknowledge and one
|
|
||||||
round-trip after the data phase, while JTAG only needs one round-trip.
|
|
||||||
For that, SWD read speed is about half the JTAG read speed.
|
|
||||||
|
|
||||||
### Reset, Target voltage readback etc
|
|
||||||
The additional pins may also control Reset functionality, provide
|
|
||||||
information if target voltage is applied. etc.
|
|
||||||
|
|
||||||
### Cable descriptions
|
|
||||||
Please help to verify the cable description and give feedback on the
|
|
||||||
cables already listed and propose other cable. A link to the schematics
|
|
||||||
is welcome.
|
|
||||||
|
|
||||||
## Feedback
|
|
||||||
### 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
|
|
@ -1,52 +0,0 @@
|
|||||||
#if !defined(__BMP_LIBUSB_H)
|
|
||||||
#define __BMP_LIBUSB_H
|
|
||||||
|
|
||||||
#include "cl_utils.h"
|
|
||||||
|
|
||||||
#if HOSTED_BMP_ONLY != 1
|
|
||||||
# include <libusb-1.0/libusb.h>
|
|
||||||
struct trans_ctx {
|
|
||||||
#define TRANS_FLAGS_IS_DONE (1 << 0)
|
|
||||||
#define TRANS_FLAGS_HAS_ERROR (1 << 1)
|
|
||||||
volatile unsigned long flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct usb_link_s {
|
|
||||||
libusb_context *ul_libusb_ctx;
|
|
||||||
libusb_device_handle *ul_libusb_device_handle;
|
|
||||||
unsigned char ep_tx;
|
|
||||||
unsigned char ep_rx;
|
|
||||||
struct libusb_transfer* req_trans;
|
|
||||||
struct libusb_transfer* rep_trans;
|
|
||||||
void *priv;
|
|
||||||
} usb_link_t;
|
|
||||||
|
|
||||||
int send_recv(usb_link_t *link, uint8_t *txbuf, size_t txsize,
|
|
||||||
uint8_t *rxbuf, size_t rxsize);
|
|
||||||
#endif
|
|
||||||
typedef struct bmp_info_s {
|
|
||||||
bmp_type_t bmp_type;
|
|
||||||
char dev;
|
|
||||||
char serial[64];
|
|
||||||
char manufacturer[512];
|
|
||||||
char product[256];
|
|
||||||
char version[256];
|
|
||||||
bool is_jtag;
|
|
||||||
#if HOSTED_BMP_ONLY != 1
|
|
||||||
libusb_context *libusb_ctx;
|
|
||||||
struct ftdi_context *ftdic;
|
|
||||||
usb_link_t *usb_link;
|
|
||||||
unsigned int vid;
|
|
||||||
unsigned int pid;
|
|
||||||
uint8_t interface_num;
|
|
||||||
uint8_t in_ep;
|
|
||||||
uint8_t out_ep;
|
|
||||||
#endif
|
|
||||||
} bmp_info_t;
|
|
||||||
|
|
||||||
extern bmp_info_t info;
|
|
||||||
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);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,473 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright(C) 2020 - 2022 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Find all known usb connected debuggers */
|
|
||||||
#include "general.h"
|
|
||||||
#include "libusb-1.0/libusb.h"
|
|
||||||
#include "cl_utils.h"
|
|
||||||
#include "ftdi_bmp.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#define NO_SERIAL_NUMBER "<no serial number>"
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void libusb_exit_function(bmp_info_t *info)
|
|
||||||
{
|
|
||||||
if (!info->usb_link)
|
|
||||||
return;
|
|
||||||
libusb_free_transfer(info->usb_link->req_trans);
|
|
||||||
libusb_free_transfer(info->usb_link->rep_trans);
|
|
||||||
if (info->usb_link->ul_libusb_device_handle) {
|
|
||||||
libusb_release_interface (
|
|
||||||
info->usb_link->ul_libusb_device_handle, 0);
|
|
||||||
libusb_close(info->usb_link->ul_libusb_device_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bmp_type_t find_cmsis_dap_interface(libusb_device *dev,bmp_info_t *info) {
|
|
||||||
bmp_type_t type = BMP_TYPE_NONE;
|
|
||||||
|
|
||||||
struct libusb_config_descriptor *conf;
|
|
||||||
char interface_string[128];
|
|
||||||
|
|
||||||
int res = libusb_get_active_config_descriptor(dev, &conf);
|
|
||||||
if (res < 0) {
|
|
||||||
DEBUG_WARN( "WARN: libusb_get_active_config_descriptor() failed: %s",
|
|
||||||
libusb_strerror(res));
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
libusb_device_handle *handle;
|
|
||||||
res = libusb_open(dev, &handle);
|
|
||||||
if (res != LIBUSB_SUCCESS) {
|
|
||||||
DEBUG_INFO("INFO: libusb_open() failed: %s\n",
|
|
||||||
libusb_strerror(res));
|
|
||||||
libusb_free_config_descriptor(conf);
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < conf->bNumInterfaces; i++) {
|
|
||||||
const struct libusb_interface_descriptor *interface = &conf->interface[i].altsetting[0];
|
|
||||||
|
|
||||||
if (!interface->iInterface) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = libusb_get_string_descriptor_ascii(
|
|
||||||
handle, interface->iInterface, (uint8_t*)interface_string,
|
|
||||||
sizeof(interface_string));
|
|
||||||
if (res < 0) {
|
|
||||||
DEBUG_WARN( "WARN: libusb_get_string_descriptor_ascii() failed: %s\n",
|
|
||||||
libusb_strerror(res));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(interface_string, "CMSIS")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
type = BMP_TYPE_CMSIS_DAP;
|
|
||||||
|
|
||||||
if (interface->bInterfaceClass == 0xff && interface->bNumEndpoints == 2) {
|
|
||||||
info->interface_num = interface->bInterfaceNumber;
|
|
||||||
|
|
||||||
for (int j = 0; j < interface->bNumEndpoints; j++) {
|
|
||||||
uint8_t n = interface->endpoint[j].bEndpointAddress;
|
|
||||||
|
|
||||||
if (n & 0x80) {
|
|
||||||
info->in_ep = n;
|
|
||||||
} else {
|
|
||||||
info->out_ep = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* V2 is preferred, return early. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
libusb_free_config_descriptor(conf);
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|
||||||
{
|
|
||||||
libusb_device **devs;
|
|
||||||
int res = libusb_init(&info->libusb_ctx);
|
|
||||||
if (res) {
|
|
||||||
DEBUG_WARN( "Fatal: Failed to get USB context: %s\n",
|
|
||||||
libusb_strerror(res));
|
|
||||||
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;
|
|
||||||
DEBUG_WARN("Available cables:\n");
|
|
||||||
for (; cable->name; ++cable) {
|
|
||||||
DEBUG_WARN("\t%s\n", cable->name);
|
|
||||||
}
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
info->bmp_type = BMP_TYPE_LIBFTDI;
|
|
||||||
}
|
|
||||||
int n_devs = libusb_get_device_list(info->libusb_ctx, &devs);
|
|
||||||
if (n_devs < 0) {
|
|
||||||
DEBUG_WARN( "WARN:libusb_get_device_list() failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
bool report = false;
|
|
||||||
int found_debuggers;
|
|
||||||
struct libusb_device_descriptor desc;
|
|
||||||
char serial[64];
|
|
||||||
char manufacturer[128];
|
|
||||||
char product[128];
|
|
||||||
bool access_problems = false;
|
|
||||||
char *active_cable = NULL;
|
|
||||||
bool ftdi_unknown = false;
|
|
||||||
rescan:
|
|
||||||
found_debuggers = 0;
|
|
||||||
serial[0] = 0;
|
|
||||||
manufacturer[0] = 0;
|
|
||||||
product[0] = 0;
|
|
||||||
access_problems = false;
|
|
||||||
active_cable = NULL;
|
|
||||||
ftdi_unknown = false;
|
|
||||||
for (size_t i = 0; devs[i]; ++i) {
|
|
||||||
bmp_type_t type = BMP_TYPE_NONE;
|
|
||||||
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",
|
|
||||||
libusb_strerror(res));
|
|
||||||
libusb_free_device_list(devs, 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* Exclude hubs from testing. Probably more classes could be excluded here!*/
|
|
||||||
switch (desc.bDeviceClass) {
|
|
||||||
case LIBUSB_CLASS_HUB:
|
|
||||||
case LIBUSB_CLASS_WIRELESS:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libusb_device_handle *handle = NULL;
|
|
||||||
res = libusb_open(dev, &handle);
|
|
||||||
if (res != LIBUSB_SUCCESS) {
|
|
||||||
if (!access_problems) {
|
|
||||||
DEBUG_INFO("INFO: Open USB %04x:%04x class %2x failed\n",
|
|
||||||
desc.idVendor, desc.idProduct, desc.bDeviceClass);
|
|
||||||
access_problems = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
/* 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_product = strstr(product, cl_opts->opt_ident_string);
|
|
||||||
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)
|
|
||||||
type = BMP_TYPE_BMP;
|
|
||||||
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) {
|
|
||||||
/* find_cmsis_dap_interface has set valid type*/
|
|
||||||
} 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)
|
|
||||||
type = BMP_TYPE_STLINKV2;
|
|
||||||
else {
|
|
||||||
if (desc.idProduct == PRODUCT_ID_STLINKV1)
|
|
||||||
DEBUG_WARN( "INFO: STLINKV1 not supported\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (desc.idVendor == VENDOR_ID_SEGGER)
|
|
||||||
type = BMP_TYPE_JLINK;
|
|
||||||
else {
|
|
||||||
cable_desc_t *cable = cable_desc;
|
|
||||||
for (; cable->name; ++cable) {
|
|
||||||
bool found = false;
|
|
||||||
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)))
|
|
||||||
continue; /* cable names do not match*/
|
|
||||||
else
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
if (cable->description) {
|
|
||||||
if (strncmp(cable->description, product, strlen(cable->description)))
|
|
||||||
continue; /* discriptions do not match*/
|
|
||||||
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 */
|
|
||||||
ftdi_unknown = true;
|
|
||||||
continue; /* Cable name is needed */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
active_cable = cable->name;
|
|
||||||
type = BMP_TYPE_LIBFTDI;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cable->name)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (report) {
|
|
||||||
DEBUG_WARN("%2d: %s, %s, %s\n", found_debuggers + 1,
|
|
||||||
serial[0] ? serial : NO_SERIAL_NUMBER,
|
|
||||||
manufacturer,product);
|
|
||||||
}
|
|
||||||
info->vid = desc.idVendor;
|
|
||||||
info->pid = desc.idProduct;
|
|
||||||
info->bmp_type = type;
|
|
||||||
strncpy(info->serial, serial, sizeof(info->serial));
|
|
||||||
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) {
|
|
||||||
found_debuggers = 1;
|
|
||||||
break;
|
|
||||||
} else
|
|
||||||
++found_debuggers;
|
|
||||||
}
|
|
||||||
if (found_debuggers == 0 && ftdi_unknown)
|
|
||||||
DEBUG_WARN("Generic FTDI MPSSE VID/PID found. Please specify exact type with \"-c <cable>\" !\n");
|
|
||||||
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 (!report) {
|
|
||||||
if (found_debuggers > 1)
|
|
||||||
DEBUG_WARN("%d debuggers found!\nSelect with -P <pos> "
|
|
||||||
"or -s <(partial)serial no.>\n",
|
|
||||||
found_debuggers);
|
|
||||||
report = true;
|
|
||||||
goto rescan;
|
|
||||||
} else {
|
|
||||||
if (found_debuggers > 0)
|
|
||||||
access_problems = false;
|
|
||||||
found_debuggers = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found_debuggers && access_problems)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LIBUSB_CALL on_trans_done(struct libusb_transfer *trans)
|
|
||||||
{
|
|
||||||
struct trans_ctx * const ctx = trans->user_data;
|
|
||||||
|
|
||||||
if (trans->status != LIBUSB_TRANSFER_COMPLETED)
|
|
||||||
{
|
|
||||||
DEBUG_WARN("on_trans_done: ");
|
|
||||||
if (trans->status == LIBUSB_TRANSFER_TIMED_OUT)
|
|
||||||
DEBUG_WARN(" Timeout\n");
|
|
||||||
else if (trans->status == LIBUSB_TRANSFER_CANCELLED)
|
|
||||||
DEBUG_WARN(" cancelled\n");
|
|
||||||
else if (trans->status == LIBUSB_TRANSFER_NO_DEVICE)
|
|
||||||
DEBUG_WARN(" no device\n");
|
|
||||||
else
|
|
||||||
DEBUG_WARN(" unknown\n");
|
|
||||||
ctx->flags |= TRANS_FLAGS_HAS_ERROR;
|
|
||||||
}
|
|
||||||
ctx->flags |= TRANS_FLAGS_IS_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int submit_wait(usb_link_t *link, struct libusb_transfer *trans) {
|
|
||||||
struct trans_ctx trans_ctx;
|
|
||||||
enum libusb_error error;
|
|
||||||
|
|
||||||
trans_ctx.flags = 0;
|
|
||||||
|
|
||||||
/* brief intrusion inside the libusb interface */
|
|
||||||
trans->callback = on_trans_done;
|
|
||||||
trans->user_data = &trans_ctx;
|
|
||||||
|
|
||||||
if ((error = libusb_submit_transfer(trans))) {
|
|
||||||
DEBUG_WARN("libusb_submit_transfer(%d): %s\n", error,
|
|
||||||
libusb_strerror(error));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_time = platform_time_ms();
|
|
||||||
while (trans_ctx.flags == 0) {
|
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = 1;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
if (libusb_handle_events_timeout(link->ul_libusb_ctx, &timeout)) {
|
|
||||||
DEBUG_WARN("libusb_handle_events()\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
uint32_t now = platform_time_ms();
|
|
||||||
if (now - start_time > 1000) {
|
|
||||||
libusb_cancel_transfer(trans);
|
|
||||||
DEBUG_WARN("libusb_handle_events() timeout\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (trans_ctx.flags & TRANS_FLAGS_HAS_ERROR) {
|
|
||||||
DEBUG_WARN("libusb_handle_events() | has_error\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* One USB transaction */
|
|
||||||
int send_recv(usb_link_t *link,
|
|
||||||
uint8_t *txbuf, size_t txsize,
|
|
||||||
uint8_t *rxbuf, size_t rxsize)
|
|
||||||
{
|
|
||||||
int res = 0;
|
|
||||||
if (txsize) {
|
|
||||||
libusb_fill_bulk_transfer(link->req_trans,
|
|
||||||
link->ul_libusb_device_handle,
|
|
||||||
link->ep_tx | LIBUSB_ENDPOINT_OUT,
|
|
||||||
txbuf, txsize,
|
|
||||||
NULL, NULL, 0);
|
|
||||||
size_t i = 0;
|
|
||||||
DEBUG_WIRE(" Send (%3zu): ", txsize);
|
|
||||||
for (; i < txsize; ++i) {
|
|
||||||
DEBUG_WIRE("%02x", txbuf[i]);
|
|
||||||
if ((i & 7U) == 7U)
|
|
||||||
DEBUG_WIRE(".");
|
|
||||||
if ((i & 31U) == 31U)
|
|
||||||
DEBUG_WIRE("\n ");
|
|
||||||
}
|
|
||||||
if (!(i & 31U))
|
|
||||||
DEBUG_WIRE("\n");
|
|
||||||
if (submit_wait(link, link->req_trans)) {
|
|
||||||
libusb_clear_halt(link->ul_libusb_device_handle, link->ep_tx);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* send_only */
|
|
||||||
if (rxsize != 0) {
|
|
||||||
/* read the response */
|
|
||||||
libusb_fill_bulk_transfer(link->rep_trans, link->ul_libusb_device_handle,
|
|
||||||
link->ep_rx | LIBUSB_ENDPOINT_IN,
|
|
||||||
rxbuf, rxsize, NULL, NULL, 0);
|
|
||||||
|
|
||||||
if (submit_wait(link, link->rep_trans)) {
|
|
||||||
DEBUG_WARN("clear 1\n");
|
|
||||||
libusb_clear_halt(link->ul_libusb_device_handle, link->ep_rx);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
res = link->rep_trans->actual_length;
|
|
||||||
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", rxbuf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG_WIRE("\n");
|
|
||||||
return res;
|
|
||||||
}
|
|
@ -1,404 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
||||||
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
* Additions by Dave Marples <dave@marples.net>
|
|
||||||
* Modifications (C) 2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include "general.h"
|
|
||||||
#include "gdb_if.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "remote.h"
|
|
||||||
#include "target.h"
|
|
||||||
#include "bmp_remote.h"
|
|
||||||
#include "cl_utils.h"
|
|
||||||
#include "hex_utils.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "adiv5.h"
|
|
||||||
|
|
||||||
int remote_init(void)
|
|
||||||
{
|
|
||||||
char construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int c = snprintf(construct, REMOTE_MAX_MSG_SIZE, "%s", REMOTE_START_STR);
|
|
||||||
platform_buffer_write((uint8_t *)construct, c);
|
|
||||||
c = platform_buffer_read((uint8_t *)construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((c < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("Remote Start failed, error %s\n",
|
|
||||||
c ? (char *)&(construct[1]) : "unknown");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
DEBUG_PROBE("Remote is %s\n", &construct[1]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remote_target_get_power(void)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s=snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, "%s",
|
|
||||||
REMOTE_PWR_GET_STR);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN(" platform_target_get_power failed, error %s\n",
|
|
||||||
s ? (char *)&(construct[1]) : "unknown");
|
|
||||||
exit (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (construct[1] == '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remote_target_set_power(bool power)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,REMOTE_PWR_SET_STR,
|
|
||||||
power ? '1' : '0');
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("platform_target_set_power failed, error %s\n",
|
|
||||||
s ? (char *)&(construct[1]) : "unknown");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remote_srst_set_val(bool assert)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, REMOTE_SRST_SET_STR,
|
|
||||||
assert ? '1' : '0');
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("platform_srst_set_val failed, error %s\n",
|
|
||||||
s ? (char *)&(construct[1]) : "unknown");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remote_srst_get_val(void)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,"%s",
|
|
||||||
REMOTE_SRST_GET_STR);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("platform_srst_set_val failed, error %s\n",
|
|
||||||
s ? (char *)&(construct[1]) : "unknown");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
return (construct[1] == '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
void remote_max_frequency_set(uint32_t freq)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, REMOTE_FREQ_SET_STR,
|
|
||||||
freq);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("Update Firmware to allow to set max SWJ frequency\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t remote_max_frequency_get(void)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,"%s",
|
|
||||||
REMOTE_FREQ_GET_STR);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR))
|
|
||||||
return FREQ_FIXED;
|
|
||||||
|
|
||||||
uint32_t freq[1];
|
|
||||||
unhexify(freq, (const char*)&construct[1], 4);
|
|
||||||
return freq[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *remote_target_voltage(void)
|
|
||||||
{
|
|
||||||
static uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE," %s",
|
|
||||||
REMOTE_VOLTAGE_STR);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("platform_target_voltage failed, error %s\n",
|
|
||||||
s ? (char *)&(construct[1]) : "unknown");
|
|
||||||
exit(- 1);
|
|
||||||
}
|
|
||||||
return (char *)&construct[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t remote_adiv5_dp_read(ADIv5_DP_t *dp, uint16_t addr)
|
|
||||||
{
|
|
||||||
(void)dp;
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, REMOTE_DP_READ_STR,
|
|
||||||
dp->dp_jd_index, addr);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("%s error %d\n", __func__, s);
|
|
||||||
}
|
|
||||||
uint32_t dest[1];
|
|
||||||
unhexify(dest, (const char*)&construct[1], 4);
|
|
||||||
DEBUG_PROBE("dp_read addr %04x: %08" PRIx32 "\n", dest[0]);
|
|
||||||
return dest[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t remote_adiv5_low_access(
|
|
||||||
ADIv5_DP_t *dp, uint8_t RnW, uint16_t addr, uint32_t value)
|
|
||||||
{
|
|
||||||
(void)dp;
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,
|
|
||||||
REMOTE_LOW_ACCESS_STR, dp->dp_jd_index, RnW, addr, value);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("%s error %d\n", __func__, s);
|
|
||||||
}
|
|
||||||
uint32_t dest[1];
|
|
||||||
unhexify(dest, (const char*)&construct[1], 4);
|
|
||||||
return dest[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t remote_adiv5_ap_read(ADIv5_AP_t *ap, uint16_t addr)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,REMOTE_AP_READ_STR,
|
|
||||||
ap->dp->dp_jd_index, ap->apsel, addr);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("%s error %d\n", __func__, s);
|
|
||||||
}
|
|
||||||
uint32_t dest[1];
|
|
||||||
unhexify(dest, (const char*)&construct[1], 4);
|
|
||||||
return dest[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remote_adiv5_ap_write(ADIv5_AP_t *ap, uint16_t addr, uint32_t value)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,REMOTE_AP_WRITE_STR,
|
|
||||||
ap->dp->dp_jd_index, ap->apsel, addr, value);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
DEBUG_WARN("%s error %d\n", __func__, s);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void remote_mem_read(
|
|
||||||
ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len)
|
|
||||||
{
|
|
||||||
(void)ap;
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
DEBUG_WIRE("memread @ %" PRIx32 " len %ld, start: \n",
|
|
||||||
src, len);
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s;
|
|
||||||
int batchsize = (REMOTE_MAX_MSG_SIZE - 32) / 2;
|
|
||||||
while(len) {
|
|
||||||
int count = len;
|
|
||||||
if (count > batchsize)
|
|
||||||
count = batchsize;
|
|
||||||
s = snprintf(construct, REMOTE_MAX_MSG_SIZE,
|
|
||||||
REMOTE_MEM_READ_STR, src, count);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s > 0) && (construct[0] == REMOTE_RESP_OK)) {
|
|
||||||
unhexify(dest, (const char*)&construct[1], count);
|
|
||||||
src += count;
|
|
||||||
dest += count;
|
|
||||||
len -= count;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if(construct[0] == REMOTE_RESP_ERR) {
|
|
||||||
ap->dp->fault = 1;
|
|
||||||
DEBUG_WARN("%s returned REMOTE_RESP_ERR at addr: 0x%08x\n",
|
|
||||||
__func__, src);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
DEBUG_WARN("%s error %d\n", __func__, s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void remote_ap_mem_read(
|
|
||||||
ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len)
|
|
||||||
{
|
|
||||||
(void)ap;
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
char construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int batchsize = (REMOTE_MAX_MSG_SIZE - 0x20) / 2;
|
|
||||||
while(len) {
|
|
||||||
int s;
|
|
||||||
int count = len;
|
|
||||||
if (count > batchsize)
|
|
||||||
count = batchsize;
|
|
||||||
s = snprintf(construct, REMOTE_MAX_MSG_SIZE,
|
|
||||||
REMOTE_AP_MEM_READ_STR, ap->dp->dp_jd_index, ap->apsel, ap->csw, src, count);
|
|
||||||
platform_buffer_write((uint8_t*)construct, s);
|
|
||||||
s = platform_buffer_read((uint8_t*)construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s > 0) && (construct[0] == REMOTE_RESP_OK)) {
|
|
||||||
unhexify(dest, (const char*)&construct[1], count);
|
|
||||||
src += count;
|
|
||||||
dest += count;
|
|
||||||
len -= count;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if(construct[0] == REMOTE_RESP_ERR) {
|
|
||||||
ap->dp->fault = 1;
|
|
||||||
DEBUG_WARN("%s returned REMOTE_RESP_ERR at apsel %d, "
|
|
||||||
"addr: 0x%08" PRIx32 "\n", __func__, ap->apsel, src);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
DEBUG_WARN("%s error %d around 0x%08" PRIx32 "\n",
|
|
||||||
__func__, s, src);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remote_ap_mem_write_sized(
|
|
||||||
ADIv5_AP_t *ap, uint32_t dest, const void *src, size_t len,
|
|
||||||
enum align align)
|
|
||||||
{
|
|
||||||
(void)ap;
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
char construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
/* (5 * 1 (char)) + (2 * 2 (bytes)) + (3 * 8 (words)) */
|
|
||||||
int batchsize = (REMOTE_MAX_MSG_SIZE - 0x30) / 2;
|
|
||||||
while (len) {
|
|
||||||
int count = len;
|
|
||||||
if (count > batchsize)
|
|
||||||
count = batchsize;
|
|
||||||
int s = snprintf(construct, REMOTE_MAX_MSG_SIZE,
|
|
||||||
REMOTE_AP_MEM_WRITE_SIZED_STR,
|
|
||||||
ap->dp->dp_jd_index, ap->apsel, ap->csw, align, dest, count);
|
|
||||||
char *p = construct + s;
|
|
||||||
hexify(p, src, count);
|
|
||||||
p += 2 * count;
|
|
||||||
src += count;
|
|
||||||
dest += count;
|
|
||||||
len -= count;
|
|
||||||
*p++ = REMOTE_EOM;
|
|
||||||
*p = 0;
|
|
||||||
platform_buffer_write((uint8_t*)construct, p - construct);
|
|
||||||
|
|
||||||
s = platform_buffer_read((uint8_t*)construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s > 0) && (construct[0] == REMOTE_RESP_OK))
|
|
||||||
continue;
|
|
||||||
if ((s > 0) && (construct[0] == REMOTE_RESP_ERR)) {
|
|
||||||
ap->dp->fault = 1;
|
|
||||||
DEBUG_WARN("%s returned REMOTE_RESP_ERR at apsel %d, "
|
|
||||||
"addr: 0x%08x\n", __func__, ap->apsel, dest);
|
|
||||||
} else {
|
|
||||||
DEBUG_WARN("%s error %d around address 0x%08" PRIx32 "\n",
|
|
||||||
__func__, s, dest);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remote_adiv5_dp_defaults(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE, "%s",
|
|
||||||
REMOTE_HL_CHECK_STR);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
if ((s < 1) || (construct[0] == REMOTE_RESP_ERR) ||
|
|
||||||
((construct[1] - '0') < REMOTE_HL_VERSION)) {
|
|
||||||
DEBUG_WARN(
|
|
||||||
"Please update BMP firmware for substantial speed increase!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dp->low_access = remote_adiv5_low_access;
|
|
||||||
dp->dp_read = remote_adiv5_dp_read;
|
|
||||||
dp->ap_write = remote_adiv5_ap_write;
|
|
||||||
dp->ap_read = remote_adiv5_ap_read;
|
|
||||||
dp->mem_read = remote_ap_mem_read;
|
|
||||||
dp->mem_write_sized = remote_ap_mem_write_sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remote_add_jtag_dev(int i, const jtag_dev_t *jtag_dev)
|
|
||||||
{
|
|
||||||
uint8_t construct[REMOTE_MAX_MSG_SIZE];
|
|
||||||
int s = snprintf((char *)construct, REMOTE_MAX_MSG_SIZE,
|
|
||||||
REMOTE_JTAG_ADD_DEV_STR,
|
|
||||||
i,
|
|
||||||
jtag_dev->dr_prescan,
|
|
||||||
jtag_dev->dr_postscan,
|
|
||||||
jtag_dev->ir_len,
|
|
||||||
jtag_dev->ir_prescan,
|
|
||||||
jtag_dev->ir_postscan,
|
|
||||||
jtag_dev->current_ir);
|
|
||||||
platform_buffer_write(construct, s);
|
|
||||||
s = platform_buffer_read(construct, REMOTE_MAX_MSG_SIZE);
|
|
||||||
/* No check for error here. Done in remote_adiv5_dp_defaults!*/
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Uwe Bonnes
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#if !defined(__BMP_REMOTE_H_)
|
|
||||||
#define __BMP_REMOTE_H_
|
|
||||||
#include "jtagtap.h"
|
|
||||||
#include "adiv5.h"
|
|
||||||
#include "target.h"
|
|
||||||
#include "target_internal.h"
|
|
||||||
|
|
||||||
#define REMOTE_MAX_MSG_SIZE (1024)
|
|
||||||
|
|
||||||
int platform_buffer_write(const uint8_t *data, int size);
|
|
||||||
int platform_buffer_read(uint8_t *data, int size);
|
|
||||||
|
|
||||||
int remote_init(void);
|
|
||||||
int remote_swdptap_init(ADIv5_DP_t *dp);
|
|
||||||
int remote_jtagtap_init(jtag_proc_t *jtag_proc);
|
|
||||||
bool remote_target_get_power(void);
|
|
||||||
const char *remote_target_voltage(void);
|
|
||||||
bool remote_target_set_power(bool power);
|
|
||||||
void remote_srst_set_val(bool assert);
|
|
||||||
bool remote_srst_get_val(void);
|
|
||||||
void remote_max_frequency_set(uint32_t freq);
|
|
||||||
uint32_t remote_max_frequency_get(void);
|
|
||||||
const char *platform_target_voltage(void);
|
|
||||||
void remote_adiv5_dp_defaults(ADIv5_DP_t *dp);
|
|
||||||
void remote_add_jtag_dev(int i, const jtag_dev_t *jtag_dev);
|
|
||||||
#define __BMP_REMOTE_H_
|
|
||||||
#endif
|
|
@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Find all known serial connected debuggers */
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "bmp_hosted.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
void bmp_ident(bmp_info_t *info)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void libusb_exit_function(bmp_info_t *info) {(void)info;};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|
||||||
{
|
|
||||||
DEBUG_WARN("Please implement find_debuggers for MACOS!\n");
|
|
||||||
(void)cl_opts;
|
|
||||||
(void)info;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#elif defined(__WIN32__) || defined(__CYGWIN__)
|
|
||||||
|
|
||||||
|
|
||||||
/* This source has been used as an example:
|
|
||||||
* https://stackoverflow.com/questions/3438366/setupdigetdeviceproperty-usage-example */
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <setupapi.h>
|
|
||||||
#include <cfgmgr32.h> // for MAX_DEVICE_ID_LEN, CM_Get_Parent and CM_Get_Device_ID
|
|
||||||
#include <tchar.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpropdef.h */
|
|
||||||
#ifdef DEFINE_DEVPROPKEY
|
|
||||||
#undef DEFINE_DEVPROPKEY
|
|
||||||
#endif
|
|
||||||
#define DEFINE_DEVPROPKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) const DEVPROPKEY DECLSPEC_SELECTANY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid }
|
|
||||||
|
|
||||||
/* include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpkey.h */
|
|
||||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_BusReportedDeviceDesc, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4); // DEVPROP_TYPE_STRING
|
|
||||||
|
|
||||||
/* List all USB devices with some additional information.
|
|
||||||
* Unfortunately, this code is quite ugly. */
|
|
||||||
int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
DWORD dwSize;
|
|
||||||
DEVPROPTYPE ulPropertyType;
|
|
||||||
CONFIGRET status;
|
|
||||||
HDEVINFO hDevInfo;
|
|
||||||
SP_DEVINFO_DATA DeviceInfoData;
|
|
||||||
TCHAR szDeviceInstanceID [MAX_DEVICE_ID_LEN];
|
|
||||||
WCHAR busReportedDeviceSesc[4096];
|
|
||||||
int probes_found = 0;
|
|
||||||
bool is_printing_probes_info = cl_opts->opt_list_only != 0;
|
|
||||||
|
|
||||||
info->bmp_type = BMP_TYPE_BMP;
|
|
||||||
|
|
||||||
hDevInfo = SetupDiGetClassDevs (0, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
|
|
||||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
|
||||||
return -1;
|
|
||||||
print_probes_info:
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
char serial_number[sizeof info->serial];
|
|
||||||
DeviceInfoData.cbSize = sizeof (DeviceInfoData);
|
|
||||||
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData))
|
|
||||||
break;
|
|
||||||
|
|
||||||
status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID , MAX_PATH, 0);
|
|
||||||
if (status != CR_SUCCESS)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!sscanf(szDeviceInstanceID, "USB\\VID_1D50&PID_6018\\%s", serial_number))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
|
|
||||||
&ulPropertyType, (BYTE*)busReportedDeviceSesc, sizeof busReportedDeviceSesc, &dwSize, 0))
|
|
||||||
{
|
|
||||||
probes_found ++;
|
|
||||||
if (is_printing_probes_info)
|
|
||||||
{
|
|
||||||
DEBUG_WARN("%2d: %s, %ls\n", probes_found,
|
|
||||||
serial_number, busReportedDeviceSesc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool probe_identified = true;
|
|
||||||
if ((cl_opts->opt_serial && strstr(serial_number, cl_opts->opt_serial)) ||
|
|
||||||
(cl_opts->opt_position && cl_opts->opt_position == probes_found) ||
|
|
||||||
/* Special case for the very first probe found. */
|
|
||||||
(probe_identified = false, probes_found == 1)) {
|
|
||||||
|
|
||||||
strncpy(info->serial, serial_number, sizeof info->serial);
|
|
||||||
strncpy(info->manufacturer, "BMP", sizeof info->manufacturer);
|
|
||||||
snprintf(info->product, sizeof info->product, "%ls", busReportedDeviceSesc);
|
|
||||||
/* Don't bother to parse the version string. It is a part of the
|
|
||||||
* product description string. It seems that at the moment it
|
|
||||||
* is only being used to print a version string in response
|
|
||||||
* to the 'monitor version' command, so it doesn't really matter
|
|
||||||
* if the version string is printed as a part of the product string,
|
|
||||||
* or as a separate string, the result is pretty much the same. */
|
|
||||||
info->version[0] = 0;
|
|
||||||
if (probe_identified)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_printing_probes_info)
|
|
||||||
return 1;
|
|
||||||
if (probes_found == 1)
|
|
||||||
/* Exactly one probe found. Its information has already been filled
|
|
||||||
* in the detection loop, so use this probe. */
|
|
||||||
return 0;
|
|
||||||
if (probes_found < 1) {
|
|
||||||
DEBUG_WARN("No BMP probe found\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Otherwise, if this line is reached, then more than one probe has been found,
|
|
||||||
* and no probe was identified as selected by the user.
|
|
||||||
* Restart the identification loop, this time printing the probe information,
|
|
||||||
* and then return. */
|
|
||||||
DEBUG_WARN("%d debuggers found!\nSelect with -P <pos>, or "
|
|
||||||
"-s <(partial)serial no.>\n",
|
|
||||||
probes_found);
|
|
||||||
probes_found = 0;
|
|
||||||
is_printing_probes_info = true;
|
|
||||||
goto print_probes_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
/* Old ID: Black_Sphere_Technologies_Black_Magic_Probe_BFE4D6EC-if00
|
|
||||||
* 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_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/"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extract type, version and serial from /dev/serial/by_id
|
|
||||||
* Return 0 on success
|
|
||||||
*
|
|
||||||
* Old versions have different strings. Try to cope!
|
|
||||||
*/
|
|
||||||
static int scan_linux_id(char *name, char *type, char *version, char *serial)
|
|
||||||
{
|
|
||||||
name += strlen(BMP_IDSTRING_BLACKSPHERE) + 1;
|
|
||||||
while (*name == '_')
|
|
||||||
name++;
|
|
||||||
if (!*name) {
|
|
||||||
DEBUG_WARN("Unexpected end\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
char *p = name;
|
|
||||||
char *delims[4] = {0,0,0,0};
|
|
||||||
int underscores = 0;
|
|
||||||
while (*p) {
|
|
||||||
if (*p == '_') {
|
|
||||||
while (p[1] == '_')
|
|
||||||
p++; /* remove multiple underscores */
|
|
||||||
if (underscores > 2)
|
|
||||||
return -1;
|
|
||||||
delims[underscores] = p;
|
|
||||||
underscores ++;
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
if (underscores == 0) { /* Old BMP native */
|
|
||||||
int res;
|
|
||||||
res = sscanf(name, "%8s-if00", serial);
|
|
||||||
if (res != 1)
|
|
||||||
return -1;
|
|
||||||
strcpy(type, "Native");
|
|
||||||
strcpy(version, "Unknown");
|
|
||||||
} else if (underscores == 2) {
|
|
||||||
strncpy(type, name, delims[0] - name - 1);
|
|
||||||
strncpy(version, delims[0] + 1, delims[1] - delims[0] - 1);
|
|
||||||
int res = sscanf(delims[1] + 1, "%8s-if00", serial);
|
|
||||||
if (!res)
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
int res = sscanf(delims[0] + 1, "%8s-if00", serial);
|
|
||||||
if (!res)
|
|
||||||
return -1;
|
|
||||||
if (name[0] == 'v') {
|
|
||||||
strcpy(type, "Unknown");
|
|
||||||
strncpy(version, name, delims[0] - name - 1);
|
|
||||||
} else {
|
|
||||||
strncpy(type, name, delims[0] - name);
|
|
||||||
strcpy(type, "Unknown");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|
||||||
{
|
|
||||||
if (cl_opts->opt_device)
|
|
||||||
return 1;
|
|
||||||
info->bmp_type = BMP_TYPE_BMP;
|
|
||||||
DIR *dir = opendir(DEVICE_BY_ID);
|
|
||||||
if (!dir) /* No serial device connected!*/
|
|
||||||
return 0;
|
|
||||||
int found_bmps = 0;
|
|
||||||
struct dirent *dp;
|
|
||||||
int i = 0;
|
|
||||||
while ((dp = readdir(dir)) != NULL) {
|
|
||||||
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];
|
|
||||||
if (scan_linux_id(dp->d_name, type, version, serial)) {
|
|
||||||
DEBUG_WARN("Unexpected device name found \"%s\"\n",
|
|
||||||
dp->d_name);
|
|
||||||
}
|
|
||||||
if ((cl_opts->opt_serial && strstr(serial, cl_opts->opt_serial)) ||
|
|
||||||
(cl_opts->opt_position && cl_opts->opt_position == i)) {
|
|
||||||
/* With serial number given and partial match, we are done!*/
|
|
||||||
strncpy(info->serial, serial, sizeof(info->serial));
|
|
||||||
int res = snprintf(info->manufacturer, sizeof(info->manufacturer), "Black Magic Probe (%s)", type);
|
|
||||||
if (res)
|
|
||||||
DEBUG_WARN("Overflow\n");
|
|
||||||
strncpy(info->version, version, sizeof(info->version));
|
|
||||||
found_bmps = 1;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
found_bmps++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
if (found_bmps < 1) {
|
|
||||||
DEBUG_WARN("No BMP probe found\n");
|
|
||||||
return -1;
|
|
||||||
} else if ((found_bmps > 1) || cl_opts->opt_list_only) {
|
|
||||||
DEBUG_WARN("Available Probes:\n");
|
|
||||||
}
|
|
||||||
dir = opendir(DEVICE_BY_ID);
|
|
||||||
i = 0;
|
|
||||||
while ((dp = readdir(dir)) != NULL) {
|
|
||||||
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];
|
|
||||||
if (scan_linux_id(dp->d_name, type, version, serial)) {
|
|
||||||
DEBUG_WARN("Unexpected device name found \"%s\"\n",
|
|
||||||
dp->d_name);
|
|
||||||
} else if ((found_bmps == 1) && (!cl_opts->opt_list_only)) {
|
|
||||||
strncpy(info->serial, serial, sizeof(info->serial));
|
|
||||||
found_bmps = 1;
|
|
||||||
strncpy(info->serial, serial, sizeof(info->serial));
|
|
||||||
snprintf(info->manufacturer, sizeof(info->manufacturer), "Black Magic Probe (%s)", type);
|
|
||||||
strncpy(info->version, version, sizeof(info->version));
|
|
||||||
break;
|
|
||||||
} else if (found_bmps > 0) {
|
|
||||||
DEBUG_WARN("%2d: %s, Black Magic Debug, Black Magic "
|
|
||||||
"Probe (%s), %s\n", i, serial, type, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
return (found_bmps == 1 && !cl_opts->opt_list_only) ? 0 : 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,470 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019-2021 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/* Modified from edbg.c
|
|
||||||
* Links between bmp and edbg
|
|
||||||
*
|
|
||||||
* https://arm-software.github.io/CMSIS_5/DAP/html/index.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "gdb_if.h"
|
|
||||||
#include "adiv5.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <hidapi.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
|
|
||||||
#include "bmp_hosted.h"
|
|
||||||
#include "dap.h"
|
|
||||||
#include "cmsis_dap.h"
|
|
||||||
|
|
||||||
#include "cl_utils.h"
|
|
||||||
#include "target.h"
|
|
||||||
#include "target_internal.h"
|
|
||||||
|
|
||||||
uint8_t dap_caps;
|
|
||||||
uint8_t mode;
|
|
||||||
|
|
||||||
typedef enum cmsis_type_s {
|
|
||||||
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;
|
|
||||||
static uint8_t in_ep;
|
|
||||||
static uint8_t out_ep;
|
|
||||||
static hid_device *handle = NULL;
|
|
||||||
static uint8_t buffer[1024 + 1];
|
|
||||||
static int report_size = 64 + 1; // TODO: read actual report size
|
|
||||||
static bool has_swd_sequence = false;
|
|
||||||
|
|
||||||
/* LPC845 Breakout Board Rev. 0 report invalid response with > 65 bytes */
|
|
||||||
int dap_init(bmp_info_t *info)
|
|
||||||
{
|
|
||||||
type = (info->in_ep && info->out_ep) ? CMSIS_TYPE_BULK : CMSIS_TYPE_HID;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if (type == CMSIS_TYPE_HID) {
|
|
||||||
DEBUG_INFO("Using hid transfer\n");
|
|
||||||
if (hid_init())
|
|
||||||
return -1;
|
|
||||||
size = strlen(info->serial);
|
|
||||||
wchar_t serial[64] = {0}, *wc = serial;
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
*wc++ = info->serial[i];
|
|
||||||
*wc = 0;
|
|
||||||
/* Blacklist devices that do not work with 513 byte report length
|
|
||||||
* FIXME: Find a solution to decipher from the device.
|
|
||||||
*/
|
|
||||||
if ((info->vid == 0x1fc9) && (info->pid == 0x0132)) {
|
|
||||||
DEBUG_WARN("Blacklist\n");
|
|
||||||
report_size = 64 + 1;
|
|
||||||
}
|
|
||||||
handle = hid_open(info->vid, info->pid, (serial[0]) ? serial : NULL);
|
|
||||||
if (!handle) {
|
|
||||||
DEBUG_WARN("hid_open failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (type == CMSIS_TYPE_BULK) {
|
|
||||||
DEBUG_INFO("Using bulk transfer\n");
|
|
||||||
usb_handle = libusb_open_device_with_vid_pid(info->libusb_ctx, info->vid, info->pid);
|
|
||||||
if (!usb_handle) {
|
|
||||||
DEBUG_WARN("WARN: libusb_open_device_with_vid_pid() failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (libusb_claim_interface(usb_handle, info->interface_num) < 0) {
|
|
||||||
DEBUG_WARN("WARN: libusb_claim_interface() failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
in_ep = info->in_ep;
|
|
||||||
out_ep = info->out_ep;
|
|
||||||
}
|
|
||||||
dap_disconnect();
|
|
||||||
size = dap_info(DAP_INFO_FW_VER, buffer, sizeof(buffer));
|
|
||||||
if (size) {
|
|
||||||
DEBUG_INFO("Ver %s, ", buffer);
|
|
||||||
int major = -1, minor = -1, sub = -1;
|
|
||||||
if (sscanf((const char *)buffer, "%d.%d.%d",
|
|
||||||
&major, &minor, &sub)) {
|
|
||||||
if (sub == -1) {
|
|
||||||
if (minor >= 10) {
|
|
||||||
minor /= 10;
|
|
||||||
sub = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
has_swd_sequence = ((major > 1 ) || ((major > 0 ) && (minor > 1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size = dap_info(DAP_INFO_CAPABILITIES, buffer, sizeof(buffer));
|
|
||||||
dap_caps = buffer[0];
|
|
||||||
DEBUG_INFO("Cap (0x%2x): %s%s%s", dap_caps,
|
|
||||||
(dap_caps & 1)? "SWD" : "",
|
|
||||||
((dap_caps & 3) == 3) ? "/" : "",
|
|
||||||
(dap_caps & 2)? "JTAG" : "");
|
|
||||||
if (dap_caps & 4)
|
|
||||||
DEBUG_INFO(", SWO_UART");
|
|
||||||
if (dap_caps & 8)
|
|
||||||
DEBUG_INFO(", SWO_MANCHESTER");
|
|
||||||
if (dap_caps & 0x10)
|
|
||||||
DEBUG_INFO(", Atomic Cmds");
|
|
||||||
if (has_swd_sequence)
|
|
||||||
DEBUG_INFO(", DAP_SWD_Sequence");
|
|
||||||
DEBUG_INFO("\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dap_srst_set_val(bool assert)
|
|
||||||
{
|
|
||||||
dap_reset_pin(!assert);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dap_dp_abort(ADIv5_DP_t *dp, uint32_t abort)
|
|
||||||
{
|
|
||||||
/* DP Write to Reg 0.*/
|
|
||||||
dap_write_reg(dp, ADIV5_DP_ABORT, abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t dap_dp_error(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
/* Not used for SWD debugging, so no TARGETID switch needed!*/
|
|
||||||
uint32_t ctrlstat = dap_read_reg(dp, ADIV5_DP_CTRLSTAT);
|
|
||||||
uint32_t err = ctrlstat &
|
|
||||||
(ADIV5_DP_CTRLSTAT_STICKYORUN | ADIV5_DP_CTRLSTAT_STICKYCMP |
|
|
||||||
ADIV5_DP_CTRLSTAT_STICKYERR | ADIV5_DP_CTRLSTAT_WDATAERR);
|
|
||||||
uint32_t clr = 0;
|
|
||||||
if(err & ADIV5_DP_CTRLSTAT_STICKYORUN)
|
|
||||||
clr |= ADIV5_DP_ABORT_ORUNERRCLR;
|
|
||||||
if(err & ADIV5_DP_CTRLSTAT_STICKYCMP)
|
|
||||||
clr |= ADIV5_DP_ABORT_STKCMPCLR;
|
|
||||||
if(err & ADIV5_DP_CTRLSTAT_STICKYERR)
|
|
||||||
clr |= ADIV5_DP_ABORT_STKERRCLR;
|
|
||||||
if(err & ADIV5_DP_CTRLSTAT_WDATAERR)
|
|
||||||
clr |= ADIV5_DP_ABORT_WDERRCLR;
|
|
||||||
dap_write_reg(dp, ADIV5_DP_ABORT, clr);
|
|
||||||
dp->fault = 0;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t dap_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW,
|
|
||||||
uint16_t addr, uint32_t value)
|
|
||||||
{
|
|
||||||
bool APnDP = addr & ADIV5_APnDP;
|
|
||||||
uint32_t res = 0;
|
|
||||||
uint8_t reg = (addr & 0xc) | ((APnDP)? 1 : 0);
|
|
||||||
if (RnW) {
|
|
||||||
res = dap_read_reg(dp, reg);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dap_write_reg(dp, reg, value);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t dap_dp_read_reg(ADIv5_DP_t *dp, uint16_t addr)
|
|
||||||
{
|
|
||||||
uint32_t res = dap_dp_low_access(dp, ADIV5_LOW_READ, addr, 0);
|
|
||||||
DEBUG_PROBE("dp_read %04x %08" PRIx32 "\n", addr, res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dap_exit_function(void)
|
|
||||||
{
|
|
||||||
if (type == CMSIS_TYPE_HID) {
|
|
||||||
if (handle) {
|
|
||||||
dap_disconnect();
|
|
||||||
hid_close(handle);
|
|
||||||
}
|
|
||||||
} else if (type == CMSIS_TYPE_BULK) {
|
|
||||||
if (usb_handle) {
|
|
||||||
dap_disconnect();
|
|
||||||
libusb_close(usb_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dbg_get_report_size(void)
|
|
||||||
{
|
|
||||||
return report_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dbg_dap_cmd(uint8_t *data, int size, int rsize)
|
|
||||||
|
|
||||||
{
|
|
||||||
char cmd = data[0];
|
|
||||||
int res = -1;
|
|
||||||
|
|
||||||
memset(buffer, 0xff, report_size + 1);
|
|
||||||
|
|
||||||
buffer[0] = 0x00; // Report ID??
|
|
||||||
memcpy(&buffer[1], data, rsize);
|
|
||||||
|
|
||||||
DEBUG_WIRE("cmd : ");
|
|
||||||
for(int i = 0; (i < 32) && (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");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
} else if (type == CMSIS_TYPE_BULK) {
|
|
||||||
int transferred = 0;
|
|
||||||
|
|
||||||
res = libusb_bulk_transfer(usb_handle, out_ep, data, rsize, &transferred, 500);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
res = transferred;
|
|
||||||
}
|
|
||||||
DEBUG_WIRE("cmd res:");
|
|
||||||
for(int i = 0; (i < 16) && (i < size + 1); i++)
|
|
||||||
DEBUG_WIRE("%02x.", buffer[i]);
|
|
||||||
DEBUG_WIRE("\n");
|
|
||||||
if (buffer[0] != cmd) {
|
|
||||||
DEBUG_WARN("cmd %02x not implemented\n", cmd);
|
|
||||||
buffer[1] = 0xff /*DAP_ERROR*/;
|
|
||||||
}
|
|
||||||
if (size)
|
|
||||||
memcpy(data, &buffer[1], (size < res) ? size : res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
#define ALIGNOF(x) (((x) & 3) == 0 ? ALIGN_WORD : \
|
|
||||||
(((x) & 1) == 0 ? ALIGN_HALFWORD : ALIGN_BYTE))
|
|
||||||
|
|
||||||
static void dap_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len)
|
|
||||||
{
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
enum align align = MIN(ALIGNOF(src), ALIGNOF(len));
|
|
||||||
DEBUG_WIRE("memread @ %" PRIx32 " len %ld, align %d , start: \n",
|
|
||||||
src, len, align);
|
|
||||||
if (((unsigned)(1 << align)) == len)
|
|
||||||
return dap_read_single(ap, dest, src, align);
|
|
||||||
/* One word transfer for every byte/halfword/word
|
|
||||||
* Total number of bytes in transfer*/
|
|
||||||
unsigned int max_size = ((dbg_get_report_size() - 6) >> (2 - align)) & ~3;
|
|
||||||
while (len) {
|
|
||||||
dap_ap_mem_access_setup(ap, src, align);
|
|
||||||
/* Calculate length until next access setup is needed */
|
|
||||||
unsigned int blocksize = (src | 0x3ff) - src + 1;
|
|
||||||
if (blocksize > len)
|
|
||||||
blocksize = len;
|
|
||||||
while (blocksize) {
|
|
||||||
unsigned int transfersize = blocksize;
|
|
||||||
if (transfersize > max_size)
|
|
||||||
transfersize = max_size;
|
|
||||||
unsigned int res = dap_read_block(ap, dest, src, transfersize,
|
|
||||||
align);
|
|
||||||
if (res) {
|
|
||||||
DEBUG_WIRE("mem_read failed %02x\n", res);
|
|
||||||
ap->dp->fault = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blocksize -= transfersize;
|
|
||||||
len -= transfersize;
|
|
||||||
dest += transfersize;
|
|
||||||
src += transfersize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG_WIRE("memread res last data %08" PRIx32 "\n", ((uint32_t*)dest)[-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dap_mem_write_sized(
|
|
||||||
ADIv5_AP_t *ap, uint32_t dest, const void *src,
|
|
||||||
size_t len, enum align align)
|
|
||||||
{
|
|
||||||
if (len == 0)
|
|
||||||
return;
|
|
||||||
DEBUG_WIRE("memwrite @ %" PRIx32 " len %ld, align %d , %08x start: \n",
|
|
||||||
dest, len, align, *(uint32_t *)src);
|
|
||||||
if (((unsigned)(1 << align)) == len)
|
|
||||||
return dap_write_single(ap, dest, src, align);
|
|
||||||
unsigned int max_size = ((dbg_get_report_size() - 6) >> (2 - align) & ~3);
|
|
||||||
while (len) {
|
|
||||||
dap_ap_mem_access_setup(ap, dest, align);
|
|
||||||
unsigned int blocksize = (dest | 0x3ff) - dest + 1;
|
|
||||||
if (blocksize > len)
|
|
||||||
blocksize = len;
|
|
||||||
while (blocksize) {
|
|
||||||
unsigned int transfersize = blocksize;
|
|
||||||
if (transfersize > max_size)
|
|
||||||
transfersize = max_size;
|
|
||||||
unsigned int res = dap_write_block(ap, dest, src, transfersize,
|
|
||||||
align);
|
|
||||||
if (res) {
|
|
||||||
DEBUG_WARN("mem_write failed %02x\n", res);
|
|
||||||
ap->dp->fault = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blocksize -= transfersize;
|
|
||||||
len -= transfersize;
|
|
||||||
dest += transfersize;
|
|
||||||
src += transfersize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG_WIRE("memwrite done\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void dap_adiv5_dp_defaults(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
if ((mode == DAP_CAP_JTAG) && dap_jtag_configure())
|
|
||||||
return;
|
|
||||||
dp->ap_read = dap_ap_read;
|
|
||||||
dp->ap_write = dap_ap_write;
|
|
||||||
dp->mem_read = dap_mem_read;
|
|
||||||
dp->mem_write_sized = dap_mem_write_sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmsis_dap_jtagtap_reset(void)
|
|
||||||
{
|
|
||||||
jtagtap_soft_reset();
|
|
||||||
/* Is there a way to know if TRST is available?*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmsis_dap_jtagtap_tms_seq(uint32_t MS, int ticks)
|
|
||||||
{
|
|
||||||
uint8_t TMS[4] = {MS & 0xff, (MS >> 8) & 0xff, (MS >> 16) & 0xff,
|
|
||||||
(MS >> 24) & 0xff};
|
|
||||||
dap_jtagtap_tdi_tdo_seq(NULL, false, TMS, NULL, ticks);
|
|
||||||
DEBUG_PROBE("tms_seq DI %08x %d\n", MS, ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmsis_dap_jtagtap_tdi_tdo_seq(uint8_t *DO, const uint8_t final_tms,
|
|
||||||
const uint8_t *DI, int ticks)
|
|
||||||
{
|
|
||||||
dap_jtagtap_tdi_tdo_seq(DO, (final_tms), NULL, DI, ticks);
|
|
||||||
DEBUG_PROBE("jtagtap_tdi_tdo_seq %d, %02x-> %02x\n", ticks, DI[0], (DO)? DO[0] : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cmsis_dap_jtagtap_tdi_seq(const uint8_t final_tms,
|
|
||||||
const uint8_t *DI, int ticks)
|
|
||||||
{
|
|
||||||
dap_jtagtap_tdi_tdo_seq(NULL, (final_tms), NULL, DI, ticks);
|
|
||||||
DEBUG_PROBE("jtagtap_tdi_seq %d, %02x\n", ticks, DI[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t cmsis_dap_jtagtap_next(uint8_t dTMS, uint8_t dTDI)
|
|
||||||
{
|
|
||||||
uint8_t tdo[1];
|
|
||||||
dap_jtagtap_tdi_tdo_seq(tdo, false, &dTMS, &dTDI, 1);
|
|
||||||
DEBUG_PROBE("next tms %02x tdi %02x tdo %02x\n", dTMS, dTDI, tdo[0]);
|
|
||||||
return (tdo[0] & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int cmsis_dap_jtagtap_init(jtag_proc_t *jtag_proc)
|
|
||||||
{
|
|
||||||
DEBUG_PROBE("jtap_init\n");
|
|
||||||
if (!(dap_caps & DAP_CAP_JTAG))
|
|
||||||
return -1;
|
|
||||||
mode = DAP_CAP_JTAG;
|
|
||||||
dap_disconnect();
|
|
||||||
dap_connect(true);
|
|
||||||
dap_reset_link(true);
|
|
||||||
jtag_proc->jtagtap_reset = cmsis_dap_jtagtap_reset;
|
|
||||||
jtag_proc->jtagtap_next = cmsis_dap_jtagtap_next;
|
|
||||||
jtag_proc->jtagtap_tms_seq = cmsis_dap_jtagtap_tms_seq;
|
|
||||||
jtag_proc->jtagtap_tdi_tdo_seq = cmsis_dap_jtagtap_tdi_tdo_seq;
|
|
||||||
jtag_proc->jtagtap_tdi_seq = cmsis_dap_jtagtap_tdi_seq;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dap_jtag_dp_init(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
dp->dp_read = dap_dp_read_reg;
|
|
||||||
dp->error = dap_dp_error;
|
|
||||||
dp->low_access = dap_dp_low_access;
|
|
||||||
dp->abort = dap_dp_abort;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SWD_SEQUENCE_IN 0x80
|
|
||||||
#define DAP_SWD_SEQUENCE 0x1d
|
|
||||||
static bool dap_dp_low_write(ADIv5_DP_t *dp, uint16_t addr, const uint32_t data)
|
|
||||||
{
|
|
||||||
DEBUG_PROBE("dap_dp_low_write %08" PRIx32 "\n", data);
|
|
||||||
(void)dp;
|
|
||||||
unsigned int paket_request = make_packet_request(ADIV5_LOW_WRITE, addr);
|
|
||||||
uint8_t buf[32] = {
|
|
||||||
DAP_SWD_SEQUENCE,
|
|
||||||
5,
|
|
||||||
8,
|
|
||||||
paket_request,
|
|
||||||
4 + SWD_SEQUENCE_IN, /* one turn-around + read 3 bit ACK */
|
|
||||||
1, /* one bit turn around to drive SWDIO */
|
|
||||||
0,
|
|
||||||
32, /* write 32 bit data */
|
|
||||||
(data >> 0) & 0xff,
|
|
||||||
(data >> 8) & 0xff,
|
|
||||||
(data >> 16) & 0xff,
|
|
||||||
(data >> 24) & 0xff,
|
|
||||||
1, /* write parity biT */
|
|
||||||
__builtin_parity(data)
|
|
||||||
};
|
|
||||||
dbg_dap_cmd(buf, sizeof(buf), 14);
|
|
||||||
if (buf[0])
|
|
||||||
DEBUG_WARN("dap_dp_low_write failed\n");
|
|
||||||
uint32_t ack = (buf[1] >> 1) & 7;
|
|
||||||
return (ack != SWDP_ACK_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dap_swdptap_init(ADIv5_DP_t *dp)
|
|
||||||
{
|
|
||||||
if (!(dap_caps & DAP_CAP_SWD))
|
|
||||||
return 1;
|
|
||||||
mode = DAP_CAP_SWD;
|
|
||||||
dap_transfer_configure(2, 128, 128);
|
|
||||||
dap_swd_configure(0);
|
|
||||||
dap_connect(false);
|
|
||||||
dap_led(0, 1);
|
|
||||||
dap_reset_link(false);
|
|
||||||
if ((has_swd_sequence) && dap_sequence_test()) {
|
|
||||||
/* DAP_SWD_SEQUENCE does not do auto turnaround, use own!*/
|
|
||||||
dp->dp_low_write = dap_dp_low_write;
|
|
||||||
} else {
|
|
||||||
dp->dp_low_write = NULL;
|
|
||||||
}
|
|
||||||
dp->seq_out = dap_swdptap_seq_out;
|
|
||||||
dp->dp_read = dap_dp_read_reg;
|
|
||||||
/* For error() use the TARGETID switching firmware_swdp_error */
|
|
||||||
dp->low_access = dap_dp_low_access;
|
|
||||||
dp->abort = dap_dp_abort;
|
|
||||||
return 0;
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user