Compare commits
218 Commits
Author | SHA1 | Date | |
---|---|---|---|
547abf0cf2 | |||
06b21b6d82 | |||
|
ad1868f8d4 | ||
|
c712a54cbc | ||
|
a51d90ba2d | ||
|
f67a4e421d | ||
|
8fc7a559b2 | ||
|
c9a0be19cb | ||
|
2ca08f8511 | ||
|
3ae3095499 | ||
|
2a792399ca | ||
|
576f575871 | ||
|
e43b07172b | ||
|
e0f1d29d41 | ||
|
2a00dd88ec | ||
|
c121da06c8 | ||
|
542c157955 | ||
|
d4dd288426 | ||
|
b096e95488 | ||
|
7bd7c3d0f5 | ||
|
82673b5e7b | ||
|
9542cd1cfb | ||
|
328be18288 | ||
|
03331b3fff | ||
|
ca1c0acb27 | ||
|
b4af0f5145 | ||
|
e174c5b503 | ||
|
c91322e38e | ||
|
85fc728293 | ||
|
6727e74daf | ||
|
d7bf91d039 | ||
|
878dc379b9 | ||
|
4f97627b02 | ||
|
56858650cb | ||
|
dcbb1657b4 | ||
|
9dc79becc2 | ||
|
9ec6693e4d | ||
|
a2f6776bf8 | ||
|
cb1ef7d616 | ||
|
def176b240 | ||
|
d5a8717bb0 | ||
|
8acda14170 | ||
|
c1e34792f4 | ||
|
c5b6f066b8 | ||
|
f109bc2e98 | ||
|
d36213b7d0 | ||
|
389bb03fc0 | ||
|
78837173a4 | ||
|
4328e8a4c1 | ||
|
646fbcb21f | ||
|
9da2d298c3 | ||
|
95a4ebf836 | ||
|
3024cc1bfe | ||
|
7340b0065d | ||
|
dd01fe9ad3 | ||
|
336797363b | ||
|
046cc359ba | ||
|
9e02c5f0cd | ||
|
c73329f7d4 | ||
|
d90c207c50 | ||
|
b87091a59b | ||
|
f8aff4bf4b | ||
|
50b9a4ceb6 | ||
|
93cf62d944 | ||
|
09e45cea5b | ||
|
65c95fb413 | ||
|
c643726c9d | ||
|
eda19bc28d | ||
|
9b145c8398 | ||
|
53c6821734 | ||
|
842a0b4193 | ||
|
8a37449f8a | ||
|
21989d4142 | ||
|
63449912e2 | ||
|
1661396951 | ||
|
fd6610bdae | ||
|
19e01abf70 | ||
|
a89b2ead47 | ||
|
81cfa0a380 | ||
|
41449370b4 | ||
|
73285885b3 | ||
|
b71213522a | ||
|
c0c5255103 | ||
|
a2cdb32f14 | ||
|
1930380e16 | ||
|
5509264a2b | ||
|
6f902bcfe7 | ||
|
fdce017311 | ||
|
96a980b682 | ||
|
cedcd1a832 | ||
|
1737788a92 | ||
|
f65793582d | ||
|
90a7ecaaf1 | ||
|
b6896898c2 | ||
|
81bb75bba6 | ||
|
6661871707 | ||
|
885fdcc354 | ||
|
16261813ca | ||
|
fcf5b74542 | ||
|
539fba02ac | ||
|
4a2a121e89 | ||
|
16b827e4e4 | ||
|
a9197e67ea | ||
|
ab63700a80 | ||
|
0993a8ae28 | ||
|
a7c4a0c108 | ||
|
bb97e35f82 | ||
|
cbed6c911f | ||
|
272b461b3e | ||
|
a29d53cb31 | ||
|
4947f0f747 | ||
|
a6f8944afe | ||
|
33c0319263 | ||
|
93cad1ca5a | ||
|
9a9fac2e83 | ||
|
a285b2ac17 | ||
|
4b4d0fcfcf | ||
|
6b2b1aa4c3 | ||
|
fce7dd2957 | ||
|
6b2f0aeb63 | ||
|
4cae565ef3 | ||
|
d76fd844c9 | ||
|
27a099a94b | ||
|
9c33ce7979 | ||
|
1612eacab2 | ||
|
f1f59d3c1d | ||
|
a42fc8904d | ||
|
f62c9db5af | ||
|
1ba83f3283 | ||
|
c33a447210 | ||
|
81fafae68d | ||
|
447bdc50a0 | ||
|
b91712214f | ||
|
5574a14aee | ||
|
a82ab6a45d | ||
|
40ea78d57b | ||
|
eab16ef39f | ||
|
da701aff6d | ||
|
aece87bf3e | ||
|
2931169dd1 | ||
|
95655b838e | ||
|
1846795844 | ||
|
76c2f5e39c | ||
|
51f2b79437 | ||
|
02eafe9883 | ||
|
05a42576c2 | ||
|
39949eefe2 | ||
|
d7afc92b5f | ||
|
44b5eed7ca | ||
|
4346fb2405 | ||
|
9c95dfb712 | ||
|
f99fe59ce8 | ||
|
5cd430647e | ||
|
fc55400aad | ||
|
d2370f780f | ||
|
f254e86511 | ||
|
3cc6aa1236 | ||
|
65ac074410 | ||
|
c6d1bcb352 | ||
|
eb9d9893f8 | ||
|
8db1d30852 | ||
|
1f22c72634 | ||
|
7322bb3ffa | ||
|
17ba28c44b | ||
|
47c84fac85 | ||
|
1cf1ba1ddb | ||
|
34696c0fec | ||
|
a067e801d7 | ||
|
d01acd8030 | ||
|
0dffd2ffd2 | ||
|
0f1006bf08 | ||
|
f69ed07ba7 | ||
|
9591649ec6 | ||
|
e9abd83412 | ||
|
4de54fbee6 | ||
|
77a83f4ffc | ||
|
d613d29839 | ||
|
c7c5f68a84 | ||
|
d9f4d069e6 | ||
|
eb8bb01c57 | ||
|
7e91c401d3 | ||
|
54790f032e | ||
|
08956eb4fb | ||
|
289d963ba5 | ||
|
a6f9701368 | ||
|
237d6b89f6 | ||
|
2b89a07dae | ||
|
a01642e480 | ||
|
6b9b502354 | ||
|
8606b78b9e | ||
|
cd325dfe52 | ||
|
c1b3d0c629 | ||
|
94238fd95b | ||
|
5c835bc0f6 | ||
|
100fc2e7d4 | ||
|
880e977fb6 | ||
|
3a9b0eee7a | ||
|
6baf90ac4c | ||
|
77064754ad | ||
|
92ed79e68e | ||
|
b91a975d56 | ||
|
84b6818982 | ||
|
4aee9fe2a3 | ||
|
4847e0d1e6 | ||
|
a599b989cf | ||
|
50658a5c0a | ||
|
f828fc1ac1 | ||
|
bd71ca2a3c | ||
|
2d38c9614e | ||
|
7ce265f2cf | ||
|
638299534b | ||
|
919a005b65 | ||
|
58865ca5ec | ||
|
d62d82bb2f | ||
|
f1ecd66283 | ||
|
fd2cb4b8d2 | ||
|
1cc3e05c11 | ||
|
d5b6d4ab12 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,7 +16,10 @@ tags
|
||||
*.b#*
|
||||
blackmagic_upgrade
|
||||
*.exe
|
||||
.DS_Store
|
||||
*.elf
|
||||
.vscode
|
||||
cscope.out
|
||||
cscope.files
|
||||
.gdb_history
|
||||
src/artifacts/
|
||||
src/artifacts/
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "libopencm3"]
|
||||
path = libopencm3
|
||||
url = https://github.com/libopencm3/libopencm3.git
|
||||
url = https://github.com/flirc/libopencm3.git
|
||||
|
12
Makefile
12
Makefile
@ -17,13 +17,23 @@ ifndef NO_LIBOPENCM3
|
||||
git submodule init ;\
|
||||
git submodule update ;\
|
||||
fi
|
||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/stm32/f1 lib/stm32/f4 lib/lm4f
|
||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib/sam/d
|
||||
endif
|
||||
$(Q)$(MAKE) $(MFLAGS) -C src
|
||||
|
||||
all_platforms:
|
||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
||||
|
||||
clean:
|
||||
ifndef NO_LIBOPENCM3
|
||||
$(Q)$(MAKE) $(MFLAGS) -C libopencm3 $@
|
||||
endif
|
||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
||||
|
||||
clang-tidy:
|
||||
$(Q)scripts/run-clang-tidy.py -s "$(PWD)"
|
||||
|
||||
clang-format:
|
||||
$(Q)$(MAKE) $(MFLAGS) -C src $@
|
||||
|
||||
.PHONY: clean all_platforms clang-tidy clang-format
|
||||
|
153
README.md
153
README.md
@ -1,113 +1,54 @@
|
||||
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
|
||||
Jeff Probe
|
||||
==========
|
||||
|
||||
You can also build blackmagic as a PC hosted application
|
||||
"make PROBE_HOST=hosted"
|
||||
This is a fork of the [original Black Magic Probe](https://github.com/blacksphere/blackmagic).
|
||||
|
||||
This builds the same GDB server, that is running on the Black Magic Probe.
|
||||
While connection to the Black Magic Probe GDB server is via serial line,
|
||||
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.
|
||||
The original is arguably better, faster and wider supported. However, this
|
||||
project was a way to offer an affordable version and I'll rely on community
|
||||
support and pull requests.
|
||||
|
||||
When connected to a single BMP supported probe, starting "blackmagic" w/o any
|
||||
arguments starts the server. When several BMP supported probes are connected,
|
||||
their types, position and serial number is displayed and the program exits.
|
||||
Add "-P (position)" to the next invocation to select one.
|
||||
For the setup from the sample session above:
|
||||
In another terminal:
|
||||
```console
|
||||
> blackmagic
|
||||
Using 1d50:6018 E2E489E7 Black Sphere Technologies Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty)
|
||||
Remote is Black Magic Probe (STLINK), (Firmware v1.6.1-477-g70bb131-dirty) v1.6.1-477-g70bb131-dirty
|
||||
Listening on TCP: 2000
|
||||
And in the GDB terminal:
|
||||
(gdb) target ext :2000
|
||||
Remote debugging using :2000
|
||||
(gdb) mon s
|
||||
...
|
||||
One urguably better funncton is the ability to do DEBUG and Serial communication
|
||||
over a single JTAG cable when paired with a device that uses single wire JTAG.
|
||||
|
||||
Normally, the serial header can be used on a target for the serial port, and
|
||||
shows up as the second serial device on the system, however, we can dynamically
|
||||
change the pins to use the ones on the JTAG cable with the following command:
|
||||
|
||||
``` bash
|
||||
$ mon convert_tdio enable
|
||||
```
|
||||
|
||||
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.
|
||||
Compilation
|
||||
---
|
||||
|
||||
Newer toolchains can cause issues. I usually work 4_9-2014q4-20141203 found [here.](https://launchpad.net/gcc-arm-embedded/4.9/4.9-2014-q4-major/+download/gcc-arm-none-eabi-4_9-2014q4-20141203-mac.tar.bz2)
|
||||
|
||||
the versionfollowing version
|
||||
|
||||
```bash
|
||||
$ make clean
|
||||
$ make PROBE_HOST=jeff CUSTOM_SER=1
|
||||
$ dfu-util --device ,1d50:6017 -s 0x00002000:leave -D src/blackmagic.bin
|
||||
```
|
||||
|
||||
CUSTOM OPTIONS
|
||||
---
|
||||
|
||||
On mac, our device shows up with a serial number /dev/tty.cuJEFF123HDC
|
||||
|
||||
This can be annoy if we want to autocnnect with a gith script. We can override
|
||||
the use of a serial number by doing a custom compliation such that our device
|
||||
shows up with the following: /dev/cu.usbmodemJEFF1 and /dev/cu.usbmodemJEFF3
|
||||
|
||||
```bash
|
||||
$ make PROBE_HOST=jeff CUSTOM_SER=1
|
||||
```
|
||||
|
||||
More
|
||||
---
|
||||
|
||||
More helpful information can be found on the black magic probe [readme](https://github.com/blacksphere/blackmagic/blob/master/README.md#black-magic-probe), which is relevant.
|
||||
|
||||
See online documentation at https://github.com/blacksphere/blackmagic/wiki
|
||||
|
||||
Binaries from the latest automated build can be found on the release page.
|
||||
|
271
UsingRTT.md
Normal file
271
UsingRTT.md
Normal file
@ -0,0 +1,271 @@
|
||||
# Using RTT
|
||||
|
||||
When debugging arm processors, there are three ways for the target to print debug messages on the host: Semihosting, Serial Wire Output SWO, and Real-Time Transfer RTT.
|
||||
|
||||
[Black Magic Probe](https://github.com/blacksphere/blackmagic) (BMP) is an open source debugger probe that already implements Semihosting and Single Wire Output. This patch adds Real-Time Transfer RTT output to usb serial port.
|
||||
|
||||
- RTT is implemented, not as a user program, but as a serial port device. To read RTT output, use a terminal emulator and connect to the serial port.
|
||||
|
||||
- A novel way to detect RTT automatically, fast and convenient.
|
||||
|
||||
## Use
|
||||
This example uses linux as operating system. For Windows and MacOS see the *Operating Systems* section.
|
||||
|
||||
In one window open a terminal emulator (minicom, putty) and connect to the usb uart:
|
||||
```
|
||||
$ minicom -c on -D /dev/ttyBmpTarg
|
||||
```
|
||||
|
||||
In another window open a debugger:
|
||||
```
|
||||
$ gdb
|
||||
(gdb) target extended-remote /dev/ttyBmpGdb
|
||||
(gdb) monitor swdp_scan
|
||||
(gdb) attach 1
|
||||
(gdb) monitor rtt
|
||||
(gdb) run
|
||||
^C
|
||||
(gdb) monitor rtt status
|
||||
rtt: on found: yes ident: off halt: off channels: auto 0 1 3
|
||||
max poll ms: 256 min poll ms: 8 max errs: 10
|
||||
```
|
||||
|
||||
The terminal emulator displays RTT output from the target,
|
||||
and characters typed in the terminal emulator are sent via RTT to the target.
|
||||
|
||||
|
||||
## gdb commands
|
||||
|
||||
The following new gdb commands are available:
|
||||
|
||||
- ``monitor rtt``
|
||||
|
||||
switch rtt on
|
||||
|
||||
- ``monitor rtt enable``
|
||||
|
||||
switch rtt on
|
||||
|
||||
- ``monitor rtt disable``
|
||||
|
||||
switch rtt off
|
||||
|
||||
- ``monitor rtt poll `` max_poll_ms min_poll_ms max_errs
|
||||
|
||||
sets maximum time between polls, minimum time between polls, and the maximum number of errors before RTT disconnects from the target. Times in milliseconds. It is best if max_poll_ms/min_poll_ms is a power of two. As an example, if you wish to check for RTT output between once per second to eight times per second: ``monitor rtt poll 1000 125 10``.
|
||||
|
||||
- ``monitor rtt status``
|
||||
|
||||
show status.
|
||||
|
||||
rtt|found|state
|
||||
---|---|---
|
||||
rtt: off|found: no|rtt inactive
|
||||
rtt: on|found: no|searching for rtt control block
|
||||
rtt: on|found: yes|rtt active
|
||||
rtt: off|found: yes|corrupt rtt control block, or target memory access error
|
||||
|
||||
A status of `rtt: on found: no` indicates bmp is still searching for the rtt control block in target ram, but has not found anything yet. A status of `rtt: on found: yes` indicates the control block has been found and rtt is active.
|
||||
|
||||
- ``monitor rtt channel``
|
||||
|
||||
enables the first two output channels, and the first input channel. (default)
|
||||
|
||||
- ``monitor rtt channel number...``
|
||||
|
||||
enables the given RTT channel numbers. Channels are numbers from 0 to 15, inclusive. Eg. ``monitor rtt channel 0 1 4`` to enable channels 0, 1, and 4.
|
||||
|
||||
- ``monitor rtt ident string``
|
||||
|
||||
sets RTT ident to *string*. If *string* contains a space, replace the space with an underscore _. Setting ident string is optional, RTT works fine without.
|
||||
|
||||
- ``monitor rtt ident``
|
||||
|
||||
clears ident string. (default)
|
||||
|
||||
- ``monitor rtt cblock``
|
||||
|
||||
shows rtt control block data, and which channels are enabled. This is an example control block:
|
||||
|
||||
```
|
||||
(gdb) mon rtt cb
|
||||
cbaddr: 0x200000a0
|
||||
ch ena cfg i/o buf@ size head@ tail@ flg
|
||||
0 y y out 0x20000148 1024 0x200000c4 0x200000c8 2
|
||||
1 y n out 0x00000000 0 0x200000dc 0x200000e0 0
|
||||
2 n n out 0x00000000 0 0x200000f4 0x200000f8 0
|
||||
3 y y in 0x20000548 16 0x2000010c 0x20000110 0
|
||||
4 n n in 0x00000000 0 0x20000124 0x20000128 0
|
||||
5 n n in 0x00000000 0 0x2000013c 0x20000140 0
|
||||
6 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
7 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
8 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
9 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
10 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
11 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
12 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
13 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
14 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
15 n n in 0x00000000 0 0x00000000 0x00000000 0
|
||||
```
|
||||
|
||||
Channels are listed, one channel per line. The columns are: channel, enabled, configured, input/output, buffer address, buffer size, address of head pointer, address of tail pointer, flag. Each channel is a circular buffer with head and tail pointer.
|
||||
|
||||
Note the columns `ena` for enabled, `cfg` for configured.
|
||||
|
||||
Configured channels have a non-zero buffer address and non-zero size. Configured channels are marked yes `y` in the column `cfg` . What channels are configured depends upon target software.
|
||||
|
||||
Channels the user wants to see are marked yes `y` in the column enabled `ena`. The user can change which channels are shown with the `monitor rtt channel` command.
|
||||
|
||||
Output channels are displayed, and Input channels receive keyboard input, if they are marked yes in both *enabled* and *configured*.
|
||||
|
||||
The control block is cached for speed. In an interrupted program, `monitor rtt` will force a reload of the control block when the program continues.
|
||||
|
||||
## Identifier string
|
||||
It is possible to set an RTT identifier string.
|
||||
As an example, if the RTT identifier is "IDENT STR":
|
||||
```
|
||||
$ gdb
|
||||
(gdb) target extended-remote /dev/ttyBmpGdb
|
||||
(gdb) monitor swdp_scan
|
||||
(gdb) attach 1
|
||||
(gdb) monitor rtt ident IDENT_STR
|
||||
(gdb) monitor rtt
|
||||
(gdb) run
|
||||
^C
|
||||
(gdb) monitor rtt status
|
||||
rtt: on found: yes ident: "IDENT STR" halt: off channels: auto 0 1 3
|
||||
max poll ms: 256 min poll ms: 8 max errs: 10
|
||||
```
|
||||
Note replacing space with underscore _ in *monitor rtt ident*.
|
||||
|
||||
Setting an identifier string is optional. RTT gives the same output at the same speed, with or without specifying identifier string.
|
||||
|
||||
## Operating systems
|
||||
|
||||
[Configuration](https://github.com/blacksphere/blackmagic/wiki/Getting-Started) instructions for windows, linux and macos.
|
||||
|
||||
### Windows
|
||||
|
||||
After configuration, Black Magic Probe shows up in Windows as two _USB Serial (CDC)_ ports.
|
||||
|
||||
Connect arm-none-eabi-gdb, the gnu debugger for arm processors, to the lower numbered of the two COM ports. Connect an ansi terminal emulator to the higher numbered of the two COM ports.
|
||||
|
||||
Sample gdb session:
|
||||
```
|
||||
(gdb) target extended-remote COM3
|
||||
(gdb) monitor swdp_scan
|
||||
(gdb) attach 1
|
||||
(gdb) monitor rtt
|
||||
(gdb) run
|
||||
```
|
||||
|
||||
For COM port COM10 and higher, add the prefix `\\.\`, e.g.
|
||||
```
|
||||
target extended-remote \\.\COM10
|
||||
```
|
||||
|
||||
Target RTT output will appear in the terminal, and what you type in the terminal will be sent to the RTT input of the target.
|
||||
|
||||
### linux
|
||||
On linux, install [udev rules](https://github.com/blacksphere/blackmagic/blob/master/driver/99-blackmagic.rules). Disconnect and re-connect the BMP. Check the device shows up in /dev/ :
|
||||
```
|
||||
$ ls -l /dev/ttyBmp*
|
||||
lrwxrwxrwx 1 root root 7 Dec 13 07:29 /dev/ttyBmpGdb -> ttyACM0
|
||||
lrwxrwxrwx 1 root root 7 Dec 13 07:29 /dev/ttyBmpTarg -> ttyACM2
|
||||
```
|
||||
Connect terminal emulator to /dev/ttyBmpTarg and gdb to /dev/ttyBmpGdb .
|
||||
|
||||
In one window:
|
||||
```
|
||||
minicom -c on -D /dev/ttyBmpTarg
|
||||
```
|
||||
In another window :
|
||||
```
|
||||
gdb
|
||||
(gdb) target extended-remote /dev/ttyBmpGdb
|
||||
(gdb) monitor swdp_scan
|
||||
(gdb) attach 1
|
||||
(gdb) monitor rtt
|
||||
(gdb) run
|
||||
```
|
||||
|
||||
### MacOS
|
||||
|
||||
On MacOS the tty devices have different names than on linux. On connecting blackmagic to the computer 4 devices are created, 2 'tty' and 2 'cu' devices. Gdb connects to the first cu device (e.g.: `target extended-remote /dev/cu.usbmodemDDCEC9EC1`), while RTT is connected to the second tty device (`minicom -c on -D /dev/tty.usbmodemDDCEC9EC3`). In full:
|
||||
|
||||
In one Terminal window, connect a terminal emulator to /dev/tty.usbmodemDDCEC9EC3 :
|
||||
|
||||
```
|
||||
minicom -c on -D /dev/tty.usbmodemDDCEC9EC3
|
||||
```
|
||||
In another Terminal window, connect gdb to /dev/cu.usbmodemDDCEC9EC1 :
|
||||
```
|
||||
gdb
|
||||
(gdb) target extended-remote /dev/cu.usbmodemDDCEC9EC1
|
||||
(gdb) monitor swdp_scan
|
||||
(gdb) attach 1
|
||||
(gdb) monitor rtt
|
||||
(gdb) run
|
||||
```
|
||||
RTT input/output is in the window running _minicom_.
|
||||
|
||||
## Notes
|
||||
|
||||
- Design goal was smallest, simplest implementation that has good practical use.
|
||||
|
||||
- RTT code size is 3.5 kbyte - the whole debugger 110 kbyte.
|
||||
|
||||
- Because RTT is implemented as a serial port device, there is no need to write and maintain software for different host operating systems. A serial port works everywhere - linux, windows and mac. You can even use an Android mobile phone as RTT terminal.
|
||||
|
||||
- Because polling occurs between debugger probe and target, the load on the host is small. There is no constant usb traffic, there are no real-time requirements on the host.
|
||||
|
||||
- RTT polling frequency is adaptive and goes up and down with RTT activity. Use *monitor rtt poll* to balance response speed and target load for your use.
|
||||
|
||||
- Detects RTT automatically, very convenient.
|
||||
|
||||
- When using RTT as a terminal, sending data from host to target, you may need to change local echo, carriage return and/or line feed settings in your terminal emulator.
|
||||
|
||||
- Architectures such as risc-v may not allow the debugger access to target memory while the target is running. As a workaround, on these architectures RTT briefly halts the target during polling. If the target is halted during polling, `monitor rtt status` shows `halt: on`.
|
||||
|
||||
- Measured RTT speed.
|
||||
|
||||
| debugger | char/s |
|
||||
| ------------------------- | ------ |
|
||||
| bmp stm32f723 stlinkv3 | 49811 |
|
||||
| bmp stm32f411 black pill | 50073 |
|
||||
| bmp stm32f103 blue pill | 50142 |
|
||||
|
||||
This is the speed at which characters can be sent from target to debugger probe, in reasonable circumstances. Test target is an stm32f103 blue pill running an [Arduino sketch](https://github.com/koendv/Arduino-RTTStream/blob/main/examples/SpeedTest/SpeedTest.ino). Default *monitor rtt poll* settings on debugger. Default RTT buffer size in target and debugger. Overhead for printf() calls included.
|
||||
|
||||
## Compiling firmware
|
||||
To compile with RTT support, add *ENABLE_RTT=1*.
|
||||
|
||||
Eg. for STM32F103 blue pill:
|
||||
```
|
||||
make clean
|
||||
make PROBE_HOST=stlink ENABLE_RTT=1
|
||||
```
|
||||
or for the STM32F411 *[Black Pill](https://www.aliexpress.com/item/1005001456186625.html)*:
|
||||
```
|
||||
make clean
|
||||
make PROBE_HOST=f4discovery BLACKPILL=1 ENABLE_RTT=1
|
||||
```
|
||||
Setting an ident string is optional. But if you wish, you can set the default RTT ident at compile time.
|
||||
For STM32F103 *Blue Pill*:
|
||||
```
|
||||
make clean
|
||||
make PROBE_HOST=stlink ENABLE_RTT=1 "RTT_IDENT=IDENT\ STR"
|
||||
```
|
||||
or for STM32F411 *Black Pill*:
|
||||
```
|
||||
make clean
|
||||
make PROBE_HOST=f4discovery BLACKPILL=1 ENABLE_RTT=1 "RTT_IDENT=IDENT\ STR"
|
||||
```
|
||||
Note the backslash \\ before the space.
|
||||
|
||||
## Links
|
||||
- [OpenOCD](https://openocd.org/doc/html/General-Commands.html#Real-Time-Transfer-_0028RTT_0029)
|
||||
- [probe-rs](https://probe.rs/) and [rtt-target](https://github.com/mvirkkunen/rtt-target) for the _rust_ programming language.
|
||||
- [RTT Stream](https://github.com/koendv/Arduino-RTTStream) for Arduino on arm processors
|
||||
- [\[WIP\] RTT support - PR from katyo](https://github.com/blacksphere/blackmagic/pull/833)
|
@ -1,6 +1,7 @@
|
||||
# Black Magic Probe
|
||||
# there are two connections, one for GDB and one for UART debugging
|
||||
# copy this to /etc/udev/rules.d/99-blackmagic.rules
|
||||
# and run /usr/sbin/udevadm control --reload-rules
|
||||
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb"
|
||||
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
|
||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666"
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8435287300e5ca9af9f889c529e7b1fa019c42fb
|
||||
Subproject commit 63573143ef7e1b037d1f0c5baedc5264e12562b8
|
BIN
schematic/bm-sam-a04.pdf
Normal file
BIN
schematic/bm-sam-a04.pdf
Normal file
Binary file not shown.
68
src/Makefile
68
src/Makefile
@ -9,7 +9,7 @@ Q := @
|
||||
endif
|
||||
|
||||
CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts \
|
||||
-std=gnu99 -g3 -MD -I./target \
|
||||
-std=gnu99 -MD -I./target \
|
||||
-I. -Iinclude -I$(PLATFORM_DIR)
|
||||
|
||||
ifeq ($(ENABLE_DEBUG), 1)
|
||||
@ -65,11 +65,6 @@ SRC = \
|
||||
|
||||
include $(PLATFORM_DIR)/Makefile.inc
|
||||
|
||||
ifneq ($(PC_HOSTED),1)
|
||||
# Output memory usage information
|
||||
LDFLAGS += -Wl,--print-memory-usage
|
||||
endif
|
||||
|
||||
OPT_FLAGS ?= -Os
|
||||
CFLAGS += $(OPT_FLAGS)
|
||||
LDFLAGS += $(OPT_FLAGS)
|
||||
@ -95,8 +90,28 @@ VPATH += platforms/common
|
||||
CFLAGS += -Iplatforms/common
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_RTT), 1)
|
||||
CFLAGS += -DENABLE_RTT
|
||||
SRC += rtt.c rtt_if.c
|
||||
endif
|
||||
|
||||
ifdef RTT_IDENT
|
||||
CFLAGS += -DRTT_IDENT=$(RTT_IDENT)
|
||||
endif
|
||||
|
||||
OBJ = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SRC)))
|
||||
|
||||
OPTIMIZE := swdptap.o jtagtap.o \
|
||||
adiv5_jtagdp.o adiv5_swdp.o adiv5.o \
|
||||
cortexa.o cortexm.o \
|
||||
gdb_if.o gdb_main.o gdb_hostio.o gdb_packet.o \
|
||||
jtag_devs.o jtag_scan.o \
|
||||
crc32.o main.o \
|
||||
cdcacm.o jeff.o timing.o traceswo.o usbuart.o \
|
||||
|
||||
$(OPTIMIZE):: CFLAGS := $(filter-out -Os, $(CFLAGS))
|
||||
$(OPTIMIZE):: CFLAGS += -O3
|
||||
|
||||
$(TARGET): include/version.h $(OBJ)
|
||||
@echo " LD $@"
|
||||
$(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS)
|
||||
@ -112,14 +127,14 @@ $(TARGET): include/version.h $(OBJ)
|
||||
ifndef PC_HOSTED
|
||||
%.bin: %.elf
|
||||
@echo " OBJCOPY $@"
|
||||
$(Q)$(OBJCOPY) -O binary $^ $@
|
||||
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@
|
||||
|
||||
%.hex: %.elf
|
||||
@echo " OBJCOPY $@"
|
||||
$(Q)$(OBJCOPY) -O ihex $^ $@
|
||||
endif
|
||||
|
||||
.PHONY: clean host_clean all_platforms FORCE
|
||||
.PHONY: clean host_clean all_platforms clang-format FORCE
|
||||
|
||||
clean: host_clean
|
||||
$(Q)echo " CLEAN"
|
||||
@ -127,16 +142,27 @@ clean: host_clean
|
||||
-$(Q)$(RM) platforms/*/*.o platforms/*/*.d mapfile include/version.h
|
||||
|
||||
all_platforms:
|
||||
$(Q)if [ ! -f ../libopencm3/Makefile ]; then \
|
||||
echo "Initialising git submodules..." ;\
|
||||
git submodule init ;\
|
||||
git submodule update ;\
|
||||
fi
|
||||
$(Q)$(MAKE) $(MFLAGS) -C ../libopencm3 lib/stm32/f1 lib/stm32/f4 lib/lm4f
|
||||
$(Q)set -e ;\
|
||||
mkdir -p artifacts/$(shell git describe --always --dirty --tags) ;\
|
||||
echo "<html><body><ul>" > artifacts/index.html ;\
|
||||
$(MAKE) clean ;\
|
||||
for i in platforms/*/Makefile.inc ; do \
|
||||
export DIRNAME=`dirname $$i` ;\
|
||||
export PROBE_HOST=`basename $$DIRNAME` ;\
|
||||
export CFLAGS=-Werror ;\
|
||||
echo "Building for hardware platform: $$PROBE_HOST" ;\
|
||||
$(MAKE) clean ;\
|
||||
$(MAKE);\
|
||||
if [ -f blackmagic ]; then \
|
||||
mv blackmagic artifacts/blackmagic-$$PROBE_HOST ;\
|
||||
echo "<li><a href='blackmagic-$$PROBE_HOST'>$$PROBE_HOST</a></li>"\
|
||||
>> artifacts/index.html ;\
|
||||
fi ;\
|
||||
if [ -f blackmagic.bin ]; then \
|
||||
mv blackmagic.bin artifacts/blackmagic-$$PROBE_HOST.bin ;\
|
||||
echo "<li><a href='blackmagic-$$PROBE_HOST.bin'>$$PROBE_HOST</a></li>"\
|
||||
@ -147,13 +173,31 @@ all_platforms:
|
||||
echo "<li><a href='blackmagic_dfu-$$PROBE_HOST.bin'>$$PROBE_HOST DFU</a></li>"\
|
||||
>> artifacts/index.html ;\
|
||||
fi ;\
|
||||
$(MAKE) clean ;\
|
||||
done ;\
|
||||
echo "</ul></body></html>" >> artifacts/index.html ;\
|
||||
cp artifacts/*.bin artifacts/$(shell git describe --always --dirty --tags)
|
||||
cp artifacts/blackmagic* artifacts/$(shell git describe --always --dirty --tags)
|
||||
|
||||
command.c: include/version.h
|
||||
|
||||
GIT_VERSION := $(shell git describe --always --dirty --tags)
|
||||
VERSION_HEADER := \#define FIRMWARE_VERSION "$(GIT_VERSION)"
|
||||
|
||||
include/version.h: FORCE
|
||||
$(Q)echo " GIT include/version.h"
|
||||
$(Q)echo "#define FIRMWARE_VERSION \"$(shell git describe --always --dirty --tags)\"" > $@
|
||||
@# If git isn't found then GIT_VERSION will be an empty string.
|
||||
ifeq ($(GIT_VERSION),)
|
||||
@echo Git not found, assuming up to date include/version.h
|
||||
else
|
||||
@# Note that when we echo the version to the header file, echo writes a final newline
|
||||
@# to the file. This is fine and probably makes the file more human-readable, but
|
||||
@# also means we have to account for that newline in this comparison.
|
||||
$(Q)if [ ! -f $@ ] || [ "$$(cat $@)" != "$$(echo '$(VERSION_HEADER)\n')" ]; then \
|
||||
echo " GEN $@"; \
|
||||
echo '$(VERSION_HEADER)' > $@; \
|
||||
fi
|
||||
endif
|
||||
|
||||
clang-format:
|
||||
$(Q)clang-format -i *.c */*.c */*/*.c *.h */*.h */*/*.h
|
||||
|
||||
-include *.d
|
||||
|
166
src/command.c
166
src/command.c
@ -34,11 +34,18 @@
|
||||
#include "version.h"
|
||||
#include "serialno.h"
|
||||
|
||||
#ifdef ENABLE_RTT
|
||||
#include "rtt.h"
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_TRACESWO
|
||||
# include "traceswo.h"
|
||||
#endif
|
||||
|
||||
static bool cmd_version(target *t, int argc, char **argv);
|
||||
#ifdef PLATFORM_HAS_PRINTSERIAL
|
||||
static bool cmd_serial(target *t, int argc, char **argv);
|
||||
#endif
|
||||
static bool cmd_help(target *t, int argc, char **argv);
|
||||
|
||||
static bool cmd_jtag_scan(target *t, int argc, char **argv);
|
||||
@ -56,12 +63,25 @@ static bool cmd_target_power(target *t, int argc, const char **argv);
|
||||
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
static bool cmd_heapinfo(target *t, int argc, const char **argv);
|
||||
#ifdef ENABLE_RTT
|
||||
static bool cmd_rtt(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
||||
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
#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[] = {
|
||||
{"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"},
|
||||
{"jtag_scan", (cmd_handler)cmd_jtag_scan, "Scan JTAG chain for devices" },
|
||||
{"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
|
||||
@ -74,6 +94,9 @@ const struct command_s cmd_list[] = {
|
||||
#ifdef PLATFORM_HAS_POWER_SWITCH
|
||||
{"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
|
||||
#endif
|
||||
#ifdef ENABLE_RTT
|
||||
{"rtt", (cmd_handler)cmd_rtt, "enable|disable|status|channel 0..15|ident (str)|cblock|poll maxms minms maxerr" },
|
||||
#endif
|
||||
#ifdef PLATFORM_HAS_TRACESWO
|
||||
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
||||
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
||||
@ -84,6 +107,13 @@ const struct command_s cmd_list[] = {
|
||||
{"heapinfo", (cmd_handler)cmd_heapinfo, "Set semihosting heapinfo" },
|
||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
||||
{"debug_bmp", (cmd_handler)cmd_debug_bmp, "Output BMP \"debug\" strings to the second vcom: (enable|disable)"},
|
||||
#endif
|
||||
#ifdef PLATFORM_HAS_UART_WHEN_SWDP
|
||||
{"convert_tdio", (cmd_handler)cmd_convert_tdio,"Switch TDI/O pins to UART TX/RX functions"},
|
||||
{"set_srst", (cmd_handler)cmd_set_srst,"Set output state of SRST pin (enable|disable)"},
|
||||
#endif
|
||||
#ifdef PLATFORM_HAS_BOOTLOADER
|
||||
{"enter_bootldr", (cmd_handler)cmd_enter_bootldr,"Force BMP into bootloader mode"},
|
||||
#endif
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
@ -410,6 +440,82 @@ static bool cmd_target_power(target *t, int argc, const char **argv)
|
||||
}
|
||||
#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
|
||||
static bool cmd_traceswo(target *t, int argc, const char **argv)
|
||||
{
|
||||
@ -487,6 +593,66 @@ static bool cmd_debug_bmp(target *t, int argc, const char **argv)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_UART_WHEN_SWDP
|
||||
static bool cmd_convert_tdio(target *t, int argc, const char **argv)
|
||||
{
|
||||
(void)t;
|
||||
|
||||
uint8_t val;
|
||||
if (argc > 1) {
|
||||
val = (!strcmp(argv[1], "enable")) ? true : false;
|
||||
usbuart_convert_tdio(val);
|
||||
} else {
|
||||
gdb_outf("Convert_tdio: %s\n",(usbuart_convert_tdio_enabled()) ?
|
||||
"enabled" : "disabled");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmd_set_srst(target *t, int argc, const char **argv)
|
||||
{
|
||||
(void) t;
|
||||
|
||||
uint8_t val;
|
||||
if (argc > 1) {
|
||||
val = (!strcmp(argv[1], "enable")) ? true : false;
|
||||
platform_srst_set_val(val);
|
||||
} else {
|
||||
gdb_outf("SRST: %s\n",(platform_srst_get_val()) ?
|
||||
"enabled" : "disabled");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_BOOTLOADER
|
||||
static bool cmd_enter_bootldr(target *t, int argc, const char **argv)
|
||||
{
|
||||
(void) t;
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
scb_reset_system();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_PRINTSERIAL
|
||||
bool cmd_serial(target *t, int argc, char **argv)
|
||||
{
|
||||
(void) t;
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
print_serial();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool cmd_heapinfo(target *t, int argc, const char **argv)
|
||||
{
|
||||
if (t == NULL) gdb_out("not attached\n");
|
||||
|
@ -130,4 +130,3 @@ int hostio_system(struct target_controller *tc,
|
||||
gdb_putpacket_f("Fsystem,%08X/%X", cmd, cmd_len);
|
||||
return gdb_main_loop(tc, true);
|
||||
}
|
||||
|
||||
|
247
src/gdb_main.c
247
src/gdb_main.c
@ -35,6 +35,9 @@
|
||||
#include "command.h"
|
||||
#include "crc32.h"
|
||||
#include "morse.h"
|
||||
#ifdef ENABLE_RTT
|
||||
#include "rtt.h"
|
||||
#endif
|
||||
|
||||
enum gdb_signal {
|
||||
GDB_SIGINT = 2,
|
||||
@ -43,7 +46,7 @@ enum gdb_signal {
|
||||
GDB_SIGLOST = 29,
|
||||
};
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
#define BUF_SIZE 1024U
|
||||
|
||||
#define ERROR_IF_NO_TARGET() \
|
||||
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
||||
@ -51,23 +54,29 @@ enum gdb_signal {
|
||||
typedef struct
|
||||
{
|
||||
const char *cmd_prefix;
|
||||
void (*func)(const char *packet, int len);
|
||||
void (*func)(const char *packet, size_t len);
|
||||
} cmd_executer;
|
||||
|
||||
static char pbuf[BUF_SIZE + 1];
|
||||
static char pbuf[BUF_SIZE + 1U];
|
||||
|
||||
static target *cur_target;
|
||||
static target *last_target;
|
||||
static bool gdb_needs_detach_notify = false;
|
||||
|
||||
static void handle_q_packet(char *packet, int len);
|
||||
static void handle_v_packet(char *packet, int len);
|
||||
static void handle_z_packet(char *packet, int len);
|
||||
static void handle_q_packet(char *packet, size_t len);
|
||||
static void handle_v_packet(char *packet, size_t len);
|
||||
static void handle_z_packet(char *packet, size_t len);
|
||||
static void handle_kill_target(void);
|
||||
|
||||
static void gdb_target_destroy_callback(struct target_controller *tc, target *t)
|
||||
{
|
||||
(void)tc;
|
||||
if (cur_target == t)
|
||||
if (cur_target == t) {
|
||||
gdb_put_notificationz("%Stop:W00");
|
||||
gdb_out("You are now detached from the previous target.\n");
|
||||
cur_target = NULL;
|
||||
gdb_needs_detach_notify = true;
|
||||
}
|
||||
|
||||
if (last_target == t)
|
||||
last_target = NULL;
|
||||
@ -100,22 +109,20 @@ static struct target_controller gdb_controller = {
|
||||
|
||||
int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
{
|
||||
int size;
|
||||
bool single_step = false;
|
||||
|
||||
/* GDB protocol main loop */
|
||||
while(1) {
|
||||
while (1) {
|
||||
SET_IDLE_STATE(1);
|
||||
size = gdb_getpacket(pbuf, BUF_SIZE);
|
||||
size_t size = gdb_getpacket(pbuf, BUF_SIZE);
|
||||
SET_IDLE_STATE(0);
|
||||
switch(pbuf[0]) {
|
||||
switch (pbuf[0]) {
|
||||
/* Implementation of these is mandatory! */
|
||||
case 'g': { /* 'g': Read general registers */
|
||||
ERROR_IF_NO_TARGET();
|
||||
uint8_t arm_regs[target_regs_size(cur_target)];
|
||||
target_regs_read(cur_target, arm_regs);
|
||||
gdb_putpacket(hexify(pbuf, arm_regs, sizeof(arm_regs)),
|
||||
sizeof(arm_regs) * 2);
|
||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
||||
target_regs_read(cur_target, gp_regs);
|
||||
gdb_putpacket(hexify(pbuf, gp_regs, sizeof(gp_regs)), sizeof(gp_regs) * 2U);
|
||||
break;
|
||||
}
|
||||
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
||||
@ -132,19 +139,20 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
if (target_mem_read(cur_target, mem, addr, len))
|
||||
gdb_putpacketz("E01");
|
||||
else
|
||||
gdb_putpacket(hexify(pbuf, mem, len), len * 2);
|
||||
gdb_putpacket(hexify(pbuf, mem, len), len * 2U);
|
||||
break;
|
||||
}
|
||||
case 'G': { /* 'G XX': Write general registers */
|
||||
ERROR_IF_NO_TARGET();
|
||||
uint8_t arm_regs[target_regs_size(cur_target)];
|
||||
unhexify(arm_regs, &pbuf[1], sizeof(arm_regs));
|
||||
target_regs_write(cur_target, arm_regs);
|
||||
uint8_t gp_regs[target_regs_size(cur_target)];
|
||||
unhexify(gp_regs, &pbuf[1], sizeof(gp_regs));
|
||||
target_regs_write(cur_target, gp_regs);
|
||||
gdb_putpacketz("OK");
|
||||
break;
|
||||
}
|
||||
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
|
||||
uint32_t addr, len;
|
||||
uint32_t addr = 0;
|
||||
uint32_t len = 0;
|
||||
int hex;
|
||||
ERROR_IF_NO_TARGET();
|
||||
sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
|
||||
@ -162,11 +170,24 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
gdb_putpacketz("OK");
|
||||
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] */
|
||||
single_step = true;
|
||||
/* fall through */
|
||||
case 'c': /* 'c [addr]': Continue [at addr] */
|
||||
if(!cur_target) {
|
||||
if (!cur_target) {
|
||||
gdb_putpacketz("X1D");
|
||||
break;
|
||||
}
|
||||
@ -181,7 +202,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
target_addr watch;
|
||||
enum target_halt_reason reason;
|
||||
|
||||
if(!cur_target) {
|
||||
if (!cur_target) {
|
||||
/* Report "target exited" if no target */
|
||||
gdb_putpacketz("W00");
|
||||
break;
|
||||
@ -189,10 +210,13 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
|
||||
/* Wait for target halt */
|
||||
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
||||
unsigned char c = gdb_if_getchar_to(0);
|
||||
if((c == '\x03') || (c == '\x04')) {
|
||||
char c = (char)gdb_if_getchar_to(0);
|
||||
if(c == '\x03' || c == '\x04') {
|
||||
target_halt_request(cur_target);
|
||||
}
|
||||
#ifdef ENABLE_RTT
|
||||
if (rtt_enabled) poll_rtt(cur_target);
|
||||
#endif
|
||||
}
|
||||
SET_RUN_STATE(0);
|
||||
|
||||
@ -274,12 +298,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
break;
|
||||
|
||||
case 'k': /* Kill the target */
|
||||
if(cur_target) {
|
||||
target_reset(cur_target);
|
||||
target_detach(cur_target);
|
||||
last_target = cur_target;
|
||||
cur_target = NULL;
|
||||
}
|
||||
handle_kill_target();
|
||||
break;
|
||||
|
||||
case 'r': /* Reset the target system */
|
||||
@ -317,7 +336,7 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
handle_q_packet(pbuf, size);
|
||||
break;
|
||||
|
||||
case 'v': /* General query packet */
|
||||
case 'v': /* Verbose command packet */
|
||||
handle_v_packet(pbuf, size);
|
||||
break;
|
||||
|
||||
@ -335,12 +354,12 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
||||
}
|
||||
}
|
||||
|
||||
static bool exec_command(char *packet, int len, const cmd_executer *exec)
|
||||
static bool exec_command(char *packet, const size_t length, const cmd_executer *exec)
|
||||
{
|
||||
while (exec->cmd_prefix) {
|
||||
const int l = strlen(exec->cmd_prefix);
|
||||
if (!strncmp(packet, exec->cmd_prefix, l)) {
|
||||
exec->func(packet + l, len - l);
|
||||
const size_t prefix_length = strlen(exec->cmd_prefix);
|
||||
if (!strncmp(packet, exec->cmd_prefix, prefix_length)) {
|
||||
exec->func(packet + prefix_length, length - prefix_length);
|
||||
return true;
|
||||
}
|
||||
++exec;
|
||||
@ -348,19 +367,16 @@ static bool exec_command(char *packet, int len, const cmd_executer *exec)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void exec_q_rcmd(const char *packet,int len)
|
||||
static void exec_q_rcmd(const char *packet, const size_t length)
|
||||
{
|
||||
char *data;
|
||||
int datalen;
|
||||
|
||||
/* calculate size and allocate buffer for command */
|
||||
datalen = len / 2;
|
||||
data = alloca(datalen + 1);
|
||||
const size_t datalen = length / 2U;
|
||||
char *data = alloca(datalen + 1);
|
||||
/* dehexify command */
|
||||
unhexify(data, packet, datalen);
|
||||
data[datalen] = 0; /* add terminating null */
|
||||
|
||||
int c = command_process(cur_target, data);
|
||||
const int c = command_process(cur_target, data);
|
||||
if (c < 0)
|
||||
gdb_putpacketz("");
|
||||
else if (c == 0)
|
||||
@ -370,41 +386,41 @@ static void exec_q_rcmd(const char *packet,int len)
|
||||
2 * strlen("Failed\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
handle_q_string_reply(const char *str, const char *param)
|
||||
static void handle_q_string_reply(const char *reply, const char *param)
|
||||
{
|
||||
unsigned long addr, len;
|
||||
const size_t str_len = strlen(str);
|
||||
const size_t reply_length = strlen(reply);
|
||||
uint32_t addr = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
if (sscanf(param, "%08lx,%08lx", &addr, &len) != 2) {
|
||||
if (sscanf(param, "%08" PRIx32 ",%08" PRIx32, &addr, &len) != 2) {
|
||||
gdb_putpacketz("E01");
|
||||
return;
|
||||
}
|
||||
else if (addr > str_len) {
|
||||
if (addr > reply_length) {
|
||||
gdb_putpacketz("E01");
|
||||
return;
|
||||
}
|
||||
else if (addr == str_len) {
|
||||
if (addr == reply_length) {
|
||||
gdb_putpacketz("l");
|
||||
return;
|
||||
}
|
||||
unsigned long output_len = str_len - addr;
|
||||
size_t output_len = reply_length - addr;
|
||||
if (output_len > len)
|
||||
output_len = len;
|
||||
gdb_putpacket2("m", 1, str + addr, output_len);
|
||||
gdb_putpacket2("m", 1U, reply + addr, output_len);
|
||||
}
|
||||
|
||||
static void exec_q_supported(const char *packet, int len)
|
||||
static void exec_q_supported(const char *packet, const size_t length)
|
||||
{
|
||||
(void)packet;
|
||||
(void)len;
|
||||
(void)length;
|
||||
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE);
|
||||
}
|
||||
|
||||
static void exec_q_memory_map(const char *packet, int len)
|
||||
static void exec_q_memory_map(const char *packet, const size_t length)
|
||||
{
|
||||
(void)packet;
|
||||
(void)len;
|
||||
(void)length;
|
||||
/* Read target XML memory map */
|
||||
if ((!cur_target) && last_target) {
|
||||
/* Attach to last target if detached. */
|
||||
@ -420,9 +436,9 @@ static void exec_q_memory_map(const char *packet, int len)
|
||||
handle_q_string_reply(buf, packet);
|
||||
}
|
||||
|
||||
static void exec_q_feature_read(const char *packet, int len)
|
||||
static void exec_q_feature_read(const char *packet, const size_t length)
|
||||
{
|
||||
(void)len;
|
||||
(void)length;
|
||||
/* Read target description */
|
||||
if ((!cur_target) && last_target) {
|
||||
/* Attach to last target if detached. */
|
||||
@ -435,24 +451,51 @@ static void exec_q_feature_read(const char *packet, int len)
|
||||
handle_q_string_reply(target_tdesc(cur_target), packet);
|
||||
}
|
||||
|
||||
static void exec_q_crc(const char *packet, int len)
|
||||
static void exec_q_crc(const char *packet, const size_t length)
|
||||
{
|
||||
(void)len;
|
||||
uint32_t addr, alen;
|
||||
if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) {
|
||||
(void)length;
|
||||
uint32_t addr;
|
||||
uint32_t addr_length;
|
||||
if (sscanf(packet, "%" PRIx32 ",%" PRIx32, &addr, &addr_length) == 2) {
|
||||
if (!cur_target) {
|
||||
gdb_putpacketz("E01");
|
||||
return;
|
||||
}
|
||||
uint32_t crc;
|
||||
int res = generic_crc32(cur_target, &crc, addr, alen);
|
||||
if (res)
|
||||
if (generic_crc32(cur_target, &crc, addr, addr_length))
|
||||
gdb_putpacketz("E03");
|
||||
else
|
||||
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[]=
|
||||
{
|
||||
{"qRcmd,", exec_q_rcmd},
|
||||
@ -460,34 +503,60 @@ static const cmd_executer q_commands[]=
|
||||
{"qXfer:memory-map:read::", exec_q_memory_map},
|
||||
{"qXfer:features:read:target.xml:",exec_q_feature_read},
|
||||
{"qCRC:", exec_q_crc},
|
||||
{"qC", exec_q_c},
|
||||
{"qfThreadInfo", exec_q_thread_info},
|
||||
{"qsThreadInfo", exec_q_thread_info},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
static void
|
||||
handle_q_packet(char *packet, int len)
|
||||
static void handle_kill_target(void)
|
||||
{
|
||||
if (exec_command(packet, len, q_commands))
|
||||
if (cur_target) {
|
||||
target_reset(cur_target);
|
||||
target_detach(cur_target);
|
||||
last_target = cur_target;
|
||||
cur_target = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_q_packet(char *packet, const size_t length)
|
||||
{
|
||||
if (exec_command(packet, length, q_commands))
|
||||
return;
|
||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
||||
gdb_putpacket("", 0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_v_packet(char *packet, int plen)
|
||||
static void handle_v_packet(char *packet, const size_t plen)
|
||||
{
|
||||
unsigned long addr, len;
|
||||
uint32_t addr = 0;
|
||||
uint32_t len = 0;
|
||||
int bin;
|
||||
static uint8_t flash_mode = 0;
|
||||
|
||||
if (sscanf(packet, "vAttach;%08lx", &addr) == 1) {
|
||||
if (sscanf(packet, "vAttach;%08" PRIx32, &addr) == 1) {
|
||||
/* Attach to remote target processor */
|
||||
cur_target = target_attach_n(addr, &gdb_controller);
|
||||
if(cur_target) {
|
||||
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
|
||||
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)) {
|
||||
/* Parse command line for get_cmdline semihosting call */
|
||||
char cmdline[83];
|
||||
@ -518,6 +587,10 @@ handle_v_packet(char *packet, int plen)
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef ENABLE_RTT
|
||||
/* force searching rtt control block */
|
||||
rtt_found = false;
|
||||
#endif
|
||||
/* Run target program. For us (embedded) this means reset. */
|
||||
if (cur_target) {
|
||||
target_set_cmdline(cur_target, cmdline);
|
||||
@ -539,9 +612,9 @@ handle_v_packet(char *packet, int plen)
|
||||
} else
|
||||
gdb_putpacketz("E01");
|
||||
|
||||
} else if (sscanf(packet, "vFlashErase:%08lx,%08lx", &addr, &len) == 2) {
|
||||
} else if (sscanf(packet, "vFlashErase:%08" PRIx32 ",%08" PRIx32, &addr, &len) == 2) {
|
||||
/* Erase Flash Memory */
|
||||
DEBUG_GDB("Flash Erase %08lX %08lX\n", addr, len);
|
||||
DEBUG_GDB("Flash Erase %08" PRIX32 " %08" PRIX32 "\n", addr, len);
|
||||
if (!cur_target) {
|
||||
gdb_putpacketz("EFF");
|
||||
return;
|
||||
@ -560,11 +633,11 @@ handle_v_packet(char *packet, int plen)
|
||||
gdb_putpacketz("EFF");
|
||||
}
|
||||
|
||||
} else if (sscanf(packet, "vFlashWrite:%08lx:%n", &addr, &bin) == 1) {
|
||||
} else if (sscanf(packet, "vFlashWrite:%08" PRIx32 ":%n", &addr, &bin) == 1) {
|
||||
/* Write Flash Memory */
|
||||
len = plen - bin;
|
||||
DEBUG_GDB("Flash Write %08lX %08lX\n", addr, len);
|
||||
if (cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0)
|
||||
const uint32_t count = plen - bin;
|
||||
DEBUG_GDB("Flash Write %08" PRIX32 " %08" PRIX32 "\n", addr, count);
|
||||
if (cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, count) == 0)
|
||||
gdb_putpacketz("OK");
|
||||
else {
|
||||
flash_mode = 0;
|
||||
@ -576,24 +649,30 @@ handle_v_packet(char *packet, int plen)
|
||||
gdb_putpacketz(target_flash_done(cur_target) ? "EFF" : "OK");
|
||||
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 {
|
||||
DEBUG_GDB("*** Unsupported packet: %s\n", packet);
|
||||
gdb_putpacket("", 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_z_packet(char *packet, int plen)
|
||||
static void handle_z_packet(char *packet, const size_t plen)
|
||||
{
|
||||
(void)plen;
|
||||
|
||||
uint8_t set = (packet[0] == 'Z') ? 1 : 0;
|
||||
int type, len;
|
||||
uint32_t type;
|
||||
uint32_t len;
|
||||
uint32_t addr;
|
||||
int ret;
|
||||
sscanf(packet, "%*[zZ]%" PRIu32 ",%08" PRIx32 ",%" PRIu32, &type, &addr, &len);
|
||||
|
||||
sscanf(packet, "%*[zZ]%d,%08" PRIX32 ",%d", &type, &addr, &len);
|
||||
if(set)
|
||||
int ret = 0;
|
||||
if (packet[0] == 'Z')
|
||||
ret = target_breakwatch_set(cur_target, type, addr, len);
|
||||
else
|
||||
ret = target_breakwatch_clear(cur_target, type, addr, len);
|
||||
|
@ -30,12 +30,11 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int gdb_getpacket(char *packet, int size)
|
||||
size_t gdb_getpacket(char *packet, size_t size)
|
||||
{
|
||||
unsigned char c;
|
||||
unsigned char csum;
|
||||
char recv_csum[3];
|
||||
int i;
|
||||
size_t offset = 0;
|
||||
|
||||
while (1) {
|
||||
/* Wait for packet start */
|
||||
@ -44,7 +43,8 @@ int gdb_getpacket(char *packet, int size)
|
||||
* start ('$') or a BMP remote packet start ('!').
|
||||
*/
|
||||
do {
|
||||
packet[0] = gdb_if_getchar();
|
||||
/* Smells like bad code */
|
||||
packet[0] = (char)gdb_if_getchar();
|
||||
if (packet[0] == 0x04)
|
||||
return 1;
|
||||
} while ((packet[0] != '$') && (packet[0] != REMOTE_SOM));
|
||||
@ -52,18 +52,19 @@ int gdb_getpacket(char *packet, int size)
|
||||
if (packet[0] == REMOTE_SOM) {
|
||||
/* This is probably a remote control packet
|
||||
* - get and handle it */
|
||||
i = 0;
|
||||
offset = 0;
|
||||
bool gettingRemotePacket = true;
|
||||
while (gettingRemotePacket) {
|
||||
c = gdb_if_getchar();
|
||||
/* Smells like bad code */
|
||||
const char c = (char)gdb_if_getchar();
|
||||
switch (c) {
|
||||
case REMOTE_SOM: /* Oh dear, packet restarts */
|
||||
i = 0;
|
||||
offset = 0;
|
||||
break;
|
||||
|
||||
case REMOTE_EOM: /* Complete packet for processing */
|
||||
packet[i] = 0;
|
||||
remotePacketProcess(i, packet);
|
||||
packet[offset] = 0;
|
||||
remotePacketProcess(offset, packet);
|
||||
gettingRemotePacket = false;
|
||||
break;
|
||||
|
||||
@ -73,8 +74,8 @@ int gdb_getpacket(char *packet, int size)
|
||||
break;
|
||||
|
||||
default:
|
||||
if (i < size) {
|
||||
packet[i++] = c;
|
||||
if (offset < size) {
|
||||
packet[offset++] = c;
|
||||
} else {
|
||||
/* Who knows what is going on...return to normality */
|
||||
gettingRemotePacket = false;
|
||||
@ -92,30 +93,32 @@ int gdb_getpacket(char *packet, int size)
|
||||
#endif
|
||||
} while (packet[0] != '$');
|
||||
|
||||
i = 0;
|
||||
offset = 0;
|
||||
csum = 0;
|
||||
char c;
|
||||
/* Capture packet data into buffer */
|
||||
while ((c = gdb_if_getchar()) != '#') {
|
||||
while ((c = (char)gdb_if_getchar()) != '#') {
|
||||
|
||||
if (i == size) /* Oh shit */
|
||||
/* If we run out of buffer space, exit early */
|
||||
if (offset == size)
|
||||
break;
|
||||
|
||||
if (c == '$') { /* Restart capture */
|
||||
i = 0;
|
||||
offset = 0;
|
||||
csum = 0;
|
||||
continue;
|
||||
}
|
||||
if (c == '}') { /* escaped char */
|
||||
c = gdb_if_getchar();
|
||||
csum += c + '}';
|
||||
packet[i++] = c ^ 0x20;
|
||||
packet[offset++] = c ^ 0x20;
|
||||
continue;
|
||||
}
|
||||
csum += c;
|
||||
packet[i++] = c;
|
||||
packet[offset++] = c;
|
||||
}
|
||||
recv_csum[0] = gdb_if_getchar();
|
||||
recv_csum[1] = gdb_if_getchar();
|
||||
recv_csum[0] = (char)gdb_if_getchar();
|
||||
recv_csum[1] = (char)gdb_if_getchar();
|
||||
recv_csum[2] = 0;
|
||||
|
||||
/* return packet if checksum matches */
|
||||
@ -126,20 +129,20 @@ int gdb_getpacket(char *packet, int size)
|
||||
gdb_if_putchar('-', 1); /* send nack */
|
||||
}
|
||||
gdb_if_putchar('+', 1); /* send ack */
|
||||
packet[i] = 0;
|
||||
packet[offset] = 0;
|
||||
|
||||
#if PC_HOSTED == 1
|
||||
DEBUG_GDB_WIRE("%s : ", __func__);
|
||||
for(int j = 0; j < i; j++) {
|
||||
c = packet[j];
|
||||
if ((c >= 32) && (c < 127))
|
||||
for (size_t j = 0; j < offset; j++) {
|
||||
const char c = packet[j];
|
||||
if (c >= ' ' && c < 0x7F)
|
||||
DEBUG_GDB_WIRE("%c", c);
|
||||
else
|
||||
DEBUG_GDB_WIRE("\\x%02X", c);
|
||||
}
|
||||
DEBUG_GDB_WIRE("\n");
|
||||
#endif
|
||||
return i;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void gdb_next_char(char c, unsigned char *csum)
|
||||
@ -161,21 +164,19 @@ static void gdb_next_char(char c, unsigned char *csum)
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int size2)
|
||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2)
|
||||
{
|
||||
int i;
|
||||
unsigned char csum;
|
||||
char xmit_csum[3];
|
||||
int tries = 0;
|
||||
size_t tries = 0;
|
||||
|
||||
do {
|
||||
DEBUG_GDB_WIRE("%s : ", __func__);
|
||||
csum = 0;
|
||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
||||
unsigned char csum = 0;
|
||||
gdb_if_putchar('$', 0);
|
||||
|
||||
for (i = 0; i < size1; ++i)
|
||||
for (size_t i = 0; i < size1; ++i)
|
||||
gdb_next_char(packet1[i], &csum);
|
||||
for (i = 0; i < size2; ++i)
|
||||
for (size_t i = 0; i < size2; ++i)
|
||||
gdb_next_char(packet2[i], &csum);
|
||||
|
||||
gdb_if_putchar('#', 0);
|
||||
@ -183,28 +184,42 @@ void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int siz
|
||||
gdb_if_putchar(xmit_csum[0], 0);
|
||||
gdb_if_putchar(xmit_csum[1], 1);
|
||||
DEBUG_GDB_WIRE("\n");
|
||||
} while ((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
|
||||
} while (gdb_if_getchar_to(2000) != '+' && tries++ < 3);
|
||||
}
|
||||
|
||||
void gdb_putpacket(const char *packet, int size)
|
||||
void gdb_putpacket(const char *packet, size_t size)
|
||||
{
|
||||
int i;
|
||||
unsigned char csum;
|
||||
char xmit_csum[3];
|
||||
int tries = 0;
|
||||
size_t tries = 0;
|
||||
|
||||
do {
|
||||
DEBUG_GDB_WIRE("%s : ", __func__);
|
||||
csum = 0;
|
||||
DEBUG_GDB_WIRE("%s: ", __func__);
|
||||
unsigned char csum = 0;
|
||||
gdb_if_putchar('$', 0);
|
||||
for (i = 0; i < size; ++i)
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
gdb_next_char(packet[i], &csum);
|
||||
gdb_if_putchar('#', 0);
|
||||
snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum);
|
||||
gdb_if_putchar(xmit_csum[0], 0);
|
||||
gdb_if_putchar(xmit_csum[1], 1);
|
||||
DEBUG_GDB_WIRE("\n");
|
||||
} while ((gdb_if_getchar_to(2000) != '+') && (tries++ < 3));
|
||||
} 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, ...)
|
||||
|
@ -26,16 +26,16 @@
|
||||
|
||||
static const char hexdigits[] = "0123456789abcdef";
|
||||
|
||||
char * hexify(char *hex, const void *buf, size_t size)
|
||||
char *hexify(char *hex, const void *buf, const size_t size)
|
||||
{
|
||||
char *tmp = hex;
|
||||
const uint8_t *b = buf;
|
||||
char *dst = hex;
|
||||
const uint8_t *const src = buf;
|
||||
|
||||
while (size--) {
|
||||
*tmp++ = hexdigits[*b >> 4];
|
||||
*tmp++ = hexdigits[*b++ & 0xF];
|
||||
for (size_t idx = 0; idx < size; ++idx) {
|
||||
*dst++ = hexdigits[src[idx] >> 4];
|
||||
*dst++ = hexdigits[src[idx] & 0xF];
|
||||
}
|
||||
*tmp++ = 0;
|
||||
*dst++ = 0;
|
||||
|
||||
return hex;
|
||||
}
|
||||
@ -43,20 +43,18 @@ char * hexify(char *hex, const void *buf, size_t size)
|
||||
static uint8_t unhex_digit(char hex)
|
||||
{
|
||||
uint8_t tmp = hex - '0';
|
||||
if(tmp > 9)
|
||||
if (tmp > 9)
|
||||
tmp -= 'A' - '0' - 10;
|
||||
if(tmp > 16)
|
||||
if (tmp > 16)
|
||||
tmp -= 'a' - 'A';
|
||||
return tmp;
|
||||
}
|
||||
|
||||
char * unhexify(void *buf, const char *hex, size_t size)
|
||||
char *unhexify(void *buf, const char *hex, const size_t size)
|
||||
{
|
||||
uint8_t *b = buf;
|
||||
while (size--) {
|
||||
*b = unhex_digit(*hex++) << 4;
|
||||
*b++ |= unhex_digit(*hex++);
|
||||
uint8_t *const dst = buf;
|
||||
for (size_t idx = 0; idx < size; ++idx, hex += 2) {
|
||||
dst[idx] = (unhex_digit(hex[0]) << 4) | unhex_digit(hex[1]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -21,18 +21,19 @@
|
||||
#ifndef __GDB_PACKET_H
|
||||
#define __GDB_PACKET_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
int gdb_getpacket(char *packet, int size);
|
||||
void gdb_putpacket(const char *packet, int size);
|
||||
void gdb_putpacket2(const char *packet1, int size1, const char *packet2, int size2);
|
||||
size_t gdb_getpacket(char *packet, size_t size);
|
||||
void gdb_putpacket(const char *packet, size_t size);
|
||||
void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2);
|
||||
#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet))
|
||||
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_voutf(const char *fmt, va_list);
|
||||
void gdb_outf(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
85
src/include/queue.h
Normal file
85
src/include/queue.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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__ */
|
34
src/include/rtt.h
Normal file
34
src/include/rtt.h
Normal file
@ -0,0 +1,34 @@
|
||||
#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
|
36
src/include/rtt_if.h
Normal file
36
src/include/rtt_if.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef RTT_IF_H
|
||||
#define RTT_IF_H
|
||||
/* rtt i/o to terminal */
|
||||
|
||||
/* default buffer sizes, 8 bytes added to up buffer for alignment and padding */
|
||||
/* override RTT_UP_BUF_SIZE and RTT_DOWN_BUF_SIZE in platform.h if needed */
|
||||
|
||||
#if !defined(RTT_UP_BUF_SIZE) || !defined(RTT_DOWN_BUF_SIZE)
|
||||
#if (PC_HOSTED == 1)
|
||||
#define RTT_UP_BUF_SIZE (4096 + 8)
|
||||
#define RTT_DOWN_BUF_SIZE (512)
|
||||
#elif defined(STM32F7)
|
||||
#define RTT_UP_BUF_SIZE (4096 + 8)
|
||||
#define RTT_DOWN_BUF_SIZE (2048)
|
||||
#elif defined(STM32F4)
|
||||
#define RTT_UP_BUF_SIZE (2048 + 8)
|
||||
#define RTT_DOWN_BUF_SIZE (256)
|
||||
#else /* stm32f103 */
|
||||
#define RTT_UP_BUF_SIZE (1024 + 8)
|
||||
#define RTT_DOWN_BUF_SIZE (256)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* hosted initialisation */
|
||||
extern int rtt_if_init(void);
|
||||
/* hosted teardown */
|
||||
extern int rtt_if_exit(void);
|
||||
|
||||
/* target to host: write len bytes from the buffer starting at buf. return number bytes written */
|
||||
extern uint32_t rtt_write(const char *buf, uint32_t len);
|
||||
/* host to target: read one character, non-blocking. return character, -1 if no character */
|
||||
extern int32_t rtt_getchar();
|
||||
/* host to target: true if no characters available for reading */
|
||||
extern bool rtt_nodata();
|
||||
|
||||
#endif
|
@ -78,7 +78,7 @@ static const struct usb_device_descriptor dev_desc = {
|
||||
.bDeviceClass = 0xEF, /* Miscellaneous Device */
|
||||
.bDeviceSubClass = 2, /* Common Class */
|
||||
.bDeviceProtocol = 1, /* Interface Association */
|
||||
#ifdef LM4F
|
||||
#if defined(SAMD21E17) || defined(LM4F)
|
||||
.bMaxPacketSize0 = 64, /*Fixed for icdi*/
|
||||
#else
|
||||
.bMaxPacketSize0 = 32,
|
||||
@ -435,6 +435,8 @@ static void dfu_detach_complete(usbd_device *dev, struct usb_setup_data *req)
|
||||
/* Reset core to enter bootloader */
|
||||
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
|
||||
scb_reset_core();
|
||||
#else
|
||||
scb_reset_system();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -524,7 +526,7 @@ static void cdcacm_set_config(usbd_device *dev, uint16_t wValue)
|
||||
configured = wValue;
|
||||
|
||||
/* GDB interface */
|
||||
#if defined(STM32F4) || defined(LM4F)
|
||||
#if defined(STM32F4) || defined(LM4F) || defined(SAMD)
|
||||
usbd_ep_setup(dev, CDCACM_GDB_ENDPOINT, USB_ENDPOINT_ATTR_BULK,
|
||||
CDCACM_PACKET_SIZE, gdb_usb_out_cb);
|
||||
#else
|
||||
@ -580,6 +582,7 @@ void cdcacm_init(void)
|
||||
|
||||
nvic_set_priority(USB_IRQ, IRQ_PRI_USB);
|
||||
nvic_enable_irq(USB_IRQ);
|
||||
usbd_disconnect(usbdev, false);
|
||||
}
|
||||
|
||||
void USB_ISR(void)
|
||||
|
@ -233,7 +233,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
||||
struct dirent *dp;
|
||||
int i = 0;
|
||||
while ((dp = readdir(dir)) != NULL) {
|
||||
if ((strstr(dp->d_name, BMP_IDSTRING)) &&
|
||||
if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) ||
|
||||
strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) ||
|
||||
strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) &&
|
||||
(strstr(dp->d_name, "-if00"))) {
|
||||
i++;
|
||||
char type[256], version[256], serial[256];
|
||||
@ -266,7 +268,9 @@ int find_debuggers(BMP_CL_OPTIONS_t *cl_opts, bmp_info_t *info)
|
||||
dir = opendir(DEVICE_BY_ID);
|
||||
i = 0;
|
||||
while ((dp = readdir(dir)) != NULL) {
|
||||
if ((strstr(dp->d_name, BMP_IDSTRING)) &&
|
||||
if ((strstr(dp->d_name, BMP_IDSTRING_BLACKMAGIC) ||
|
||||
strstr(dp->d_name, BMP_IDSTRING_BLACKSPHERE) ||
|
||||
strstr(dp->d_name, BMP_IDSTRING_1BITSQUARED)) &&
|
||||
(strstr(dp->d_name, "-if00"))) {
|
||||
i++;
|
||||
char type[256], version[256], serial[256];
|
||||
|
@ -30,6 +30,10 @@
|
||||
#include "gdb_if.h"
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef ENABLE_RTT
|
||||
#include "rtt_if.h"
|
||||
#endif
|
||||
|
||||
#include "bmp_remote.h"
|
||||
#include "bmp_hosted.h"
|
||||
#include "stlinkv2.h"
|
||||
@ -58,6 +62,9 @@ static void exit_function(void)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#ifdef ENABLE_RTT
|
||||
rtt_if_exit();
|
||||
#endif
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@ -110,6 +117,9 @@ void platform_init(int argc, char **argv)
|
||||
exit(cl_execute(&cl_opts));
|
||||
else {
|
||||
gdb_if_init();
|
||||
#ifdef ENABLE_RTT
|
||||
rtt_if_init();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
127
src/platforms/hosted/rtt_if.c
Normal file
127
src/platforms/hosted/rtt_if.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <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
|
61
src/platforms/jeff/Makefile.inc
Normal file
61
src/platforms/jeff/Makefile.inc
Normal file
@ -0,0 +1,61 @@
|
||||
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
|
346
src/platforms/jeff/platform.c
Normal file
346
src/platforms/jeff/platform.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
227
src/platforms/jeff/platform.h
Normal file
227
src/platforms/jeff/platform.h
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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
|
351
src/platforms/jeff/usbdfu.c
Normal file
351
src/platforms/jeff/usbdfu.c
Normal file
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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_disable_external_trigger_regular(ADC1);
|
||||
adc_set_right_aligned(ADC1);
|
||||
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC);
|
||||
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_239DOT5CYC);
|
||||
|
||||
adc_power_on(ADC1);
|
||||
|
||||
@ -316,6 +316,8 @@ uint32_t platform_target_voltage_sense(void)
|
||||
while (!adc_eoc(ADC1));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
103
src/platforms/samd/gdb_if.c
Normal file
103
src/platforms/samd/gdb_if.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
||||
* Written by Gareth McMullin <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;
|
||||
}
|
||||
|
134
src/platforms/samd/rtt_if.c
Normal file
134
src/platforms/samd/rtt_if.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "general.h"
|
||||
#include "platform.h"
|
||||
#include <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;
|
||||
}
|
28
src/platforms/samd/samd.ld
Normal file
28
src/platforms/samd/samd.ld
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
28
src/platforms/samd/samd_boot.ld
Normal file
28
src/platforms/samd/samd_boot.ld
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
57
src/platforms/samd/traceswo.c
Normal file
57
src/platforms/samd/traceswo.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
251
src/platforms/samd/usbuart.c
Normal file
251
src/platforms/samd/usbuart.c
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
134
src/platforms/stm32/rtt_if.c
Normal file
134
src/platforms/stm32/rtt_if.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "general.h"
|
||||
#include "platform.h"
|
||||
#include <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,6 +259,7 @@ static void usbuart_change_dma_tx_buf(void)
|
||||
buf_tx_act_idx ^= 1;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_RTT
|
||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||
{
|
||||
(void)ep;
|
||||
@ -301,6 +302,7 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||
if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE)
|
||||
usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USBUART_DEBUG
|
||||
int usbuart_debug_write(const char *buf, size_t len)
|
||||
|
@ -99,6 +99,7 @@ void usbuart_set_line_coding(struct usb_cdc_line_coding *coding)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ENABLE_RTT
|
||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||
{
|
||||
(void)ep;
|
||||
@ -110,7 +111,7 @@ void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||
for(int i = 0; i < len; i++)
|
||||
uart_send_blocking(USBUART, buf[i]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
||||
{
|
||||
|
456
src/rtt.c
Normal file
456
src/rtt.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021 Koen De Vleeschauwer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "general.h"
|
||||
#include "platform.h"
|
||||
#include "gdb_packet.h"
|
||||
#include "target.h"
|
||||
#include "target/target_internal.h"
|
||||
#include "rtt.h"
|
||||
#include "rtt_if.h"
|
||||
|
||||
bool rtt_enabled = false;
|
||||
bool rtt_found = false;
|
||||
static bool rtt_halt = false; // true if rtt needs to halt target to access memory
|
||||
uint32_t rtt_cbaddr = 0;
|
||||
bool rtt_auto_channel = true;
|
||||
struct rtt_channel_struct rtt_channel[MAX_RTT_CHAN];
|
||||
|
||||
uint32_t rtt_min_poll_ms = 8; /* 8 ms */
|
||||
uint32_t rtt_max_poll_ms = 256; /* 0.256 s */
|
||||
uint32_t rtt_max_poll_errs = 10;
|
||||
static uint32_t poll_ms;
|
||||
static uint32_t poll_errs;
|
||||
static uint32_t last_poll_ms;
|
||||
/* flags for data from host to target */
|
||||
bool rtt_flag_skip = false;
|
||||
bool rtt_flag_block = false;
|
||||
|
||||
typedef enum rtt_retval {
|
||||
RTT_OK,
|
||||
RTT_IDLE,
|
||||
RTT_ERR
|
||||
} rtt_retval;
|
||||
|
||||
#ifdef RTT_IDENT
|
||||
#define Q(x) #x
|
||||
#define QUOTE(x) Q(x)
|
||||
char rtt_ident[16] = QUOTE(RTT_IDENT);
|
||||
#else
|
||||
char rtt_ident[16] = {0};
|
||||
#endif
|
||||
|
||||
/* usb uart transmit buffer */
|
||||
static char xmit_buf[RTT_UP_BUF_SIZE];
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* rtt control block
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
uint32_t fastsrch(target *cur_target)
|
||||
{
|
||||
const uint32_t m = 16;
|
||||
const uint64_t q = 0x797a9691; /* prime */
|
||||
const uint64_t rm = 0x73b07d01;
|
||||
const uint64_t p = 0x444110cd;
|
||||
const uint32_t stride = 128;
|
||||
uint64_t t = 0;
|
||||
uint8_t srch_buf[m+stride];
|
||||
|
||||
for (struct target_ram *r = cur_target->ram; r; r = r->next) {
|
||||
const uint32_t ram_start = r->start;
|
||||
const uint32_t ram_end = r->start + r->length;
|
||||
|
||||
t = 0;
|
||||
memset(srch_buf, 0, sizeof(srch_buf));
|
||||
|
||||
for (uint32_t addr = ram_start; addr < ram_end; addr += stride) {
|
||||
uint32_t buf_siz = MIN(stride, ram_end - addr);
|
||||
memcpy(srch_buf, srch_buf + stride, m);
|
||||
if (target_mem_read(cur_target, srch_buf + m, addr, buf_siz)) {
|
||||
gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr);
|
||||
return 0;
|
||||
}
|
||||
for (uint32_t i = 0; i < buf_siz; i++) {
|
||||
t = (t + q - rm * srch_buf[i] % q) % q;
|
||||
t = ((t << 8) + srch_buf[i + m]) % q;
|
||||
if (p == t) {
|
||||
uint32_t offset = i - m + 1;
|
||||
return addr + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* no match */
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t memsrch(target *cur_target)
|
||||
{
|
||||
char *srch_str = rtt_ident;
|
||||
uint32_t srch_str_len = strlen(srch_str);
|
||||
uint8_t srch_buf[128];
|
||||
|
||||
if (srch_str_len == 0 || srch_str_len > sizeof(srch_buf) / 2)
|
||||
return 0;
|
||||
|
||||
if (rtt_cbaddr && !target_mem_read(cur_target, srch_buf, rtt_cbaddr, srch_str_len)
|
||||
&& strncmp((const char *)(srch_buf), srch_str, srch_str_len) == 0)
|
||||
/* still at same place */
|
||||
return rtt_cbaddr;
|
||||
|
||||
for (struct target_ram *r = cur_target->ram; r; r = r->next) {
|
||||
uint32_t ram_end = r->start + r->length;
|
||||
for (uint32_t addr = r->start; addr < ram_end; addr += sizeof(srch_buf) - srch_str_len - 1) {
|
||||
uint32_t buf_siz = MIN(ram_end - addr, sizeof(srch_buf));
|
||||
if (target_mem_read(cur_target, srch_buf, addr, buf_siz)) {
|
||||
gdb_outf("rtt: read fail at 0x%" PRIx32 "\r\n", addr);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t offset = 0; offset + srch_str_len + 1 < buf_siz; offset++) {
|
||||
if (strncmp((const char *)(srch_buf + offset), srch_str, srch_str_len) == 0) {
|
||||
uint32_t cb_addr = addr + offset;
|
||||
return cb_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void find_rtt(target *cur_target)
|
||||
{
|
||||
rtt_found = false;
|
||||
poll_ms = rtt_max_poll_ms;
|
||||
poll_errs = 0;
|
||||
last_poll_ms = 0;
|
||||
|
||||
if (!cur_target || !rtt_enabled)
|
||||
return;
|
||||
|
||||
if (rtt_ident[0] == 0)
|
||||
rtt_cbaddr = fastsrch(cur_target);
|
||||
else
|
||||
rtt_cbaddr = memsrch(cur_target);
|
||||
DEBUG_INFO("rtt: match at 0x%" PRIx32 "\r\n", rtt_cbaddr);
|
||||
|
||||
if (rtt_cbaddr) {
|
||||
uint32_t num_buf[2];
|
||||
int32_t num_up_buf;
|
||||
int32_t num_down_buf;
|
||||
if (target_mem_read(cur_target, num_buf, rtt_cbaddr + 16, sizeof(num_buf)))
|
||||
return;
|
||||
num_up_buf = num_buf[0];
|
||||
num_down_buf = num_buf[1];
|
||||
|
||||
if (num_up_buf > 255 || num_down_buf > 255) {
|
||||
gdb_out("rtt: bad cblock\r\n");
|
||||
rtt_enabled = false;
|
||||
return;
|
||||
} else if (num_up_buf == 0 && num_down_buf == 0)
|
||||
gdb_out("rtt: empty cblock\r\n");
|
||||
|
||||
for (int32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
||||
uint32_t buf_desc[6];
|
||||
|
||||
rtt_channel[i].is_configured = false;
|
||||
rtt_channel[i].is_output = false;
|
||||
rtt_channel[i].buf_addr = 0;
|
||||
rtt_channel[i].buf_size = 0;
|
||||
rtt_channel[i].head_addr = 0;
|
||||
rtt_channel[i].tail_addr = 0;
|
||||
rtt_channel[i].flag = 0;
|
||||
|
||||
if (i >= num_up_buf + num_down_buf)
|
||||
continue;
|
||||
if (target_mem_read(cur_target, buf_desc, rtt_cbaddr + 24 + i * 24, sizeof(buf_desc)))
|
||||
return;
|
||||
rtt_channel[i].is_output = i < num_up_buf;
|
||||
rtt_channel[i].buf_addr = buf_desc[1];
|
||||
rtt_channel[i].buf_size = buf_desc[2];
|
||||
rtt_channel[i].head_addr = rtt_cbaddr + 24 + i * 24 + 12;
|
||||
rtt_channel[i].tail_addr = rtt_cbaddr + 24 + i * 24 + 16;
|
||||
rtt_channel[i].flag = buf_desc[5];
|
||||
rtt_channel[i].is_configured = (rtt_channel[i].buf_addr != 0) && (rtt_channel[i].buf_size != 0);
|
||||
}
|
||||
|
||||
/* auto channel: enable output channels 0 and 1 and first input channel */
|
||||
if (rtt_auto_channel) {
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||
rtt_channel[i].is_enabled = false;
|
||||
rtt_channel[0].is_enabled = num_up_buf > 0;
|
||||
rtt_channel[1].is_enabled = num_up_buf > 1;
|
||||
if ((num_up_buf < MAX_RTT_CHAN) && (num_down_buf > 0))
|
||||
rtt_channel[num_up_buf].is_enabled = true;
|
||||
}
|
||||
|
||||
/* get flags for data from host to target */
|
||||
rtt_flag_skip = false;
|
||||
rtt_flag_block = false;
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||
if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured && !rtt_channel[i].is_output) {
|
||||
rtt_flag_skip = rtt_channel[i].flag == 0;
|
||||
rtt_flag_block = rtt_channel[i].flag == 2;
|
||||
break;
|
||||
}
|
||||
|
||||
rtt_found = true;
|
||||
DEBUG_INFO("rtt found\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* rtt from host to target
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/* poll if host has new data for target */
|
||||
static rtt_retval read_rtt(target *cur_target, uint32_t i)
|
||||
{
|
||||
uint32_t head_tail[2];
|
||||
uint32_t buf_head;
|
||||
uint32_t buf_tail;
|
||||
uint32_t next_head;
|
||||
int ch;
|
||||
|
||||
/* copy data from recv_buf to target rtt 'down' buffer */
|
||||
if (rtt_nodata())
|
||||
return RTT_IDLE;
|
||||
|
||||
if (cur_target == NULL || rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].buf_size == 0)
|
||||
return RTT_IDLE;
|
||||
|
||||
/* read down buffer head and tail from target */
|
||||
if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail)))
|
||||
return RTT_ERR;
|
||||
|
||||
buf_head = head_tail[0];
|
||||
buf_tail = head_tail[1];
|
||||
|
||||
if (buf_head >= rtt_channel[i].buf_size || buf_tail >= rtt_channel[i].buf_size)
|
||||
return RTT_ERR;
|
||||
|
||||
/* write recv_buf to target rtt 'down' buf */
|
||||
while ((next_head = ((buf_head + 1) % rtt_channel[i].buf_size)) != buf_tail && (ch = rtt_getchar()) != -1) {
|
||||
if (target_mem_write(cur_target, rtt_channel[i].buf_addr + buf_head, &ch, 1))
|
||||
return RTT_ERR;
|
||||
|
||||
/* advance pointers */
|
||||
buf_head = next_head;
|
||||
}
|
||||
|
||||
/* update head of target 'down' buffer */
|
||||
if (target_mem_write(cur_target, rtt_channel[i].head_addr, &buf_head, sizeof(buf_head)))
|
||||
return RTT_ERR;
|
||||
return RTT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* rtt from target to host
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/* target_mem_read, word aligned for speed.
|
||||
note: dest has to be len + 8 bytes, to allow for alignment and padding.
|
||||
*/
|
||||
int target_aligned_mem_read(target *t, void *dest, target_addr src, size_t len)
|
||||
{
|
||||
uint32_t src0 = src;
|
||||
uint32_t len0 = len;
|
||||
uint32_t offset = src & 0x3;
|
||||
src0 -= offset;
|
||||
len0 += offset;
|
||||
if ((len0 & 0x3) != 0)
|
||||
len0 = (len0 + 4) & ~0x3;
|
||||
|
||||
if (src0 == src && len0 == len)
|
||||
return target_mem_read(t, dest, src, len);
|
||||
else {
|
||||
uint32_t retval = target_mem_read(t, dest, src0, len0);
|
||||
memmove(dest, dest + offset, len);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* poll if target has new data for host */
|
||||
static rtt_retval print_rtt(target *cur_target, uint32_t i)
|
||||
{
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
|
||||
if (!cur_target || !rtt_channel[i].is_output || rtt_channel[i].buf_addr == 0 || rtt_channel[i].head_addr == 0)
|
||||
return RTT_IDLE;
|
||||
|
||||
uint32_t head_tail[2];
|
||||
if (target_mem_read(cur_target, head_tail, rtt_channel[i].head_addr, sizeof(head_tail)))
|
||||
return RTT_ERR;
|
||||
head = head_tail[0];
|
||||
tail = head_tail[1];
|
||||
|
||||
if (head >= rtt_channel[i].buf_size || tail >= rtt_channel[i].buf_size)
|
||||
return RTT_ERR;
|
||||
else if (head == tail)
|
||||
return RTT_IDLE;
|
||||
|
||||
uint32_t bytes_free = sizeof(xmit_buf) - 8; /* need 8 bytes for alignment and padding */
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
if (tail > head) {
|
||||
uint32_t len = rtt_channel[i].buf_size - tail;
|
||||
if (len > bytes_free)
|
||||
len = bytes_free;
|
||||
if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len))
|
||||
return RTT_ERR;
|
||||
bytes_free -= len;
|
||||
bytes_read += len;
|
||||
tail = (tail + len) % rtt_channel[i].buf_size;
|
||||
}
|
||||
|
||||
if (head > tail && bytes_free > 0) {
|
||||
uint32_t len = head - tail;
|
||||
if (len > bytes_free)
|
||||
len = bytes_free;
|
||||
if (target_aligned_mem_read(cur_target, xmit_buf + bytes_read, rtt_channel[i].buf_addr + tail, len))
|
||||
return RTT_ERR;
|
||||
bytes_read += len;
|
||||
tail = (tail + len) % rtt_channel[i].buf_size;
|
||||
}
|
||||
|
||||
/* update tail on target */
|
||||
if (target_mem_write(cur_target, rtt_channel[i].tail_addr, &tail, sizeof(tail)))
|
||||
return RTT_ERR;
|
||||
|
||||
/* write buffer to usb */
|
||||
rtt_write(xmit_buf, bytes_read);
|
||||
|
||||
return RTT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* target background memory access
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/* target_no_background_memory_access() is true if the target needs to be halted during jtag memory access
|
||||
target_no_background_memory_access() is false if the target allows jtag memory access while running */
|
||||
|
||||
bool target_no_background_memory_access(target *cur_target)
|
||||
{
|
||||
/* if error message is 'rtt: read fail at' add target to expression below.
|
||||
As a first approximation, assume all arm processors allow memory access while running, and no riscv does. */
|
||||
bool riscv_core = cur_target && target_core_name(cur_target) && strstr(target_core_name(cur_target), "RVDBG");
|
||||
return riscv_core;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* rtt top level
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
void poll_rtt(target *cur_target)
|
||||
{
|
||||
/* rtt off */
|
||||
if (!cur_target || !rtt_enabled)
|
||||
return;
|
||||
/* target present and rtt enabled */
|
||||
uint32_t now = platform_time_ms();
|
||||
bool rtt_err = false;
|
||||
bool rtt_busy = false;
|
||||
|
||||
if (last_poll_ms + poll_ms <= now || now < last_poll_ms) {
|
||||
target_addr watch;
|
||||
enum target_halt_reason reason;
|
||||
bool resume_target = false;
|
||||
if (!rtt_found)
|
||||
/* check if target needs to be halted during memory access */
|
||||
rtt_halt = target_no_background_memory_access(cur_target);
|
||||
if (rtt_halt && target_halt_poll(cur_target, &watch) == TARGET_HALT_RUNNING) {
|
||||
/* briefly halt target during target memory access */
|
||||
target_halt_request(cur_target);
|
||||
while((reason = target_halt_poll(cur_target, &watch)) == TARGET_HALT_RUNNING)
|
||||
continue;
|
||||
resume_target = reason == TARGET_HALT_REQUEST;
|
||||
}
|
||||
if (!rtt_found)
|
||||
/* find rtt control block in target memory */
|
||||
find_rtt(cur_target);
|
||||
/* do rtt i/o if control block found */
|
||||
if (rtt_found) {
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
||||
rtt_retval v;
|
||||
if (rtt_channel[i].is_enabled && rtt_channel[i].is_configured) {
|
||||
if (rtt_channel[i].is_output)
|
||||
v = print_rtt(cur_target, i);
|
||||
else
|
||||
v = read_rtt(cur_target, i);
|
||||
if (v == RTT_OK) rtt_busy = true;
|
||||
else if (v == RTT_ERR) rtt_err = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* continue target if halted */
|
||||
if (resume_target)
|
||||
target_halt_resume(cur_target, false);
|
||||
|
||||
/* update last poll time */
|
||||
last_poll_ms = now;
|
||||
|
||||
/* rtt polling frequency goes up and down with rtt activity */
|
||||
if (rtt_busy && !rtt_err)
|
||||
poll_ms /= 2;
|
||||
else
|
||||
poll_ms *= 2;
|
||||
|
||||
if (poll_ms > rtt_max_poll_ms)
|
||||
poll_ms = rtt_max_poll_ms;
|
||||
else if (poll_ms < rtt_min_poll_ms)
|
||||
poll_ms = rtt_min_poll_ms;
|
||||
|
||||
if (rtt_err) {
|
||||
gdb_out("rtt: err\r\n");
|
||||
poll_errs++;
|
||||
if (rtt_max_poll_errs != 0 && poll_errs > rtt_max_poll_errs) {
|
||||
gdb_out("\r\nrtt lost\r\n");
|
||||
rtt_enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
@ -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)
|
||||
{
|
||||
volatile struct exception e;
|
||||
target_list_free();
|
||||
static bool scan_multidrop = true;
|
||||
ADIv5_DP_t idp = {
|
||||
.dp_low_write = firmware_dp_low_write,
|
||||
.error = firmware_swdp_error,
|
||||
@ -77,6 +77,7 @@ int adiv5_swdp_scan(uint32_t targetid)
|
||||
.abort = firmware_swdp_abort,
|
||||
};
|
||||
ADIv5_DP_t *initial_dp = &idp;
|
||||
target_list_free();
|
||||
if (swdptap_init(initial_dp))
|
||||
return -1;
|
||||
/* DORMANT-> SWD sequence*/
|
||||
@ -93,7 +94,6 @@ int adiv5_swdp_scan(uint32_t targetid)
|
||||
initial_dp->seq_out(0x1a0, 12);
|
||||
uint32_t idcode = 0;
|
||||
volatile uint32_t target_id = 0;
|
||||
bool scan_multidrop = true;
|
||||
if (!targetid || !initial_dp->dp_low_write) {
|
||||
/* No targetID given on the command line or probe can not
|
||||
* handle multi-drop. Try to read ID */
|
||||
|
@ -18,12 +18,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This file implements CH32F1xx target specific functions.
|
||||
/* This file implements CH32F1xx target specific functions.
|
||||
The ch32 flash is rather slow so this code is using the so called fast mode (ch32 specific).
|
||||
128 bytes are copied to a write buffer, then the write buffer is committed to flash
|
||||
/!\ There is some sort of bus stall/bus arbitration going on that does NOT work when
|
||||
programmed through SWD/jtag
|
||||
The workaround is to wait a few cycles before filling the write buffer. This is performed by reading the flash a few times
|
||||
The workaround is to wait a few cycles before filling the write buffer. This is performed by reading the flash a few times
|
||||
|
||||
*/
|
||||
|
||||
@ -32,29 +32,20 @@
|
||||
#include "target_internal.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
|
||||
|
||||
static int ch32f1_flash_erase(struct target_flash *f,
|
||||
target_addr addr, size_t len);
|
||||
static int ch32f1_flash_write(struct target_flash *f,
|
||||
target_addr dest, const void *src, size_t len);
|
||||
|
||||
static int ch32f1_flash_erase(struct target_flash *f,
|
||||
target_addr addr, size_t len);
|
||||
static int ch32f1_flash_write(struct target_flash *f,
|
||||
target_addr dest, const void *src, size_t len);
|
||||
|
||||
// these are common with stm32f1/gd32f1/...
|
||||
#define FPEC_BASE 0x40022000
|
||||
#define FLASH_ACR (FPEC_BASE+0x00)
|
||||
#define FLASH_KEYR (FPEC_BASE+0x04)
|
||||
#define FLASH_SR (FPEC_BASE+0x0C)
|
||||
#define FLASH_CR (FPEC_BASE+0x10)
|
||||
#define FLASH_AR (FPEC_BASE+0x14)
|
||||
#define FLASH_ACR (FPEC_BASE + 0x00)
|
||||
#define FLASH_KEYR (FPEC_BASE + 0x04)
|
||||
#define FLASH_SR (FPEC_BASE + 0x0C)
|
||||
#define FLASH_CR (FPEC_BASE + 0x10)
|
||||
#define FLASH_AR (FPEC_BASE + 0x14)
|
||||
#define FLASH_CR_LOCK (1 << 7)
|
||||
#define FLASH_CR_STRT (1 << 6)
|
||||
#define FLASH_SR_BSY (1 << 0)
|
||||
@ -66,19 +57,16 @@ extern const struct command_s stm32f1_cmd_list[]; // Reuse stm32f1 stuff
|
||||
#define FLASHSIZE 0x1FFFF7E0
|
||||
|
||||
// these are specific to ch32f1
|
||||
#define FLASH_MAGIC (FPEC_BASE+0x34)
|
||||
#define FLASH_MODEKEYR_CH32 (FPEC_BASE+0x24) // Fast mode for CH32F10x
|
||||
#define FLASH_CR_FLOCK_CH32 (1<<15) // fast unlock
|
||||
#define FLASH_CR_FTPG_CH32 (1<<16) // fast page program
|
||||
#define FLASH_CR_FTER_CH32 (1<<17) // fast page erase
|
||||
#define FLASH_CR_BUF_LOAD_CH32 (1<<18) // Buffer load
|
||||
#define FLASH_CR_BUF_RESET_CH32 (1<<19) // Buffer reset
|
||||
#define FLASH_SR_EOP (1<<5) // End of programming
|
||||
#define FLASH_MAGIC (FPEC_BASE + 0x34)
|
||||
#define FLASH_MODEKEYR_CH32 (FPEC_BASE + 0x24) // Fast mode for CH32F10x
|
||||
#define FLASH_CR_FLOCK_CH32 (1 << 15) // fast unlock
|
||||
#define FLASH_CR_FTPG_CH32 (1 << 16) // fast page program
|
||||
#define FLASH_CR_FTER_CH32 (1 << 17) // fast page erase
|
||||
#define FLASH_CR_BUF_LOAD_CH32 (1 << 18) // Buffer load
|
||||
#define FLASH_CR_BUF_RESET_CH32 (1 << 19) // Buffer reset
|
||||
#define FLASH_SR_EOP (1 << 5) // End of programming
|
||||
#define FLASH_BEGIN_ADDRESS_CH32 0x8000000
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
\fn ch32f1_add_flash
|
||||
\brief "fast" flash driver for CH32F10x chips
|
||||
@ -101,38 +89,41 @@ static void ch32f1_add_flash(target *t, uint32_t addr, size_t length, size_t era
|
||||
target_add_flash(t, f);
|
||||
}
|
||||
|
||||
#define WAIT_BUSY() do { \
|
||||
sr = target_mem_read32(t, FLASH_SR); \
|
||||
if(target_check_error(t)) { \
|
||||
ERROR_CH("ch32f1 flash write: comm error\n"); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (sr & FLASH_SR_BSY);
|
||||
#define WAIT_BUSY() do { \
|
||||
sr = target_mem_read32(t, FLASH_SR); \
|
||||
if (target_check_error(t)) { \
|
||||
DEBUG_WARN("ch32f1 flash write: comm error\n"); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (sr & FLASH_SR_BSY);
|
||||
|
||||
#define WAIT_EOP() do { \
|
||||
sr = target_mem_read32(t, FLASH_SR); \
|
||||
if(target_check_error(t)) { \
|
||||
ERROR_CH("ch32f1 flash write: comm error\n"); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (!(sr & FLASH_SR_EOP));
|
||||
#define WAIT_EOP() do { \
|
||||
sr = target_mem_read32(t, FLASH_SR); \
|
||||
if (target_check_error(t)) { \
|
||||
DEBUG_WARN("ch32f1 flash write: comm error\n"); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (!(sr & FLASH_SR_EOP));
|
||||
|
||||
#define CLEAR_EOP() target_mem_write32(t, FLASH_SR,FLASH_SR_EOP)
|
||||
#define CLEAR_EOP() target_mem_write32(t, FLASH_SR,FLASH_SR_EOP)
|
||||
|
||||
#define SET_CR(bit) { ct = target_mem_read32(t, FLASH_CR); \
|
||||
ct|=(bit); \
|
||||
target_mem_write32(t, FLASH_CR, ct);}
|
||||
#define SET_CR(bit) do { \
|
||||
const uint32_t cr = target_mem_read32(t, FLASH_CR) | (bit); \
|
||||
target_mem_write32(t, FLASH_CR, cr); \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define CLEAR_CR(bit) {ct = target_mem_read32(t, FLASH_CR); \
|
||||
ct&=~(bit); \
|
||||
target_mem_write32(t, FLASH_CR, ct);}
|
||||
#define CLEAR_CR(bit) do { \
|
||||
const uint32_t cr = target_mem_read32(t, FLASH_CR) & (~(bit)); \
|
||||
target_mem_write32(t, FLASH_CR, cr); \
|
||||
} while(0)
|
||||
|
||||
// Which one is the right value ?
|
||||
#define MAGIC_WORD 0x100
|
||||
// #define MAGIC_WORD 0x1000
|
||||
#define MAGIC(adr) { magic=target_mem_read32(t,(adr) ^ MAGIC_WORD); \
|
||||
target_mem_write32(t, FLASH_MAGIC , magic); }
|
||||
#define MAGIC(addr) do { \
|
||||
magic = target_mem_read32(t, (addr) ^ MAGIC_WORD); \
|
||||
target_mem_write32(t, FLASH_MAGIC , magic); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
\fn ch32f1_flash_unlock
|
||||
@ -140,24 +131,24 @@ static void ch32f1_add_flash(target *t, uint32_t addr, size_t length, size_t era
|
||||
*/
|
||||
static int ch32f1_flash_unlock(target *t)
|
||||
{
|
||||
DEBUG_CH("CH32: flash unlock \n");
|
||||
DEBUG_INFO("CH32: flash unlock \n");
|
||||
|
||||
target_mem_write32(t, FLASH_KEYR , KEY1);
|
||||
target_mem_write32(t, FLASH_KEYR , KEY2);
|
||||
target_mem_write32(t, FLASH_KEYR, KEY1);
|
||||
target_mem_write32(t, FLASH_KEYR, KEY2);
|
||||
// fast mode
|
||||
target_mem_write32(t, FLASH_MODEKEYR_CH32 , KEY1);
|
||||
target_mem_write32(t, FLASH_MODEKEYR_CH32 , KEY2);
|
||||
target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY1);
|
||||
target_mem_write32(t, FLASH_MODEKEYR_CH32, KEY2);
|
||||
uint32_t cr = target_mem_read32(t, FLASH_CR);
|
||||
if (cr & FLASH_CR_FLOCK_CH32){
|
||||
ERROR_CH("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr);
|
||||
if (cr & FLASH_CR_FLOCK_CH32) {
|
||||
DEBUG_WARN("Fast unlock failed, cr: 0x%08" PRIx32 "\n", cr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ch32f1_flash_lock(target *t)
|
||||
{
|
||||
volatile uint32_t ct;
|
||||
DEBUG_CH("CH32: flash lock \n");
|
||||
DEBUG_INFO("CH32: flash lock \n");
|
||||
SET_CR(FLASH_CR_LOCK);
|
||||
return 0;
|
||||
}
|
||||
@ -166,56 +157,55 @@ static int ch32f1_flash_lock(target *t)
|
||||
\brief identify the ch32f1 chip
|
||||
Actually grab all cortex m3 with designer = arm not caught earlier...
|
||||
*/
|
||||
|
||||
bool ch32f1_probe(target *t)
|
||||
{
|
||||
t->idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0xfff;
|
||||
if ((t->cpuid & CPUID_PARTNO_MASK) != CORTEX_M3)
|
||||
return false;
|
||||
if(t->idcode !=0x410) { // only ch32f103
|
||||
const uint32_t idcode = target_mem_read32(t, DBGMCU_IDCODE) & 0x00000fffU;
|
||||
if (idcode != 0x410) // only ch32f103
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to flock
|
||||
ch32f1_flash_lock(t);
|
||||
// if this fails it is not a CH32 chip
|
||||
if(ch32f1_flash_unlock(t)) {
|
||||
if (ch32f1_flash_unlock(t))
|
||||
return false;
|
||||
}
|
||||
|
||||
t->idcode = idcode;
|
||||
uint32_t signature = target_mem_read32(t, FLASHSIZE);
|
||||
uint32_t flashSize = signature & 0xFFFF;
|
||||
|
||||
target_add_ram(t, 0x20000000, 0x5000);
|
||||
ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize*1024, 128);
|
||||
ch32f1_add_flash(t, FLASH_BEGIN_ADDRESS_CH32, flashSize * 1024, 128);
|
||||
target_add_commands(t, stm32f1_cmd_list, "STM32 LD/MD/VL-LD/VL-MD");
|
||||
t->driver = "CH32F1 medium density (stm32f1 clone)";
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\fn ch32f1_flash_erase
|
||||
\brief fast erase of CH32
|
||||
*/
|
||||
int ch32f1_flash_erase (struct target_flash *f, target_addr addr, size_t len)
|
||||
int ch32f1_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
||||
{
|
||||
volatile uint32_t ct, sr, magic;
|
||||
volatile uint32_t sr, magic;
|
||||
target *t = f->t;
|
||||
DEBUG_CH("CH32: flash erase \n");
|
||||
DEBUG_INFO("CH32: flash erase \n");
|
||||
|
||||
if (ch32f1_flash_unlock(t)) {
|
||||
ERROR_CH("CH32: Unlock failed\n");
|
||||
DEBUG_WARN("CH32: Unlock failed\n");
|
||||
return -1;
|
||||
}
|
||||
// Fast Erase 128 bytes pages (ch32 mode)
|
||||
while(len) {
|
||||
while (len) {
|
||||
SET_CR(FLASH_CR_FTER_CH32);// CH32 PAGE_ER
|
||||
/* write address to FMA */
|
||||
target_mem_write32(t, FLASH_AR , addr);
|
||||
target_mem_write32(t, FLASH_AR, addr);
|
||||
/* Flash page erase start instruction */
|
||||
SET_CR( FLASH_CR_STRT );
|
||||
SET_CR(FLASH_CR_STRT);
|
||||
WAIT_EOP();
|
||||
CLEAR_EOP();
|
||||
CLEAR_CR( FLASH_CR_STRT );
|
||||
CLEAR_CR(FLASH_CR_STRT);
|
||||
// Magic
|
||||
MAGIC(addr);
|
||||
if (len > 128)
|
||||
@ -226,8 +216,8 @@ int ch32f1_flash_erase (struct target_flash *f, target_addr addr, size_t len)
|
||||
}
|
||||
sr = target_mem_read32(t, FLASH_SR);
|
||||
ch32f1_flash_lock(t);
|
||||
if ((sr & SR_ERROR_MASK)) {
|
||||
ERROR_CH("ch32f1 flash erase error 0x%" PRIx32 "\n", sr);
|
||||
if (sr & SR_ERROR_MASK) {
|
||||
DEBUG_WARN("ch32f1 flash erase error 0x%" PRIx32 "\n", sr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -241,39 +231,38 @@ 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
|
||||
*/
|
||||
|
||||
static bool ch32f1_wait_flash_ready(target *t,uint32_t adr)
|
||||
static bool ch32f1_wait_flash_ready(target *t, uint32_t addr)
|
||||
{
|
||||
uint32_t ff;
|
||||
for(int i = 0; i < 32; i++) {
|
||||
ff = target_mem_read32(t,adr);
|
||||
}
|
||||
if(ff != 0xffffffffUL) {
|
||||
ERROR_CH("ch32f1 Not erased properly at %x or flash access issue\n",adr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
uint32_t ff = 0;
|
||||
for (size_t i = 0; i < 32; i++)
|
||||
ff = target_mem_read32(t, addr);
|
||||
if (ff != 0xffffffffUL) {
|
||||
DEBUG_WARN("ch32f1 Not erased properly at %" PRIx32 " or flash access issue\n", addr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
\fn ch32f1_flash_write
|
||||
\brief fast flash for ch32. Load 128 bytes chunk and then flash them
|
||||
*/
|
||||
|
||||
static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t offset)
|
||||
static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t offset)
|
||||
{
|
||||
volatile uint32_t ct, sr, magic;
|
||||
volatile uint32_t sr, magic;
|
||||
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);
|
||||
target_mem_write32(t, dd+0,ss[0]);
|
||||
target_mem_write32(t, dd+4,ss[1]);
|
||||
target_mem_write32(t, dd+8,ss[2]);
|
||||
target_mem_write32(t, dd+12,ss[3]);
|
||||
target_mem_write32(t, dd + 0, ss[0]);
|
||||
target_mem_write32(t, dd + 4, ss[1]);
|
||||
target_mem_write32(t, dd + 8, ss[2]);
|
||||
target_mem_write32(t, dd + 12, ss[3]);
|
||||
SET_CR(FLASH_CR_BUF_LOAD_CH32); /* BUF LOAD */
|
||||
WAIT_EOP();
|
||||
CLEAR_EOP();
|
||||
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
||||
MAGIC((dest+offset));
|
||||
MAGIC(dest + offset);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
@ -282,12 +271,12 @@ static int ch32f1_upload(target *t, uint32_t dest, const void *src, uint32_t of
|
||||
*/
|
||||
int ch32f1_buffer_clear(target *t)
|
||||
{
|
||||
volatile uint32_t ct,sr;
|
||||
SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||
SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5-
|
||||
WAIT_BUSY(); // 6-
|
||||
CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||
return 0;
|
||||
volatile uint32_t sr;
|
||||
SET_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||
SET_CR(FLASH_CR_BUF_RESET_CH32); // BUF_RESET 5-
|
||||
WAIT_BUSY(); // 6-
|
||||
CLEAR_CR(FLASH_CR_FTPG_CH32); // Fast page program 4-
|
||||
return 0;
|
||||
}
|
||||
//#define CH32_VERIFY
|
||||
|
||||
@ -295,21 +284,21 @@ int ch32f1_buffer_clear(target *t)
|
||||
|
||||
*/
|
||||
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 ct, sr, magic;
|
||||
volatile uint32_t sr, magic;
|
||||
target *t = f->t;
|
||||
size_t length = len;
|
||||
#ifdef CH32_VERIFY
|
||||
target_addr orgDest=dest;
|
||||
const void *orgSrc=src;
|
||||
target_addr org_dest = dest;
|
||||
const void *org_src = src;
|
||||
#endif
|
||||
DEBUG_CH("CH32: flash write 0x%x ,size=%d\n",dest,len);
|
||||
DEBUG_INFO("CH32: flash write 0x%" PRIx32 " ,size=%zu\n", dest, len);
|
||||
|
||||
while(length > 0)
|
||||
while (length > 0)
|
||||
{
|
||||
if(ch32f1_flash_unlock(t)) {
|
||||
ERROR_CH("ch32f1 cannot fast unlock\n");
|
||||
if (ch32f1_flash_unlock(t)) {
|
||||
DEBUG_WARN("ch32f1 cannot fast unlock\n");
|
||||
return -1;
|
||||
}
|
||||
WAIT_BUSY();
|
||||
@ -317,12 +306,12 @@ static int ch32f1_flash_write(struct target_flash *f,
|
||||
// Buffer reset...
|
||||
ch32f1_buffer_clear(t);
|
||||
// Load 128 bytes to buffer
|
||||
if(!ch32f1_wait_flash_ready(t,dest)) {
|
||||
if (!ch32f1_wait_flash_ready(t,dest))
|
||||
return -1;
|
||||
}
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(ch32f1_upload(t,dest,src, 16*i)) {
|
||||
ERROR_CH("Cannot upload to buffer\n");
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
if (ch32f1_upload(t, dest, src, i * 16U)) {
|
||||
DEBUG_WARN("Cannot upload to buffer\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -334,11 +323,11 @@ static int ch32f1_flash_write(struct target_flash *f,
|
||||
CLEAR_EOP();
|
||||
CLEAR_CR(FLASH_CR_FTPG_CH32);
|
||||
|
||||
MAGIC((dest));
|
||||
MAGIC(dest);
|
||||
|
||||
// next
|
||||
if(length > 128)
|
||||
length -=128;
|
||||
if (length > 128)
|
||||
length -=128;
|
||||
else
|
||||
length = 0;
|
||||
dest += 128;
|
||||
@ -346,24 +335,23 @@ static int ch32f1_flash_write(struct target_flash *f,
|
||||
|
||||
sr = target_mem_read32(t, FLASH_SR); // 13
|
||||
ch32f1_flash_lock(t);
|
||||
if ((sr & SR_ERROR_MASK) ) {
|
||||
ERROR_CH("ch32f1 flash write error 0x%" PRIx32 "\n", sr);
|
||||
if (sr & SR_ERROR_MASK) {
|
||||
DEBUG_WARN("ch32f1 flash write error 0x%" PRIx32 "\n", sr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef CH32_VERIFY
|
||||
DEBUG_CH("Verifying\n");
|
||||
size_t i = 0;
|
||||
for(i = 0; i < len; i+= 4)
|
||||
DEBUG_INFO("Verifying\n");
|
||||
for (size_t i = 0; i < len; i += 4)
|
||||
{
|
||||
uint32_t mem=target_mem_read32(t, orgDest+i);
|
||||
uint32_t mem2=*(uint32_t *)(orgSrc+i);
|
||||
if(mem!=mem2)
|
||||
const uint32_t expected = *(uint32_t *)(org_src + i);
|
||||
const uint32_t actual = target_mem_read32(t, org_dest + i);
|
||||
if (expected != actual)
|
||||
{
|
||||
ERROR_CH(">>>>write mistmatch at address 0x%x\n",orgDest+i);
|
||||
ERROR_CH(">>>>expected 0x%x\n",mem2);
|
||||
ERROR_CH(">>>>flash 0x%x\n",mem);
|
||||
DEBUG_WARN(">>>>write mistmatch at address 0x%x\n", org_dest + i);
|
||||
DEBUG_WARN(">>>>expected: 0x%x\n", expected);
|
||||
DEBUG_WARN(">>>> actual: 0x%x\n", actual);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -371,4 +359,3 @@ static int ch32f1_flash_write(struct target_flash *f,
|
||||
|
||||
return 0;
|
||||
}
|
||||
// EOF
|
||||
|
@ -378,8 +378,25 @@ bool cortexm_probe(ADIv5_AP_t *ap)
|
||||
} else {
|
||||
target_check_error(t);
|
||||
}
|
||||
#if PC_HOSTED
|
||||
#define STRINGIFY(x) #x
|
||||
#define PROBE(x) \
|
||||
do { if ((x)(t)) {return true;} else target_check_error(t); } while (0)
|
||||
do { \
|
||||
DEBUG_INFO("Calling " STRINGIFY(x) "\n"); \
|
||||
if ((x)(t)) \
|
||||
return true; \
|
||||
else \
|
||||
target_check_error(t); \
|
||||
} while (0)
|
||||
#else
|
||||
#define PROBE(x) \
|
||||
do { \
|
||||
if ((x)(t)) \
|
||||
return true; \
|
||||
else \
|
||||
target_check_error(t); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
switch (ap->ap_designer) {
|
||||
case AP_DESIGNER_FREESCALE:
|
||||
@ -502,7 +519,7 @@ bool cortexm_attach(target *t)
|
||||
priv->flash_patch_revision = (r >> 28);
|
||||
priv->hw_watchpoint_max = CORTEXM_MAX_WATCHPOINTS;
|
||||
r = target_mem_read32(t, CORTEXM_DWT_CTRL);
|
||||
if ((r >> 28) > priv->hw_watchpoint_max)
|
||||
if ((r >> 28) < priv->hw_watchpoint_max)
|
||||
priv->hw_watchpoint_max = r >> 28;
|
||||
|
||||
/* Clear any stale breakpoints */
|
||||
@ -675,7 +692,7 @@ static int dcrsr_regnum(target *t, unsigned reg)
|
||||
return regnum_cortex_m[reg];
|
||||
} else if ((t->target_options & TOPT_FLAVOUR_V7MF) &&
|
||||
(reg < (sizeof(regnum_cortex_m) +
|
||||
sizeof(regnum_cortex_mf) / 4))) {
|
||||
sizeof(regnum_cortex_mf)) / 4)) {
|
||||
return regnum_cortex_mf[reg - sizeof(regnum_cortex_m)/4];
|
||||
} else {
|
||||
return -1;
|
||||
|
@ -37,77 +37,96 @@
|
||||
#include "general.h"
|
||||
#include "target.h"
|
||||
#include "target_internal.h"
|
||||
#include "adiv5.h"
|
||||
|
||||
#define SIM_SDID 0x40048024
|
||||
#define SIM_FCFG1 0x4004804C
|
||||
#define KINETIS_MDM_IDR_K22F 0x1c0000
|
||||
#define KINETIS_MDM_IDR_KZ03 0x1c0020
|
||||
|
||||
#define FTFA_BASE 0x40020000
|
||||
#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 ADIV5_AP_REG(0x00)
|
||||
#define MDM_CONTROL ADIV5_AP_REG(0x04)
|
||||
|
||||
#define FTFA_FSTAT_CCIF (1 << 7)
|
||||
#define FTFA_FSTAT_RDCOLERR (1 << 6)
|
||||
#define FTFA_FSTAT_ACCERR (1 << 5)
|
||||
#define FTFA_FSTAT_FPVIOL (1 << 4)
|
||||
#define FTFA_FSTAT_MGSTAT0 (1 << 0)
|
||||
#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_STATUS_BACK_KEY_ENABLED (1 << 6)
|
||||
|
||||
#define FTFA_CMD_CHECK_ERASE 0x01
|
||||
#define FTFA_CMD_PROGRAM_CHECK 0x02
|
||||
#define FTFA_CMD_READ_RESOURCE 0x03
|
||||
#define FTFA_CMD_PROGRAM_LONGWORD 0x06
|
||||
#define MDM_CONTROL_MASS_ERASE (1 << 0)
|
||||
#define MDM_CONTROL_SYS_RESET (1 << 3)
|
||||
|
||||
#define SIM_SDID 0x40048024
|
||||
#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 */
|
||||
#define FTFE_CMD_PROGRAM_PHRASE 0x07
|
||||
#define FTFA_CMD_ERASE_SECTOR 0x09
|
||||
#define FTFA_CMD_CHECK_ERASE_ALL 0x40
|
||||
#define FTFA_CMD_READ_ONCE 0x41
|
||||
#define FTFA_CMD_PROGRAM_ONCE 0x43
|
||||
#define FTFA_CMD_ERASE_ALL 0x44
|
||||
#define FTFA_CMD_BACKDOOR_ACCESS 0x45
|
||||
#define FTFx_CMD_PROGRAM_PHRASE 0x07
|
||||
#define FTFx_CMD_ERASE_SECTOR 0x09
|
||||
#define FTFx_CMD_CHECK_ERASE_ALL 0x40
|
||||
#define FTFx_CMD_READ_ONCE 0x41
|
||||
#define FTFx_CMD_PROGRAM_ONCE 0x43
|
||||
#define FTFx_CMD_ERASE_ALL 0x44
|
||||
#define FTFx_CMD_BACKDOOR_ACCESS 0x45
|
||||
|
||||
#define KL_WRITE_LEN 4
|
||||
/* 8 byte phrases need to be written to the k64 flash */
|
||||
#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[] = {
|
||||
{"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) {
|
||||
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 {
|
||||
parse_enable_or_disable(argv[1], &t->unsafe_enabled);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len);
|
||||
static int kl_gen_flash_write(struct target_flash *f,
|
||||
target_addr dest, const void *src, size_t len);
|
||||
static int kl_gen_flash_done(struct target_flash *f);
|
||||
static int kinetis_flash_cmd_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 kinetis_flash_done(struct target_flash *f);
|
||||
|
||||
struct kinetis_flash {
|
||||
struct target_flash f;
|
||||
uint8_t write_len;
|
||||
};
|
||||
|
||||
static void kl_gen_add_flash(target *t, uint32_t addr, size_t length,
|
||||
size_t erasesize, size_t write_len)
|
||||
static void kinetis_add_flash(
|
||||
target *const t, const uint32_t addr, const size_t length, const size_t erasesize, const size_t write_len)
|
||||
{
|
||||
struct kinetis_flash *kf = calloc(1, sizeof(*kf));
|
||||
struct target_flash *f;
|
||||
|
||||
if (!kf) { /* calloc failed: heap exhaustion */
|
||||
if (!kf) { /* calloc failed: heap exhaustion */
|
||||
DEBUG_WARN("calloc: failed in %s\n", __func__);
|
||||
return;
|
||||
}
|
||||
@ -116,15 +135,26 @@ static void kl_gen_add_flash(target *t, uint32_t addr, size_t length,
|
||||
f->start = addr;
|
||||
f->length = length;
|
||||
f->blocksize = erasesize;
|
||||
f->erase = kl_gen_flash_erase;
|
||||
f->write = kl_gen_flash_write;
|
||||
f->done = kl_gen_flash_done;
|
||||
f->erase = kinetis_flash_cmd_erase;
|
||||
f->write = kinetis_flash_cmd_write;
|
||||
f->done = kinetis_flash_done;
|
||||
f->erased = 0xff;
|
||||
kf->write_len = write_len;
|
||||
target_add_flash(t, f);
|
||||
}
|
||||
|
||||
bool kinetis_probe(target *t)
|
||||
static void kl_s32k14_setup(
|
||||
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 fcfg1 = target_mem_read32(t, SIM_FCFG1);
|
||||
@ -132,52 +162,52 @@ bool kinetis_probe(target *t)
|
||||
switch (sdid >> 20) {
|
||||
case 0x161:
|
||||
/* sram memory size */
|
||||
switch((sdid >> 16) & 0x0f) {
|
||||
case 0x03:/* 4 KB */
|
||||
target_add_ram(t, 0x1ffffc00, 0x0400);
|
||||
target_add_ram(t, 0x20000000, 0x0C00);
|
||||
break;
|
||||
case 0x04:/* 8 KB */
|
||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||
target_add_ram(t, 0x20000000, 0x1800);
|
||||
break;
|
||||
case 0x05:/* 16 KB */
|
||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||
target_add_ram(t, 0x20000000, 0x3000);
|
||||
break;
|
||||
case 0x06:/* 32 KB */
|
||||
target_add_ram(t, 0x1fffe000, 0x2000);
|
||||
target_add_ram(t, 0x20000000, 0x6000);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
switch ((sdid >> 16) & 0x0f) {
|
||||
case 0x03: /* 4 KB */
|
||||
target_add_ram(t, 0x1ffffc00, 0x0400);
|
||||
target_add_ram(t, 0x20000000, 0x0C00);
|
||||
break;
|
||||
case 0x04: /* 8 KB */
|
||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||
target_add_ram(t, 0x20000000, 0x1800);
|
||||
break;
|
||||
case 0x05: /* 16 KB */
|
||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||
target_add_ram(t, 0x20000000, 0x3000);
|
||||
break;
|
||||
case 0x06: /* 32 KB */
|
||||
target_add_ram(t, 0x1fffe000, 0x2000);
|
||||
target_add_ram(t, 0x20000000, 0x6000);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* flash memory size */
|
||||
switch((fcfg1 >> 24) & 0x0f) {
|
||||
case 0x03: /* 32 KB */
|
||||
t->driver = "KL16Z32Vxxx";
|
||||
kl_gen_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
switch ((fcfg1 >> 24) & 0x0f) {
|
||||
case 0x03: /* 32 KB */
|
||||
t->driver = "KL16Z32Vxxx";
|
||||
kinetis_add_flash(t, 0x00000000, 0x08000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
|
||||
case 0x05: /* 64 KB */
|
||||
t->driver = "KL16Z64Vxxx";
|
||||
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x05: /* 64 KB */
|
||||
t->driver = "KL16Z64Vxxx";
|
||||
kinetis_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
|
||||
case 0x07: /* 128 KB */
|
||||
t->driver = "KL16Z128Vxxx";
|
||||
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x07: /* 128 KB */
|
||||
t->driver = "KL16Z128Vxxx";
|
||||
kinetis_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
|
||||
case 0x09: /* 256 KB */
|
||||
t->driver = "KL16Z256Vxxx";
|
||||
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
case 0x09: /* 256 KB */
|
||||
t->driver = "KL16Z256Vxxx";
|
||||
kinetis_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -186,68 +216,68 @@ bool kinetis_probe(target *t)
|
||||
t->driver = "KL25";
|
||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||
target_add_ram(t, 0x20000000, 0x3000);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0x00000000, 0x20000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x231:
|
||||
t->driver = "KL27x128"; // MKL27 >=128kb
|
||||
target_add_ram(t, 0x1fffe000, 0x2000);
|
||||
target_add_ram(t, 0x20000000, 0x6000);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0x00000000, 0x40000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x271:
|
||||
switch((sdid >> 16) & 0x0f) {
|
||||
case 4:
|
||||
t->driver = "KL27x32";
|
||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||
target_add_ram(t, 0x20000000, 0x1800);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x8000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 5:
|
||||
t->driver = "KL27x64";
|
||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||
target_add_ram(t, 0x20000000, 0x3000);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
switch ((sdid >> 16) & 0x0f) {
|
||||
case 4:
|
||||
t->driver = "KL27x32";
|
||||
target_add_ram(t, 0x1ffff800, 0x0800);
|
||||
target_add_ram(t, 0x20000000, 0x1800);
|
||||
kinetis_add_flash(t, 0x00000000, 0x8000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 5:
|
||||
t->driver = "KL27x64";
|
||||
target_add_ram(t, 0x1ffff000, 0x1000);
|
||||
target_add_ram(t, 0x20000000, 0x3000);
|
||||
kinetis_add_flash(t, 0x00000000, 0x10000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 0x021: /* KL02 family */
|
||||
switch((sdid >> 16) & 0x0f) {
|
||||
case 3:
|
||||
t->driver = "KL02x32";
|
||||
target_add_ram(t, 0x1FFFFC00, 0x400);
|
||||
target_add_ram(t, 0x20000000, 0xc00);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 2:
|
||||
t->driver = "KL02x16";
|
||||
target_add_ram(t, 0x1FFFFE00, 0x200);
|
||||
target_add_ram(t, 0x20000000, 0x600);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 1:
|
||||
t->driver = "KL02x8";
|
||||
target_add_ram(t, 0x1FFFFF00, 0x100);
|
||||
target_add_ram(t, 0x20000000, 0x300);
|
||||
kl_gen_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
switch ((sdid >> 16) & 0x0f) {
|
||||
case 3:
|
||||
t->driver = "KL02x32";
|
||||
target_add_ram(t, 0x1FFFFC00, 0x400);
|
||||
target_add_ram(t, 0x20000000, 0xc00);
|
||||
kinetis_add_flash(t, 0x00000000, 0x7FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 2:
|
||||
t->driver = "KL02x16";
|
||||
target_add_ram(t, 0x1FFFFE00, 0x200);
|
||||
target_add_ram(t, 0x20000000, 0x600);
|
||||
kinetis_add_flash(t, 0x00000000, 0x3FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 1:
|
||||
t->driver = "KL02x8";
|
||||
target_add_ram(t, 0x1FFFFF00, 0x100);
|
||||
target_add_ram(t, 0x20000000, 0x300);
|
||||
kinetis_add_flash(t, 0x00000000, 0x1FFF, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 0x031: /* KL03 family */
|
||||
t->driver = "KL03";
|
||||
target_add_ram(t, 0x1ffffe00, 0x200);
|
||||
target_add_ram(t, 0x20000000, 0x600);
|
||||
kl_gen_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0, 0x8000, 0x400, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x220: /* K22F family */
|
||||
t->driver = "K22F";
|
||||
target_add_ram(t, 0x1c000000, 0x4000000);
|
||||
target_add_ram(t, 0x20000000, 0x100000);
|
||||
kl_gen_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN);
|
||||
kl_gen_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0, 0x40000, 0x800, KL_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0x40000, 0x40000, 0x800, KL_WRITE_LEN);
|
||||
break;
|
||||
case 0x620: /* K64F family. */
|
||||
/* This should be 0x640, but according to the errata sheet
|
||||
@ -255,115 +285,107 @@ bool kinetis_probe(target *t)
|
||||
* subfamily nibble as 2
|
||||
*/
|
||||
t->driver = "K64";
|
||||
target_add_ram(t, 0x1FFF0000, 0x10000);
|
||||
target_add_ram(t, 0x20000000, 0x30000);
|
||||
kl_gen_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||
kl_gen_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||
target_add_ram(t, 0x1FFF0000, 0x10000);
|
||||
target_add_ram(t, 0x20000000, 0x30000);
|
||||
kinetis_add_flash(t, 0, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||
kinetis_add_flash(t, 0x80000, 0x80000, 0x1000, K64_WRITE_LEN);
|
||||
break;
|
||||
case 0x000: /* Older K-series */
|
||||
switch(sdid & 0xff0) {
|
||||
case 0x000: /* K10 Family, DIEID=0x0 */
|
||||
case 0x080: /* K10 Family, DIEID=0x1 */
|
||||
case 0x100: /* K10 Family, DIEID=0x2 */
|
||||
case 0x180: /* K10 Family, DIEID=0x3 */
|
||||
case 0x220: /* K11 Family, DIEID=0x4 */
|
||||
return false;
|
||||
case 0x200: /* K12 Family, DIEID=0x4 */
|
||||
switch((fcfg1 >> 24) & 0x0f) {
|
||||
/* K12 Sub-Family Reference Manual, K12P80M50SF4RM, Rev. 4, February 2013 */
|
||||
case 0x7:
|
||||
t->driver = "MK12DX128Vxx5";
|
||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
||||
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
||||
kl_gen_add_flash(t, 0x00000000, 0x00020000, 0x800, KL_WRITE_LEN); /* P-Flash, 128 KB, 2 KB Sectors */
|
||||
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||
break;
|
||||
case 0x9:
|
||||
t->driver = "MK12DX256Vxx5";
|
||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 16 KB */
|
||||
target_add_ram(t, 0x20000000, 0x00004000); /* SRAM_H, 16 KB */
|
||||
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
||||
kl_gen_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||
break;
|
||||
case 0xb:
|
||||
t->driver = "MK12DN512Vxx5";
|
||||
target_add_ram(t, 0x1fff8000, 0x00008000); /* SRAM_L, 32 KB */
|
||||
target_add_ram(t, 0x20000000, 0x00008000); /* SRAM_H, 32 KB */
|
||||
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, KL_WRITE_LEN); /* P-Flash, 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;
|
||||
}
|
||||
switch (sdid & 0xff0) {
|
||||
case 0x000: /* K10 Family, DIEID=0x0 */
|
||||
case 0x080: /* K10 Family, DIEID=0x1 */
|
||||
case 0x100: /* K10 Family, DIEID=0x2 */
|
||||
case 0x180: /* K10 Family, DIEID=0x3 */
|
||||
case 0x220: /* K11 Family, DIEID=0x4 */
|
||||
return false;
|
||||
case 0x200: /* K12 Family, DIEID=0x4 */
|
||||
switch ((fcfg1 >> 24) & 0x0f) {
|
||||
/* K12 Sub-Family Reference Manual, K12P80M50SF4RM, Rev. 4, February 2013 */
|
||||
case 0x7:
|
||||
t->driver = "MK12DX128Vxx5";
|
||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 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 */
|
||||
kinetis_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||
break;
|
||||
case 0x9:
|
||||
t->driver = "MK12DX256Vxx5";
|
||||
target_add_ram(t, 0x1fffc000, 0x00004000); /* SRAM_L, 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 */
|
||||
kinetis_add_flash(t, 0x10000000, 0x00010000, 0x800, KL_WRITE_LEN); /* FlexNVM, 64 KB, 2 KB Sectors */
|
||||
break;
|
||||
case 0xb:
|
||||
t->driver = "MK12DN512Vxx5";
|
||||
target_add_ram(t, 0x1fff8000, 0x00008000); /* SRAM_L, 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 */
|
||||
kinetis_add_flash(t, 0x00040000, 0x00040000, 0x800, KL_WRITE_LEN); /* FlexNVM, 256 KB, 2 KB Sectors */
|
||||
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;
|
||||
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;
|
||||
case 0x118: /* S32K118 */
|
||||
t->driver = "S32K118";
|
||||
target_add_ram(t, 0x1ffffc00, 0x00000400); /* SRAM_L, 1 KB */
|
||||
target_add_ram(t, 0x20000000, 0x00005800); /* SRAM_H, 22 KB */
|
||||
kl_gen_add_flash(t, 0x00000000, 0x00040000, 0x800, K64_WRITE_LEN); /* P-Flash, 256 KB, 2 KB Sectors */
|
||||
kl_gen_add_flash(t, 0x10000000, 0x00008000, 0x800, K64_WRITE_LEN); /* FlexNVM, 32 KB, 2 KB Sectors */
|
||||
target_add_ram(t, 0x1ffffc00, 0x00000400); /* SRAM_L, 1 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 */
|
||||
kinetis_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;
|
||||
/* 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 */
|
||||
sram_l = 0x1ffe0000; /* SRAM_L, 128 KB */
|
||||
sram_h = 0x1f000; /* SRAM_H, 124 KB */
|
||||
flash = 0x00180000; /* flash 1536 KB */
|
||||
flexmem = 0x80000; /* FlexNVM 512 KB */
|
||||
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 */
|
||||
/* SRAM_L = 128 KiB */
|
||||
/* SRAM_H = 124 KiB */
|
||||
/* Flash = 1536 KiB */
|
||||
/* FlexNVM = 512 KiB */
|
||||
kl_s32k14_setup(t, 0x1ffe0000, 0x1f000, 0x00180000, 0x80000);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -372,47 +394,48 @@ do_common_s32k14x:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
kl_gen_command(target *t, uint8_t cmd, uint32_t addr, const uint32_t *data, int n_items)
|
||||
static bool kinetis_fccob_cmd(target *t, uint8_t cmd, uint32_t addr, const uint32_t *data, int n_items)
|
||||
{
|
||||
uint8_t fstat;
|
||||
|
||||
/* clear errors unconditionally, so we can start a new operation */
|
||||
target_mem_write8(t,FTFA_FSTAT,(FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL));
|
||||
target_mem_write8(t, FTFx_FSTAT, (FTFx_FSTAT_ACCERR | FTFx_FSTAT_FPVIOL));
|
||||
|
||||
/* Wait for CCIF to be high */
|
||||
do {
|
||||
fstat = target_mem_read8(t, FTFA_FSTAT);
|
||||
} while (!(fstat & FTFA_FSTAT_CCIF));
|
||||
fstat = target_mem_read8(t, FTFx_FSTAT);
|
||||
} while (!(fstat & FTFx_FSTAT_CCIF));
|
||||
|
||||
/* Write command to FCCOB */
|
||||
addr &= 0xffffff;
|
||||
addr |= (uint32_t)cmd << 24;
|
||||
target_mem_write32(t, FTFA_FCCOB_0, addr);
|
||||
if (data) {
|
||||
target_mem_write32(t, FTFA_FCCOB_1, data[0]);
|
||||
addr &= 0x00ffffffU;
|
||||
addr |= cmd << 24U;
|
||||
target_mem_write32(t, FTFx_FCCOB0, addr);
|
||||
if (data && n_items) {
|
||||
target_mem_write32(t, FTFx_FCCOB4, data[0]);
|
||||
if (n_items > 1)
|
||||
target_mem_write32(t, FTFA_FCCOB_2, data[1]);
|
||||
target_mem_write32(t, FTFx_FCCOB8, data[1]);
|
||||
else
|
||||
target_mem_write32(t, FTFx_FCCOB8, 0);
|
||||
}
|
||||
|
||||
/* Enable execution by clearing CCIF */
|
||||
target_mem_write8(t, FTFA_FSTAT, FTFA_FSTAT_CCIF);
|
||||
target_mem_write8(t, FTFx_FSTAT, FTFx_FSTAT_CCIF);
|
||||
|
||||
/* Wait for execution to complete */
|
||||
do {
|
||||
fstat = target_mem_read8(t, FTFA_FSTAT);
|
||||
fstat = target_mem_read8(t, FTFx_FSTAT);
|
||||
/* Check ACCERR and FPVIOL are zero in FSTAT */
|
||||
if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL))
|
||||
if (fstat & (FTFx_FSTAT_ACCERR | FTFx_FSTAT_FPVIOL))
|
||||
return false;
|
||||
} while (!(fstat & FTFA_FSTAT_CCIF));
|
||||
} while (!(fstat & FTFx_FSTAT_CCIF));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t len)
|
||||
static int kinetis_flash_cmd_erase(struct target_flash *const f, target_addr addr, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
if (kl_gen_command(f->t, FTFA_CMD_ERASE_SECTOR, addr, NULL, 0)) {
|
||||
if (kinetis_fccob_cmd(f->t, FTFx_CMD_ERASE_SECTOR, addr, NULL, 0)) {
|
||||
/* Different targets have different flash erase sizes */
|
||||
if (len > f->blocksize)
|
||||
len -= f->blocksize;
|
||||
@ -426,72 +449,60 @@ static int kl_gen_flash_erase(struct target_flash *f, target_addr addr, size_t l
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
static int kinetis_flash_cmd_write(struct target_flash *f, target_addr dest, const void *src, size_t len)
|
||||
{
|
||||
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
||||
struct kinetis_flash *const kf = (struct kinetis_flash *)f;
|
||||
|
||||
/* 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)) {
|
||||
((uint8_t*)src)[FLASH_SECURITY_BYTE_ADDRESS - dest] =
|
||||
FLASH_SECURITY_BYTE_UNSECURED;
|
||||
if (!f->t->unsafe_enabled && 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. */
|
||||
uint8_t write_cmd;
|
||||
if (kf->write_len == K64_WRITE_LEN) {
|
||||
write_cmd = FTFE_CMD_PROGRAM_PHRASE;
|
||||
} else {
|
||||
write_cmd = FTFA_CMD_PROGRAM_LONGWORD;
|
||||
}
|
||||
if (kf->write_len == K64_WRITE_LEN)
|
||||
write_cmd = FTFx_CMD_PROGRAM_PHRASE;
|
||||
else
|
||||
write_cmd = FTFx_CMD_PROGRAM_LONGWORD;
|
||||
|
||||
while (len) {
|
||||
if (kl_gen_command(f->t, write_cmd, dest, src, 1)) {
|
||||
if (kinetis_fccob_cmd(f->t, write_cmd, dest, src, kf->write_len >> 2U)) {
|
||||
if (len > kf->write_len)
|
||||
len -= kf->write_len;
|
||||
else
|
||||
len = 0;
|
||||
dest += kf->write_len;
|
||||
src += kf->write_len;
|
||||
} else {
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kl_gen_flash_done(struct target_flash *f)
|
||||
static int kinetis_flash_done(struct target_flash *const f)
|
||||
{
|
||||
struct kinetis_flash *kf = (struct kinetis_flash *)f;
|
||||
struct kinetis_flash *const kf = (struct kinetis_flash *)f;
|
||||
|
||||
if (f->t->unsafe_enabled)
|
||||
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;
|
||||
|
||||
/* Load the security byte based on the alignment (determine 8 byte phrases
|
||||
* vs 4 byte phrases).
|
||||
*/
|
||||
if (kf->write_len == 8) {
|
||||
uint32_t vals[2];
|
||||
vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS-4);
|
||||
vals[1] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
||||
vals[1] = (vals[1] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||
kl_gen_command(f->t, FTFE_CMD_PROGRAM_PHRASE,
|
||||
FLASH_SECURITY_BYTE_ADDRESS - 4, vals, 2);
|
||||
if (kf->write_len == K64_WRITE_LEN) {
|
||||
uint32_t vals[2] = {
|
||||
target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS - 4),
|
||||
target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS)
|
||||
};
|
||||
vals[1] = (vals[1] & 0xffffff00U) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||
kinetis_fccob_cmd(f->t, FTFx_CMD_PROGRAM_PHRASE, FLASH_SECURITY_BYTE_ADDRESS - 4, vals, 2);
|
||||
} else {
|
||||
uint32_t vals[1];
|
||||
vals[0] = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
||||
vals[0] = (vals[0] & 0xffffff00) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||
kl_gen_command(f->t, FTFA_CMD_PROGRAM_LONGWORD,
|
||||
FLASH_SECURITY_BYTE_ADDRESS, vals, 1);
|
||||
uint32_t val = target_mem_read32(f->t, FLASH_SECURITY_BYTE_ADDRESS);
|
||||
val = (val & 0xffffff00U) | FLASH_SECURITY_BYTE_UNSECURED;
|
||||
kinetis_fccob_cmd(f->t, FTFx_CMD_PROGRAM_LONGWORD, FLASH_SECURITY_BYTE_ADDRESS, &val, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -504,10 +515,6 @@ static int kl_gen_flash_done(struct target_flash *f)
|
||||
* 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
|
||||
*/
|
||||
#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_ke04_mode(target *t, int argc, const char **argv);
|
||||
@ -515,18 +522,19 @@ static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv);
|
||||
const struct command_s kinetis_mdm_cmd_list[] = {
|
||||
{"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"},
|
||||
{NULL, NULL, NULL}
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
enum target_halt_reason mdm_halt_poll(target *t, target_addr *watch)
|
||||
enum target_halt_reason mdm_halt_poll(target *t, const target_addr *const watch)
|
||||
{
|
||||
(void)t; (void)watch;
|
||||
(void)t;
|
||||
(void)watch;
|
||||
return TARGET_HALT_REQUEST;
|
||||
}
|
||||
|
||||
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_K22F:
|
||||
break;
|
||||
@ -541,23 +549,13 @@ void kinetis_mdm_probe(ADIv5_AP_t *ap)
|
||||
|
||||
adiv5_ap_ref(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->regs_size = 4;
|
||||
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 *
|
||||
* tell a KE04 from other kinetis in kinetis_mdm_probe() */
|
||||
static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
||||
@ -569,6 +567,7 @@ static bool kinetis_mdm_cmd_ke04_mode(target *t, int argc, const char **argv)
|
||||
tc_printf(t, "Mass erase for KE04 now allowed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
@ -576,13 +575,12 @@ static bool kinetis_mdm_cmd_erase_mass(target *t, int argc, const char **argv)
|
||||
ADIv5_AP_t *ap = t->priv;
|
||||
|
||||
/* 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);
|
||||
|
||||
uint32_t status, control;
|
||||
status = adiv5_ap_read(ap, MDM_STATUS);
|
||||
control = adiv5_ap_read(ap, MDM_CONTROL);
|
||||
tc_printf(t, "Requesting mass erase (status = 0x%"PRIx32")\n", status);
|
||||
uint32_t status = adiv5_ap_read(ap, MDM_STATUS);
|
||||
uint32_t 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 */
|
||||
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,
|
||||
target_addr dest, const void *src, size_t len);
|
||||
|
||||
/* Flash Program ad Erase Controller Register Map */
|
||||
/* Flash Program and Erase Controller Register Map */
|
||||
#define FPEC_BASE 0x40023C00
|
||||
#define FLASH_ACR (FPEC_BASE+0x00)
|
||||
#define FLASH_KEYR (FPEC_BASE+0x04)
|
||||
@ -503,11 +503,8 @@ static bool stm32f4_cmd_erase_mass(target *t, int argc, const char **argv)
|
||||
tc_printf(t, "\n");
|
||||
|
||||
/* Check for error */
|
||||
uint32_t sr = target_mem_read32(t, FLASH_SR);
|
||||
if ((sr & SR_ERROR_MASK) || !(sr & SR_EOP))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
const uint32_t result = target_mem_read32(t, FLASH_SR);
|
||||
return !(result & SR_ERROR_MASK);
|
||||
}
|
||||
|
||||
/* Dev | DOC |Rev|ID |OPTCR |OPTCR |OPTCR1 |OPTCR1 | OPTCR2
|
||||
|
@ -271,6 +271,11 @@ int target_flash_write(target *t,
|
||||
dest += tmplen;
|
||||
src += 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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user