Compare commits
No commits in common. "stable" and "v1.8.0" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,10 +16,7 @@ tags
|
|||||||
*.b#*
|
*.b#*
|
||||||
blackmagic_upgrade
|
blackmagic_upgrade
|
||||||
*.exe
|
*.exe
|
||||||
.DS_Store
|
|
||||||
*.elf
|
*.elf
|
||||||
.vscode
|
.vscode
|
||||||
cscope.out
|
|
||||||
cscope.files
|
|
||||||
.gdb_history
|
.gdb_history
|
||||||
src/artifacts/
|
src/artifacts/
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "libopencm3"]
|
[submodule "libopencm3"]
|
||||||
path = libopencm3
|
path = libopencm3
|
||||||
url = https://github.com/flirc/libopencm3.git
|
url = https://github.com/libopencm3/libopencm3.git
|
||||||
|
12
Makefile
12
Makefile
@ -17,23 +17,13 @@ ifndef NO_LIBOPENCM3
|
|||||||
git submodule init ;\
|
git submodule init ;\
|
||||||
git submodule update ;\
|
git submodule update ;\
|
||||||
fi
|
fi
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/sam/d
|
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/stm32/f1 lib/stm32/f4 lib/lm4f
|
||||||
endif
|
endif
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C src
|
$(Q)$(MAKE) $(MFLAGS) -C src
|
||||||
|
|
||||||
all_platforms:
|
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifndef NO_LIBOPENCM3
|
ifndef NO_LIBOPENCM3
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 $@
|
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 $@
|
||||||
endif
|
endif
|
||||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
$(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
|
|
||||||
|
153
README.md
153
README.md
@ -1,54 +1,113 @@
|
|||||||
Jeff Probe
|
Black Magic Probe
|
||||||
|
=================
|
||||||
|
|
||||||
|
[](https://discord.gg/P7FYThy)
|
||||||
|
|
||||||
|
Firmware for the Black Magic Debug Probe.
|
||||||
|
|
||||||
|
The Black Magic Probe is a modern, in-application debugging tool for
|
||||||
|
embedded microprocessors. It allows you see what is going on 'inside' an
|
||||||
|
application running on an embedded microprocessor while it executes. It is
|
||||||
|
able to control and examine the state of the target microprocessor using a
|
||||||
|
JTAG or Serial Wire Debugging (SWD) port and on-chip debug logic provided
|
||||||
|
by the microprocessor. The probe connects to a host computer using a
|
||||||
|
standard USB interface. The user is able to control exactly what happens
|
||||||
|
using the GNU source level debugging software, GDB.
|
||||||
|
Serial Wire Output (SWO) allows the target to write tracing and logging to the host
|
||||||
|
without using usb or serial port. Decoding SWO in the probe itself
|
||||||
|
makes [SWO viewing as simple as connecting to a serial port](https://github.com/blackmagic-debug/blackmagic/wiki/Serial-Wire-Output).
|
||||||
|
|
||||||
|
|
||||||
|
Resources
|
||||||
|
=========
|
||||||
|
|
||||||
|
* [Documentation](https://github.com/blackmagic-debug/blackmagic/wiki)
|
||||||
|
* [Binary builds](http://builds.blacksphere.co.nz/blackmagic)
|
||||||
|
|
||||||
|
|
||||||
|
Toolchain specific remarks
|
||||||
|
==========================
|
||||||
|
Most firmware building is done with the most recent suite from https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm.
|
||||||
|
If you have a toolchain from other sources and find problems, check if it is a failure of your toolchain and if not open an issue or better provide a pull request with a fix.
|
||||||
|
|
||||||
|
OS specific remarks for BMP-Hosted
|
||||||
|
==================================
|
||||||
|
Most hosted building is done on and for Linux. BMP-hosted for windows can also be build with Mingw on Linux.<br>
|
||||||
|
Building hosted for BMP firmware probes only with "make PROBE_HOST HOSTED_BMP_ONLY=1" does not require libusb, libftdi and evt. libhidapi development headers and libraries for running.<br>
|
||||||
|
On BSD/Macos, using dev/tty.usbmodemXXX should work but unresolved discussions indicate a hanging open() call on the second invocation. If that happens, try with cu.usbmodemXXX.<br>
|
||||||
|
|
||||||
|
Reporting problems
|
||||||
|
==================
|
||||||
|
Before reporting issues, check against the latest git version. If possible, test against another target /and/or debug probe. Consider broken USB cables and connectors. Try to reproduce with bmp-hosted with at least debug bit 1 set (blackmagic -v 1 ...), as debug messages will be dumped to the starting console. When reporting issues, be as specific as possible!
|
||||||
|
|
||||||
|
Sample Session
|
||||||
|
=============
|
||||||
|
```console
|
||||||
|
> arm-none-eabi-gdb gpio.elf
|
||||||
|
...<GDB Copyright message>
|
||||||
|
(gdb) tar ext /dev/ttyACM0
|
||||||
|
Remote debugging using /dev/ttyACM0
|
||||||
|
(gdb) mon s
|
||||||
|
Target voltage: 2.94V
|
||||||
|
Available Targets:
|
||||||
|
No. Att Driver
|
||||||
|
1 STM32F40x M3/M4
|
||||||
|
(gdb) att 1
|
||||||
|
Attaching to program: /devel/en_apps/gpio/f4_discovery/gpio.elf, Remote target
|
||||||
|
0x08002298 in UsartIOCtl ()
|
||||||
|
(gdb) load
|
||||||
|
Loading section .text, size 0x5868 lma 0x8000000
|
||||||
|
Loading section .data, size 0x9e0 lma 0x8005868
|
||||||
|
Loading section .rodata, size 0x254 lma 0x8006248
|
||||||
|
Start address 0x800007c, load size 25756
|
||||||
|
Transfer rate: 31 KB/sec, 919 bytes/write.
|
||||||
|
(gdb) b main
|
||||||
|
Breakpoint 1 at 0x80000e8: file /devel/en_apps/gpio/f4_discovery/../gpio.c, line 70.
|
||||||
|
(gdb) r
|
||||||
|
Starting program: /devel/en_apps/gpio/f4_discovery/gpio.elf
|
||||||
|
Note: automatically using hardware breakpoints for read-only addresses.
|
||||||
|
|
||||||
|
Breakpoint 1, main () at /devel/en_apps/gpio/f4_discovery/../gpio.c:70
|
||||||
|
70 {
|
||||||
|
```
|
||||||
|
|
||||||
|
BLACKMAGIC
|
||||||
==========
|
==========
|
||||||
|
|
||||||
This is a fork of the [original Black Magic Probe](https://github.com/blacksphere/blackmagic).
|
You can also build blackmagic as a PC hosted application
|
||||||
|
"make PROBE_HOST=hosted"
|
||||||
|
|
||||||
The original is arguably better, faster and wider supported. However, this
|
This builds the same GDB server, that is running on the Black Magic Probe.
|
||||||
project was a way to offer an affordable version and I'll rely on community
|
While connection to the Black Magic Probe GDB server is via serial line,
|
||||||
support and pull requests.
|
connection to the PC-Hosted GDB server is via TCP port 2000 for the first
|
||||||
|
GDB server and higher for more invokations. Use "tar(get) ext(ented) :2000"
|
||||||
|
to connect.
|
||||||
|
PC-hosted BMP GDB server can talk to
|
||||||
|
- Black Magic Probe firmware probes via the USB-serial port
|
||||||
|
- ST-LinkV2 and V3 with recent firmware
|
||||||
|
- CMSIS-DAP compatible probes
|
||||||
|
- JLINK probes
|
||||||
|
- FTDI MPSSE based probe.
|
||||||
|
|
||||||
One urguably better funncton is the ability to do DEBUG and Serial communication
|
When connected to a single BMP supported probe, starting "blackmagic" w/o any
|
||||||
over a single JTAG cable when paired with a device that uses single wire JTAG.
|
arguments starts the server. When several BMP supported probes are connected,
|
||||||
|
their types, position and serial number is displayed and the program exits.
|
||||||
Normally, the serial header can be used on a target for the serial port, and
|
Add "-P (position)" to the next invocation to select one.
|
||||||
shows up as the second serial device on the system, however, we can dynamically
|
For the setup from the sample session above:
|
||||||
change the pins to use the ones on the JTAG cable with the following command:
|
In another terminal:
|
||||||
|
```console
|
||||||
``` bash
|
> blackmagic
|
||||||
$ mon convert_tdio enable
|
Using 1d50:6018 E2E489E7 Black Sphere Technologies Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty)
|
||||||
|
Remote is Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty) v1.6.1-477-g70bb131-dirty
|
||||||
|
Listening on TCP: 2000
|
||||||
|
And in the GDB terminal:
|
||||||
|
(gdb) target ext :2000
|
||||||
|
Remote debugging using :2000
|
||||||
|
(gdb) mon s
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Compilation
|
PC hosted BMP also allows to flash, read and verify a binary file, by default
|
||||||
---
|
starting at lowest flash address. The "-t" argument displays information about the
|
||||||
|
connected target. Use "-h " to get a list of supported options.
|
||||||
|
|
||||||
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)
|
|
@ -1,7 +1,6 @@
|
|||||||
# Black Magic Probe
|
# Black Magic Probe
|
||||||
# there are two connections, one for GDB and one for UART debugging
|
# there are two connections, one for GDB and one for UART debugging
|
||||||
# copy this to /etc/udev/rules.d/99-blackmagic.rules
|
# 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 GDB Server", SYMLINK+="ttyBmpGdb"
|
||||||
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
|
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}=="6017", MODE="0666"
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 63573143ef7e1b037d1f0c5baedc5264e12562b8
|
Subproject commit 8435287300e5ca9af9f889c529e7b1fa019c42fb
|
Binary file not shown.
68
src/Makefile
68
src/Makefile
@ -9,7 +9,7 @@ Q := @
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts \
|
CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts \
|
||||||
-std=gnu99 -MD -I./target \
|
-std=gnu99 -g3 -MD -I./target \
|
||||||
-I. -Iinclude -I$(PLATFORM_DIR)
|
-I. -Iinclude -I$(PLATFORM_DIR)
|
||||||
|
|
||||||
ifeq ($(ENABLE_DEBUG), 1)
|
ifeq ($(ENABLE_DEBUG), 1)
|
||||||
@ -65,6 +65,11 @@ SRC = \
|
|||||||
|
|
||||||
include $(PLATFORM_DIR)/Makefile.inc
|
include $(PLATFORM_DIR)/Makefile.inc
|
||||||
|
|
||||||
|
ifneq ($(PC_HOSTED),1)
|
||||||
|
# Output memory usage information
|
||||||
|
LDFLAGS += -Wl,--print-memory-usage
|
||||||
|
endif
|
||||||
|
|
||||||
OPT_FLAGS ?= -Os
|
OPT_FLAGS ?= -Os
|
||||||
CFLAGS += $(OPT_FLAGS)
|
CFLAGS += $(OPT_FLAGS)
|
||||||
LDFLAGS += $(OPT_FLAGS)
|
LDFLAGS += $(OPT_FLAGS)
|
||||||
@ -90,28 +95,8 @@ VPATH += platforms/common
|
|||||||
CFLAGS += -Iplatforms/common
|
CFLAGS += -Iplatforms/common
|
||||||
endif
|
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)))
|
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)
|
$(TARGET): include/version.h $(OBJ)
|
||||||
@echo " LD $@"
|
@echo " LD $@"
|
||||||
$(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
$(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
||||||
@ -127,14 +112,14 @@ $(TARGET): include/version.h $(OBJ)
|
|||||||
ifndef PC_HOSTED
|
ifndef PC_HOSTED
|
||||||
%.bin: %.elf
|
%.bin: %.elf
|
||||||
@echo " OBJCOPY $@"
|
@echo " OBJCOPY $@"
|
||||||
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@
|
$(Q)$(OBJCOPY) -O binary $^ $@
|
||||||
|
|
||||||
%.hex: %.elf
|
%.hex: %.elf
|
||||||
@echo " OBJCOPY $@"
|
@echo " OBJCOPY $@"
|
||||||
$(Q)$(OBJCOPY) -O ihex $^ $@
|
$(Q)$(OBJCOPY) -O ihex $^ $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: clean host_clean all_platforms clang-format FORCE
|
.PHONY: clean host_clean all_platforms FORCE
|
||||||
|
|
||||||
clean: host_clean
|
clean: host_clean
|
||||||
$(Q)echo " CLEAN"
|
$(Q)echo " CLEAN"
|
||||||
@ -142,27 +127,16 @@ clean: host_clean
|
|||||||
-$(Q)$(RM) platforms/*/*.o platforms/*/*.d mapfile include/version.h
|
-$(Q)$(RM) platforms/*/*.o platforms/*/*.d mapfile include/version.h
|
||||||
|
|
||||||
all_platforms:
|
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 ;\
|
$(Q)set -e ;\
|
||||||
mkdir -p artifacts/$(shell git describe --always --dirty --tags) ;\
|
mkdir -p artifacts/$(shell git describe --always --dirty --tags) ;\
|
||||||
echo "<html><body><ul>" > artifacts/index.html ;\
|
echo "<html><body><ul>" > artifacts/index.html ;\
|
||||||
$(MAKE) clean ;\
|
|
||||||
for i in platforms/*/Makefile.inc ; do \
|
for i in platforms/*/Makefile.inc ; do \
|
||||||
export DIRNAME=`dirname $$i` ;\
|
export DIRNAME=`dirname $$i` ;\
|
||||||
export PROBE_HOST=`basename $$DIRNAME` ;\
|
export PROBE_HOST=`basename $$DIRNAME` ;\
|
||||||
export CFLAGS=-Werror ;\
|
export CFLAGS=-Werror ;\
|
||||||
echo "Building for hardware platform: $$PROBE_HOST" ;\
|
echo "Building for hardware platform: $$PROBE_HOST" ;\
|
||||||
|
$(MAKE) clean ;\
|
||||||
$(MAKE);\
|
$(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 \
|
if [ -f blackmagic.bin ]; then \
|
||||||
mv blackmagic.bin artifacts/blackmagic-$$PROBE_HOST.bin ;\
|
mv blackmagic.bin artifacts/blackmagic-$$PROBE_HOST.bin ;\
|
||||||
echo "<li><a href='blackmagic-$$PROBE_HOST.bin'>$$PROBE_HOST</a></li>"\
|
echo "<li><a href='blackmagic-$$PROBE_HOST.bin'>$$PROBE_HOST</a></li>"\
|
||||||
@ -173,31 +147,13 @@ all_platforms:
|
|||||||
echo "<li><a href='blackmagic_dfu-$$PROBE_HOST.bin'>$$PROBE_HOST DFU</a></li>"\
|
echo "<li><a href='blackmagic_dfu-$$PROBE_HOST.bin'>$$PROBE_HOST DFU</a></li>"\
|
||||||
>> artifacts/index.html ;\
|
>> artifacts/index.html ;\
|
||||||
fi ;\
|
fi ;\
|
||||||
$(MAKE) clean ;\
|
|
||||||
done ;\
|
done ;\
|
||||||
echo "</ul></body></html>" >> artifacts/index.html ;\
|
echo "</ul></body></html>" >> artifacts/index.html ;\
|
||||||
cp artifacts/blackmagic* artifacts/$(shell git describe --always --dirty --tags)
|
cp artifacts/*.bin artifacts/$(shell git describe --always --dirty --tags)
|
||||||
|
|
||||||
command.c: include/version.h
|
command.c: include/version.h
|
||||||
|
|
||||||
GIT_VERSION := $(shell git describe --always --dirty --tags)
|
|
||||||
VERSION_HEADER := \#define FIRMWARE_VERSION "$(GIT_VERSION)"
|
|
||||||
|
|
||||||
include/version.h: FORCE
|
include/version.h: FORCE
|
||||||
@# If git isn't found then GIT_VERSION will be an empty string.
|
$(Q)echo " GIT include/version.h"
|
||||||
ifeq ($(GIT_VERSION),)
|
$(Q)echo "#define FIRMWARE_VERSION \"$(shell git describe --always --dirty --tags)\"" > $@
|
||||||
@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
|
||||||
|
166
src/command.c
166
src/command.c
@ -34,18 +34,11 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "serialno.h"
|
#include "serialno.h"
|
||||||
|
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
#include "rtt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
# include "traceswo.h"
|
# include "traceswo.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool cmd_version(target *t, int argc, char **argv);
|
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_help(target *t, int argc, char **argv);
|
||||||
|
|
||||||
static bool cmd_jtag_scan(target *t, int argc, char **argv);
|
static bool cmd_jtag_scan(target *t, int argc, char **argv);
|
||||||
@ -63,25 +56,12 @@ static bool cmd_target_power(target *t, int argc, const char **argv);
|
|||||||
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
||||||
#endif
|
#endif
|
||||||
static bool cmd_heapinfo(target *t, int argc, const char **argv);
|
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)
|
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
||||||
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
||||||
#endif
|
#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
|
|
||||||
|
|
||||||
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" },
|
||||||
@ -94,9 +74,6 @@ const struct command_s cmd_list[] = {
|
|||||||
#ifdef PLATFORM_HAS_POWER_SWITCH
|
#ifdef PLATFORM_HAS_POWER_SWITCH
|
||||||
{"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
|
{"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
|
||||||
#endif
|
#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
|
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
||||||
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
||||||
@ -107,13 +84,6 @@ const struct command_s cmd_list[] = {
|
|||||||
{"heapinfo", (cmd_handler)cmd_heapinfo, "Set semihosting heapinfo" },
|
{"heapinfo", (cmd_handler)cmd_heapinfo, "Set semihosting heapinfo" },
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
#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)"},
|
{"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}
|
||||||
};
|
};
|
||||||
@ -440,82 +410,6 @@ static bool cmd_target_power(target *t, int argc, const char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#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 bool cmd_traceswo(target *t, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
@ -593,66 +487,6 @@ static bool cmd_debug_bmp(target *t, int argc, const char **argv)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
static bool cmd_heapinfo(target *t, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
if (t == NULL) gdb_out("not attached\n");
|
if (t == NULL) gdb_out("not attached\n");
|
||||||
|
@ -130,3 +130,4 @@ int hostio_system(struct target_controller *tc,
|
|||||||
gdb_putpacket_f("Fsystem,%08X/%X", cmd, cmd_len);
|
gdb_putpacket_f("Fsystem,%08X/%X", cmd, cmd_len);
|
||||||
return gdb_main_loop(tc, true);
|
return gdb_main_loop(tc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
247
src/gdb_main.c
247
src/gdb_main.c
@ -35,9 +35,6 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
#include "rtt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum gdb_signal {
|
enum gdb_signal {
|
||||||
GDB_SIGINT = 2,
|
GDB_SIGINT = 2,
|
||||||
@ -46,7 +43,7 @@ enum gdb_signal {
|
|||||||
GDB_SIGLOST = 29,
|
GDB_SIGLOST = 29,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BUF_SIZE 1024U
|
#define BUF_SIZE 1024
|
||||||
|
|
||||||
#define ERROR_IF_NO_TARGET() \
|
#define ERROR_IF_NO_TARGET() \
|
||||||
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
||||||
@ -54,29 +51,23 @@ enum gdb_signal {
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char *cmd_prefix;
|
const char *cmd_prefix;
|
||||||
void (*func)(const char *packet, size_t len);
|
void (*func)(const char *packet, int len);
|
||||||
} cmd_executer;
|
} cmd_executer;
|
||||||
|
|
||||||
static char pbuf[BUF_SIZE + 1U];
|
static char pbuf[BUF_SIZE + 1];
|
||||||
|
|
||||||
static target *cur_target;
|
static target *cur_target;
|
||||||
static target *last_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_q_packet(char *packet, int len);
|
||||||
static void handle_v_packet(char *packet, size_t len);
|
static void handle_v_packet(char *packet, int len);
|
||||||
static void handle_z_packet(char *packet, size_t len);
|
static void handle_z_packet(char *packet, int len);
|
||||||
static void handle_kill_target(void);
|
|
||||||
|
|
||||||
static void gdb_target_destroy_callback(struct target_controller *tc, target *t)
|
static void gdb_target_destroy_callback(struct target_controller *tc, target *t)
|
||||||
{
|
{
|
||||||
(void)tc;
|
(void)tc;
|
||||||
if (cur_target == t) {
|
if (cur_target == t)
|
||||||
gdb_put_notificationz("%Stop:W00");
|
|
||||||
gdb_out("You are now detached from the previous target.\n");
|
|
||||||
cur_target = NULL;
|
cur_target = NULL;
|
||||||
gdb_needs_detach_notify = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_target == t)
|
if (last_target == t)
|
||||||
last_target = NULL;
|
last_target = NULL;
|
||||||
@ -109,20 +100,22 @@ static struct target_controller gdb_controller = {
|
|||||||
|
|
||||||
int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||||
{
|
{
|
||||||
|
int size;
|
||||||
bool single_step = false;
|
bool single_step = false;
|
||||||
|
|
||||||
/* 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 */
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
uint8_t arm_regs[target_regs_size(cur_target)];
|
||||||
target_regs_read(cur_target, gp_regs);
|
target_regs_read(cur_target, arm_regs);
|
||||||
gdb_putpacket(hexify(pbuf, gp_regs, sizeof(gp_regs)), sizeof(gp_regs) * 2U);
|
gdb_putpacket(hexify(pbuf, arm_regs, sizeof(arm_regs)),
|
||||||
|
sizeof(arm_regs) * 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
||||||
@ -139,20 +132,19 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
if (target_mem_read(cur_target, mem, addr, len))
|
if (target_mem_read(cur_target, mem, addr, len))
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
else
|
else
|
||||||
gdb_putpacket(hexify(pbuf, mem, len), len * 2U);
|
gdb_putpacket(hexify(pbuf, mem, len), len * 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'G': { /* 'G XX': Write general registers */
|
case 'G': { /* 'G XX': Write general registers */
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
uint8_t arm_regs[target_regs_size(cur_target)];
|
||||||
unhexify(gp_regs, &pbuf[1], sizeof(gp_regs));
|
unhexify(arm_regs, &pbuf[1], sizeof(arm_regs));
|
||||||
target_regs_write(cur_target, gp_regs);
|
target_regs_write(cur_target, arm_regs);
|
||||||
gdb_putpacketz("OK");
|
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;
|
uint32_t addr, len;
|
||||||
uint32_t len = 0;
|
|
||||||
int hex;
|
int hex;
|
||||||
ERROR_IF_NO_TARGET();
|
ERROR_IF_NO_TARGET();
|
||||||
sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
|
sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
|
||||||
@ -170,24 +162,11 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
gdb_putpacketz("OK");
|
gdb_putpacketz("OK");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* '[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
|
|
||||||
gdb_putpacketz("E01");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 's': /* 's [addr]': Single step [start at addr] */
|
case 's': /* 's [addr]': Single step [start at addr] */
|
||||||
single_step = true;
|
single_step = true;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
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;
|
||||||
}
|
}
|
||||||
@ -202,7 +181,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
target_addr watch;
|
target_addr watch;
|
||||||
enum target_halt_reason reason;
|
enum target_halt_reason reason;
|
||||||
|
|
||||||
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;
|
||||||
@ -210,13 +189,10 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
|
|
||||||
/* Wait for target halt */
|
/* Wait for target halt */
|
||||||
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
if (rtt_enabled) poll_rtt(cur_target);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
SET_RUN_STATE(0);
|
SET_RUN_STATE(0);
|
||||||
|
|
||||||
@ -298,7 +274,12 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
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 */
|
||||||
@ -336,7 +317,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
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;
|
||||||
|
|
||||||
@ -354,12 +335,12 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool exec_command(char *packet, const size_t length, const cmd_executer *exec)
|
static bool exec_command(char *packet, int len, const cmd_executer *exec)
|
||||||
{
|
{
|
||||||
while (exec->cmd_prefix) {
|
while (exec->cmd_prefix) {
|
||||||
const size_t prefix_length = strlen(exec->cmd_prefix);
|
const int l = strlen(exec->cmd_prefix);
|
||||||
if (!strncmp(packet, exec->cmd_prefix, prefix_length)) {
|
if (!strncmp(packet, exec->cmd_prefix, l)) {
|
||||||
exec->func(packet + prefix_length, length - prefix_length);
|
exec->func(packet + l, len - l);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
++exec;
|
++exec;
|
||||||
@ -367,16 +348,19 @@ static bool exec_command(char *packet, const size_t length, const cmd_executer *
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_q_rcmd(const char *packet, const size_t length)
|
static void exec_q_rcmd(const char *packet,int len)
|
||||||
{
|
{
|
||||||
|
char *data;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
/* calculate size and allocate buffer for command */
|
/* calculate size and allocate buffer for command */
|
||||||
const size_t datalen = length / 2U;
|
datalen = len / 2;
|
||||||
char *data = alloca(datalen + 1);
|
data = alloca(datalen + 1);
|
||||||
/* dehexify command */
|
/* dehexify command */
|
||||||
unhexify(data, packet, datalen);
|
unhexify(data, packet, datalen);
|
||||||
data[datalen] = 0; /* add terminating null */
|
data[datalen] = 0; /* add terminating null */
|
||||||
|
|
||||||
const int c = command_process(cur_target, data);
|
int c = command_process(cur_target, data);
|
||||||
if (c < 0)
|
if (c < 0)
|
||||||
gdb_putpacketz("");
|
gdb_putpacketz("");
|
||||||
else if (c == 0)
|
else if (c == 0)
|
||||||
@ -386,41 +370,41 @@ static void exec_q_rcmd(const char *packet, const size_t length)
|
|||||||
2 * strlen("Failed\n"));
|
2 * strlen("Failed\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_q_string_reply(const char *reply, const char *param)
|
static void
|
||||||
|
handle_q_string_reply(const char *str, const char *param)
|
||||||
{
|
{
|
||||||
const size_t reply_length = strlen(reply);
|
unsigned long addr, len;
|
||||||
uint32_t addr = 0;
|
const size_t str_len = strlen(str);
|
||||||
uint32_t len = 0;
|
|
||||||
|
|
||||||
if (sscanf(param, "%08" PRIx32 ",%08" PRIx32, &addr, &len) != 2) {
|
if (sscanf(param, "%08lx,%08lx", &addr, &len) != 2) {
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (addr > reply_length) {
|
else if (addr > str_len) {
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (addr == reply_length) {
|
else if (addr == str_len) {
|
||||||
gdb_putpacketz("l");
|
gdb_putpacketz("l");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t output_len = reply_length - addr;
|
unsigned long output_len = str_len - addr;
|
||||||
if (output_len > len)
|
if (output_len > len)
|
||||||
output_len = len;
|
output_len = len;
|
||||||
gdb_putpacket2("m", 1U, reply + addr, output_len);
|
gdb_putpacket2("m", 1, str + addr, output_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_q_supported(const char *packet, const size_t length)
|
static void exec_q_supported(const char *packet, int len)
|
||||||
{
|
{
|
||||||
(void)packet;
|
(void)packet;
|
||||||
(void)length;
|
(void)len;
|
||||||
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE);
|
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)
|
static void exec_q_memory_map(const char *packet, int len)
|
||||||
{
|
{
|
||||||
(void)packet;
|
(void)packet;
|
||||||
(void)length;
|
(void)len;
|
||||||
/* Read target XML memory map */
|
/* Read target XML memory map */
|
||||||
if ((!cur_target) && last_target) {
|
if ((!cur_target) && last_target) {
|
||||||
/* Attach to last target if detached. */
|
/* Attach to last target if detached. */
|
||||||
@ -436,9 +420,9 @@ static void exec_q_memory_map(const char *packet, const size_t length)
|
|||||||
handle_q_string_reply(buf, packet);
|
handle_q_string_reply(buf, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_q_feature_read(const char *packet, const size_t length)
|
static void exec_q_feature_read(const char *packet, int len)
|
||||||
{
|
{
|
||||||
(void)length;
|
(void)len;
|
||||||
/* Read target description */
|
/* Read target description */
|
||||||
if ((!cur_target) && last_target) {
|
if ((!cur_target) && last_target) {
|
||||||
/* Attach to last target if detached. */
|
/* Attach to last target if detached. */
|
||||||
@ -451,51 +435,24 @@ static void exec_q_feature_read(const char *packet, const size_t length)
|
|||||||
handle_q_string_reply(target_tdesc(cur_target), packet);
|
handle_q_string_reply(target_tdesc(cur_target), packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_q_crc(const char *packet, const size_t length)
|
static void exec_q_crc(const char *packet, int len)
|
||||||
{
|
{
|
||||||
(void)length;
|
(void)len;
|
||||||
uint32_t addr;
|
uint32_t addr, alen;
|
||||||
uint32_t addr_length;
|
if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) {
|
||||||
if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &addr_length) == 2) {
|
|
||||||
if (!cur_target) {
|
if (!cur_target) {
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t crc;
|
uint32_t crc;
|
||||||
if (generic_crc32(cur_target, &crc, addr, addr_length))
|
int res = generic_crc32(cur_target, &crc, addr, alen);
|
||||||
|
if (res)
|
||||||
gdb_putpacketz("E03");
|
gdb_putpacketz("E03");
|
||||||
else
|
else
|
||||||
gdb_putpacket_f("C%lx", crc);
|
gdb_putpacket_f("C%lx", crc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* qC queries are for the current thread. We don't support threads but GDB 11 and 12 require this,
|
|
||||||
* 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;
|
|
||||||
(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[]=
|
static const cmd_executer q_commands[]=
|
||||||
{
|
{
|
||||||
{"qRcmd,", exec_q_rcmd},
|
{"qRcmd,", exec_q_rcmd},
|
||||||
@ -503,60 +460,34 @@ static const cmd_executer q_commands[]=
|
|||||||
{"qXfer:memory-map:read::", exec_q_memory_map},
|
{"qXfer:memory-map:read::", exec_q_memory_map},
|
||||||
{"qXfer:features:read:target.xml:",exec_q_feature_read},
|
{"qXfer:features:read:target.xml:",exec_q_feature_read},
|
||||||
{"qCRC:", exec_q_crc},
|
{"qCRC:", exec_q_crc},
|
||||||
{"qC", exec_q_c},
|
|
||||||
{"qfThreadInfo", exec_q_thread_info},
|
|
||||||
{"qsThreadInfo", exec_q_thread_info},
|
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handle_kill_target(void)
|
static void
|
||||||
|
handle_q_packet(char *packet, int len)
|
||||||
{
|
{
|
||||||
if (cur_target) {
|
if (exec_command(packet, len, q_commands))
|
||||||
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;
|
return;
|
||||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
||||||
gdb_putpacket("", 0);
|
gdb_putpacket("", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_v_packet(char *packet, const size_t plen)
|
static void
|
||||||
|
handle_v_packet(char *packet, int plen)
|
||||||
{
|
{
|
||||||
uint32_t addr = 0;
|
unsigned long addr, len;
|
||||||
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);
|
cur_target = target_attach_n(addr, &gdb_controller);
|
||||||
if(cur_target) {
|
if(cur_target) {
|
||||||
morse(NULL, false);
|
morse(NULL, false);
|
||||||
/*
|
gdb_putpacketz("T05");
|
||||||
* We don't actually support threads, but GDB 11 and 12 can't work without
|
|
||||||
* us saying we attached to thread 1.. see the following for the low-down of this:
|
|
||||||
* https://sourceware.org/bugzilla/show_bug.cgi?id=28405
|
|
||||||
* https://sourceware.org/bugzilla/show_bug.cgi?id=28874
|
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2021-December/184171.html
|
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2022-April/188058.html
|
|
||||||
* https://sourceware.org/pipermail/gdb-patches/2022-July/190869.html
|
|
||||||
*/
|
|
||||||
gdb_putpacketz("T05thread:1;");
|
|
||||||
} else
|
} else
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
|
|
||||||
} else if (!strncmp(packet, "vKill;", 6)) {
|
|
||||||
/* 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)) {
|
} else if (!strncmp(packet, "vRun", 4)) {
|
||||||
/* Parse command line for get_cmdline semihosting call */
|
/* Parse command line for get_cmdline semihosting call */
|
||||||
char cmdline[83];
|
char cmdline[83];
|
||||||
@ -587,10 +518,6 @@ static void handle_v_packet(char *packet, const size_t plen)
|
|||||||
}
|
}
|
||||||
break;
|
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_set_cmdline(cur_target, cmdline);
|
||||||
@ -612,9 +539,9 @@ static void handle_v_packet(char *packet, const size_t plen)
|
|||||||
} else
|
} else
|
||||||
gdb_putpacketz("E01");
|
gdb_putpacketz("E01");
|
||||||
|
|
||||||
} else if (sscanf(packet, "vFlashErase:%08" PRIx32 ",%08" PRIx32, &addr, &len) == 2) {
|
} else if (sscanf(packet, "vFlashErase:%08lx,%08lx", &addr, &len) == 2) {
|
||||||
/* Erase Flash Memory */
|
/* Erase Flash Memory */
|
||||||
DEBUG_GDB("Flash Erase %08" PRIX32 " %08" PRIX32 "\n", addr, len);
|
DEBUG_GDB("Flash Erase %08lX %08lX\n", addr, len);
|
||||||
if (!cur_target) {
|
if (!cur_target) {
|
||||||
gdb_putpacketz("EFF");
|
gdb_putpacketz("EFF");
|
||||||
return;
|
return;
|
||||||
@ -633,11 +560,11 @@ static void handle_v_packet(char *packet, const size_t plen)
|
|||||||
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_GDB("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(cur_target, addr, (void*)packet + bin, len) == 0)
|
||||||
gdb_putpacketz("OK");
|
gdb_putpacketz("OK");
|
||||||
else {
|
else {
|
||||||
flash_mode = 0;
|
flash_mode = 0;
|
||||||
@ -649,30 +576,24 @@ static void handle_v_packet(char *packet, const size_t plen)
|
|||||||
gdb_putpacketz(target_flash_done(cur_target) ? "EFF" : "OK");
|
gdb_putpacketz(target_flash_done(cur_target) ? "EFF" : "OK");
|
||||||
flash_mode = 0;
|
flash_mode = 0;
|
||||||
|
|
||||||
} else if (!strcmp(packet, "vStopped")) {
|
|
||||||
if (gdb_needs_detach_notify) {
|
|
||||||
gdb_putpacketz("W00");
|
|
||||||
gdb_needs_detach_notify = false;
|
|
||||||
} else
|
|
||||||
gdb_putpacketz("OK");
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
||||||
gdb_putpacket("", 0);
|
gdb_putpacket("", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_z_packet(char *packet, const size_t plen)
|
static void
|
||||||
|
handle_z_packet(char *packet, int plen)
|
||||||
{
|
{
|
||||||
(void)plen;
|
(void)plen;
|
||||||
|
|
||||||
uint32_t type;
|
uint8_t set = (packet[0] == 'Z') ? 1 : 0;
|
||||||
uint32_t len;
|
int type, len;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
sscanf(packet, "%*[zZ]%" PRIu32 ",%08" PRIx32 ",%" PRIu32, &type, &addr, &len);
|
int ret;
|
||||||
|
|
||||||
int ret = 0;
|
sscanf(packet, "%*[zZ]%d,%08" PRIX32 ",%d", &type, &addr, &len);
|
||||||
if (packet[0] == 'Z')
|
if(set)
|
||||||
ret = target_breakwatch_set(cur_target, type, addr, len);
|
ret = target_breakwatch_set(cur_target, type, addr, len);
|
||||||
else
|
else
|
||||||
ret = target_breakwatch_clear(cur_target, type, addr, len);
|
ret = target_breakwatch_clear(cur_target, type, addr, len);
|
||||||
|
@ -30,11 +30,12 @@
|
|||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
size_t gdb_getpacket(char *packet, size_t size)
|
int gdb_getpacket(char *packet, int 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 */
|
||||||
@ -43,8 +44,7 @@ size_t gdb_getpacket(char *packet, size_t size)
|
|||||||
* start ('$') or a BMP remote packet start ('!').
|
* start ('$') or a BMP remote packet start ('!').
|
||||||
*/
|
*/
|
||||||
do {
|
do {
|
||||||
/* Smells like bad code */
|
packet[0] = gdb_if_getchar();
|
||||||
packet[0] = (char)gdb_if_getchar();
|
|
||||||
if (packet[0] == 0x04)
|
if (packet[0] == 0x04)
|
||||||
return 1;
|
return 1;
|
||||||
} while ((packet[0] != '$') && (packet[0] != REMOTE_SOM));
|
} while ((packet[0] != '$') && (packet[0] != REMOTE_SOM));
|
||||||
@ -52,19 +52,18 @@ size_t gdb_getpacket(char *packet, size_t size)
|
|||||||
if (packet[0] == REMOTE_SOM) {
|
if (packet[0] == REMOTE_SOM) {
|
||||||
/* This is probably a remote control packet
|
/* This is probably a remote control packet
|
||||||
* - get and handle it */
|
* - get and handle it */
|
||||||
offset = 0;
|
i = 0;
|
||||||
bool gettingRemotePacket = true;
|
bool gettingRemotePacket = true;
|
||||||
while (gettingRemotePacket) {
|
while (gettingRemotePacket) {
|
||||||
/* Smells like bad code */
|
c = gdb_if_getchar();
|
||||||
const char c = (char)gdb_if_getchar();
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case REMOTE_SOM: /* Oh dear, packet restarts */
|
case REMOTE_SOM: /* Oh dear, packet restarts */
|
||||||
offset = 0;
|
i = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REMOTE_EOM: /* Complete packet for processing */
|
case REMOTE_EOM: /* Complete packet for processing */
|
||||||
packet[offset] = 0;
|
packet[i] = 0;
|
||||||
remotePacketProcess(offset, packet);
|
remotePacketProcess(i, packet);
|
||||||
gettingRemotePacket = false;
|
gettingRemotePacket = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -74,8 +73,8 @@ size_t gdb_getpacket(char *packet, size_t size)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (offset < size) {
|
if (i < size) {
|
||||||
packet[offset++] = c;
|
packet[i++] = c;
|
||||||
} else {
|
} else {
|
||||||
/* Who knows what is going on...return to normality */
|
/* Who knows what is going on...return to normality */
|
||||||
gettingRemotePacket = false;
|
gettingRemotePacket = false;
|
||||||
@ -93,32 +92,30 @@ size_t gdb_getpacket(char *packet, size_t size)
|
|||||||
#endif
|
#endif
|
||||||
} while (packet[0] != '$');
|
} while (packet[0] != '$');
|
||||||
|
|
||||||
offset = 0;
|
i = 0;
|
||||||
csum = 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) /* Oh shit */
|
||||||
if (offset == size)
|
|
||||||
break;
|
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 */
|
||||||
@ -129,20 +126,20 @@ size_t gdb_getpacket(char *packet, size_t size)
|
|||||||
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
|
#if PC_HOSTED == 1
|
||||||
DEBUG_GDB_WIRE("%s : ", __func__);
|
DEBUG_GDB_WIRE("%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_GDB_WIRE("%c", c);
|
||||||
else
|
else
|
||||||
DEBUG_GDB_WIRE("\\x%02X", c);
|
DEBUG_GDB_WIRE("\\x%02X", c);
|
||||||
}
|
}
|
||||||
DEBUG_GDB_WIRE("\n");
|
DEBUG_GDB_WIRE("\n");
|
||||||
#endif
|
#endif
|
||||||
return offset;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gdb_next_char(char c, unsigned char *csum)
|
static void gdb_next_char(char c, unsigned char *csum)
|
||||||
@ -164,19 +161,21 @@ static void gdb_next_char(char c, unsigned char *csum)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2)
|
void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int size2)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char csum;
|
||||||
char xmit_csum[3];
|
char xmit_csum[3];
|
||||||
size_t tries = 0;
|
int tries = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
DEBUG_GDB_WIRE("%s : ", __func__);
|
||||||
unsigned char csum = 0;
|
csum = 0;
|
||||||
gdb_if_putchar('$', 0);
|
gdb_if_putchar('$', 0);
|
||||||
|
|
||||||
for (size_t i = 0; i < size1; ++i)
|
for (i = 0; i < size1; ++i)
|
||||||
gdb_next_char(packet1[i], &csum);
|
gdb_next_char(packet1[i], &csum);
|
||||||
for (size_t i = 0; i < size2; ++i)
|
for (i = 0; i < size2; ++i)
|
||||||
gdb_next_char(packet2[i], &csum);
|
gdb_next_char(packet2[i], &csum);
|
||||||
|
|
||||||
gdb_if_putchar('#', 0);
|
gdb_if_putchar('#', 0);
|
||||||
@ -184,42 +183,28 @@ void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size
|
|||||||
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");
|
DEBUG_GDB_WIRE("\n");
|
||||||
} while (gdb_if_getchar_to(2000) != '+' && tries++ < 3);
|
} while ((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdb_putpacket(const char *packet, size_t size)
|
void gdb_putpacket(const char *packet, int size)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char csum;
|
||||||
char xmit_csum[3];
|
char xmit_csum[3];
|
||||||
size_t tries = 0;
|
int tries = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
DEBUG_GDB_WIRE("%s : ", __func__);
|
||||||
unsigned char csum = 0;
|
csum = 0;
|
||||||
gdb_if_putchar('$', 0);
|
gdb_if_putchar('$', 0);
|
||||||
for (size_t i = 0; i < size; ++i)
|
for (i = 0; i < size; ++i)
|
||||||
gdb_next_char(packet[i], &csum);
|
gdb_next_char(packet[i], &csum);
|
||||||
gdb_if_putchar('#', 0);
|
gdb_if_putchar('#', 0);
|
||||||
snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum);
|
snprintf(xmit_csum, sizeof(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");
|
DEBUG_GDB_WIRE("\n");
|
||||||
} while (gdb_if_getchar_to(2000) != '+' && tries++ < 3);
|
} 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, ...)
|
void gdb_putpacket_f(const char *fmt, ...)
|
||||||
|
@ -26,16 +26,16 @@
|
|||||||
|
|
||||||
static const char hexdigits[] = "0123456789abcdef";
|
static const char hexdigits[] = "0123456789abcdef";
|
||||||
|
|
||||||
char *hexify(char *hex, const void *buf, const size_t size)
|
char * hexify(char *hex, const void *buf, size_t size)
|
||||||
{
|
{
|
||||||
char *dst = hex;
|
char *tmp = hex;
|
||||||
const uint8_t *const src = buf;
|
const uint8_t *b = buf;
|
||||||
|
|
||||||
for (size_t idx = 0; idx < size; ++idx) {
|
while (size--) {
|
||||||
*dst++ = hexdigits[src[idx] >> 4];
|
*tmp++ = hexdigits[*b >> 4];
|
||||||
*dst++ = hexdigits[src[idx] & 0xF];
|
*tmp++ = hexdigits[*b++ & 0xF];
|
||||||
}
|
}
|
||||||
*dst++ = 0;
|
*tmp++ = 0;
|
||||||
|
|
||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
@ -43,18 +43,20 @@ 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(void *buf, const char *hex, size_t size)
|
||||||
{
|
{
|
||||||
uint8_t *const dst = buf;
|
uint8_t *b = buf;
|
||||||
for (size_t idx = 0; idx < size; ++idx, hex += 2) {
|
while (size--) {
|
||||||
dst[idx] = (unhex_digit(hex[0]) << 4) | unhex_digit(hex[1]);
|
*b = unhex_digit(*hex++) << 4;
|
||||||
|
*b++ |= unhex_digit(*hex++);
|
||||||
}
|
}
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,19 +21,18 @@
|
|||||||
#ifndef __GDB_PACKET_H
|
#ifndef __GDB_PACKET_H
|
||||||
#define __GDB_PACKET_H
|
#define __GDB_PACKET_H
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
size_t gdb_getpacket(char *packet, size_t size);
|
int gdb_getpacket(char *packet, int size);
|
||||||
void gdb_putpacket(const char *packet, size_t size);
|
void gdb_putpacket(const char *packet, int size);
|
||||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2);
|
void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int 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 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_voutf(const char *fmt, va_list);
|
||||||
void gdb_outf(const char *fmt, ...);
|
void gdb_outf(const char *fmt, ...);
|
||||||
|
|
||||||
#endif
|
#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
|
|
@ -78,7 +78,7 @@ static const struct usb_device_descriptor dev_desc = {
|
|||||||
.bDeviceClass = 0xEF, /* Miscellaneous Device */
|
.bDeviceClass = 0xEF, /* Miscellaneous Device */
|
||||||
.bDeviceSubClass = 2, /* Common Class */
|
.bDeviceSubClass = 2, /* Common Class */
|
||||||
.bDeviceProtocol = 1, /* Interface Association */
|
.bDeviceProtocol = 1, /* Interface Association */
|
||||||
#if defined(SAMD21E17) || defined(LM4F)
|
#ifdef LM4F
|
||||||
.bMaxPacketSize0 = 64, /*Fixed for icdi*/
|
.bMaxPacketSize0 = 64, /*Fixed for icdi*/
|
||||||
#else
|
#else
|
||||||
.bMaxPacketSize0 = 32,
|
.bMaxPacketSize0 = 32,
|
||||||
@ -435,8 +435,6 @@ static void dfu_detach_complete(usbd_device *dev, struct usb_setup_data *req)
|
|||||||
/* Reset core to enter bootloader */
|
/* Reset core to enter bootloader */
|
||||||
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
|
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
|
||||||
scb_reset_core();
|
scb_reset_core();
|
||||||
#else
|
|
||||||
scb_reset_system();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,7 +524,7 @@ static void cdcacm_set_config(usbd_device *dev, uint16_t wValue)
|
|||||||
configured = wValue;
|
configured = wValue;
|
||||||
|
|
||||||
/* GDB interface */
|
/* GDB interface */
|
||||||
#if defined(STM32F4) || defined(LM4F) || defined(SAMD)
|
#if defined(STM32F4) || defined(LM4F)
|
||||||
usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK,
|
usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK,
|
||||||
CDCACM_PACKET_SIZE, gdb_usb_out_cb);
|
CDCACM_PACKET_SIZE, gdb_usb_out_cb);
|
||||||
#else
|
#else
|
||||||
@ -582,7 +580,6 @@ void cdcacm_init(void)
|
|||||||
|
|
||||||
nvic_set_priority(USB_IRQ, IRQ_PRI_USB);
|
nvic_set_priority(USB_IRQ, IRQ_PRI_USB);
|
||||||
nvic_enable_irq(USB_IRQ);
|
nvic_enable_irq(USB_IRQ);
|
||||||
usbd_disconnect(usbdev, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void USB_ISR(void)
|
void USB_ISR(void)
|
||||||
|
@ -233,9 +233,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ((dp = readdir(dir)) != NULL) {
|
while ((dp = readdir(dir)) != NULL) {
|
||||||
if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) ||
|
if ((strstr(dp->d_name, BMP_IDSTRING)) &&
|
||||||
strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) ||
|
|
||||||
strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) &&
|
|
||||||
(strstr(dp->d_name, "-if00"))) {
|
(strstr(dp->d_name, "-if00"))) {
|
||||||
i++;
|
i++;
|
||||||
char type[256], version[256], serial[256];
|
char type[256], version[256], serial[256];
|
||||||
@ -268,9 +266,7 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
|||||||
dir = opendir(DEVICE_BY_ID);
|
dir = opendir(DEVICE_BY_ID);
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((dp = readdir(dir)) != NULL) {
|
while ((dp = readdir(dir)) != NULL) {
|
||||||
if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) ||
|
if ((strstr(dp->d_name, BMP_IDSTRING)) &&
|
||||||
strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) ||
|
|
||||||
strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) &&
|
|
||||||
(strstr(dp->d_name, "-if00"))) {
|
(strstr(dp->d_name, "-if00"))) {
|
||||||
i++;
|
i++;
|
||||||
char type[256], version[256], serial[256];
|
char type[256], version[256], serial[256];
|
||||||
|
@ -30,10 +30,6 @@
|
|||||||
#include "gdb_if.h"
|
#include "gdb_if.h"
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
#include "rtt_if.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "bmp_remote.h"
|
#include "bmp_remote.h"
|
||||||
#include "bmp_hosted.h"
|
#include "bmp_hosted.h"
|
||||||
#include "stlinkv2.h"
|
#include "stlinkv2.h"
|
||||||
@ -62,9 +58,6 @@ static void exit_function(void)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
rtt_if_exit();
|
|
||||||
#endif
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,9 +110,6 @@ void platform_init(int argc, char **argv)
|
|||||||
exit(cl_execute(&cl_opts));
|
exit(cl_execute(&cl_opts));
|
||||||
else {
|
else {
|
||||||
gdb_if_init();
|
gdb_if_init();
|
||||||
#ifdef ENABLE_RTT
|
|
||||||
rtt_if_init();
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <general.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <rtt_if.h>
|
|
||||||
|
|
||||||
/* maybe rewrite this as tcp server */
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
#include <termios.h>
|
|
||||||
|
|
||||||
/* linux */
|
|
||||||
static struct termios saved_ttystate;
|
|
||||||
static bool tty_saved = false;
|
|
||||||
|
|
||||||
/* set up and tear down */
|
|
||||||
|
|
||||||
int rtt_if_init()
|
|
||||||
{
|
|
||||||
struct termios ttystate;
|
|
||||||
tcgetattr(STDIN_FILENO, &saved_ttystate);
|
|
||||||
tty_saved = true;
|
|
||||||
tcgetattr(STDIN_FILENO, &ttystate);
|
|
||||||
ttystate.c_lflag &= ~ICANON;
|
|
||||||
ttystate.c_lflag &= ~ECHO;
|
|
||||||
ttystate.c_cc[VMIN] = 1;
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
|
|
||||||
int flags = fcntl(0, F_GETFL, 0);
|
|
||||||
fcntl(0, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rtt_if_exit()
|
|
||||||
{
|
|
||||||
if (tty_saved)
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &saved_ttystate);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write buffer to terminal */
|
|
||||||
|
|
||||||
uint32_t rtt_write(const char *buf, uint32_t len)
|
|
||||||
{
|
|
||||||
write(1, buf, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read character from terminal */
|
|
||||||
|
|
||||||
int32_t rtt_getchar()
|
|
||||||
{
|
|
||||||
char ch;
|
|
||||||
int len;
|
|
||||||
len = read(0, &ch, 1);
|
|
||||||
if (len == 1) return ch;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* true if no characters available */
|
|
||||||
|
|
||||||
bool rtt_nodata()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* windows, output only */
|
|
||||||
|
|
||||||
int rtt_if_init()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rtt_if_exit()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write buffer to terminal */
|
|
||||||
|
|
||||||
uint32_t rtt_write(const char *buf, uint32_t len)
|
|
||||||
{
|
|
||||||
write(1, buf, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read character from terminal */
|
|
||||||
|
|
||||||
int32_t rtt_getchar()
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* true if no characters available */
|
|
||||||
|
|
||||||
bool rtt_nodata()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,61 +0,0 @@
|
|||||||
CROSS_COMPILE ?= arm-none-eabi-
|
|
||||||
CC = $(CROSS_COMPILE)gcc
|
|
||||||
OBJCOPY = $(CROSS_COMPILE)objcopy
|
|
||||||
|
|
||||||
OPT_FLAGS := -Os
|
|
||||||
|
|
||||||
ifeq ($(ENABLE_DEBUG), 1)
|
|
||||||
CFLAGS += -DDEBUG_ME
|
|
||||||
CFLAGS += -DENABLE_DEBUG
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(CUSTOM_SER), 1)
|
|
||||||
CFLAGS += -DCUSTOM_SER
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(CONSOLE_NO_AUTO_CRLF), 1)
|
|
||||||
CFLAGS += -DCONSOLE_NO_AUTO_CRLF
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ENABLE_DEBUG), 1)
|
|
||||||
CFLAGS += -g3 -ggdb
|
|
||||||
endif
|
|
||||||
|
|
||||||
CFLAGS += -mthumb -mcpu=cortex-m0plus -DDFU_SERIAL_LENGTH=9 \
|
|
||||||
-DSAMD -DSAMD21E17 -DBLACKMAGIC -I../libopencm3/include \
|
|
||||||
-Iplatforms/samd -msoft-float -ffunction-sections -fdata-sections -MD
|
|
||||||
|
|
||||||
LINKER_SCRIPT="platforms/samd/samd.ld"
|
|
||||||
LDFLAGS = -mthumb -mcpu=cortex-m0plus -msoft-float -nostartfiles -lc \
|
|
||||||
$(CPU_FLAGS) -T$(LINKER_SCRIPT) -Wl,--gc-sections \
|
|
||||||
-L../libopencm3/lib -lopencm3_samd -lnosys -lm -lgcc
|
|
||||||
|
|
||||||
ifeq ($(ENABLE_DEBUG), 1)
|
|
||||||
LDFLAGS += --specs=rdimon.specs
|
|
||||||
else
|
|
||||||
LDFLAGS += --specs=nano.specs
|
|
||||||
endif
|
|
||||||
|
|
||||||
VPATH += platforms/samd
|
|
||||||
|
|
||||||
SRC += cdcacm.c \
|
|
||||||
timing.c \
|
|
||||||
traceswo.o \
|
|
||||||
usbuart.c \
|
|
||||||
|
|
||||||
all: blackmagic_full.bin blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex
|
|
||||||
|
|
||||||
blackmagic_dfu.bin : OBJCOPY_FLAGS := --pad-to 0x00002000 --gap-fill 0xFF -j .text -j .data
|
|
||||||
blackmagic_dfu.bin : LINKER_SCRIPT := "platforms/samd/samd_boot.ld"
|
|
||||||
|
|
||||||
blackmagic_dfu.elf: usbdfu.o
|
|
||||||
@echo " LD $@"
|
|
||||||
$(Q)$(CC) $^ -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
blackmagic_full.bin: blackmagic_dfu.bin blackmagic.bin
|
|
||||||
@echo " CAT $@"
|
|
||||||
$(Q)cp blackmagic.bin jeff-$(shell git describe --tags --always --dirty).bin
|
|
||||||
$(Q)cat $^ > $@
|
|
||||||
|
|
||||||
host_clean:
|
|
||||||
$(Q)$(RM) -f blackmagic.bin blackmagic_full.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex jeff*.bin
|
|
@ -1,346 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 Flirc Inc.
|
|
||||||
* Written by Jason Kotzin <jasonkotzin@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "gdb_if.h"
|
|
||||||
#include "cdcacm.h"
|
|
||||||
#include "usbuart.h"
|
|
||||||
#include "gdb_packet.h"
|
|
||||||
|
|
||||||
#include <libopencm3/sam/d/nvic.h>
|
|
||||||
#include <libopencm3/sam/d/port.h>
|
|
||||||
#include <libopencm3/sam/d/gclk.h>
|
|
||||||
#include <libopencm3/sam/d/pm.h>
|
|
||||||
#include <libopencm3/sam/d/uart.h>
|
|
||||||
#include <libopencm3/sam/d/adc.h>
|
|
||||||
|
|
||||||
#include <libopencm3/cm3/systick.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
|
|
||||||
#include <libopencm3/sam/d/tc.h>
|
|
||||||
#include <libopencm3/sam/d/eic.h>
|
|
||||||
|
|
||||||
static struct gclk_hw clock = {
|
|
||||||
.gclk0 = SRC_DFLL48M,
|
|
||||||
.gclk1 = SRC_OSC8M,
|
|
||||||
.gclk1_div = 30, /* divide clock for ADC */
|
|
||||||
.gclk2 = SRC_OSC8M,
|
|
||||||
.gclk2_div = 100, /* divide clock for TC */
|
|
||||||
.gclk3 = SRC_DFLL48M,
|
|
||||||
.gclk4 = SRC_DFLL48M,
|
|
||||||
.gclk5 = SRC_DFLL48M,
|
|
||||||
.gclk6 = SRC_DFLL48M,
|
|
||||||
.gclk7 = SRC_DFLL48M,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void trace_tick(void);
|
|
||||||
|
|
||||||
uint8_t running_status;
|
|
||||||
static volatile uint32_t time_ms;
|
|
||||||
|
|
||||||
uint8_t button_pressed;
|
|
||||||
|
|
||||||
uint8_t tpwr_enabled;
|
|
||||||
|
|
||||||
void sys_tick_handler(void)
|
|
||||||
{
|
|
||||||
if(running_status)
|
|
||||||
gpio_toggle(LED_PORT, LED_IDLE_RUN);
|
|
||||||
|
|
||||||
time_ms += 10;
|
|
||||||
|
|
||||||
uart_pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t platform_time_ms(void)
|
|
||||||
{
|
|
||||||
return time_ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_setup(void)
|
|
||||||
{
|
|
||||||
/* Enable USB */
|
|
||||||
INSERTBF(PM_APBBMASK_USB, 1, PM->apbbmask);
|
|
||||||
|
|
||||||
/* enable clocking to usb */
|
|
||||||
set_periph_clk(GCLK0, GCLK_ID_USB);
|
|
||||||
periph_clk_en(GCLK_ID_USB, 1);
|
|
||||||
|
|
||||||
gpio_config_special(PORTA, GPIO24, SOC_GPIO_PERIPH_G);
|
|
||||||
gpio_config_special(PORTA, GPIO25, SOC_GPIO_PERIPH_G);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t timing_init(void)
|
|
||||||
{
|
|
||||||
uint32_t cal = 0;
|
|
||||||
|
|
||||||
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
|
|
||||||
systick_set_reload(4800); /* Interrupt us at 10 Hz */
|
|
||||||
systick_interrupt_enable();
|
|
||||||
|
|
||||||
systick_counter_enable();
|
|
||||||
return cal;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void adc_init(void)
|
|
||||||
{
|
|
||||||
gpio_config_special(ADC_PORT, ADC_POS_PIN, SOC_GPIO_PERIPH_B); /* +input */
|
|
||||||
gpio_config_special(ADC_PORT, ADC_REF_PIN, SOC_GPIO_PERIPH_B); /* reference */
|
|
||||||
|
|
||||||
set_periph_clk(GCLK1, GCLK_ID_ADC);
|
|
||||||
periph_clk_en(GCLK_ID_ADC, 1);
|
|
||||||
|
|
||||||
adc_enable(ADC_REFCTRL_VREFA,0,ADC_INPUTCTRL_GND,ADC_MUXPOS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void counter_init(void)
|
|
||||||
{
|
|
||||||
/* enable bus and clock */
|
|
||||||
INSERTBF(PM_APBCMASK_TC3, 1, PM->apbcmask);
|
|
||||||
|
|
||||||
set_periph_clk(GCLK2, GCLK_ID_TC3);
|
|
||||||
periph_clk_en(GCLK_ID_TC3, 1);
|
|
||||||
|
|
||||||
/* reset */
|
|
||||||
tc_reset(3);
|
|
||||||
|
|
||||||
/* set CTRLA.PRESCALER and CTRLA.PRESYNC */
|
|
||||||
tc_config_ctrla(3,1,(7<<8));
|
|
||||||
|
|
||||||
/* set CC0 (approx. 5 seconds delay) */
|
|
||||||
tc_set_cc(3,0,1000);
|
|
||||||
|
|
||||||
/* enable MC0 interrupt */
|
|
||||||
tc_enable_interrupt(3,(1<<4));
|
|
||||||
nvic_enable_irq(NVIC_TC3_IRQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void button_init(void)
|
|
||||||
{
|
|
||||||
gpio_config_special(BUTTON_PORT, BUTTON_PIN, SOC_GPIO_PERIPH_A);
|
|
||||||
|
|
||||||
/* enable bus and clock */
|
|
||||||
INSERTBF(PM_APBAMASK_EIC, 1, PM->apbamask);
|
|
||||||
|
|
||||||
set_periph_clk(GCLK0, GCLK_ID_EIC);
|
|
||||||
periph_clk_en(GCLK_ID_EIC, 1);
|
|
||||||
|
|
||||||
/* configure r/f edge, enable filtering */
|
|
||||||
eic_set_config(15, 1, EIC_FALL);
|
|
||||||
|
|
||||||
/* enable the IEC */
|
|
||||||
eic_enable(1);
|
|
||||||
|
|
||||||
/* enable interrupts */
|
|
||||||
eic_enable_interrupt((1<<15));
|
|
||||||
nvic_enable_irq(NVIC_EIC_IRQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_init(void)
|
|
||||||
{
|
|
||||||
gclk_init(&clock);
|
|
||||||
|
|
||||||
usb_setup();
|
|
||||||
|
|
||||||
gpio_config_output(LED_PORT, LED_IDLE_RUN, 0);
|
|
||||||
gpio_config_output(TMS_PORT, TMS_PIN, 0);
|
|
||||||
gpio_config_output(TCK_PORT, TCK_PIN, 0);
|
|
||||||
gpio_config_output(TDI_PORT, TDI_PIN, 0);
|
|
||||||
|
|
||||||
gpio_config_output(TMS_PORT, TMS_DIR_PIN, 0);
|
|
||||||
gpio_set(TMS_PORT, TMS_DIR_PIN);
|
|
||||||
|
|
||||||
/* enable both input and output with pullup disabled by default */
|
|
||||||
PORT_DIRSET(SWDIO_PORT) = SWDIO_PIN;
|
|
||||||
PORT_PINCFG(SWDIO_PORT, SWDIO_PIN_NUM) |= GPIO_PINCFG_INEN | GPIO_PINCFG_PULLEN;
|
|
||||||
gpio_clear(SWDIO_PORT, SWDIO_PIN);
|
|
||||||
|
|
||||||
/* configure swclk_pin as output */
|
|
||||||
gpio_config_output(SWCLK_PORT, SWCLK_PIN, 0);
|
|
||||||
gpio_clear(SWCLK_PORT, SWCLK_PIN);
|
|
||||||
|
|
||||||
gpio_config_input(TDO_PORT, TDO_PIN, 0);
|
|
||||||
gpio_config_output(SRST_PORT, SRST_PIN, GPIO_OUT_FLAG_DEFAULT_HIGH);
|
|
||||||
gpio_clear(SRST_PORT, SRST_PIN);
|
|
||||||
|
|
||||||
/* setup uart led, disable by default*/
|
|
||||||
gpio_config_output(LED_PORT_UART, LED_UART, 0);//GPIO_OUT_FLAG_DEFAULT_HIGH);
|
|
||||||
gpio_clear(LED_PORT_UART, LED_UART);
|
|
||||||
|
|
||||||
/* set up TPWR */
|
|
||||||
gpio_set(PWR_BR_PORT, PWR_BR_PIN);
|
|
||||||
gpio_config_output(PWR_BR_PORT, PWR_BR_PIN, GPIO_OUT_FLAG_DEFAULT_HIGH);
|
|
||||||
|
|
||||||
timing_init();
|
|
||||||
usbuart_init();
|
|
||||||
cdcacm_init();
|
|
||||||
adc_init();
|
|
||||||
counter_init();
|
|
||||||
button_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t srst_state;
|
|
||||||
void platform_srst_set_val(bool assert)
|
|
||||||
{
|
|
||||||
volatile int i;
|
|
||||||
if (!assert) {
|
|
||||||
gpio_clear(SRST_PORT, SRST_PIN);
|
|
||||||
for(i = 0; i < 10000; i++) asm("nop");
|
|
||||||
srst_state = 0;
|
|
||||||
} else {
|
|
||||||
gpio_set(SRST_PORT, SRST_PIN);
|
|
||||||
srst_state = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_srst_get_val(void)
|
|
||||||
{
|
|
||||||
//return gpio_get(SRST_PORT, SRST_PIN) != 0;
|
|
||||||
return srst_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_target_get_power(void)
|
|
||||||
{
|
|
||||||
//return !gpio_get(PWR_BR_PORT, PWR_BR_PIN);
|
|
||||||
return tpwr_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_target_set_power(bool power)
|
|
||||||
{
|
|
||||||
gpio_set_val(PWR_BR_PORT, PWR_BR_PIN, !power);
|
|
||||||
tpwr_enabled = power;
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_delay(uint32_t ms)
|
|
||||||
{
|
|
||||||
platform_timeout timeout;
|
|
||||||
platform_timeout_set(&timeout, ms);
|
|
||||||
while (!platform_timeout_is_expired(&timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t platform_target_voltage_sense(void)
|
|
||||||
{
|
|
||||||
uint32_t val;
|
|
||||||
adc_start();
|
|
||||||
|
|
||||||
while (!(1&(ADC->intflag)));
|
|
||||||
val = ((485*adc_result())>>12); /* 330 without divider, 485 with it */
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *platform_target_voltage(void)
|
|
||||||
{
|
|
||||||
uint32_t voltage;
|
|
||||||
static char out[] = "0.0V";
|
|
||||||
|
|
||||||
adc_start();
|
|
||||||
|
|
||||||
voltage = platform_target_voltage_sense();
|
|
||||||
|
|
||||||
out[0] = '0' + (char)(voltage/100);
|
|
||||||
out[2] = '0' + (char)((voltage/10) % 10);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *serial_no_read(char *s)
|
|
||||||
{
|
|
||||||
#ifdef CUSTOM_SER
|
|
||||||
s[0] = 'J';
|
|
||||||
s[1] = 'E';
|
|
||||||
s[2] = 'F';
|
|
||||||
s[3] = 'F';
|
|
||||||
return s;
|
|
||||||
#else
|
|
||||||
int i;
|
|
||||||
|
|
||||||
volatile uint32_t unique_id = *(volatile uint32_t *)0x0080A00C +
|
|
||||||
*(volatile uint32_t *)0x0080A040 +
|
|
||||||
*(volatile uint32_t *)0x0080A044 +
|
|
||||||
*(volatile uint32_t *)0x0080A048;
|
|
||||||
|
|
||||||
/* Fetch serial number from chip's unique ID */
|
|
||||||
for(i = 0; i < 8; i++) {
|
|
||||||
s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < 8; i++)
|
|
||||||
if(s[i] > '9')
|
|
||||||
s[i] += 'A' - '9' - 1;
|
|
||||||
s[8] = 0;
|
|
||||||
|
|
||||||
return s;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_serial(void)
|
|
||||||
{
|
|
||||||
gdb_outf("0x%08X%08X%08X%08X\n", *(volatile uint32_t *)0x0080A048,
|
|
||||||
*(volatile uint32_t *)0x0080A044,
|
|
||||||
*(volatile uint32_t *)0x0080A040,
|
|
||||||
*(volatile uint32_t *)0x0080A00C);
|
|
||||||
}
|
|
||||||
|
|
||||||
void platform_request_boot(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void eic_isr(void)
|
|
||||||
{
|
|
||||||
if (!button_pressed){
|
|
||||||
/* set to rising-edge detection */
|
|
||||||
eic_set_config(15, 1, EIC_RISE);
|
|
||||||
|
|
||||||
/* enable counter */
|
|
||||||
tc_enable(3,1);
|
|
||||||
|
|
||||||
button_pressed = 1;
|
|
||||||
} else {
|
|
||||||
/* set to falling-edge detection */
|
|
||||||
eic_set_config(15, 1, EIC_FALL);
|
|
||||||
|
|
||||||
/* disable and reset counter */
|
|
||||||
tc_enable(3,0);
|
|
||||||
|
|
||||||
button_pressed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear the interrupt */
|
|
||||||
eic_clr_interrupt((1<<15));
|
|
||||||
}
|
|
||||||
|
|
||||||
void tc3_isr(void)
|
|
||||||
{
|
|
||||||
if (tc_interrupt_flag(3) & 16)
|
|
||||||
scb_reset_system();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t swd_delay_cnt = 0;
|
|
||||||
|
|
||||||
void platform_max_frequency_set(uint32_t freq)
|
|
||||||
{
|
|
||||||
(void)freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t platform_max_frequency_get(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 Flirc Inc.
|
|
||||||
* Written by Jason Kotzin <jasonkotzin@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __PLATFORM_H
|
|
||||||
#define __PLATFORM_H
|
|
||||||
|
|
||||||
#include <libopencm3/sam/d/port.h>
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
#include <libopencm3/sam/d/nvmctrl.h>
|
|
||||||
|
|
||||||
#include "timing.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
//#define PLATFORM_HAS_DEBUG
|
|
||||||
//#define USBUART_DEBUG
|
|
||||||
#define PLATFORM_HAS_UART_WHEN_SWDP
|
|
||||||
#define PLATFORM_HAS_POWER_SWITCH
|
|
||||||
#define PLATFORM_HAS_BOOTLOADER
|
|
||||||
#define PLATFORM_HAS_PRINTSERIAL
|
|
||||||
|
|
||||||
//#define BOARD_IDENT "Black Magic Probe (SAMD), (Firmware " FIRMWARE_VERSION ")"
|
|
||||||
#define BOARD_IDENT_DFU "Black Magic (Upgrade) for Launchpad, (Firmware " FIRMWARE_VERSION ")"
|
|
||||||
//#define DFU_IDENT "Black Magic Firmware Upgrade (SAMD)"
|
|
||||||
#define DFU_IFACE_STRING "hid"
|
|
||||||
|
|
||||||
#define BOARD_IDENT_UPD "Black Magic (DFU Upgrade), SAMD21, (Firmware " FIRMWARE_VERSION ")"
|
|
||||||
#define UPD_IFACE_STRING "@Internal Flash /0x00000000/1*008Ka,15*8Kg"
|
|
||||||
|
|
||||||
#define PLATFORM_IDENT " "
|
|
||||||
|
|
||||||
extern uint8_t running_status;
|
|
||||||
extern uint32_t swd_delay_cnt;
|
|
||||||
|
|
||||||
#ifdef DEBUG_ME
|
|
||||||
|
|
||||||
#define LED_PORT PORTA
|
|
||||||
#define LED_IDLE_RUN GPIO11
|
|
||||||
#define LED_ERROR GPIO10
|
|
||||||
|
|
||||||
#define TMS_PORT PORTA
|
|
||||||
#define TMS_PIN GPIO1
|
|
||||||
#define TMS_DIR_PIN GPIO5
|
|
||||||
|
|
||||||
#define TCK_PORT PORTA
|
|
||||||
#define TCK_PIN GPIO2
|
|
||||||
|
|
||||||
#define TDI_PORT PORTA
|
|
||||||
#define TDI_PIN GPIO16
|
|
||||||
|
|
||||||
#define TDO_PORT PORTA
|
|
||||||
#define TDO_PIN GPIO19
|
|
||||||
|
|
||||||
#define SWO_PORT PORTA
|
|
||||||
#define SWO_PIN GPIO6
|
|
||||||
|
|
||||||
#define SWDIO_PORT PORTA
|
|
||||||
#define SWDIO_PIN TMS_PIN
|
|
||||||
#define SWDIO_PIN_NUM 1
|
|
||||||
|
|
||||||
#define SWCLK_PORT PORTA
|
|
||||||
#define SWCLK_PIN TCK_PIN
|
|
||||||
|
|
||||||
#define SRST_PORT PORTA
|
|
||||||
#define SRST_PIN GPIO7
|
|
||||||
|
|
||||||
#define LED_PORT_UART PORTA
|
|
||||||
#define LED_UART GPIO12
|
|
||||||
|
|
||||||
#define UART_TX_PIN GPIO8
|
|
||||||
#define UART_RX_PIN GPIO9
|
|
||||||
#define UART_PERIPH SOC_GPIO_PERIPH_C
|
|
||||||
#define UART_PERIPH_2 SOC_GPIO_PERIPH_C
|
|
||||||
|
|
||||||
#define ADC_PORT PORTA
|
|
||||||
#define ADC_REF_PIN GPIO3
|
|
||||||
#define ADC_POS_PIN GPIO4
|
|
||||||
#define ADC_MUXPOS 4
|
|
||||||
|
|
||||||
#define BUTTON_PORT PORTA
|
|
||||||
#define BUTTON_PIN GPIO27
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* Hardware definitions... */
|
|
||||||
#define JTAG_PORT PORTA
|
|
||||||
#define TDI_PORT JTAG_PORT
|
|
||||||
#define TMS_DIR_PORT JTAG_PORT
|
|
||||||
#define TMS_PORT JTAG_PORT
|
|
||||||
#define TCK_PORT JTAG_PORT
|
|
||||||
#define TDO_PORT JTAG_PORT
|
|
||||||
#define TMS_DIR_PIN GPIO15
|
|
||||||
#define TMS_PIN GPIO0
|
|
||||||
#define TCK_PIN GPIO6
|
|
||||||
#define TDI_PIN GPIO16
|
|
||||||
#define TDO_PIN GPIO19
|
|
||||||
|
|
||||||
#define SWDIO_DIR_PORT JTAG_PORT
|
|
||||||
#define SWDIO_PORT JTAG_PORT
|
|
||||||
#define SWCLK_PORT JTAG_PORT
|
|
||||||
#define SWDIO_DIR_PIN TMS_DIR_PIN
|
|
||||||
#define SWDIO_PIN TMS_PIN
|
|
||||||
#define SWDIO_PIN_NUM 0
|
|
||||||
#define SWCLK_PIN TCK_PIN
|
|
||||||
|
|
||||||
#define TRST_PORT PORTA
|
|
||||||
#define TRST_PIN GPIO27
|
|
||||||
#define PWR_BR_PORT PORTA
|
|
||||||
#define PWR_BR_PIN GPIO28
|
|
||||||
#define SRST_PORT PORTA
|
|
||||||
#define SRST_PIN GPIO8
|
|
||||||
#define SRST_SENSE_PORT GPIOA
|
|
||||||
#define SRST_SENSE_PIN GPIO9
|
|
||||||
#define TRGT_SENSE GPIO2
|
|
||||||
|
|
||||||
#define LED_PORT PORTA
|
|
||||||
#define LED_PORT_UART PORTA
|
|
||||||
#define LED_0 GPIO10
|
|
||||||
#define LED_1 GPIO11
|
|
||||||
#define LED_2 GPIO14
|
|
||||||
//#define LED_2 GPIO13
|
|
||||||
#define LED_UART LED_1 /* Orange */
|
|
||||||
#define LED_IDLE_RUN LED_0 /* Yellow */
|
|
||||||
#define LED_ERROR LED_2 /* Red */
|
|
||||||
|
|
||||||
#define UART_TX_PIN GPIO4
|
|
||||||
#define UART_RX_PIN GPIO7
|
|
||||||
#define UART_PERIPH SOC_GPIO_PERIPH_D
|
|
||||||
#define UART_PERIPH_2 SOC_GPIO_PERIPH_C
|
|
||||||
|
|
||||||
#define SWO_PORT JTAG_PORT
|
|
||||||
#define SWO_PIN SWD_PIN
|
|
||||||
|
|
||||||
#define ADC_PORT PORTA
|
|
||||||
#define ADC_REF_PIN GPIO3
|
|
||||||
#define ADC_POS_PIN GPIO2
|
|
||||||
#define ADC_MUXPOS 0
|
|
||||||
|
|
||||||
#define BUTTON_PORT PORTA
|
|
||||||
#define BUTTON_PIN GPIO27
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TMS_SET_MODE() { \
|
|
||||||
gpio_config_output(TMS_PORT, TMS_PIN, 0); \
|
|
||||||
gpio_set(TMS_PORT, TMS_DIR_PIN); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SWDIO_MODE_FLOAT() do { \
|
|
||||||
PORT_DIRCLR(SWDIO_PORT) = SWDIO_PIN; \
|
|
||||||
gpio_set(SWDIO_PORT, SWDIO_PIN); \
|
|
||||||
gpio_clear(TMS_PORT, TMS_DIR_PIN); \
|
|
||||||
} while(0)
|
|
||||||
#define SWDIO_MODE_DRIVE() do { \
|
|
||||||
PORT_DIRSET(SWDIO_PORT) = SWDIO_PIN; \
|
|
||||||
gpio_set(TMS_PORT, TMS_DIR_PIN); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
/* extern usbd_driver samd21_usb_driver; */
|
|
||||||
#define USB_DRIVER samd21_usb_driver
|
|
||||||
#define USB_IRQ NVIC_USB_IRQ
|
|
||||||
#define USB_ISR usb_isr
|
|
||||||
|
|
||||||
#define IRQ_PRI_USB (2 << 4)
|
|
||||||
|
|
||||||
#define INLINE_GPIO
|
|
||||||
|
|
||||||
#define gpio_set_val(port, pin, val) do { \
|
|
||||||
if(val) \
|
|
||||||
_gpio_set((port), (pin)); \
|
|
||||||
else \
|
|
||||||
_gpio_clear((port), (pin)); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#ifdef INLINE_GPIO
|
|
||||||
static inline void _gpio_set(uint32_t gpioport, uint32_t gpios)
|
|
||||||
{
|
|
||||||
PORT_OUTSET(gpioport) = gpios;
|
|
||||||
}
|
|
||||||
#define gpio_set _gpio_set
|
|
||||||
|
|
||||||
static inline void _gpio_clear(uint32_t gpioport, uint32_t gpios)
|
|
||||||
{
|
|
||||||
PORT_OUTCLR(gpioport) = gpios;
|
|
||||||
}
|
|
||||||
#define gpio_clear _gpio_clear
|
|
||||||
|
|
||||||
static inline uint16_t _gpio_get(uint32_t gpioport, uint32_t gpios)
|
|
||||||
{
|
|
||||||
return (uint32_t)PORT_IN(gpioport) & gpios;
|
|
||||||
}
|
|
||||||
#define gpio_get _gpio_get
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEBUG(...)
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uart_pop(void);
|
|
||||||
int usbuart_convert_tdio(uint32_t arg);
|
|
||||||
int usbuart_convert_tdio_enabled(void);
|
|
||||||
void print_serial(void);
|
|
||||||
#endif
|
|
@ -1,351 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the libopencm3 project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
|
|
||||||
*
|
|
||||||
* This library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <libopencm3/sam/d/gclk.h>
|
|
||||||
#include <libopencm3/sam/d/port.h>
|
|
||||||
#include <libopencm3/sam/d/nvmctrl.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
#include <libopencm3/usb/dfu.h>
|
|
||||||
|
|
||||||
#include <libopencm3/sam/d/nvic.h>
|
|
||||||
#include <libopencm3/sam/d/pm.h>
|
|
||||||
#include <libopencm3/sam/d/bitfield.h>
|
|
||||||
#include <libopencm3/sam/d/usb.h>
|
|
||||||
|
|
||||||
//#define APP_ADDRESS 0x08002000
|
|
||||||
//#define APP_ADDRESS 0x00002000
|
|
||||||
#define APP_ADDRESS 0x00002000
|
|
||||||
//#define APP_ADDRESS 0x00004000
|
|
||||||
|
|
||||||
/* Commands sent with wBlockNum == 0 as per ST implementation. */
|
|
||||||
#define CMD_SETADDR 0x21
|
|
||||||
#define CMD_ERASE 0x41
|
|
||||||
|
|
||||||
#define BUTTON_PORT PORTA
|
|
||||||
#define BUTTON_PIN GPIO27
|
|
||||||
|
|
||||||
#define BUF_SIZE 4096
|
|
||||||
|
|
||||||
static struct gclk_hw clock = {
|
|
||||||
.gclk0 = SRC_DFLL48M,
|
|
||||||
.gclk1 = SRC_OSC8M,
|
|
||||||
/* clock 1 has 8 divider, clock should be over 1khz for 1ms timer */
|
|
||||||
.gclk1_div = 100,
|
|
||||||
.gclk2 = SRC_DFLL48M,
|
|
||||||
.gclk3 = SRC_DFLL48M,
|
|
||||||
.gclk3_div = 1,
|
|
||||||
.gclk4 = SRC_OSC8M,
|
|
||||||
.gclk4_div = 1,
|
|
||||||
.gclk5 = SRC_DFLL48M,
|
|
||||||
.gclk6 = SRC_DFLL48M,
|
|
||||||
.gclk7 = SRC_DFLL48M,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* We need a special large control buffer for this device: */
|
|
||||||
uint8_t usbd_control_buffer[BUF_SIZE];
|
|
||||||
|
|
||||||
static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
uint8_t buf[sizeof(usbd_control_buffer)];
|
|
||||||
uint16_t len;
|
|
||||||
uint32_t addr;
|
|
||||||
uint16_t blocknum;
|
|
||||||
} prog;
|
|
||||||
|
|
||||||
const struct usb_device_descriptor dev = {
|
|
||||||
.bLength = USB_DT_DEVICE_SIZE,
|
|
||||||
.bDescriptorType = USB_DT_DEVICE,
|
|
||||||
.bcdUSB = 0x0200,
|
|
||||||
.bDeviceClass = 0,
|
|
||||||
.bDeviceSubClass = 0,
|
|
||||||
.bDeviceProtocol = 0,
|
|
||||||
.bMaxPacketSize0 = 64,
|
|
||||||
.idVendor = 0x1D50,
|
|
||||||
.idProduct = 0x6017,
|
|
||||||
.bcdDevice = 0x0200,
|
|
||||||
.iManufacturer = 1,
|
|
||||||
.iProduct = 2,
|
|
||||||
.iSerialNumber = 3,
|
|
||||||
.bNumConfigurations = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct usb_dfu_descriptor dfu_function = {
|
|
||||||
.bLength = sizeof(struct usb_dfu_descriptor),
|
|
||||||
.bDescriptorType = DFU_FUNCTIONAL,
|
|
||||||
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
|
|
||||||
.wDetachTimeout = 255,
|
|
||||||
.wTransferSize = BUF_SIZE,
|
|
||||||
.bcdDFUVersion = 0x011A,
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct usb_interface_descriptor iface = {
|
|
||||||
.bLength = USB_DT_INTERFACE_SIZE,
|
|
||||||
.bDescriptorType = USB_DT_INTERFACE,
|
|
||||||
.bInterfaceNumber = 0,
|
|
||||||
.bAlternateSetting = 0,
|
|
||||||
.bNumEndpoints = 0,
|
|
||||||
.bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
|
|
||||||
.bInterfaceSubClass = 1,
|
|
||||||
.bInterfaceProtocol = 2,
|
|
||||||
|
|
||||||
/* The ST Microelectronics DfuSe application needs this string.
|
|
||||||
* The format isn't documented... */
|
|
||||||
.iInterface = 4,
|
|
||||||
|
|
||||||
.extra = &dfu_function,
|
|
||||||
.extralen = sizeof(dfu_function),
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct usb_interface ifaces[] = {{
|
|
||||||
.num_altsetting = 1,
|
|
||||||
.altsetting = &iface,
|
|
||||||
}};
|
|
||||||
|
|
||||||
const struct usb_config_descriptor config = {
|
|
||||||
.bLength = USB_DT_CONFIGURATION_SIZE,
|
|
||||||
.bDescriptorType = USB_DT_CONFIGURATION,
|
|
||||||
.wTotalLength = 0,
|
|
||||||
.bNumInterfaces = 1,
|
|
||||||
.bConfigurationValue = 1,
|
|
||||||
.iConfiguration = 0,
|
|
||||||
.bmAttributes = 0xC0,
|
|
||||||
.bMaxPower = 0x32,
|
|
||||||
|
|
||||||
.interface = ifaces,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *usb_strings[] = {
|
|
||||||
"Black Sphere Technologies",
|
|
||||||
"DFU Demo",
|
|
||||||
"DEMO",
|
|
||||||
/* This string is used by ST Microelectronics' DfuSe utility. */
|
|
||||||
//"@Internal Flash /0x08000000/8*001Ka,56*001Kg",
|
|
||||||
"@Internal Flash /0x00000000/1*008Ka,15*008Kg",
|
|
||||||
//"@Internal Flash /0x00000000/1*0016Ka,15*0016Kg",
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t usbdfu_getstatus(uint32_t *bwPollTimeout)
|
|
||||||
{
|
|
||||||
switch (usbdfu_state) {
|
|
||||||
case STATE_DFU_DNLOAD_SYNC:
|
|
||||||
usbdfu_state = STATE_DFU_DNBUSY;
|
|
||||||
*bwPollTimeout = 100;
|
|
||||||
return DFU_STATUS_OK;
|
|
||||||
case STATE_DFU_MANIFEST_SYNC:
|
|
||||||
/* Device will reset when read is complete. */
|
|
||||||
usbdfu_state = STATE_DFU_MANIFEST;
|
|
||||||
return DFU_STATUS_OK;
|
|
||||||
default:
|
|
||||||
return DFU_STATUS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usbdfu_getstatus_complete(usbd_device *usbd_dev, struct usb_setup_data *req)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
(void)req;
|
|
||||||
(void)usbd_dev;
|
|
||||||
|
|
||||||
switch (usbdfu_state) {
|
|
||||||
case STATE_DFU_DNBUSY:
|
|
||||||
//flash_unlock();
|
|
||||||
if (prog.blocknum == 0) {
|
|
||||||
switch (prog.buf[0]) {
|
|
||||||
case CMD_ERASE:
|
|
||||||
{
|
|
||||||
uint32_t *dat = (uint32_t *)(prog.buf + 1);
|
|
||||||
nvmctrl_erase_row(*dat); //flash_erase_page(*dat);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CMD_SETADDR:
|
|
||||||
{
|
|
||||||
uint32_t *dat = (uint32_t *)(prog.buf + 1);
|
|
||||||
prog.addr = *dat;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) *
|
|
||||||
// dfu_function.wTransferSize);
|
|
||||||
uint32_t baseaddr = prog.addr;
|
|
||||||
//for (i = 0; i < prog.len; i += 2) {
|
|
||||||
//uint16_t *dat = (uint16_t *)(prog.buf + i);
|
|
||||||
//flash_program_half_word(baseaddr + i,
|
|
||||||
// *dat);
|
|
||||||
for (i = 0; i < BUF_SIZE; i += 256){
|
|
||||||
nvmctrl_erase_row(baseaddr+i);
|
|
||||||
nvmctrl_write_row(baseaddr+i, prog.buf+i);
|
|
||||||
}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
//flash_lock();
|
|
||||||
|
|
||||||
/* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */
|
|
||||||
usbdfu_state = STATE_DFU_DNLOAD_IDLE;
|
|
||||||
return;
|
|
||||||
case STATE_DFU_MANIFEST:
|
|
||||||
/* reset USB */
|
|
||||||
INSERTBF(USB_CTRLA_SWRST, 1, USB->ctrla);
|
|
||||||
/* jump to app */
|
|
||||||
if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
|
|
||||||
|
|
||||||
/* Set vector table base address. */
|
|
||||||
//SCB_VTOR = APP_ADDRESS & 0xFFFF;
|
|
||||||
SCB_VTOR = APP_ADDRESS;
|
|
||||||
/* Initialise master stack pointer. */
|
|
||||||
asm volatile("msr msp, %0"::"g"
|
|
||||||
(*(volatile uint32_t *)APP_ADDRESS));
|
|
||||||
/* Jump to application. */
|
|
||||||
(*(void (**)())(APP_ADDRESS + 4))();
|
|
||||||
}
|
|
||||||
|
|
||||||
//scb_reset_system();
|
|
||||||
return; /* Will never return. */
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum usbd_request_return_codes usbdfu_control_request(usbd_device *dev,
|
|
||||||
struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
|
|
||||||
void (**complete)(usbd_device *dev, struct usb_setup_data *req))
|
|
||||||
{
|
|
||||||
(void)dev;
|
|
||||||
|
|
||||||
if ((req->bmRequestType & 0x7F) != 0x21)
|
|
||||||
return 0; /* Only accept class request. */
|
|
||||||
|
|
||||||
switch (req->bRequest) {
|
|
||||||
case DFU_DNLOAD:
|
|
||||||
if ((len == NULL) || (*len == 0)) {
|
|
||||||
usbdfu_state = STATE_DFU_MANIFEST_SYNC;
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
/* Copy download data for use on GET_STATUS. */
|
|
||||||
prog.blocknum = req->wValue;
|
|
||||||
prog.len = *len;
|
|
||||||
memcpy(prog.buf, *buf, *len);
|
|
||||||
usbdfu_state = STATE_DFU_DNLOAD_SYNC;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case DFU_CLRSTATUS:
|
|
||||||
/* Clear error and return to dfuIDLE. */
|
|
||||||
if (usbdfu_state == STATE_DFU_ERROR)
|
|
||||||
usbdfu_state = STATE_DFU_IDLE;
|
|
||||||
return 1;
|
|
||||||
case DFU_ABORT:
|
|
||||||
/* Abort returns to dfuIDLE state. */
|
|
||||||
usbdfu_state = STATE_DFU_IDLE;
|
|
||||||
return 1;
|
|
||||||
case DFU_UPLOAD:
|
|
||||||
/* Upload not supported for now. */
|
|
||||||
return 0;
|
|
||||||
case DFU_GETSTATUS: {
|
|
||||||
uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */
|
|
||||||
(*buf)[0] = usbdfu_getstatus(&bwPollTimeout);
|
|
||||||
(*buf)[1] = bwPollTimeout & 0xFF;
|
|
||||||
(*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
|
|
||||||
(*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
|
|
||||||
(*buf)[4] = usbdfu_state;
|
|
||||||
(*buf)[5] = 0; /* iString not used here */
|
|
||||||
*len = 6;
|
|
||||||
*complete = usbdfu_getstatus_complete;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case DFU_GETSTATE:
|
|
||||||
/* Return state with no state transision. */
|
|
||||||
*buf[0] = usbdfu_state;
|
|
||||||
*len = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usbdfu_set_config(usbd_device *usbd_dev, uint16_t wValue)
|
|
||||||
{
|
|
||||||
(void)wValue;
|
|
||||||
|
|
||||||
usbd_register_control_callback(
|
|
||||||
usbd_dev,
|
|
||||||
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
|
|
||||||
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
|
|
||||||
usbdfu_control_request);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usb_setup(void)
|
|
||||||
{
|
|
||||||
/* Enable USB */
|
|
||||||
INSERTBF(PM_APBBMASK_USB, 1, PM->apbbmask);
|
|
||||||
|
|
||||||
/* enable clocking to usb */
|
|
||||||
set_periph_clk(GCLK0, GCLK_ID_USB);
|
|
||||||
periph_clk_en(GCLK_ID_USB, 1);
|
|
||||||
|
|
||||||
gpio_config_special(PORTA, GPIO24, SOC_GPIO_PERIPH_G);
|
|
||||||
gpio_config_special(PORTA, GPIO25, SOC_GPIO_PERIPH_G);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
usbd_device *usbd_dev;
|
|
||||||
|
|
||||||
gclk_init(&clock);
|
|
||||||
//rcc_periph_clock_enable(RCC_GPIOA);
|
|
||||||
//gpio_config_input(BUTTON_PORT,BUTTON_PIN,GPIO_IN_FLAG_PULLUP);
|
|
||||||
gpio_config_input(BUTTON_PORT,BUTTON_PIN,0);
|
|
||||||
|
|
||||||
nvmctrl_init(0,0);
|
|
||||||
|
|
||||||
usb_setup();
|
|
||||||
|
|
||||||
if (PM->rcause != (1<<6))
|
|
||||||
if (gpio_get(BUTTON_PORT, BUTTON_PIN)) {
|
|
||||||
//if (gpio_get(PORTA, GPIO27)) {
|
|
||||||
/* Boot the application if it's valid. */
|
|
||||||
if ((*(volatile uint32_t *)APP_ADDRESS & 0x2FFE0000) == 0x20000000) {
|
|
||||||
|
|
||||||
/* Set vector table base address. */
|
|
||||||
//SCB_VTOR = APP_ADDRESS & 0xFFFF;
|
|
||||||
SCB_VTOR = APP_ADDRESS;
|
|
||||||
/* Initialise master stack pointer. */
|
|
||||||
asm volatile("msr msp, %0"::"g"
|
|
||||||
(*(volatile uint32_t *)APP_ADDRESS));
|
|
||||||
/* Jump to application. */
|
|
||||||
(*(void (**)())(APP_ADDRESS + 4))();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usbd_dev = usbd_init(&samd21_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer));
|
|
||||||
usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
|
|
||||||
|
|
||||||
//nvic_enable_irq(NVIC_USB_IRQ);
|
|
||||||
|
|
||||||
/* Connect USB cable */
|
|
||||||
usbd_disconnect(usbd_dev, false);
|
|
||||||
|
|
||||||
//gpio_clear(GPIOC, GPIO11);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
usbd_poll(usbd_dev);
|
|
||||||
}
|
|
@ -285,7 +285,7 @@ static void adc_init(void)
|
|||||||
adc_set_single_conversion_mode(ADC1);
|
adc_set_single_conversion_mode(ADC1);
|
||||||
adc_disable_external_trigger_regular(ADC1);
|
adc_disable_external_trigger_regular(ADC1);
|
||||||
adc_set_right_aligned(ADC1);
|
adc_set_right_aligned(ADC1);
|
||||||
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_239DOT5CYC);
|
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC);
|
||||||
|
|
||||||
adc_power_on(ADC1);
|
adc_power_on(ADC1);
|
||||||
|
|
||||||
@ -316,8 +316,6 @@ uint32_t platform_target_voltage_sense(void)
|
|||||||
while (!adc_eoc(ADC1));
|
while (!adc_eoc(ADC1));
|
||||||
|
|
||||||
uint32_t val = adc_read_regular(ADC1); /* 0-4095 */
|
uint32_t val = adc_read_regular(ADC1); /* 0-4095 */
|
||||||
/* Clear EOC bit. The GD32F103 does not automatically reset it on ADC read. */
|
|
||||||
ADC_SR(ADC1) &= ~ADC_SR_EOC;
|
|
||||||
return (val * 99) / 8191;
|
return (val * 99) / 8191;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,103 +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 a transparent channel over which the GDB Remote
|
|
||||||
* Serial Debugging protocol is implemented. This implementation for STM32
|
|
||||||
* uses the USB CDC-ACM device bulk endpoints to implement the channel.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "gdb_if.h"
|
|
||||||
#include "cdcacm.h"
|
|
||||||
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
|
|
||||||
static volatile uint32_t head_out, tail_out;
|
|
||||||
static volatile uint32_t count_in;
|
|
||||||
static volatile uint8_t buffer_out[16*CDCACM_PACKET_SIZE];
|
|
||||||
static volatile uint8_t buffer_in[CDCACM_PACKET_SIZE];
|
|
||||||
|
|
||||||
void gdb_if_putchar(unsigned char c, int flush)
|
|
||||||
{
|
|
||||||
buffer_in[count_in++] = c;
|
|
||||||
if(flush || (count_in == CDCACM_PACKET_SIZE)) {
|
|
||||||
/* Refuse to send if USB isn't configured, and
|
|
||||||
* don't bother if nobody's listening */
|
|
||||||
if((cdcacm_get_config() != 1) || !cdcacm_get_dtr()) {
|
|
||||||
count_in = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while(usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT,
|
|
||||||
(uint8_t *)buffer_in, count_in) <= 0);
|
|
||||||
count_in = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gdb_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
(void)ep;
|
|
||||||
static uint8_t buf[CDCACM_PACKET_SIZE];
|
|
||||||
|
|
||||||
usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 1);
|
|
||||||
uint32_t count = usbd_ep_read_packet(dev, CDCACM_GDB_ENDPOINT,
|
|
||||||
(uint8_t *)buf, CDCACM_PACKET_SIZE);
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t idx;
|
|
||||||
for (idx=0; idx<count; idx++) {
|
|
||||||
buffer_out[head_out++ % sizeof(buffer_out)] = buf[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
usbd_ep_nak_set(dev, CDCACM_GDB_ENDPOINT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char gdb_if_getchar(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
while(tail_out == head_out) {
|
|
||||||
/* Detach if port closed */
|
|
||||||
if(!cdcacm_get_dtr())
|
|
||||||
return 0x04;
|
|
||||||
|
|
||||||
while(cdcacm_get_config() != 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer_out[tail_out++ % sizeof(buffer_out)];
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char gdb_if_getchar_to(int timeout)
|
|
||||||
{
|
|
||||||
platform_timeout t;
|
|
||||||
platform_timeout_set(&t, timeout);
|
|
||||||
|
|
||||||
if(head_out == tail_out) do {
|
|
||||||
/* Detach if port closed */
|
|
||||||
if(!cdcacm_get_dtr())
|
|
||||||
return 0x04;
|
|
||||||
|
|
||||||
while(cdcacm_get_config() != 1);
|
|
||||||
} while(!platform_timeout_is_expired(&t) && head_out == tail_out);
|
|
||||||
|
|
||||||
if(head_out != tail_out)
|
|
||||||
return gdb_if_getchar();
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "platform.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include "cdcacm.h"
|
|
||||||
#include "rtt.h"
|
|
||||||
#include "rtt_if.h"
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt terminal i/o
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* usb uart receive buffer */
|
|
||||||
static char recv_buf[RTT_DOWN_BUF_SIZE];
|
|
||||||
static uint32_t recv_head = 0;
|
|
||||||
static uint32_t recv_tail = 0;
|
|
||||||
|
|
||||||
/* data from host to target: number of free bytes in usb receive buffer */
|
|
||||||
inline static uint32_t recv_bytes_free()
|
|
||||||
{
|
|
||||||
if (recv_tail <= recv_head)
|
|
||||||
return sizeof(recv_buf) - recv_head + recv_tail - 1;
|
|
||||||
else
|
|
||||||
return recv_tail - recv_head - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* data from host to target: true if not enough free buffer space and we need to close flow control */
|
|
||||||
inline static bool recv_set_nak()
|
|
||||||
{
|
|
||||||
assert(sizeof(recv_buf) > 2 * CDCACM_PACKET_SIZE);
|
|
||||||
return recv_bytes_free() < 2 * CDCACM_PACKET_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* usbuart_usb_out_cb is called when usb uart has received new data for target.
|
|
||||||
this routine has to be fast */
|
|
||||||
|
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
(void)dev;
|
|
||||||
(void)ep;
|
|
||||||
char usb_buf[CDCACM_PACKET_SIZE];
|
|
||||||
|
|
||||||
/* close flow control while processing packet */
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 1);
|
|
||||||
|
|
||||||
const uint16_t len = usbd_ep_read_packet(usbdev, CDCACM_UART_ENDPOINT, usb_buf, CDCACM_PACKET_SIZE);
|
|
||||||
|
|
||||||
/* skip flag: drop packet if not enough free buffer space */
|
|
||||||
if (rtt_flag_skip && len > recv_bytes_free()) {
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy data to recv_buf */
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
uint32_t next_recv_head = (recv_head + 1) % sizeof(recv_buf);
|
|
||||||
if (next_recv_head == recv_tail)
|
|
||||||
break; /* overflow */
|
|
||||||
recv_buf[recv_head] = usb_buf[i];
|
|
||||||
recv_head = next_recv_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* block flag: flow control closed if not enough free buffer space */
|
|
||||||
if (!(rtt_flag_block && recv_set_nak()))
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt host to target: read one character */
|
|
||||||
int32_t rtt_getchar()
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
if (recv_head == recv_tail)
|
|
||||||
return -1;
|
|
||||||
retval = recv_buf[recv_tail];
|
|
||||||
recv_tail = (recv_tail + 1) % sizeof(recv_buf);
|
|
||||||
|
|
||||||
/* open flow control if enough free buffer space */
|
|
||||||
if (!recv_set_nak())
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt host to target: true if no characters available for reading */
|
|
||||||
bool rtt_nodata()
|
|
||||||
{
|
|
||||||
return recv_head == recv_tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt target to host: write string */
|
|
||||||
uint32_t rtt_write(const char *buf, uint32_t len)
|
|
||||||
{
|
|
||||||
if (len != 0 && usbdev && cdcacm_get_config() && cdcacm_get_dtr()) {
|
|
||||||
for (uint32_t p = 0; p < len; p += CDCACM_PACKET_SIZE) {
|
|
||||||
uint32_t plen = MIN(CDCACM_PACKET_SIZE, len - p);
|
|
||||||
while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, buf + p, plen) <= 0);
|
|
||||||
}
|
|
||||||
/* flush 64-byte packet on full-speed */
|
|
||||||
if (CDCACM_PACKET_SIZE == 64 && (len % CDCACM_PACKET_SIZE) == 0)
|
|
||||||
while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, NULL, 0) <= 0);
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the libopenstm32 project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define memory regions. */
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
rom (rx) : ORIGIN = 0x00002000, LENGTH = 120K /* 128k - 8k for bootloader */
|
|
||||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Include the common ld script from libopenstm32. */
|
|
||||||
INCLUDE cortex-m-generic.ld
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the libopenstm32 project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define memory regions. */
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K /* 128k - 8k for bootloader */
|
|
||||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Include the common ld script from libopenstm32. */
|
|
||||||
INCLUDE cortex-m-generic.ld
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 Flirc Inc.
|
|
||||||
* Written by Jason Kotzin <jasonkotzin@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This file implements capture of the TRACESWO output.
|
|
||||||
*
|
|
||||||
* ARM DDI 0403D - ARMv7M Architecture Reference Manual
|
|
||||||
* ARM DDI 0337I - Cortex-M3 Technical Reference Manual
|
|
||||||
* ARM DDI 0314H - CoreSight Components Technical Reference Manual
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "cdcacm.h"
|
|
||||||
|
|
||||||
void traceswo_init(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void traceswo_baud(unsigned int baud)
|
|
||||||
{
|
|
||||||
baud++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace_buf_push(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace_buf_drain(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
ep ++;
|
|
||||||
if (dev == NULL)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace_tick(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TRACEUART_ISR(void)
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,251 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2018 Flirc Inc.
|
|
||||||
* Written by Jason Kotzin <jasonkotzin@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "cdcacm.h"
|
|
||||||
|
|
||||||
#include <libopencm3/sam/d/bitfield.h>
|
|
||||||
#include <libopencm3/sam/d/gclk.h>
|
|
||||||
#include <libopencm3/sam/d/pm.h>
|
|
||||||
#include <libopencm3/sam/d/port.h>
|
|
||||||
#include <libopencm3/sam/d/nvic.h>
|
|
||||||
#include <libopencm3/sam/d/uart.h>
|
|
||||||
|
|
||||||
#include <libopencm3/cm3/cortex.h>
|
|
||||||
#include <libopencm3/cm3/nvic.h>
|
|
||||||
#include <libopencm3/cm3/scb.h>
|
|
||||||
#include <libopencm3/cm3/scs.h>
|
|
||||||
#include <libopencm3/cm3/systick.h>
|
|
||||||
|
|
||||||
#include <libopencm3/usb/usbd.h>
|
|
||||||
#include <libopencm3/usb/cdc.h>
|
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
|
|
||||||
#define Q_SIZE 1024
|
|
||||||
|
|
||||||
/* Active USART number */
|
|
||||||
static uint8_t USART_NUM = 0;
|
|
||||||
|
|
||||||
/* Current Baud Rate setting */
|
|
||||||
static uint32_t current_baud = 115200;
|
|
||||||
|
|
||||||
usbd_device * usbdev;
|
|
||||||
|
|
||||||
/* input and output ring buffer */
|
|
||||||
struct {
|
|
||||||
char buf[Q_SIZE];
|
|
||||||
volatile size_t head, tail;
|
|
||||||
} rx, tx;
|
|
||||||
|
|
||||||
#ifndef ENABLE_RTT
|
|
||||||
/* non blocking putc function */
|
|
||||||
static void usart_putc(char c)
|
|
||||||
{
|
|
||||||
#ifdef CONSOLE_NO_AUTO_CRLF
|
|
||||||
if (c == '\n')
|
|
||||||
usart_putc('\r');
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (qfull(tx.head, tx.tail, Q_SIZE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
cm_disable_interrupts();
|
|
||||||
tx.buf[tx.head] = c;
|
|
||||||
tx.head = qinc(tx.head, Q_SIZE);
|
|
||||||
cm_enable_interrupts();
|
|
||||||
|
|
||||||
/* kick the transmitter to restart interrupts */
|
|
||||||
usart_enable_tx_interrupt(USART_NUM);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void usbuart_init(void)
|
|
||||||
{
|
|
||||||
/* enable gpios */
|
|
||||||
gpio_config_special(PORTA, UART_TX_PIN, UART_PERIPH); /* tx pin */
|
|
||||||
gpio_config_special(PORTA, UART_RX_PIN, UART_PERIPH); /* rx pin */
|
|
||||||
|
|
||||||
/* enable clocking to sercom0 */
|
|
||||||
set_periph_clk(GCLK0, GCLK_ID_SERCOM0_CORE);
|
|
||||||
periph_clk_en(GCLK_ID_SERCOM0_CORE, 1);
|
|
||||||
|
|
||||||
//usart_enable(USART_NUM, current_baud);
|
|
||||||
usart_setup(USART_NUM, current_baud);
|
|
||||||
#ifndef DEBUG_ME
|
|
||||||
usart_set_pads(USART_NUM, 3, 0); /* bm-sam uses different pads */
|
|
||||||
#endif
|
|
||||||
usart_enable(USART_NUM, 0); /* baud==0 so setup is skipped */
|
|
||||||
|
|
||||||
usart_enable_rx_interrupt(USART_NUM);
|
|
||||||
usart_enable_tx_interrupt(USART_NUM);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t convert_tdio_enabled;
|
|
||||||
int usbuart_convert_tdio(uint32_t arg)
|
|
||||||
{
|
|
||||||
|
|
||||||
(void) arg;
|
|
||||||
|
|
||||||
convert_tdio_enabled = arg;
|
|
||||||
|
|
||||||
if (!convert_tdio_enabled) {
|
|
||||||
usart_disable(1);
|
|
||||||
USART_NUM = 0;
|
|
||||||
usbuart_init();
|
|
||||||
return current_baud;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio_config_special(PORTA, TDI_PIN, UART_PERIPH_2); /* TX */
|
|
||||||
gpio_config_special(PORTA, TDO_PIN, UART_PERIPH_2); /* RX */
|
|
||||||
|
|
||||||
/* disable USART0 (we will be using USART1 now) */
|
|
||||||
usart_disable(0);
|
|
||||||
|
|
||||||
USART_NUM = 1;
|
|
||||||
|
|
||||||
/* Select and Enable system clock */
|
|
||||||
set_periph_clk(GCLK0, GCLK_ID_SERCOM1_CORE);
|
|
||||||
periph_clk_en(GCLK_ID_SERCOM1_CORE, 1);
|
|
||||||
|
|
||||||
usart_setup(1, current_baud);
|
|
||||||
usart_set_pads(1, 3, 0); /* uses different pads than the default */
|
|
||||||
usart_enable(1, 0); /* baud==0 so setup is skipped */
|
|
||||||
|
|
||||||
usart_enable_rx_interrupt(1);
|
|
||||||
usart_enable_tx_interrupt(1);
|
|
||||||
|
|
||||||
return current_baud;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int usbuart_convert_tdio_enabled(void)
|
|
||||||
{
|
|
||||||
return convert_tdio_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
|
|
||||||
{
|
|
||||||
uint8_t sbmode = (coding->bCharFormat == 2) ? 1 : 0;
|
|
||||||
uint8_t parity = (coding->bParityType == 1) ? 0 : 1;
|
|
||||||
uint8_t form = (coding->bParityType) ? 1 : 0;
|
|
||||||
uint8_t chsize = (form) ? coding->bDataBits + 1 : coding->bDataBits;
|
|
||||||
|
|
||||||
usart_disable(USART_NUM);
|
|
||||||
|
|
||||||
/* set baud rate */
|
|
||||||
usart_set_baudrate(USART_NUM, coding->dwDTERate);
|
|
||||||
|
|
||||||
/* set data size, stop mode, and parity */
|
|
||||||
usart_set_chsize(USART_NUM, chsize);
|
|
||||||
usart_set_sbmode(USART_NUM, sbmode);
|
|
||||||
usart_set_parity(USART_NUM, parity, form);
|
|
||||||
|
|
||||||
usart_enable(USART_NUM, 0);
|
|
||||||
|
|
||||||
current_baud = coding->dwDTERate;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ENABLE_RTT
|
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
(void)ep;
|
|
||||||
|
|
||||||
char buf[CDCACM_PACKET_SIZE];
|
|
||||||
int len = usbd_ep_read_packet(dev, CDCACM_UART_ENDPOINT,
|
|
||||||
buf, CDCACM_PACKET_SIZE);
|
|
||||||
|
|
||||||
gpio_set(LED_PORT_UART, LED_UART);
|
|
||||||
for(int i = 0; i < len; i++) {
|
|
||||||
usart_putc(buf[i]);
|
|
||||||
}
|
|
||||||
gpio_clear(LED_PORT_UART, LED_UART);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* run by our systick timer */
|
|
||||||
void uart_pop(void)
|
|
||||||
{
|
|
||||||
if (cdcacm_get_config() != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!qempty(rx.head, rx.tail)) {
|
|
||||||
if (usbd_ep_write_packet(usbdev, 0x83, &rx.buf[rx.tail], 1) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rx.tail = qinc(rx.tail, Q_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
(void) dev;
|
|
||||||
(void) ep;
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************** UART Interrupt Handlers *************************/
|
|
||||||
static void uart_rx_irq(void)
|
|
||||||
{
|
|
||||||
char c = UART(USART_NUM)->data;
|
|
||||||
|
|
||||||
/* bug?, need to re-enable rx complete interrupt */
|
|
||||||
INSERTBF(UART_INTENSET_RXC, 1, UART(USART_NUM)->intenset);
|
|
||||||
|
|
||||||
if (!qfull(rx.head, rx.tail, Q_SIZE)) {
|
|
||||||
rx.buf[rx.head] = c;
|
|
||||||
rx.head = qinc(rx.head, Q_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uart_tx_irq(void)
|
|
||||||
{
|
|
||||||
if (!qempty(tx.head, tx.tail)) {
|
|
||||||
usart_send(USART_NUM, tx.buf[tx.tail]);
|
|
||||||
tx.tail = qinc(tx.tail, Q_SIZE);
|
|
||||||
} else {
|
|
||||||
usart_disable_tx_interrupt(USART_NUM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sercom0_isr(void)
|
|
||||||
{
|
|
||||||
/* Turn on LED */
|
|
||||||
gpio_set(LED_PORT_UART, LED_UART);
|
|
||||||
|
|
||||||
if (GETBF(UART_INTFLAG_RXC, UART(USART_NUM)->intflag))
|
|
||||||
uart_rx_irq();
|
|
||||||
|
|
||||||
if (GETBF(UART_INTFLAG_DRE, UART(USART_NUM)->intflag))
|
|
||||||
uart_tx_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sercom1_isr(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Turn on LED */
|
|
||||||
gpio_set(LED_PORT_UART, LED_UART);
|
|
||||||
|
|
||||||
if (GETBF(UART_INTFLAG_RXC, UART(USART_NUM)->intflag))
|
|
||||||
uart_rx_irq();
|
|
||||||
|
|
||||||
if (GETBF(UART_INTFLAG_DRE, UART(USART_NUM)->intflag))
|
|
||||||
uart_tx_irq();
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "platform.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include "cdcacm.h"
|
|
||||||
#include "rtt.h"
|
|
||||||
#include "rtt_if.h"
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt terminal i/o
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* usb uart receive buffer */
|
|
||||||
static char recv_buf[RTT_DOWN_BUF_SIZE];
|
|
||||||
static uint32_t recv_head = 0;
|
|
||||||
static uint32_t recv_tail = 0;
|
|
||||||
|
|
||||||
/* data from host to target: number of free bytes in usb receive buffer */
|
|
||||||
inline static uint32_t recv_bytes_free()
|
|
||||||
{
|
|
||||||
if (recv_tail <= recv_head)
|
|
||||||
return sizeof(recv_buf) - recv_head + recv_tail - 1;
|
|
||||||
else
|
|
||||||
return recv_tail - recv_head - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* data from host to target: true if not enough free buffer space and we need to close flow control */
|
|
||||||
inline static bool recv_set_nak()
|
|
||||||
{
|
|
||||||
assert(sizeof(recv_buf) > 2 * CDCACM_PACKET_SIZE);
|
|
||||||
return recv_bytes_free() < 2 * CDCACM_PACKET_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* usbuart_usb_out_cb is called when usb uart has received new data for target.
|
|
||||||
this routine has to be fast */
|
|
||||||
|
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|
||||||
{
|
|
||||||
(void)dev;
|
|
||||||
(void)ep;
|
|
||||||
char usb_buf[CDCACM_PACKET_SIZE];
|
|
||||||
|
|
||||||
/* close flow control while processing packet */
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 1);
|
|
||||||
|
|
||||||
const uint16_t len = usbd_ep_read_packet(usbdev, CDCACM_UART_ENDPOINT, usb_buf, CDCACM_PACKET_SIZE);
|
|
||||||
|
|
||||||
/* skip flag: drop packet if not enough free buffer space */
|
|
||||||
if (rtt_flag_skip && len > recv_bytes_free()) {
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy data to recv_buf */
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
uint32_t next_recv_head = (recv_head + 1) % sizeof(recv_buf);
|
|
||||||
if (next_recv_head == recv_tail)
|
|
||||||
break; /* overflow */
|
|
||||||
recv_buf[recv_head] = usb_buf[i];
|
|
||||||
recv_head = next_recv_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* block flag: flow control closed if not enough free buffer space */
|
|
||||||
if (!(rtt_flag_block && recv_set_nak()))
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt host to target: read one character */
|
|
||||||
int32_t rtt_getchar()
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
if (recv_head == recv_tail)
|
|
||||||
return -1;
|
|
||||||
retval = recv_buf[recv_tail];
|
|
||||||
recv_tail = (recv_tail + 1) % sizeof(recv_buf);
|
|
||||||
|
|
||||||
/* open flow control if enough free buffer space */
|
|
||||||
if (!recv_set_nak())
|
|
||||||
usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt host to target: true if no characters available for reading */
|
|
||||||
bool rtt_nodata()
|
|
||||||
{
|
|
||||||
return recv_head == recv_tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* rtt target to host: write string */
|
|
||||||
uint32_t rtt_write(const char *buf, uint32_t len)
|
|
||||||
{
|
|
||||||
if (len != 0 && usbdev && cdcacm_get_config() && cdcacm_get_dtr()) {
|
|
||||||
for (uint32_t p = 0; p < len; p += CDCACM_PACKET_SIZE) {
|
|
||||||
uint32_t plen = MIN(CDCACM_PACKET_SIZE, len - p);
|
|
||||||
while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, buf + p, plen) <= 0);
|
|
||||||
}
|
|
||||||
/* flush 64-byte packet on full-speed */
|
|
||||||
if (CDCACM_PACKET_SIZE == 64 && (len % CDCACM_PACKET_SIZE) == 0)
|
|
||||||
while(usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, NULL, 0) <= 0);
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
@ -259,7 +259,6 @@ static void usbuart_change_dma_tx_buf(void)
|
|||||||
buf_tx_act_idx ^= 1;
|
buf_tx_act_idx ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ENABLE_RTT
|
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)ep;
|
(void)ep;
|
||||||
@ -302,7 +301,6 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|||||||
if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE)
|
if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE)
|
||||||
usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0);
|
usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USBUART_DEBUG
|
#ifdef USBUART_DEBUG
|
||||||
int usbuart_debug_write(const char *buf, size_t len)
|
int usbuart_debug_write(const char *buf, size_t len)
|
||||||
|
@ -99,7 +99,6 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ENABLE_RTT
|
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)ep;
|
(void)ep;
|
||||||
@ -111,7 +110,7 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
|||||||
for(int i = 0; i < len; i++)
|
for(int i = 0; i < len; i++)
|
||||||
uart_send_blocking(USBUART, buf[i]);
|
uart_send_blocking(USBUART, buf[i]);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
|
456
src/rtt.c
456
src/rtt.c
@ -1,456 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Black Magic Debug project.
|
|
||||||
*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "general.h"
|
|
||||||
#include "platform.h"
|
|
||||||
#include "gdb_packet.h"
|
|
||||||
#include "target.h"
|
|
||||||
#include "target/target_internal.h"
|
|
||||||
#include "rtt.h"
|
|
||||||
#include "rtt_if.h"
|
|
||||||
|
|
||||||
bool rtt_enabled = false;
|
|
||||||
bool rtt_found = false;
|
|
||||||
static bool rtt_halt = false; // true if rtt needs to halt target to access memory
|
|
||||||
uint32_t rtt_cbaddr = 0;
|
|
||||||
bool rtt_auto_channel = true;
|
|
||||||
struct rtt_channel_struct rtt_channel[MAX_RTT_CHAN];
|
|
||||||
|
|
||||||
uint32_t rtt_min_poll_ms = 8; /* 8 ms */
|
|
||||||
uint32_t rtt_max_poll_ms = 256; /* 0.256 s */
|
|
||||||
uint32_t rtt_max_poll_errs = 10;
|
|
||||||
static uint32_t poll_ms;
|
|
||||||
static uint32_t poll_errs;
|
|
||||||
static uint32_t last_poll_ms;
|
|
||||||
/* flags for data from host to target */
|
|
||||||
bool rtt_flag_skip = false;
|
|
||||||
bool rtt_flag_block = false;
|
|
||||||
|
|
||||||
typedef enum rtt_retval {
|
|
||||||
RTT_OK,
|
|
||||||
RTT_IDLE,
|
|
||||||
RTT_ERR
|
|
||||||
} rtt_retval;
|
|
||||||
|
|
||||||
#ifdef RTT_IDENT
|
|
||||||
#define Q(x) #x
|
|
||||||
#define QUOTE(x) Q(x)
|
|
||||||
char rtt_ident[16] = QUOTE(RTT_IDENT);
|
|
||||||
#else
|
|
||||||
char rtt_ident[16] = {0};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* usb uart transmit buffer */
|
|
||||||
static char xmit_buf[RTT_UP_BUF_SIZE];
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt control block
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint32_t fastsrch(target *cur_target)
|
|
||||||
{
|
|
||||||
const uint32_t m = 16;
|
|
||||||
const uint64_t q = 0x797a9691; /* prime */
|
|
||||||
const uint64_t rm = 0x73b07d01;
|
|
||||||
const uint64_t p = 0x444110cd;
|
|
||||||
const uint32_t stride = 128;
|
|
||||||
uint64_t t = 0;
|
|
||||||
uint8_t srch_buf[m+stride];
|
|
||||||
|
|
||||||
for (struct target_ram *r = cur_target->ram; r; r = r->next) {
|
|
||||||
const uint32_t ram_start = r->start;
|
|
||||||
const uint32_t ram_end = r->start + r->length;
|
|
||||||
|
|
||||||
t = 0;
|
|
||||||
memset(srch_buf, 0, sizeof(srch_buf));
|
|
||||||
|
|
||||||
for (uint32_t addr = ram_start; addr < ram_end; addr += stride) {
|
|
||||||
uint32_t buf_siz = MIN(stride, ram_end - addr);
|
|
||||||
memcpy(srch_buf, srch_buf + stride, m);
|
|
||||||
if (target_mem_read(cur_target, srch_buf + m, addr, buf_siz)) {
|
|
||||||
gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < buf_siz; i++) {
|
|
||||||
t = (t + q - rm * srch_buf[i] % q) % q;
|
|
||||||
t = ((t << 8) + srch_buf[i + m]) % q;
|
|
||||||
if (p == t) {
|
|
||||||
uint32_t offset = i - m + 1;
|
|
||||||
return addr + offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* no match */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t memsrch(target *cur_target)
|
|
||||||
{
|
|
||||||
char *srch_str = rtt_ident;
|
|
||||||
uint32_t srch_str_len = strlen(srch_str);
|
|
||||||
uint8_t srch_buf[128];
|
|
||||||
|
|
||||||
if (srch_str_len == 0 || srch_str_len > sizeof(srch_buf) / 2)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (rtt_cbaddr && !target_mem_read(cur_target, srch_buf, rtt_cbaddr, srch_str_len)
|
|
||||||
&& strncmp((const char *)(srch_buf), srch_str, srch_str_len) == 0)
|
|
||||||
/* still at same place */
|
|
||||||
return rtt_cbaddr;
|
|
||||||
|
|
||||||
for (struct target_ram *r = cur_target->ram; r; r = r->next) {
|
|
||||||
uint32_t ram_end = r->start + r->length;
|
|
||||||
for (uint32_t addr = r->start; addr < ram_end; addr += sizeof(srch_buf) - srch_str_len - 1) {
|
|
||||||
uint32_t buf_siz = MIN(ram_end - addr, sizeof(srch_buf));
|
|
||||||
if (target_mem_read(cur_target, srch_buf, addr, buf_siz)) {
|
|
||||||
gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (uint32_t offset = 0; offset + srch_str_len + 1 < buf_siz; offset++) {
|
|
||||||
if (strncmp((const char *)(srch_buf + offset), srch_str, srch_str_len) == 0) {
|
|
||||||
uint32_t cb_addr = addr + offset;
|
|
||||||
return cb_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void find_rtt(target *cur_target)
|
|
||||||
{
|
|
||||||
rtt_found = false;
|
|
||||||
poll_ms = rtt_max_poll_ms;
|
|
||||||
poll_errs = 0;
|
|
||||||
last_poll_ms = 0;
|
|
||||||
|
|
||||||
if (!cur_target || !rtt_enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rtt_ident[0] == 0)
|
|
||||||
rtt_cbaddr = fastsrch(cur_target);
|
|
||||||
else
|
|
||||||
rtt_cbaddr = memsrch(cur_target);
|
|
||||||
DEBUG_INFO("rtt: match at 0x%" PRIx32 "\r\n", rtt_cbaddr);
|
|
||||||
|
|
||||||
if (rtt_cbaddr) {
|
|
||||||
uint32_t num_buf[2];
|
|
||||||
int32_t num_up_buf;
|
|
||||||
int32_t num_down_buf;
|
|
||||||
if (target_mem_read(cur_target, num_buf, rtt_cbaddr + 16, sizeof(num_buf)))
|
|
||||||
return;
|
|
||||||
num_up_buf = num_buf[0];
|
|
||||||
num_down_buf = num_buf[1];
|
|
||||||
|
|
||||||
if (num_up_buf > 255 || num_down_buf > 255) {
|
|
||||||
gdb_out("rtt: bad cblock\r\n");
|
|
||||||
rtt_enabled = false;
|
|
||||||
return;
|
|
||||||
} else if (num_up_buf == 0 && num_down_buf == 0)
|
|
||||||
gdb_out("rtt: empty cblock\r\n");
|
|
||||||
|
|
||||||
for (int32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
|
||||||
uint32_t buf_desc[6];
|
|
||||||
|
|
||||||
rtt_channel[i].is_configured = false;
|
|
||||||
rtt_channel[i].is_output = false;
|
|
||||||
rtt_channel[i].buf_addr = 0;
|
|
||||||
rtt_channel[i].buf_size = 0;
|
|
||||||
rtt_channel[i].head_addr = 0;
|
|
||||||
rtt_channel[i].tail_addr = 0;
|
|
||||||
rtt_channel[i].flag = 0;
|
|
||||||
|
|
||||||
if (i >= num_up_buf + num_down_buf)
|
|
||||||
continue;
|
|
||||||
if (target_mem_read(cur_target, buf_desc, rtt_cbaddr + 24 + i * 24, sizeof(buf_desc)))
|
|
||||||
return;
|
|
||||||
rtt_channel[i].is_output = i < num_up_buf;
|
|
||||||
rtt_channel[i].buf_addr = buf_desc[1];
|
|
||||||
rtt_channel[i].buf_size = buf_desc[2];
|
|
||||||
rtt_channel[i].head_addr = rtt_cbaddr + 24 + i * 24 + 12;
|
|
||||||
rtt_channel[i].tail_addr = rtt_cbaddr + 24 + i * 24 + 16;
|
|
||||||
rtt_channel[i].flag = buf_desc[5];
|
|
||||||
rtt_channel[i].is_configured = (rtt_channel[i].buf_addr != 0) && (rtt_channel[i].buf_size != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* auto channel: enable output channels 0 and 1 and first input channel */
|
|
||||||
if (rtt_auto_channel) {
|
|
||||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
|
||||||
rtt_channel[i].is_enabled = false;
|
|
||||||
rtt_channel[0].is_enabled = num_up_buf > 0;
|
|
||||||
rtt_channel[1].is_enabled = num_up_buf > 1;
|
|
||||||
if ((num_up_buf < MAX_RTT_CHAN) && (num_down_buf > 0))
|
|
||||||
rtt_channel[num_up_buf].is_enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get flags for data from host to target */
|
|
||||||
rtt_flag_skip = false;
|
|
||||||
rtt_flag_block = false;
|
|
||||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
|
||||||
if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured && !rtt_channel[i].is_output) {
|
|
||||||
rtt_flag_skip = rtt_channel[i].flag == 0;
|
|
||||||
rtt_flag_block = rtt_channel[i].flag == 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
rtt_found = true;
|
|
||||||
DEBUG_INFO("rtt found\n");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt from host to target
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* poll if host has new data for target */
|
|
||||||
static rtt_retval read_rtt(target *cur_target, uint32_t i)
|
|
||||||
{
|
|
||||||
uint32_t head_tail[2];
|
|
||||||
uint32_t buf_head;
|
|
||||||
uint32_t buf_tail;
|
|
||||||
uint32_t next_head;
|
|
||||||
int ch;
|
|
||||||
|
|
||||||
/* copy data from recv_buf to target rtt 'down' buffer */
|
|
||||||
if (rtt_nodata())
|
|
||||||
return RTT_IDLE;
|
|
||||||
|
|
||||||
if (cur_target == NULL || rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].buf_size == 0)
|
|
||||||
return RTT_IDLE;
|
|
||||||
|
|
||||||
/* read down buffer head and tail from target */
|
|
||||||
if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail)))
|
|
||||||
return RTT_ERR;
|
|
||||||
|
|
||||||
buf_head = head_tail[0];
|
|
||||||
buf_tail = head_tail[1];
|
|
||||||
|
|
||||||
if (buf_head >= rtt_channel[i].buf_size || buf_tail >= rtt_channel[i].buf_size)
|
|
||||||
return RTT_ERR;
|
|
||||||
|
|
||||||
/* write recv_buf to target rtt 'down' buf */
|
|
||||||
while ((next_head = ((buf_head + 1) % rtt_channel[i].buf_size)) != buf_tail && (ch = rtt_getchar()) != -1) {
|
|
||||||
if (target_mem_write(cur_target, rtt_channel[i].buf_addr + buf_head, &ch, 1))
|
|
||||||
return RTT_ERR;
|
|
||||||
|
|
||||||
/* advance pointers */
|
|
||||||
buf_head = next_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update head of target 'down' buffer */
|
|
||||||
if (target_mem_write(cur_target, rtt_channel[i].head_addr, &buf_head, sizeof(buf_head)))
|
|
||||||
return RTT_ERR;
|
|
||||||
return RTT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt from target to host
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* target_mem_read, word aligned for speed.
|
|
||||||
note: dest has to be len + 8 bytes, to allow for alignment and padding.
|
|
||||||
*/
|
|
||||||
int target_aligned_mem_read(target *t, void *dest, target_addr src, size_t len)
|
|
||||||
{
|
|
||||||
uint32_t src0 = src;
|
|
||||||
uint32_t len0 = len;
|
|
||||||
uint32_t offset = src & 0x3;
|
|
||||||
src0 -= offset;
|
|
||||||
len0 += offset;
|
|
||||||
if ((len0 & 0x3) != 0)
|
|
||||||
len0 = (len0 + 4) & ~0x3;
|
|
||||||
|
|
||||||
if (src0 == src && len0 == len)
|
|
||||||
return target_mem_read(t, dest, src, len);
|
|
||||||
else {
|
|
||||||
uint32_t retval = target_mem_read(t, dest, src0, len0);
|
|
||||||
memmove(dest, dest + offset, len);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poll if target has new data for host */
|
|
||||||
static rtt_retval print_rtt(target *cur_target, uint32_t i)
|
|
||||||
{
|
|
||||||
uint32_t head;
|
|
||||||
uint32_t tail;
|
|
||||||
|
|
||||||
if (!cur_target || !rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].head_addr == 0)
|
|
||||||
return RTT_IDLE;
|
|
||||||
|
|
||||||
uint32_t head_tail[2];
|
|
||||||
if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail)))
|
|
||||||
return RTT_ERR;
|
|
||||||
head = head_tail[0];
|
|
||||||
tail = head_tail[1];
|
|
||||||
|
|
||||||
if (head >= rtt_channel[i].buf_size || tail >= rtt_channel[i].buf_size)
|
|
||||||
return RTT_ERR;
|
|
||||||
else if (head == tail)
|
|
||||||
return RTT_IDLE;
|
|
||||||
|
|
||||||
uint32_t bytes_free = sizeof(xmit_buf) - 8; /* need 8 bytes for alignment and padding */
|
|
||||||
uint32_t bytes_read = 0;
|
|
||||||
|
|
||||||
if (tail > head) {
|
|
||||||
uint32_t len = rtt_channel[i].buf_size - tail;
|
|
||||||
if (len > bytes_free)
|
|
||||||
len = bytes_free;
|
|
||||||
if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len))
|
|
||||||
return RTT_ERR;
|
|
||||||
bytes_free -= len;
|
|
||||||
bytes_read += len;
|
|
||||||
tail = (tail + len) % rtt_channel[i].buf_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (head > tail && bytes_free > 0) {
|
|
||||||
uint32_t len = head - tail;
|
|
||||||
if (len > bytes_free)
|
|
||||||
len = bytes_free;
|
|
||||||
if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len))
|
|
||||||
return RTT_ERR;
|
|
||||||
bytes_read += len;
|
|
||||||
tail = (tail + len) % rtt_channel[i].buf_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update tail on target */
|
|
||||||
if (target_mem_write(cur_target, rtt_channel[i].tail_addr, &tail, sizeof(tail)))
|
|
||||||
return RTT_ERR;
|
|
||||||
|
|
||||||
/* write buffer to usb */
|
|
||||||
rtt_write(xmit_buf, bytes_read);
|
|
||||||
|
|
||||||
return RTT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* target background memory access
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* target_no_background_memory_access() is true if the target needs to be halted during jtag memory access
|
|
||||||
target_no_background_memory_access() is false if the target allows jtag memory access while running */
|
|
||||||
|
|
||||||
bool target_no_background_memory_access(target *cur_target)
|
|
||||||
{
|
|
||||||
/* if error message is 'rtt: read fail at' add target to expression below.
|
|
||||||
As a first approximation, assume all arm processors allow memory access while running, and no riscv does. */
|
|
||||||
bool riscv_core = cur_target && target_core_name(cur_target) && strstr(target_core_name(cur_target), "RVDBG");
|
|
||||||
return riscv_core;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************
|
|
||||||
*
|
|
||||||
* rtt top level
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
void poll_rtt(target *cur_target)
|
|
||||||
{
|
|
||||||
/* rtt off */
|
|
||||||
if (!cur_target || !rtt_enabled)
|
|
||||||
return;
|
|
||||||
/* target present and rtt enabled */
|
|
||||||
uint32_t now = platform_time_ms();
|
|
||||||
bool rtt_err = false;
|
|
||||||
bool rtt_busy = false;
|
|
||||||
|
|
||||||
if (last_poll_ms + poll_ms <= now || now < last_poll_ms) {
|
|
||||||
target_addr watch;
|
|
||||||
enum target_halt_reason reason;
|
|
||||||
bool resume_target = false;
|
|
||||||
if (!rtt_found)
|
|
||||||
/* check if target needs to be halted during memory access */
|
|
||||||
rtt_halt = target_no_background_memory_access(cur_target);
|
|
||||||
if (rtt_halt && target_halt_poll(cur_target, &watch) == TARGET_HALT_RUNNING) {
|
|
||||||
/* briefly halt target during target memory access */
|
|
||||||
target_halt_request(cur_target);
|
|
||||||
while((reason = target_halt_poll(cur_target, &watch)) == TARGET_HALT_RUNNING)
|
|
||||||
continue;
|
|
||||||
resume_target = reason == TARGET_HALT_REQUEST;
|
|
||||||
}
|
|
||||||
if (!rtt_found)
|
|
||||||
/* find rtt control block in target memory */
|
|
||||||
find_rtt(cur_target);
|
|
||||||
/* do rtt i/o if control block found */
|
|
||||||
if (rtt_found) {
|
|
||||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
|
||||||
rtt_retval v;
|
|
||||||
if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured) {
|
|
||||||
if (rtt_channel[i].is_output)
|
|
||||||
v = print_rtt(cur_target, i);
|
|
||||||
else
|
|
||||||
v = read_rtt(cur_target, i);
|
|
||||||
if (v == RTT_OK) rtt_busy = true;
|
|
||||||
else if (v == RTT_ERR) rtt_err = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* continue target if halted */
|
|
||||||
if (resume_target)
|
|
||||||
target_halt_resume(cur_target, false);
|
|
||||||
|
|
||||||
/* update last poll time */
|
|
||||||
last_poll_ms = now;
|
|
||||||
|
|
||||||
/* rtt polling frequency goes up and down with rtt activity */
|
|
||||||
if (rtt_busy && !rtt_err)
|
|
||||||
poll_ms /= 2;
|
|
||||||
else
|
|
||||||
poll_ms *= 2;
|
|
||||||
|
|
||||||
if (poll_ms > rtt_max_poll_ms)
|
|
||||||
poll_ms = rtt_max_poll_ms;
|
|
||||||
else if (poll_ms < rtt_min_poll_ms)
|
|
||||||
poll_ms = rtt_min_poll_ms;
|
|
||||||
|
|
||||||
if (rtt_err) {
|
|
||||||
gdb_out("rtt: err\r\n");
|
|
||||||
poll_errs++;
|
|
||||||
if (rtt_max_poll_errs != 0 && poll_errs > rtt_max_poll_errs) {
|
|
||||||
gdb_out("\r\nrtt lost\r\n");
|
|
||||||
rtt_enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
@ -68,7 +68,7 @@ bool firmware_dp_low_write(ADIv5_DP_t *dp, uint16_t addr, const uint32_t data)
|
|||||||
int adiv5_swdp_scan(uint32_t targetid)
|
int adiv5_swdp_scan(uint32_t targetid)
|
||||||
{
|
{
|
||||||
volatile struct exception e;
|
volatile struct exception e;
|
||||||
static bool scan_multidrop = true;
|
target_list_free();
|
||||||
ADIv5_DP_t idp = {
|
ADIv5_DP_t idp = {
|
||||||
.dp_low_write = firmware_dp_low_write,
|
.dp_low_write = firmware_dp_low_write,
|
||||||
.error = firmware_swdp_error,
|
.error = firmware_swdp_error,
|
||||||
@ -77,7 +77,6 @@ int adiv5_swdp_scan(uint32_t targetid)
|
|||||||
.abort = firmware_swdp_abort,
|
.abort = firmware_swdp_abort,
|
||||||
};
|
};
|
||||||
ADIv5_DP_t *initial_dp = &idp;
|
ADIv5_DP_t *initial_dp = &idp;
|
||||||
target_list_free();
|
|
||||||
if (swdptap_init(initial_dp))
|
if (swdptap_init(initial_dp))
|
||||||
return -1;
|
return -1;
|
||||||
/* DORMANT-> SWD sequence*/
|
/* DORMANT-> SWD sequence*/
|
||||||
@ -94,6 +93,7 @@ int adiv5_swdp_scan(uint32_t targetid)
|
|||||||
initial_dp->seq_out(0x1a0, 12);
|
initial_dp->seq_out(0x1a0, 12);
|
||||||
uint32_t idcode = 0;
|
uint32_t idcode = 0;
|
||||||
volatile uint32_t target_id = 0;
|
volatile uint32_t target_id = 0;
|
||||||
|
bool scan_multidrop = true;
|
||||||
if (!targetid || !initial_dp->dp_low_write) {
|
if (!targetid || !initial_dp->dp_low_write) {
|
||||||
/* No targetID given on the command line or probe can not
|
/* No targetID given on the command line or probe can not
|
||||||
* handle multi-drop. Try to read ID */
|
* handle multi-drop. Try to read ID */
|
||||||
|
@ -32,20 +32,29 @@
|
|||||||
#include "target_internal.h"
|
#include "target_internal.h"
|
||||||
#include "cortexm.h"
|
#include "cortexm.h"
|
||||||
|
|
||||||
|
#if PC_HOSTED == 1
|
||||||
|
#define DEBUG_CH DEBUG_INFO
|
||||||
|
#define ERROR_CH DEBUG_WARN
|
||||||
|
#else
|
||||||
|
#define DEBUG_CH(...) {} //DEBUG_WARN //(...) {}
|
||||||
|
#define ERROR_CH DEBUG_WARN //DEBUG_WARN
|
||||||
|
#endif
|
||||||
|
|
||||||
extern const struct command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff
|
extern const struct command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff
|
||||||
|
|
||||||
static int ch32f1_flash_erase(struct target_flash *f,
|
static int ch32f1_flash_erase(struct target_flash *f,
|
||||||
target_addr addr, size_t len);
|
target_addr addr, size_t len);
|
||||||
static int ch32f1_flash_write(struct target_flash *f,
|
static int ch32f1_flash_write(struct target_flash *f,
|
||||||
target_addr dest, const void *src, size_t len);
|
target_addr dest, const void *src, size_t len);
|
||||||
|
|
||||||
|
|
||||||
// these are common with stm32f1/gd32f1/...
|
// these are common with stm32f1/gd32f1/...
|
||||||
#define FPEC_BASE 0x40022000
|
#define FPEC_BASE 0x40022000
|
||||||
#define FLASH_ACR (FPEC_BASE + 0x00)
|
#define FLASH_ACR (FPEC_BASE+0x00)
|
||||||
#define FLASH_KEYR (FPEC_BASE + 0x04)
|
#define FLASH_KEYR (FPEC_BASE+0x04)
|
||||||
#define FLASH_SR (FPEC_BASE + 0x0C)
|
#define FLASH_SR (FPEC_BASE+0x0C)
|
||||||
#define FLASH_CR (FPEC_BASE + 0x10)
|
#define FLASH_CR (FPEC_BASE+0x10)
|
||||||
#define FLASH_AR (FPEC_BASE + 0x14)
|
#define FLASH_AR (FPEC_BASE+0x14)
|
||||||
#define FLASH_CR_LOCK (1 << 7)
|
#define FLASH_CR_LOCK (1 << 7)
|
||||||
#define FLASH_CR_STRT (1 << 6)
|
#define FLASH_CR_STRT (1 << 6)
|
||||||
#define FLASH_SR_BSY (1 << 0)
|
#define FLASH_SR_BSY (1 << 0)
|
||||||
@ -57,16 +66,19 @@ static int ch32f1_flash_write(struct target_flash *f,
|
|||||||
#define FLASHSIZE 0x1FFFF7E0
|
#define FLASHSIZE 0x1FFFF7E0
|
||||||
|
|
||||||
// these are specific to ch32f1
|
// these are specific to ch32f1
|
||||||
#define FLASH_MAGIC (FPEC_BASE + 0x34)
|
#define FLASH_MAGIC (FPEC_BASE+0x34)
|
||||||
#define FLASH_MODEKEYR_CH32 (FPEC_BASE + 0x24) // Fast mode for CH32F10x
|
#define FLASH_MODEKEYR_CH32 (FPEC_BASE+0x24) // Fast mode for CH32F10x
|
||||||
#define FLASH_CR_FLOCK_CH32 (1 << 15) // fast unlock
|
#define FLASH_CR_FLOCK_CH32 (1<<15) // fast unlock
|
||||||
#define FLASH_CR_FTPG_CH32 (1 << 16) // fast page program
|
#define FLASH_CR_FTPG_CH32 (1<<16) // fast page program
|
||||||
#define FLASH_CR_FTER_CH32 (1 << 17) // fast page erase
|
#define FLASH_CR_FTER_CH32 (1<<17) // fast page erase
|
||||||
#define FLASH_CR_BUF_LOAD_CH32 (1 << 18) // Buffer load
|
#define FLASH_CR_BUF_LOAD_CH32 (1<<18) // Buffer load
|
||||||
#define FLASH_CR_BUF_RESET_CH32 (1 << 19) // Buffer reset
|
#define FLASH_CR_BUF_RESET_CH32 (1<<19) // Buffer reset
|
||||||
#define FLASH_SR_EOP (1 << 5) // End of programming
|
#define FLASH_SR_EOP (1<<5) // End of programming
|
||||||
#define FLASH_BEGIN_ADDRESS_CH32 0x8000000
|
#define FLASH_BEGIN_ADDRESS_CH32 0x8000000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\fn ch32f1_add_flash
|
\fn ch32f1_add_flash
|
||||||
\brief "fast" flash driver for CH32F10x chips
|
\brief "fast" flash driver for CH32F10x chips
|
||||||
@ -89,41 +101,38 @@ static void ch32f1_add_flash(target *t, uint32_t addr, size_t length, size_t era
|
|||||||
target_add_flash(t, f);
|
target_add_flash(t, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WAIT_BUSY() do { \
|
#define WAIT_BUSY() do { \
|
||||||
sr = target_mem_read32(t, FLASH_SR); \
|
sr = target_mem_read32(t, FLASH_SR); \
|
||||||
if (target_check_error(t)) { \
|
if(target_check_error(t)) { \
|
||||||
DEBUG_WARN("ch32f1 flash write: comm error\n"); \
|
ERROR_CH("ch32f1 flash write: comm error\n"); \
|
||||||
return -1; \
|
return -1; \
|
||||||
} \
|
} \
|
||||||
} while (sr & FLASH_SR_BSY);
|
} while (sr & FLASH_SR_BSY);
|
||||||
|
|
||||||
#define WAIT_EOP() do { \
|
#define WAIT_EOP() do { \
|
||||||
sr = target_mem_read32(t, FLASH_SR); \
|
sr = target_mem_read32(t, FLASH_SR); \
|
||||||
if (target_check_error(t)) { \
|
if(target_check_error(t)) { \
|
||||||
DEBUG_WARN("ch32f1 flash write: comm error\n"); \
|
ERROR_CH("ch32f1 flash write: comm error\n"); \
|
||||||
return -1; \
|
return -1; \
|
||||||
} \
|
} \
|
||||||
} while (!(sr & FLASH_SR_EOP));
|
} while (!(sr & FLASH_SR_EOP));
|
||||||
|
|
||||||
#define CLEAR_EOP() target_mem_write32(t, FLASH_SR,FLASH_SR_EOP)
|
#define CLEAR_EOP() target_mem_write32(t, FLASH_SR,FLASH_SR_EOP)
|
||||||
|
|
||||||
#define SET_CR(bit) do { \
|
#define SET_CR(bit) { ct = target_mem_read32(t, FLASH_CR); \
|
||||||
const uint32_t cr = target_mem_read32(t, FLASH_CR) | (bit); \
|
ct|=(bit); \
|
||||||
target_mem_write32(t, FLASH_CR, cr); \
|
target_mem_write32(t, FLASH_CR, ct);}
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define CLEAR_CR(bit) do { \
|
|
||||||
const uint32_t cr = target_mem_read32(t, FLASH_CR) & (~(bit)); \
|
#define CLEAR_CR(bit) {ct = target_mem_read32(t, FLASH_CR); \
|
||||||
target_mem_write32(t, FLASH_CR, cr); \
|
ct&=~(bit); \
|
||||||
} while(0)
|
target_mem_write32(t, FLASH_CR, ct);}
|
||||||
|
|
||||||
// Which one is the right value ?
|
// Which one is the right value ?
|
||||||
#define MAGIC_WORD 0x100
|
#define MAGIC_WORD 0x100
|
||||||
// #define MAGIC_WORD 0x1000
|
// #define MAGIC_WORD 0x1000
|
||||||
#define MAGIC(addr) do { \
|
#define MAGIC(adr) { magic=target_mem_read32(t,(adr) ^ MAGIC_WORD); \
|
||||||
magic = target_mem_read32(t, (addr) ^ MAGIC_WORD); \
|
target_mem_write32(t, FLASH_MAGIC , magic); }
|
||||||
target_mem_write32(t, FLASH_MAGIC , magic); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\fn ch32f1_flash_unlock
|
\fn ch32f1_flash_unlock
|
||||||
@ -131,24 +140,24 @@ static void ch32f1_add_flash(target *t, uint32_t addr, size_t length, size_t era
|
|||||||
*/
|
*/
|
||||||
static int ch32f1_flash_unlock(target *t)
|
static int ch32f1_flash_unlock(target *t)
|
||||||
{
|
{
|
||||||
DEBUG_INFO("CH32: flash unlock \n");
|
DEBUG_CH("CH32: flash unlock \n");
|
||||||
|
|
||||||
target_mem_write32(t, FLASH_KEYR, KEY1);
|
target_mem_write32(t, FLASH_KEYR , KEY1);
|
||||||
target_mem_write32(t, FLASH_KEYR, KEY2);
|
target_mem_write32(t, FLASH_KEYR , KEY2);
|
||||||
// fast mode
|
// fast mode
|
||||||
target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY1);
|
target_mem_write32(t, FLASH_MODEKEYR_CH32 , KEY1);
|
||||||
target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY2);
|
target_mem_write32(t, FLASH_MODEKEYR_CH32 , KEY2);
|
||||||
uint32_t cr = target_mem_read32(t, FLASH_CR);
|
uint32_t cr = target_mem_read32(t, FLASH_CR);
|
||||||
if (cr & FLASH_CR_FLOCK_CH32) {
|
if (cr & FLASH_CR_FLOCK_CH32){
|
||||||
DEBUG_WARN("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr);
|
ERROR_CH("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ch32f1_flash_lock(target *t)
|
static int ch32f1_flash_lock(target *t)
|
||||||
{
|
{
|
||||||
DEBUG_INFO("CH32: flash lock \n");
|
volatile uint32_t ct;
|
||||||
|
DEBUG_CH("CH32: flash lock \n");
|
||||||
SET_CR(FLASH_CR_LOCK);
|
SET_CR(FLASH_CR_LOCK);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -157,55 +166,56 @@ static int ch32f1_flash_lock(target *t)
|
|||||||
\brief identify the ch32f1 chip
|
\brief identify the ch32f1 chip
|
||||||
Actually grab all cortex m3 with designer = arm not caught earlier...
|
Actually grab all cortex m3 with designer = arm not caught earlier...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool ch32f1_probe(target *t)
|
bool ch32f1_probe(target *t)
|
||||||
{
|
{
|
||||||
|
t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff;
|
||||||
if ((t->cpuid & CPUID_PARTNO_MASK) != CORTEX_M3)
|
if ((t->cpuid & CPUID_PARTNO_MASK) != CORTEX_M3)
|
||||||
return false;
|
return false;
|
||||||
const uint32_t idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0x00000fffU;
|
if(t->idcode !=0x410) { // only ch32f103
|
||||||
if (idcode != 0x410) // only ch32f103
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// try to flock
|
// try to flock
|
||||||
ch32f1_flash_lock(t);
|
ch32f1_flash_lock(t);
|
||||||
// if this fails it is not a CH32 chip
|
// if this fails it is not a CH32 chip
|
||||||
if (ch32f1_flash_unlock(t))
|
if(ch32f1_flash_unlock(t)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
t->idcode = idcode;
|
|
||||||
uint32_t signature = target_mem_read32(t, FLASHSIZE);
|
uint32_t signature = target_mem_read32(t, FLASHSIZE);
|
||||||
uint32_t flashSize = signature & 0xFFFF;
|
uint32_t flashSize = signature & 0xFFFF;
|
||||||
|
|
||||||
target_add_ram(t, 0x20000000, 0x5000);
|
target_add_ram(t, 0x20000000, 0x5000);
|
||||||
ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024, 128);
|
ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize*1024, 128);
|
||||||
target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD");
|
target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD");
|
||||||
t->driver = "CH32F1 medium density (stm32f1 clone)";
|
t->driver = "CH32F1 medium density (stm32f1 clone)";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\fn ch32f1_flash_erase
|
\fn ch32f1_flash_erase
|
||||||
\brief fast erase of CH32
|
\brief fast erase of CH32
|
||||||
*/
|
*/
|
||||||
int ch32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
int ch32f1_flash_erase (struct target_flash *f, target_addr addr, size_t len)
|
||||||
{
|
{
|
||||||
volatile uint32_t sr, magic;
|
volatile uint32_t ct, sr, magic;
|
||||||
target *t = f->t;
|
target *t = f->t;
|
||||||
DEBUG_INFO("CH32: flash erase \n");
|
DEBUG_CH("CH32: flash erase \n");
|
||||||
|
|
||||||
if (ch32f1_flash_unlock(t)) {
|
if (ch32f1_flash_unlock(t)) {
|
||||||
DEBUG_WARN("CH32: Unlock failed\n");
|
ERROR_CH("CH32: Unlock failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Fast Erase 128 bytes pages (ch32 mode)
|
// Fast Erase 128 bytes pages (ch32 mode)
|
||||||
while (len) {
|
while(len) {
|
||||||
SET_CR(FLASH_CR_FTER_CH32);// CH32 PAGE_ER
|
SET_CR(FLASH_CR_FTER_CH32);// CH32 PAGE_ER
|
||||||
/* write address to FMA */
|
/* write address to FMA */
|
||||||
target_mem_write32(t, FLASH_AR, addr);
|
target_mem_write32(t, FLASH_AR , addr);
|
||||||
/* Flash page erase start instruction */
|
/* Flash page erase start instruction */
|
||||||
SET_CR(FLASH_CR_STRT);
|
SET_CR( FLASH_CR_STRT );
|
||||||
WAIT_EOP();
|
WAIT_EOP();
|
||||||
CLEAR_EOP();
|
CLEAR_EOP();
|
||||||
CLEAR_CR(FLASH_CR_STRT);
|
CLEAR_CR( FLASH_CR_STRT );
|
||||||
// Magic
|
// Magic
|
||||||
MAGIC(addr);
|
MAGIC(addr);
|
||||||
if (len > 128)
|
if (len > 128)
|
||||||
@ -216,8 +226,8 @@ int ch32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
|||||||
}
|
}
|
||||||
sr = target_mem_read32(t, FLASH_SR);
|
sr = target_mem_read32(t, FLASH_SR);
|
||||||
ch32f1_flash_lock(t);
|
ch32f1_flash_lock(t);
|
||||||
if (sr & SR_ERROR_MASK) {
|
if ((sr & SR_ERROR_MASK)) {
|
||||||
DEBUG_WARN("ch32f1 flash erase error 0x%" PRIx32 "\n", sr);
|
ERROR_CH("ch32f1 flash erase error 0x%" PRIx32 "\n", sr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -231,38 +241,39 @@ int ch32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
|||||||
NB: Just reading fff is not enough as it could be a transient previous operation value
|
NB: Just reading fff is not enough as it could be a transient previous operation value
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool ch32f1_wait_flash_ready(target *t, uint32_t addr)
|
static bool ch32f1_wait_flash_ready(target *t,uint32_t adr)
|
||||||
{
|
{
|
||||||
uint32_t ff = 0;
|
uint32_t ff;
|
||||||
for (size_t i = 0; i < 32; i++)
|
for(int i = 0; i < 32; i++) {
|
||||||
ff = target_mem_read32(t, addr);
|
ff = target_mem_read32(t,adr);
|
||||||
if (ff != 0xffffffffUL) {
|
}
|
||||||
DEBUG_WARN("ch32f1 Not erased properly at %" PRIx32 " or flash access issue\n", addr);
|
if(ff != 0xffffffffUL) {
|
||||||
return false;
|
ERROR_CH("ch32f1 Not erased properly at %x or flash access issue\n",adr);
|
||||||
}
|
return false;
|
||||||
return true;
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
\fn ch32f1_flash_write
|
\fn ch32f1_flash_write
|
||||||
\brief fast flash for ch32. Load 128 bytes chunk and then flash them
|
\brief fast flash for ch32. Load 128 bytes chunk and then flash them
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t offset)
|
static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t offset)
|
||||||
{
|
{
|
||||||
volatile uint32_t sr, magic;
|
volatile uint32_t ct, sr, magic;
|
||||||
const uint32_t *ss = (const uint32_t *)(src+offset);
|
const uint32_t *ss = (const uint32_t *)(src+offset);
|
||||||
uint32_t dd = dest + offset;
|
uint32_t dd = dest+offset;
|
||||||
|
|
||||||
SET_CR(FLASH_CR_FTPG_CH32);
|
SET_CR(FLASH_CR_FTPG_CH32);
|
||||||
target_mem_write32(t, dd + 0, ss[0]);
|
target_mem_write32(t, dd+0,ss[0]);
|
||||||
target_mem_write32(t, dd + 4, ss[1]);
|
target_mem_write32(t, dd+4,ss[1]);
|
||||||
target_mem_write32(t, dd + 8, ss[2]);
|
target_mem_write32(t, dd+8,ss[2]);
|
||||||
target_mem_write32(t, dd + 12, ss[3]);
|
target_mem_write32(t, dd+12,ss[3]);
|
||||||
SET_CR(FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */
|
SET_CR(FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */
|
||||||
WAIT_EOP();
|
WAIT_EOP();
|
||||||
CLEAR_EOP();
|
CLEAR_EOP();
|
||||||
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
||||||
MAGIC(dest + offset);
|
MAGIC((dest+offset));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -271,12 +282,12 @@ static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t off
|
|||||||
*/
|
*/
|
||||||
int ch32f1_buffer_clear(target *t)
|
int ch32f1_buffer_clear(target *t)
|
||||||
{
|
{
|
||||||
volatile uint32_t sr;
|
volatile uint32_t ct,sr;
|
||||||
SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||||
SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5-
|
SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5-
|
||||||
WAIT_BUSY(); // 6-
|
WAIT_BUSY(); // 6-
|
||||||
CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//#define CH32_VERIFY
|
//#define CH32_VERIFY
|
||||||
|
|
||||||
@ -284,21 +295,21 @@ int ch32f1_buffer_clear(target *t)
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
static int ch32f1_flash_write(struct target_flash *f,
|
static int ch32f1_flash_write(struct target_flash *f,
|
||||||
target_addr dest, const void *src, size_t len)
|
target_addr dest, const void *src, size_t len)
|
||||||
{
|
{
|
||||||
volatile uint32_t sr, magic;
|
volatile uint32_t ct, sr, magic;
|
||||||
target *t = f->t;
|
target *t = f->t;
|
||||||
size_t length = len;
|
size_t length = len;
|
||||||
#ifdef CH32_VERIFY
|
#ifdef CH32_VERIFY
|
||||||
target_addr org_dest = dest;
|
target_addr orgDest=dest;
|
||||||
const void *org_src = src;
|
const void *orgSrc=src;
|
||||||
#endif
|
#endif
|
||||||
DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%zu\n", dest, len);
|
DEBUG_CH("CH32: flash write 0x%x ,size=%d\n",dest,len);
|
||||||
|
|
||||||
while (length > 0)
|
while(length > 0)
|
||||||
{
|
{
|
||||||
if (ch32f1_flash_unlock(t)) {
|
if(ch32f1_flash_unlock(t)) {
|
||||||
DEBUG_WARN("ch32f1 cannot fast unlock\n");
|
ERROR_CH("ch32f1 cannot fast unlock\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
WAIT_BUSY();
|
WAIT_BUSY();
|
||||||
@ -306,12 +317,12 @@ static int ch32f1_flash_write(struct target_flash *f,
|
|||||||
// Buffer reset...
|
// Buffer reset...
|
||||||
ch32f1_buffer_clear(t);
|
ch32f1_buffer_clear(t);
|
||||||
// Load 128 bytes to buffer
|
// Load 128 bytes to buffer
|
||||||
if (!ch32f1_wait_flash_ready(t,dest))
|
if(!ch32f1_wait_flash_ready(t,dest)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for(int i = 0; i < 8; i++) {
|
||||||
if (ch32f1_upload(t, dest, src, i * 16U)) {
|
if(ch32f1_upload(t,dest,src, 16*i)) {
|
||||||
DEBUG_WARN("Cannot upload to buffer\n");
|
ERROR_CH("Cannot upload to buffer\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,11 +334,11 @@ static int ch32f1_flash_write(struct target_flash *f,
|
|||||||
CLEAR_EOP();
|
CLEAR_EOP();
|
||||||
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
||||||
|
|
||||||
MAGIC(dest);
|
MAGIC((dest));
|
||||||
|
|
||||||
// next
|
// next
|
||||||
if (length > 128)
|
if(length > 128)
|
||||||
length -=128;
|
length -=128;
|
||||||
else
|
else
|
||||||
length = 0;
|
length = 0;
|
||||||
dest += 128;
|
dest += 128;
|
||||||
@ -335,23 +346,24 @@ static int ch32f1_flash_write(struct target_flash *f,
|
|||||||
|
|
||||||
sr = target_mem_read32(t, FLASH_SR); // 13
|
sr = target_mem_read32(t, FLASH_SR); // 13
|
||||||
ch32f1_flash_lock(t);
|
ch32f1_flash_lock(t);
|
||||||
if (sr & SR_ERROR_MASK) {
|
if ((sr & SR_ERROR_MASK) ) {
|
||||||
DEBUG_WARN("ch32f1 flash write error 0x%" PRIx32 "\n", sr);
|
ERROR_CH("ch32f1 flash write error 0x%" PRIx32 "\n", sr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
#ifdef CH32_VERIFY
|
#ifdef CH32_VERIFY
|
||||||
DEBUG_INFO("Verifying\n");
|
DEBUG_CH("Verifying\n");
|
||||||
for (size_t i = 0; i < len; i += 4)
|
size_t i = 0;
|
||||||
|
for(i = 0; i < len; i+= 4)
|
||||||
{
|
{
|
||||||
const uint32_t expected = *(uint32_t *)(org_src + i);
|
uint32_t mem=target_mem_read32(t, orgDest+i);
|
||||||
const uint32_t actual = target_mem_read32(t, org_dest + i);
|
uint32_t mem2=*(uint32_t *)(orgSrc+i);
|
||||||
if (expected != actual)
|
if(mem!=mem2)
|
||||||
{
|
{
|
||||||
DEBUG_WARN(">>>>write mistmatch at address 0x%x\n", org_dest + i);
|
ERROR_CH(">>>>write mistmatch at address 0x%x\n",orgDest+i);
|
||||||
DEBUG_WARN(">>>>expected: 0x%x\n", expected);
|
ERROR_CH(">>>>expected 0x%x\n",mem2);
|
||||||
DEBUG_WARN(">>>> actual: 0x%x\n", actual);
|
ERROR_CH(">>>>flash 0x%x\n",mem);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -359,3 +371,4 @@ static int ch32f1_flash_write(struct target_flash *f,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// EOF
|
||||||
|
@ -378,25 +378,8 @@ bool cortexm_probe(ADIv5_AP_t *ap)
|
|||||||
} else {
|
} else {
|
||||||
target_check_error(t);
|
target_check_error(t);
|
||||||
}
|
}
|
||||||
#if PC_HOSTED
|
|
||||||
#define STRINGIFY(x) #x
|
|
||||||
#define PROBE(x) \
|
#define PROBE(x) \
|
||||||
do { \
|
do { if ((x)(t)) {return true;} else target_check_error(t); } while (0)
|
||||||
DEBUG_INFO("Calling " STRINGIFY(x) "\n"); \
|
|
||||||
if ((x)(t)) \
|
|
||||||
return true; \
|
|
||||||
else \
|
|
||||||
target_check_error(t); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define PROBE(x) \
|
|
||||||
do { \
|
|
||||||
if ((x)(t)) \
|
|
||||||
return true; \
|
|
||||||
else \
|
|
||||||
target_check_error(t); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (ap->ap_designer) {
|
switch (ap->ap_designer) {
|
||||||
case AP_DESIGNER_FREESCALE:
|
case AP_DESIGNER_FREESCALE:
|
||||||
@ -519,7 +502,7 @@ bool cortexm_attach(target *t)
|
|||||||
priv->flash_patch_revision = (r >> 28);
|
priv->flash_patch_revision = (r >> 28);
|
||||||
priv->hw_watchpoint_max = CORTEXM_MAX_WATCHPOINTS;
|
priv->hw_watchpoint_max = CORTEXM_MAX_WATCHPOINTS;
|
||||||
r = target_mem_read32(t, CORTEXM_DWT_CTRL);
|
r = target_mem_read32(t, CORTEXM_DWT_CTRL);
|
||||||
if ((r >> 28) < priv->hw_watchpoint_max)
|
if ((r >> 28) > priv->hw_watchpoint_max)
|
||||||
priv->hw_watchpoint_max = r >> 28;
|
priv->hw_watchpoint_max = r >> 28;
|
||||||
|
|
||||||
/* Clear any stale breakpoints */
|
/* Clear any stale breakpoints */
|
||||||
@ -692,7 +675,7 @@ static int dcrsr_regnum(target *t, unsigned reg)
|
|||||||
return regnum_cortex_m[reg];
|
return regnum_cortex_m[reg];
|
||||||
} else if ((t->target_options & TOPT_FLAVOUR_V7MF) &&
|
} else if ((t->target_options & TOPT_FLAVOUR_V7MF) &&
|
||||||
(reg < (sizeof(regnum_cortex_m) +
|
(reg < (sizeof(regnum_cortex_m) +
|
||||||
sizeof(regnum_cortex_mf)) / 4)) {
|
sizeof(regnum_cortex_mf) / 4))) {
|
||||||
return regnum_cortex_mf[reg - sizeof(regnum_cortex_m)/4];
|
return regnum_cortex_mf[reg - sizeof(regnum_cortex_m)/4];
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -37,96 +37,77 @@
|
|||||||
#include "general.h"
|
#include "general.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
#include "target_internal.h"
|
#include "target_internal.h"
|
||||||
#include "adiv5.h"
|
|
||||||
|
|
||||||
#define KINETIS_MDM_IDR_K22F 0x1c0000
|
#define SIM_SDID 0x40048024
|
||||||
#define KINETIS_MDM_IDR_KZ03 0x1c0020
|
#define SIM_FCFG1 0x4004804C
|
||||||
|
|
||||||
#define MDM_STATUS ADIV5_AP_REG(0x00)
|
#define FTFA_BASE 0x40020000
|
||||||
#define MDM_CONTROL ADIV5_AP_REG(0x04)
|
#define FTFA_FSTAT (FTFA_BASE + 0x00)
|
||||||
|
#define FTFA_FCNFG (FTFA_BASE + 0x01)
|
||||||
|
#define FTFA_FSEC (FTFA_BASE + 0x02)
|
||||||
|
#define FTFA_FOPT (FTFA_BASE + 0x03)
|
||||||
|
#define FTFA_FCCOB_0 (FTFA_BASE + 0x04)
|
||||||
|
#define FTFA_FCCOB_1 (FTFA_BASE + 0x08)
|
||||||
|
#define FTFA_FCCOB_2 (FTFA_BASE + 0x0C)
|
||||||
|
|
||||||
#define MDM_STATUS_MASS_ERASE_ACK (1 << 0)
|
#define FTFA_FSTAT_CCIF (1 << 7)
|
||||||
#define MDM_STATUS_FLASH_READY (1 << 1)
|
#define FTFA_FSTAT_RDCOLERR (1 << 6)
|
||||||
#define MDM_STATUS_MASS_ERASE_ENABLED (1 << 5)
|
#define FTFA_FSTAT_ACCERR (1 << 5)
|
||||||
#define MDM_STATUS_BACK_KEY_ENABLED (1 << 6)
|
#define FTFA_FSTAT_FPVIOL (1 << 4)
|
||||||
|
#define FTFA_FSTAT_MGSTAT0 (1 << 0)
|
||||||
|
|
||||||
#define MDM_CONTROL_MASS_ERASE (1 << 0)
|
#define FTFA_CMD_CHECK_ERASE 0x01
|
||||||
#define MDM_CONTROL_SYS_RESET (1 << 3)
|
#define FTFA_CMD_PROGRAM_CHECK 0x02
|
||||||
|
#define FTFA_CMD_READ_RESOURCE 0x03
|
||||||
#define SIM_SDID 0x40048024
|
#define FTFA_CMD_PROGRAM_LONGWORD 0x06
|
||||||
#define SIM_FCFG1 0x4004804C
|
|
||||||
|
|
||||||
#define FLASH_SECURITY_BYTE_ADDRESS 0x40C
|
|
||||||
#define FLASH_SECURITY_BYTE_UNSECURED 0xFE
|
|
||||||
|
|
||||||
#define FTFx_BASE 0x40020000
|
|
||||||
#define FTFx_FSTAT (FTFx_BASE + 0x00)
|
|
||||||
#define FTFx_FCNFG (FTFx_BASE + 0x01)
|
|
||||||
#define FTFx_FSEC (FTFx_BASE + 0x02)
|
|
||||||
#define FTFx_FOPT (FTFx_BASE + 0x03)
|
|
||||||
#define FTFx_FCCOB0 (FTFx_BASE + 0x04)
|
|
||||||
#define FTFx_FCCOB4 (FTFx_BASE + 0x08)
|
|
||||||
#define FTFx_FCCOB8 (FTFx_BASE + 0x0C)
|
|
||||||
|
|
||||||
#define FTFx_FSTAT_CCIF (1 << 7)
|
|
||||||
#define FTFx_FSTAT_RDCOLERR (1 << 6)
|
|
||||||
#define FTFx_FSTAT_ACCERR (1 << 5)
|
|
||||||
#define FTFx_FSTAT_FPVIOL (1 << 4)
|
|
||||||
#define FTFx_FSTAT_MGSTAT0 (1 << 0)
|
|
||||||
|
|
||||||
#define FTFx_FSEC_KEYEN_MSK (0b11 << 6)
|
|
||||||
#define FTFx_FSEC_KEYEN (0b10 << 6)
|
|
||||||
|
|
||||||
#define FTFx_CMD_CHECK_ERASE 0x01
|
|
||||||
#define FTFx_CMD_PROGRAM_CHECK 0x02
|
|
||||||
#define FTFx_CMD_READ_RESOURCE 0x03
|
|
||||||
#define FTFx_CMD_PROGRAM_LONGWORD 0x06
|
|
||||||
/* Part of the FTFE module for K64 */
|
/* Part of the FTFE module for K64 */
|
||||||
#define FTFx_CMD_PROGRAM_PHRASE 0x07
|
#define FTFE_CMD_PROGRAM_PHRASE 0x07
|
||||||
#define FTFx_CMD_ERASE_SECTOR 0x09
|
#define FTFA_CMD_ERASE_SECTOR 0x09
|
||||||
#define FTFx_CMD_CHECK_ERASE_ALL 0x40
|
#define FTFA_CMD_CHECK_ERASE_ALL 0x40
|
||||||
#define FTFx_CMD_READ_ONCE 0x41
|
#define FTFA_CMD_READ_ONCE 0x41
|
||||||
#define FTFx_CMD_PROGRAM_ONCE 0x43
|
#define FTFA_CMD_PROGRAM_ONCE 0x43
|
||||||
#define FTFx_CMD_ERASE_ALL 0x44
|
#define FTFA_CMD_ERASE_ALL 0x44
|
||||||
#define FTFx_CMD_BACKDOOR_ACCESS 0x45
|
#define FTFA_CMD_BACKDOOR_ACCESS 0x45
|
||||||
|
|
||||||
#define KL_WRITE_LEN 4
|
#define KL_WRITE_LEN 4
|
||||||
/* 8 byte phrases need to be written to the k64 flash */
|
/* 8 byte phrases need to be written to the k64 flash */
|
||||||
#define K64_WRITE_LEN 8
|
#define K64_WRITE_LEN 8
|
||||||
|
|
||||||
static bool kinetis_cmd_unsafe(target *t, int argc, char **argv);
|
static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[]);
|
||||||
|
|
||||||
const struct command_s kinetis_cmd_list[] = {
|
const struct command_s kinetis_cmd_list[] = {
|
||||||
{"unsafe", (cmd_handler)kinetis_cmd_unsafe, "Allow programming security byte (enable|disable)"},
|
{"unsafe", (cmd_handler)kinetis_cmd_unsafe, "Allow programming security byte (enable|disable)"},
|
||||||
{NULL, NULL, NULL},
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool kinetis_cmd_unsafe(target *t, int argc, char **argv)
|
static bool kinetis_cmd_unsafe(target *t, int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
tc_printf(t, "Allow programming security byte: %s\n", t->unsafe_enabled ? "enabled" : "disabled");
|
tc_printf(t, "Allow programming security byte: %s\n",
|
||||||
|
t->unsafe_enabled ? "enabled" : "disabled");
|
||||||
} else {
|
} else {
|
||||||
parse_enable_or_disable(argv[1], &t->unsafe_enabled);
|
parse_enable_or_disable(argv[1], &t->unsafe_enabled);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kinetis_flash_cmd_erase(struct target_flash *f, target_addr addr, size_t len);
|
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len);
|
||||||
static int kinetis_flash_cmd_write(struct target_flash *f, target_addr dest, const void *src, size_t len);
|
static int kl_gen_flash_write(struct target_flash *f,
|
||||||
static int kinetis_flash_done(struct target_flash *f);
|
target_addr dest, const void *src, size_t len);
|
||||||
|
static int kl_gen_flash_done(struct target_flash *f);
|
||||||
|
|
||||||
struct kinetis_flash {
|
struct kinetis_flash {
|
||||||
struct target_flash f;
|
struct target_flash f;
|
||||||
uint8_t write_len;
|
uint8_t write_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void kinetis_add_flash(
|
static void kl_gen_add_flash(target *t, uint32_t addr, size_t length,
|
||||||
target *const t, const uint32_t addr, const size_t length, const size_t erasesize, const size_t write_len)
|
size_t erasesize, size_t write_len)
|
||||||
{
|
{
|
||||||
struct kinetis_flash *kf = calloc(1, sizeof(*kf));
|
struct kinetis_flash *kf = calloc(1, sizeof(*kf));
|
||||||
struct target_flash *f;
|
struct target_flash *f;
|
||||||
|
|
||||||
if (!kf) { /* calloc failed: heap exhaustion */
|
if (!kf) { /* calloc failed: heap exhaustion */
|
||||||
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -135,26 +116,15 @@ static void kinetis_add_flash(
|
|||||||
f->start = addr;
|
f->start = addr;
|
||||||
f->length = length;
|
f->length = length;
|
||||||
f->blocksize = erasesize;
|
f->blocksize = erasesize;
|
||||||
f->erase = kinetis_flash_cmd_erase;
|
f->erase = kl_gen_flash_erase;
|
||||||
f->write = kinetis_flash_cmd_write;
|
f->write = kl_gen_flash_write;
|
||||||
f->done = kinetis_flash_done;
|
f->done = kl_gen_flash_done;
|
||||||
f->erased = 0xff;
|
f->erased = 0xff;
|
||||||
kf->write_len = write_len;
|
kf->write_len = write_len;
|
||||||
target_add_flash(t, f);
|
target_add_flash(t, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kl_s32k14_setup(
|
bool kinetis_probe(target *t)
|
||||||
target *const t, const uint32_t sram_l, const uint32_t sram_h, const size_t flash_size, const size_t flexmem_size)
|
|
||||||
{
|
|
||||||
t->driver = "S32K14x";
|
|
||||||
target_add_ram(t, sram_l, 0x20000000 - sram_l);
|
|
||||||
target_add_ram(t, 0x20000000, sram_h);
|
|
||||||
|
|
||||||
kinetis_add_flash(t, 0x00000000, flash_size, 0x1000, K64_WRITE_LEN); /* P-Flash, 4 KB Sectors */
|
|
||||||
kinetis_add_flash(t, 0x10000000, flexmem_size, 0x1000, K64_WRITE_LEN); /* FlexNVM, 4 KB Sectors */
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kinetis_probe(target *const t)
|
|
||||||
{
|
{
|
||||||
uint32_t sdid = target_mem_read32(t, SIM_SDID);
|
uint32_t sdid = target_mem_read32(t, SIM_SDID);
|
||||||
uint32_t fcfg1 = target_mem_read32(t, SIM_FCFG1);
|
uint32_t fcfg1 = target_mem_read32(t, SIM_FCFG1);
|
||||||
@ -162,52 +132,52 @@ bool kinetis_probe(target *const t)
|
|||||||
switch (sdid >> 20) {
|
switch (sdid >> 20) {
|
||||||
case 0x161:
|
case 0x161:
|
||||||
/* sram memory size */
|
/* sram memory size */
|
||||||
switch ((sdid >> 16) & 0x0f) {
|
switch((sdid >> 16) & 0x0f) {
|
||||||
case 0x03: /* 4 KB */
|
case 0x03:/* 4 KB */
|
||||||
target_add_ram(t, 0x1ffffc00, 0x0400);
|
target_add_ram(t, 0x1ffffc00, 0x0400);
|
||||||
target_add_ram(t, 0x20000000, 0x0C00);
|
target_add_ram(t, 0x20000000, 0x0C00);
|
||||||
break;
|
break;
|
||||||
case 0x04: /* 8 KB */
|
case 0x04:/* 8 KB */
|
||||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||||
target_add_ram(t, 0x20000000, 0x1800);
|
target_add_ram(t, 0x20000000, 0x1800);
|
||||||
break;
|
break;
|
||||||
case 0x05: /* 16 KB */
|
case 0x05:/* 16 KB */
|
||||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||||
target_add_ram(t, 0x20000000, 0x3000);
|
target_add_ram(t, 0x20000000, 0x3000);
|
||||||
break;
|
break;
|
||||||
case 0x06: /* 32 KB */
|
case 0x06:/* 32 KB */
|
||||||
target_add_ram(t, 0x1fffe000, 0x2000);
|
target_add_ram(t, 0x1fffe000, 0x2000);
|
||||||
target_add_ram(t, 0x20000000, 0x6000);
|
target_add_ram(t, 0x20000000, 0x6000);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* flash memory size */
|
/* flash memory size */
|
||||||
switch ((fcfg1 >> 24) & 0x0f) {
|
switch((fcfg1 >> 24) & 0x0f) {
|
||||||
case 0x03: /* 32 KB */
|
case 0x03: /* 32 KB */
|
||||||
t->driver = "KL16Z32Vxxx";
|
t->driver = "KL16Z32Vxxx";
|
||||||
kinetis_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x05: /* 64 KB */
|
case 0x05: /* 64 KB */
|
||||||
t->driver = "KL16Z64Vxxx";
|
t->driver = "KL16Z64Vxxx";
|
||||||
kinetis_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x07: /* 128 KB */
|
case 0x07: /* 128 KB */
|
||||||
t->driver = "KL16Z128Vxxx";
|
t->driver = "KL16Z128Vxxx";
|
||||||
kinetis_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x09: /* 256 KB */
|
case 0x09: /* 256 KB */
|
||||||
t->driver = "KL16Z256Vxxx";
|
t->driver = "KL16Z256Vxxx";
|
||||||
kinetis_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -216,68 +186,68 @@ bool kinetis_probe(target *const t)
|
|||||||
t->driver = "KL25";
|
t->driver = "KL25";
|
||||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||||
target_add_ram(t, 0x20000000, 0x3000);
|
target_add_ram(t, 0x20000000, 0x3000);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 0x231:
|
case 0x231:
|
||||||
t->driver = "KL27x128"; // MKL27 >=128kb
|
t->driver = "KL27x128"; // MKL27 >=128kb
|
||||||
target_add_ram(t, 0x1fffe000, 0x2000);
|
target_add_ram(t, 0x1fffe000, 0x2000);
|
||||||
target_add_ram(t, 0x20000000, 0x6000);
|
target_add_ram(t, 0x20000000, 0x6000);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 0x271:
|
case 0x271:
|
||||||
switch ((sdid >> 16) & 0x0f) {
|
switch((sdid >> 16) & 0x0f) {
|
||||||
case 4:
|
case 4:
|
||||||
t->driver = "KL27x32";
|
t->driver = "KL27x32";
|
||||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||||
target_add_ram(t, 0x20000000, 0x1800);
|
target_add_ram(t, 0x20000000, 0x1800);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x8000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x8000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
t->driver = "KL27x64";
|
t->driver = "KL27x64";
|
||||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||||
target_add_ram(t, 0x20000000, 0x3000);
|
target_add_ram(t, 0x20000000, 0x3000);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x021: /* KL02 family */
|
case 0x021: /* KL02 family */
|
||||||
switch ((sdid >> 16) & 0x0f) {
|
switch((sdid >> 16) & 0x0f) {
|
||||||
case 3:
|
case 3:
|
||||||
t->driver = "KL02x32";
|
t->driver = "KL02x32";
|
||||||
target_add_ram(t, 0x1FFFFC00, 0x400);
|
target_add_ram(t, 0x1FFFFC00, 0x400);
|
||||||
target_add_ram(t, 0x20000000, 0xc00);
|
target_add_ram(t, 0x20000000, 0xc00);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
t->driver = "KL02x16";
|
t->driver = "KL02x16";
|
||||||
target_add_ram(t, 0x1FFFFE00, 0x200);
|
target_add_ram(t, 0x1FFFFE00, 0x200);
|
||||||
target_add_ram(t, 0x20000000, 0x600);
|
target_add_ram(t, 0x20000000, 0x600);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
t->driver = "KL02x8";
|
t->driver = "KL02x8";
|
||||||
target_add_ram(t, 0x1FFFFF00, 0x100);
|
target_add_ram(t, 0x1FFFFF00, 0x100);
|
||||||
target_add_ram(t, 0x20000000, 0x300);
|
target_add_ram(t, 0x20000000, 0x300);
|
||||||
kinetis_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x031: /* KL03 family */
|
case 0x031: /* KL03 family */
|
||||||
t->driver = "KL03";
|
t->driver = "KL03";
|
||||||
target_add_ram(t, 0x1ffffe00, 0x200);
|
target_add_ram(t, 0x1ffffe00, 0x200);
|
||||||
target_add_ram(t, 0x20000000, 0x600);
|
target_add_ram(t, 0x20000000, 0x600);
|
||||||
kinetis_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 0x220: /* K22F family */
|
case 0x220: /* K22F family */
|
||||||
t->driver = "K22F";
|
t->driver = "K22F";
|
||||||
target_add_ram(t, 0x1c000000, 0x4000000);
|
target_add_ram(t, 0x1c000000, 0x4000000);
|
||||||
target_add_ram(t, 0x20000000, 0x100000);
|
target_add_ram(t, 0x20000000, 0x100000);
|
||||||
kinetis_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN);
|
||||||
kinetis_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN);
|
kl_gen_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 0x620: /* K64F family. */
|
case 0x620: /* K64F family. */
|
||||||
/* This should be 0x640, but according to the errata sheet
|
/* This should be 0x640, but according to the errata sheet
|
||||||
@ -285,107 +255,115 @@ bool kinetis_probe(target *const t)
|
|||||||
* subfamily nibble as 2
|
* subfamily nibble as 2
|
||||||
*/
|
*/
|
||||||
t->driver = "K64";
|
t->driver = "K64";
|
||||||
target_add_ram(t, 0x1FFF0000, 0x10000);
|
target_add_ram(t, 0x1FFF0000, 0x10000);
|
||||||
target_add_ram(t, 0x20000000, 0x30000);
|
target_add_ram(t, 0x20000000, 0x30000);
|
||||||
kinetis_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN);
|
kl_gen_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||||
kinetis_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN);
|
kl_gen_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||||
break;
|
break;
|
||||||
case 0x000: /* Older K-series */
|
case 0x000: /* Older K-series */
|
||||||
switch (sdid & 0xff0) {
|
switch(sdid & 0xff0) {
|
||||||
case 0x000: /* K10 Family, DIEID=0x0 */
|
case 0x000: /* K10 Family, DIEID=0x0 */
|
||||||
case 0x080: /* K10 Family, DIEID=0x1 */
|
case 0x080: /* K10 Family, DIEID=0x1 */
|
||||||
case 0x100: /* K10 Family, DIEID=0x2 */
|
case 0x100: /* K10 Family, DIEID=0x2 */
|
||||||
case 0x180: /* K10 Family, DIEID=0x3 */
|
case 0x180: /* K10 Family, DIEID=0x3 */
|
||||||
case 0x220: /* K11 Family, DIEID=0x4 */
|
case 0x220: /* K11 Family, DIEID=0x4 */
|
||||||
return false;
|
return false;
|
||||||
case 0x200: /* K12 Family, DIEID=0x4 */
|
case 0x200: /* K12 Family, DIEID=0x4 */
|
||||||
switch ((fcfg1 >> 24) & 0x0f) {
|
switch((fcfg1 >> 24) & 0x0f) {
|
||||||
/* K12 Sub-Family Reference Manual, K12P80M50SF4RM, Rev. 4, February 2013 */
|
/* K12 Sub-Family Reference Manual, K12P80M50SF4RM, Rev. 4, February 2013 */
|
||||||
case 0x7:
|
case 0x7:
|
||||||
t->driver = "MK12DX128Vxx5";
|
t->driver = "MK12DX128Vxx5";
|
||||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
||||||
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
||||||
kinetis_add_flash(t, 0x00000000, 0x00020000, 0x800, KL_WRITE_LEN); /* P-Flash, 128 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x00000000, 0x00020000, 0x800, KL_WRITE_LEN); /* P-Flash, 128 KB, 2 KB Sectors */
|
||||||
kinetis_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||||
break;
|
break;
|
||||||
case 0x9:
|
case 0x9:
|
||||||
t->driver = "MK12DX256Vxx5";
|
t->driver = "MK12DX256Vxx5";
|
||||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
||||||
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
||||||
kinetis_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
||||||
kinetis_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||||
break;
|
break;
|
||||||
case 0xb:
|
case 0xb:
|
||||||
t->driver = "MK12DN512Vxx5";
|
t->driver = "MK12DN512Vxx5";
|
||||||
target_add_ram(t, 0x1fff8000, 0x00008000); /* SRAM_L, 32 KB */
|
target_add_ram(t, 0x1fff8000, 0x00008000); /* SRAM_L, 32 KB */
|
||||||
target_add_ram(t, 0x20000000, 0x00008000); /* SRAM_H, 32 KB */
|
target_add_ram(t, 0x20000000, 0x00008000); /* SRAM_H, 32 KB */
|
||||||
kinetis_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
||||||
kinetis_add_flash(t, 0x00040000, 0x00040000, 0x800, KL_WRITE_LEN); /* FlexNVM, 256 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x00040000, 0x00040000, 0x800, KL_WRITE_LEN); /* FlexNVM, 256 KB, 2 KB Sectors */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0x010: /* K20 Family, DIEID=0x0 */
|
||||||
|
case 0x090: /* K20 Family, DIEID=0x1 */
|
||||||
|
case 0x110: /* K20 Family, DIEID=0x2 */
|
||||||
|
case 0x190: /* K20 Family, DIEID=0x3 */
|
||||||
|
case 0x230: /* K21 Family, DIEID=0x4 */
|
||||||
|
case 0x330: /* K21 Family, DIEID=0x6 */
|
||||||
|
case 0x210: /* K22 Family, DIEID=0x4 */
|
||||||
|
case 0x310: /* K22 Family, DIEID=0x6 */
|
||||||
|
case 0x0a0: /* K30 Family, DIEID=0x1 */
|
||||||
|
case 0x120: /* K30 Family, DIEID=0x2 */
|
||||||
|
case 0x0b0: /* K40 Family, DIEID=0x1 */
|
||||||
|
case 0x130: /* K40 Family, DIEID=0x2 */
|
||||||
|
case 0x0e0: /* K50 Family, DIEID=0x1 */
|
||||||
|
case 0x0f0: /* K51 Family, DIEID=0x1 */
|
||||||
|
case 0x170: /* K53 Family, DIEID=0x2 */
|
||||||
|
case 0x140: /* K60 Family, DIEID=0x2 */
|
||||||
|
case 0x1c0: /* K60 Family, DIEID=0x3 */
|
||||||
|
case 0x1d0: /* K70 Family, DIEID=0x3 */
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x010: /* K20 Family, DIEID=0x0 */
|
|
||||||
case 0x090: /* K20 Family, DIEID=0x1 */
|
|
||||||
case 0x110: /* K20 Family, DIEID=0x2 */
|
|
||||||
case 0x190: /* K20 Family, DIEID=0x3 */
|
|
||||||
case 0x230: /* K21 Family, DIEID=0x4 */
|
|
||||||
case 0x330: /* K21 Family, DIEID=0x6 */
|
|
||||||
case 0x210: /* K22 Family, DIEID=0x4 */
|
|
||||||
case 0x310: /* K22 Family, DIEID=0x6 */
|
|
||||||
case 0x0a0: /* K30 Family, DIEID=0x1 */
|
|
||||||
case 0x120: /* K30 Family, DIEID=0x2 */
|
|
||||||
case 0x0b0: /* K40 Family, DIEID=0x1 */
|
|
||||||
case 0x130: /* K40 Family, DIEID=0x2 */
|
|
||||||
case 0x0e0: /* K50 Family, DIEID=0x1 */
|
|
||||||
case 0x0f0: /* K51 Family, DIEID=0x1 */
|
|
||||||
case 0x170: /* K53 Family, DIEID=0x2 */
|
|
||||||
case 0x140: /* K60 Family, DIEID=0x2 */
|
|
||||||
case 0x1c0: /* K60 Family, DIEID=0x3 */
|
|
||||||
case 0x1d0: /* K70 Family, DIEID=0x3 */
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x118: /* S32K118 */
|
case 0x118: /* S32K118 */
|
||||||
t->driver = "S32K118";
|
t->driver = "S32K118";
|
||||||
target_add_ram(t, 0x1ffffc00, 0x00000400); /* SRAM_L, 1 KB */
|
target_add_ram(t, 0x1ffffc00, 0x00000400); /* SRAM_L, 1 KB */
|
||||||
target_add_ram(t, 0x20000000, 0x00005800); /* SRAM_H, 22 KB */
|
target_add_ram(t, 0x20000000, 0x00005800); /* SRAM_H, 22 KB */
|
||||||
kinetis_add_flash(t, 0x00000000, 0x00040000, 0x800, K64_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, K64_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
||||||
kinetis_add_flash(t, 0x10000000, 0x00008000, 0x800, K64_WRITE_LEN); /* FlexNVM, 32 KB, 2 KB Sectors */
|
kl_gen_add_flash(t, 0x10000000, 0x00008000, 0x800, K64_WRITE_LEN); /* FlexNVM, 32 KB, 2 KB Sectors */
|
||||||
break;
|
|
||||||
/* gen1 s32k14x */
|
|
||||||
case 0x142: /* S32K142 */
|
|
||||||
case 0x143: /* S32K142W */
|
|
||||||
/* SRAM_L = 16KiB */
|
|
||||||
/* SRAM_H = 12KiB */
|
|
||||||
/* Flash = 256 KiB */
|
|
||||||
/* FlexNVM = 64 KiB */
|
|
||||||
kl_s32k14_setup(t, 0x1FFFC000, 0x03000, 0x00040000, 0x10000);
|
|
||||||
break;
|
|
||||||
case 0x144: /* S32K144 */
|
|
||||||
case 0x145: /* S32K144W */
|
|
||||||
/* SRAM_L = 32KiB */
|
|
||||||
/* SRAM_H = 28KiB */
|
|
||||||
/* Flash = 512 KiB */
|
|
||||||
/* FlexNVM = 64 KiB */
|
|
||||||
kl_s32k14_setup(t, 0x1FFF8000, 0x07000, 0x00080000, 0x10000);
|
|
||||||
break;
|
|
||||||
case 0x146: /* S32K146 */
|
|
||||||
/* SRAM_L = 64KiB */
|
|
||||||
/* SRAM_H = 60KiB */
|
|
||||||
/* Flash = 1024 KiB */
|
|
||||||
/* FlexNVM = 64 KiB */
|
|
||||||
kl_s32k14_setup(t, 0x1fff0000, 0x0f000, 0x00100000, 0x10000);
|
|
||||||
break;
|
break;
|
||||||
|
/* gen1 s32k14x */
|
||||||
|
{
|
||||||
|
uint32_t sram_l, sram_h;
|
||||||
|
uint32_t flash, flexmem;
|
||||||
|
case 0x142: /* s32k142 */
|
||||||
|
case 0x143: /* s32k142w */
|
||||||
|
sram_l = 0x1FFFC000; /* SRAM_L, 16k */
|
||||||
|
sram_h = 0x03000; /* SRAM_H, 12k */
|
||||||
|
flash = 0x00040000; /* flash 256 KB */
|
||||||
|
flexmem = 0x10000; /* FlexNVM 64 KB */
|
||||||
|
goto do_common_s32k14x;
|
||||||
|
case 0x144: /* s32k144 */
|
||||||
|
case 0x145: /* s32k144w */
|
||||||
|
sram_l = 0x1FFF8000; /* SRAM_L, 32k */
|
||||||
|
sram_h = 0x07000; /* SRAM_H, 28k */
|
||||||
|
flash = 0x00080000; /* flash 512 KB */
|
||||||
|
flexmem = 0x10000; /* FlexNVM 64 KB */
|
||||||
|
goto do_common_s32k14x;
|
||||||
|
case 0x146: /* s32k146 */
|
||||||
|
sram_l = 0x1fff0000; /* SRAM_L, 64k */
|
||||||
|
sram_h = 0x0f000; /* SRAM_H, 60k */
|
||||||
|
flash = 0x00100000; /* flash 1024 KB */
|
||||||
|
flexmem = 0x10000; /* FlexNVM 64 KB */
|
||||||
|
goto do_common_s32k14x;
|
||||||
case 0x148: /* S32K148 */
|
case 0x148: /* S32K148 */
|
||||||
/* SRAM_L = 128 KiB */
|
sram_l = 0x1ffe0000; /* SRAM_L, 128 KB */
|
||||||
/* SRAM_H = 124 KiB */
|
sram_h = 0x1f000; /* SRAM_H, 124 KB */
|
||||||
/* Flash = 1536 KiB */
|
flash = 0x00180000; /* flash 1536 KB */
|
||||||
/* FlexNVM = 512 KiB */
|
flexmem = 0x80000; /* FlexNVM 512 KB */
|
||||||
kl_s32k14_setup(t, 0x1ffe0000, 0x1f000, 0x00180000, 0x80000);
|
goto do_common_s32k14x;
|
||||||
|
do_common_s32k14x:
|
||||||
|
t->driver = "S32K14x";
|
||||||
|
target_add_ram(t, sram_l, 0x20000000 - sram_l);
|
||||||
|
target_add_ram(t, 0x20000000, sram_h);
|
||||||
|
|
||||||
|
kl_gen_add_flash(t, 0x00000000, flash, 0x1000, K64_WRITE_LEN); /* P-Flash, 4 KB Sectors */
|
||||||
|
kl_gen_add_flash(t, 0x10000000, flexmem, 0x1000, K64_WRITE_LEN); /* FlexNVM, 4 KB Sectors */
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -394,48 +372,47 @@ bool kinetis_probe(target *const t)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kinetis_fccob_cmd(target *t, uint8_t cmd, uint32_t addr, const uint32_t *data, int n_items)
|
static bool
|
||||||
|
kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint32_t *data, int n_items)
|
||||||
{
|
{
|
||||||
uint8_t fstat;
|
uint8_t fstat;
|
||||||
|
|
||||||
/* clear errors unconditionally, so we can start a new operation */
|
/* clear errors unconditionally, so we can start a new operation */
|
||||||
target_mem_write8(t, FTFx_FSTAT, (FTFx_FSTAT_ACCERR | FTFx_FSTAT_FPVIOL));
|
target_mem_write8(t,FTFA_FSTAT,(FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL));
|
||||||
|
|
||||||
/* Wait for CCIF to be high */
|
/* Wait for CCIF to be high */
|
||||||
do {
|
do {
|
||||||
fstat = target_mem_read8(t, FTFx_FSTAT);
|
fstat = target_mem_read8(t, FTFA_FSTAT);
|
||||||
} while (!(fstat & FTFx_FSTAT_CCIF));
|
} while (!(fstat & FTFA_FSTAT_CCIF));
|
||||||
|
|
||||||
/* Write command to FCCOB */
|
/* Write command to FCCOB */
|
||||||
addr &= 0x00ffffffU;
|
addr &= 0xffffff;
|
||||||
addr |= cmd << 24U;
|
addr |= (uint32_t)cmd << 24;
|
||||||
target_mem_write32(t, FTFx_FCCOB0, addr);
|
target_mem_write32(t, FTFA_FCCOB_0, addr);
|
||||||
if (data && n_items) {
|
if (data) {
|
||||||
target_mem_write32(t, FTFx_FCCOB4, data[0]);
|
target_mem_write32(t, FTFA_FCCOB_1, data[0]);
|
||||||
if (n_items > 1)
|
if (n_items > 1)
|
||||||
target_mem_write32(t, FTFx_FCCOB8, data[1]);
|
target_mem_write32(t, FTFA_FCCOB_2, data[1]);
|
||||||
else
|
|
||||||
target_mem_write32(t, FTFx_FCCOB8, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable execution by clearing CCIF */
|
/* Enable execution by clearing CCIF */
|
||||||
target_mem_write8(t, FTFx_FSTAT, FTFx_FSTAT_CCIF);
|
target_mem_write8(t, FTFA_FSTAT, FTFA_FSTAT_CCIF);
|
||||||
|
|
||||||
/* Wait for execution to complete */
|
/* Wait for execution to complete */
|
||||||
do {
|
do {
|
||||||
fstat = target_mem_read8(t, FTFx_FSTAT);
|
fstat = target_mem_read8(t, FTFA_FSTAT);
|
||||||
/* Check ACCERR and FPVIOL are zero in FSTAT */
|
/* Check ACCERR and FPVIOL are zero in FSTAT */
|
||||||
if (fstat & (FTFx_FSTAT_ACCERR | FTFx_FSTAT_FPVIOL))
|
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
|
||||||
return false;
|
return false;
|
||||||
} while (!(fstat & FTFx_FSTAT_CCIF));
|
} while (!(fstat & FTFA_FSTAT_CCIF));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kinetis_flash_cmd_erase(struct target_flash *const f, target_addr addr, size_t len)
|
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
||||||
{
|
{
|
||||||
while (len) {
|
while (len) {
|
||||||
if (kinetis_fccob_cmd(f->t, FTFx_CMD_ERASE_SECTOR, addr, NULL, 0)) {
|
if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL, 0)) {
|
||||||
/* Different targets have different flash erase sizes */
|
/* Different targets have different flash erase sizes */
|
||||||
if (len > f->blocksize)
|
if (len > f->blocksize)
|
||||||
len -= f->blocksize;
|
len -= f->blocksize;
|
||||||
@ -449,60 +426,72 @@ static int kinetis_flash_cmd_erase(struct target_flash *const f, target_addr add
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kinetis_flash_cmd_write(struct target_flash *f, target_addr dest, const void *src, size_t len)
|
#define FLASH_SECURITY_BYTE_ADDRESS 0x40C
|
||||||
|
#define FLASH_SECURITY_BYTE_UNSECURED 0xFE
|
||||||
|
|
||||||
|
static int kl_gen_flash_write(struct target_flash *f,
|
||||||
|
target_addr dest, const void *src, size_t len)
|
||||||
{
|
{
|
||||||
struct kinetis_flash *const kf = (struct kinetis_flash *)f;
|
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
||||||
|
|
||||||
/* Ensure we don't write something horrible over the security byte */
|
/* Ensure we don't write something horrible over the security byte */
|
||||||
if (!f->t->unsafe_enabled && dest <= FLASH_SECURITY_BYTE_ADDRESS && dest + len > FLASH_SECURITY_BYTE_ADDRESS) {
|
if (!f->t->unsafe_enabled &&
|
||||||
((uint8_t *)src)[FLASH_SECURITY_BYTE_ADDRESS - dest] = FLASH_SECURITY_BYTE_UNSECURED;
|
(dest <= FLASH_SECURITY_BYTE_ADDRESS) &&
|
||||||
|
((dest + len) > FLASH_SECURITY_BYTE_ADDRESS)) {
|
||||||
|
((uint8_t*)src)[FLASH_SECURITY_BYTE_ADDRESS - dest] =
|
||||||
|
FLASH_SECURITY_BYTE_UNSECURED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine write command based on the alignment. */
|
/* Determine write command based on the alignment. */
|
||||||
uint8_t write_cmd;
|
uint8_t write_cmd;
|
||||||
if (kf->write_len == K64_WRITE_LEN)
|
if (kf->write_len == K64_WRITE_LEN) {
|
||||||
write_cmd = FTFx_CMD_PROGRAM_PHRASE;
|
write_cmd = FTFE_CMD_PROGRAM_PHRASE;
|
||||||
else
|
} else {
|
||||||
write_cmd = FTFx_CMD_PROGRAM_LONGWORD;
|
write_cmd = FTFA_CMD_PROGRAM_LONGWORD;
|
||||||
|
}
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
if (kinetis_fccob_cmd(f->t, write_cmd, dest, src, kf->write_len >> 2U)) {
|
if (kl_gen_command(f->t, write_cmd, dest, src, 1)) {
|
||||||
if (len > kf->write_len)
|
if (len > kf->write_len)
|
||||||
len -= kf->write_len;
|
len -= kf->write_len;
|
||||||
else
|
else
|
||||||
len = 0;
|
len = 0;
|
||||||
dest += kf->write_len;
|
dest += kf->write_len;
|
||||||
src += kf->write_len;
|
src += kf->write_len;
|
||||||
} else
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kinetis_flash_done(struct target_flash *const f)
|
static int kl_gen_flash_done(struct target_flash *f)
|
||||||
{
|
{
|
||||||
struct kinetis_flash *const kf = (struct kinetis_flash *)f;
|
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
||||||
|
|
||||||
if (f->t->unsafe_enabled)
|
if (f->t->unsafe_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (target_mem_read8(f->t, FLASH_SECURITY_BYTE_ADDRESS) == FLASH_SECURITY_BYTE_UNSECURED)
|
if (target_mem_read8(f->t, FLASH_SECURITY_BYTE_ADDRESS) ==
|
||||||
|
FLASH_SECURITY_BYTE_UNSECURED)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Load the security byte based on the alignment (determine 8 byte phrases
|
/* Load the security byte based on the alignment (determine 8 byte phrases
|
||||||
* vs 4 byte phrases).
|
* vs 4 byte phrases).
|
||||||
*/
|
*/
|
||||||
if (kf->write_len == K64_WRITE_LEN) {
|
if (kf->write_len == 8) {
|
||||||
uint32_t vals[2] = {
|
uint32_t vals[2];
|
||||||
target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS - 4),
|
vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS-4);
|
||||||
target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS)
|
vals[1] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
||||||
};
|
vals[1] = (vals[1] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||||
vals[1] = (vals[1] & 0xffffff00U) | FLASH_SECURITY_BYTE_UNSECURED;
|
kl_gen_command(f->t, FTFE_CMD_PROGRAM_PHRASE,
|
||||||
kinetis_fccob_cmd(f->t, FTFx_CMD_PROGRAM_PHRASE, FLASH_SECURITY_BYTE_ADDRESS - 4, vals, 2);
|
FLASH_SECURITY_BYTE_ADDRESS - 4, vals, 2);
|
||||||
} else {
|
} else {
|
||||||
uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
uint32_t vals[1];
|
||||||
val = (val & 0xffffff00U) | FLASH_SECURITY_BYTE_UNSECURED;
|
vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
||||||
kinetis_fccob_cmd(f->t, FTFx_CMD_PROGRAM_LONGWORD, FLASH_SECURITY_BYTE_ADDRESS, &val, 1);
|
vals[0] = (vals[0] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||||
|
kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD,
|
||||||
|
FLASH_SECURITY_BYTE_ADDRESS, vals, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -515,6 +504,10 @@ static int kinetis_flash_done(struct target_flash *const f)
|
|||||||
* a backdoor AP is provided which may allow a mass erase to recover the
|
* a backdoor AP is provided which may allow a mass erase to recover the
|
||||||
* device. This provides a fake target to allow a monitor command interface
|
* device. This provides a fake target to allow a monitor command interface
|
||||||
*/
|
*/
|
||||||
|
#include "adiv5.h"
|
||||||
|
|
||||||
|
#define KINETIS_MDM_IDR_K22F 0x1c0000
|
||||||
|
#define KINETIS_MDM_IDR_KZ03 0x1c0020
|
||||||
|
|
||||||
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv);
|
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv);
|
||||||
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv);
|
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv);
|
||||||
@ -522,19 +515,18 @@ static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv);
|
|||||||
const struct command_s kinetis_mdm_cmd_list[] = {
|
const struct command_s kinetis_mdm_cmd_list[] = {
|
||||||
{"erase_mass", (cmd_handler)kinetis_mdm_cmd_erase_mass, "Erase entire flash memory"},
|
{"erase_mass", (cmd_handler)kinetis_mdm_cmd_erase_mass, "Erase entire flash memory"},
|
||||||
{"ke04_mode", (cmd_handler)kinetis_mdm_cmd_ke04_mode, "Allow erase for KE04"},
|
{"ke04_mode", (cmd_handler)kinetis_mdm_cmd_ke04_mode, "Allow erase for KE04"},
|
||||||
{NULL, NULL, NULL},
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum target_halt_reason mdm_halt_poll(target *t, const target_addr *const watch)
|
enum target_halt_reason mdm_halt_poll(target *t, target_addr *watch)
|
||||||
{
|
{
|
||||||
(void)t;
|
(void)t; (void)watch;
|
||||||
(void)watch;
|
|
||||||
return TARGET_HALT_REQUEST;
|
return TARGET_HALT_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kinetis_mdm_probe(ADIv5_AP_t *ap)
|
void kinetis_mdm_probe(ADIv5_AP_t *ap)
|
||||||
{
|
{
|
||||||
switch (ap->idr) {
|
switch(ap->idr) {
|
||||||
case KINETIS_MDM_IDR_KZ03: /* Also valid for KE04, no way to check! */
|
case KINETIS_MDM_IDR_KZ03: /* Also valid for KE04, no way to check! */
|
||||||
case KINETIS_MDM_IDR_K22F:
|
case KINETIS_MDM_IDR_K22F:
|
||||||
break;
|
break;
|
||||||
@ -549,13 +541,23 @@ void kinetis_mdm_probe(ADIv5_AP_t *ap)
|
|||||||
|
|
||||||
adiv5_ap_ref(ap);
|
adiv5_ap_ref(ap);
|
||||||
t->priv = ap;
|
t->priv = ap;
|
||||||
t->priv_free = (void *)adiv5_ap_unref;
|
t->priv_free = (void*)adiv5_ap_unref;
|
||||||
|
|
||||||
t->driver = "Kinetis Recovery (MDM-AP)";
|
t->driver = "Kinetis Recovery (MDM-AP)";
|
||||||
t->regs_size = 4;
|
t->regs_size = 4;
|
||||||
target_add_commands(t, kinetis_mdm_cmd_list, t->driver);
|
target_add_commands(t, kinetis_mdm_cmd_list, t->driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MDM_STATUS ADIV5_AP_REG(0x00)
|
||||||
|
#define MDM_CONTROL ADIV5_AP_REG(0x04)
|
||||||
|
|
||||||
|
#define MDM_STATUS_MASS_ERASE_ACK (1 << 0)
|
||||||
|
#define MDM_STATUS_FLASH_READY (1 << 1)
|
||||||
|
#define MDM_STATUS_MASS_ERASE_ENABLED (1 << 5)
|
||||||
|
|
||||||
|
#define MDM_CONTROL_MASS_ERASE (1 << 0)
|
||||||
|
#define MDM_CONTROL_SYS_RESET (1 << 3)
|
||||||
|
|
||||||
/* This is needed as a separate command, as there's no way to *
|
/* This is needed as a separate command, as there's no way to *
|
||||||
* tell a KE04 from other kinetis in kinetis_mdm_probe() */
|
* tell a KE04 from other kinetis in kinetis_mdm_probe() */
|
||||||
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
||||||
@ -567,7 +569,6 @@ static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
|||||||
tc_printf(t, "Mass erase for KE04 now allowed\n");
|
tc_printf(t, "Mass erase for KE04 now allowed\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
(void)argc;
|
(void)argc;
|
||||||
@ -575,12 +576,13 @@ static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
|||||||
ADIv5_AP_t *ap = t->priv;
|
ADIv5_AP_t *ap = t->priv;
|
||||||
|
|
||||||
/* Keep the MCU in reset as stated in KL25PxxM48SF0RM */
|
/* Keep the MCU in reset as stated in KL25PxxM48SF0RM */
|
||||||
if (t->ke04_mode)
|
if(t->ke04_mode)
|
||||||
adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_SYS_RESET);
|
adiv5_ap_write(ap, MDM_CONTROL, MDM_CONTROL_SYS_RESET);
|
||||||
|
|
||||||
uint32_t status = adiv5_ap_read(ap, MDM_STATUS);
|
uint32_t status, control;
|
||||||
uint32_t control = adiv5_ap_read(ap, MDM_CONTROL);
|
status = adiv5_ap_read(ap, MDM_STATUS);
|
||||||
tc_printf(t, "Requesting mass erase (status = 0x%" PRIx32 ")\n", status);
|
control = adiv5_ap_read(ap, MDM_CONTROL);
|
||||||
|
tc_printf(t, "Requesting mass erase (status = 0x%"PRIx32")\n", status);
|
||||||
|
|
||||||
/* This flag does not exist on KE04 */
|
/* This flag does not exist on KE04 */
|
||||||
if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !t->ke04_mode) {
|
if (!(status & MDM_STATUS_MASS_ERASE_ENABLED) && !t->ke04_mode) {
|
||||||
|
@ -56,7 +56,7 @@ static int stm32f4_flash_erase(struct target_flash *f, target_addr addr,
|
|||||||
static int stm32f4_flash_write(struct target_flash *f,
|
static int stm32f4_flash_write(struct target_flash *f,
|
||||||
target_addr dest, const void *src, size_t len);
|
target_addr dest, const void *src, size_t len);
|
||||||
|
|
||||||
/* Flash Program and Erase Controller Register Map */
|
/* Flash Program ad Erase Controller Register Map */
|
||||||
#define FPEC_BASE 0x40023C00
|
#define FPEC_BASE 0x40023C00
|
||||||
#define FLASH_ACR (FPEC_BASE+0x00)
|
#define FLASH_ACR (FPEC_BASE+0x00)
|
||||||
#define FLASH_KEYR (FPEC_BASE+0x04)
|
#define FLASH_KEYR (FPEC_BASE+0x04)
|
||||||
@ -503,8 +503,11 @@ static bool stm32f4_cmd_erase_mass(target *t, int argc, const char **argv)
|
|||||||
tc_printf(t, "\n");
|
tc_printf(t, "\n");
|
||||||
|
|
||||||
/* Check for error */
|
/* Check for error */
|
||||||
const uint32_t result = target_mem_read32(t, FLASH_SR);
|
uint32_t sr = target_mem_read32(t, FLASH_SR);
|
||||||
return !(result & SR_ERROR_MASK);
|
if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dev | DOC |Rev|ID |OPTCR |OPTCR |OPTCR1 |OPTCR1 | OPTCR2
|
/* Dev | DOC |Rev|ID |OPTCR |OPTCR |OPTCR1 |OPTCR1 | OPTCR2
|
||||||
|
@ -271,11 +271,6 @@ int target_flash_write(target *t,
|
|||||||
dest += tmplen;
|
dest += tmplen;
|
||||||
src += tmplen;
|
src += tmplen;
|
||||||
len -= tmplen;
|
len -= tmplen;
|
||||||
/* If the current chunk of Flash is now full from this operation
|
|
||||||
* then finish operations on the Flash chunk and free the internal buffer.
|
|
||||||
*/
|
|
||||||
if (dest == f->start + f->length)
|
|
||||||
ret |= target_flash_done_buffered(f);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user