rtt
# Conflicts: # src/gdb_main.c
This commit is contained in:
parent
f67a4e421d
commit
a51d90ba2d
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
|
# Black Magic Probe
|
||||||
# there are two connections, one for GDB and one for UART debugging
|
# there are two connections, one for GDB and one for UART debugging
|
||||||
# copy this to /etc/udev/rules.d/99-blackmagic.rules
|
# copy this to /etc/udev/rules.d/99-blackmagic.rules
|
||||||
|
# and run /usr/sbin/udevadm control --reload-rules
|
||||||
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb"
|
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb"
|
||||||
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
|
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
|
||||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666"
|
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1d50", ATTR{idProduct}=="6017", MODE="0666"
|
||||||
|
@ -34,6 +34,10 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "serialno.h"
|
#include "serialno.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
#include "rtt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
# include "traceswo.h"
|
# include "traceswo.h"
|
||||||
#endif
|
#endif
|
||||||
@ -59,6 +63,9 @@ static bool cmd_target_power(target *t, int argc, const char **argv);
|
|||||||
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
||||||
#endif
|
#endif
|
||||||
static bool cmd_heapinfo(target *t, int argc, const char **argv);
|
static bool cmd_heapinfo(target *t, int argc, const char **argv);
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
static bool cmd_rtt(target *t, int argc, const char **argv);
|
||||||
|
#endif
|
||||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
||||||
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
||||||
#endif
|
#endif
|
||||||
@ -87,6 +94,9 @@ const struct command_s cmd_list[] = {
|
|||||||
#ifdef PLATFORM_HAS_POWER_SWITCH
|
#ifdef PLATFORM_HAS_POWER_SWITCH
|
||||||
{"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
|
{"tpwr", (cmd_handler)cmd_target_power, "Supplies power to the target: (enable|disable)"},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
{"rtt", (cmd_handler)cmd_rtt, "enable|disable|status|channel 0..15|ident (str)|cblock|poll maxms minms maxerr" },
|
||||||
|
#endif
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2
|
||||||
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" },
|
||||||
@ -430,6 +440,93 @@ static bool cmd_target_power(target *t, int argc, const char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
const char* onoroffstr[2] = {"off", "on"};
|
||||||
|
static const char* onoroff(bool bval) {
|
||||||
|
return bval ? onoroffstr[1] : onoroffstr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cmd_rtt(target *t, int argc, const char **argv)
|
||||||
|
{
|
||||||
|
(void)t;
|
||||||
|
if ((argc == 1) || ((argc == 2) && !strncmp(argv[1], "enabled", strlen(argv[1])))) {
|
||||||
|
rtt_enabled = true;
|
||||||
|
rtt_found = false;
|
||||||
|
}
|
||||||
|
else if ((argc == 2) && !strncmp(argv[1], "disabled", strlen(argv[1]))) {
|
||||||
|
rtt_enabled = false;
|
||||||
|
rtt_found = false;
|
||||||
|
}
|
||||||
|
else if ((argc == 2) && !strncmp(argv[1], "status", strlen(argv[1]))) {
|
||||||
|
gdb_outf("rtt: %s found: %s ident: ",
|
||||||
|
onoroff(rtt_enabled), rtt_found ? "yes" : "no");
|
||||||
|
if (rtt_ident[0] == '\0')
|
||||||
|
gdb_out("off");
|
||||||
|
else
|
||||||
|
gdb_outf("\"%s\"", rtt_ident);
|
||||||
|
gdb_outf(" halt: %s", onoroff(target_no_background_memory_access(t)));
|
||||||
|
gdb_out(" channels: ");
|
||||||
|
if (rtt_auto_channel) gdb_out("auto ");
|
||||||
|
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||||
|
if (rtt_channel[i].is_enabled) gdb_outf("%d ", i);
|
||||||
|
gdb_outf("\nmax poll ms: %u min poll ms: %u max errs: %u\n",
|
||||||
|
rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs);
|
||||||
|
}
|
||||||
|
else if ((argc >= 2) && !strncmp(argv[1], "channel", strlen(argv[1]))) {
|
||||||
|
/* mon rtt channel switches to auto rtt channel selection
|
||||||
|
mon rtt channel number... selects channels given */
|
||||||
|
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||||
|
rtt_channel[i].is_enabled = false;
|
||||||
|
if (argc == 2) {
|
||||||
|
rtt_auto_channel = true;
|
||||||
|
} else {
|
||||||
|
rtt_auto_channel = false;
|
||||||
|
for (int i = 2; i < argc; i++) {
|
||||||
|
int chan = atoi(argv[i]);
|
||||||
|
if ((chan >= 0) && (chan < MAX_RTT_CHAN))
|
||||||
|
rtt_channel[chan].is_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((argc == 2) && !strncmp(argv[1], "ident", strlen(argv[1]))) {
|
||||||
|
rtt_ident[0] = '\0';
|
||||||
|
}
|
||||||
|
else if ((argc == 2) && !strncmp(argv[1], "poll", strlen(argv[1])))
|
||||||
|
gdb_outf("%u %u %u\n", rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs);
|
||||||
|
else if ((argc == 2) && !strncmp(argv[1], "cblock", strlen(argv[1]))) {
|
||||||
|
gdb_outf("cbaddr: 0x%x\n", rtt_cbaddr);
|
||||||
|
gdb_out("ch ena cfg i/o buf@ size head@ tail@ flg\n");
|
||||||
|
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
||||||
|
gdb_outf("%2d %c %c %s 0x%08x %5d 0x%08x 0x%08x %d\n",
|
||||||
|
i, rtt_channel[i].is_enabled ? 'y' : 'n', rtt_channel[i].is_configured ? 'y' : 'n',
|
||||||
|
rtt_channel[i].is_output ? "out" : "in ", rtt_channel[i].buf_addr, rtt_channel[i].buf_size,
|
||||||
|
rtt_channel[i].head_addr, rtt_channel[i].tail_addr, rtt_channel[i].flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((argc == 3) && !strncmp(argv[1], "ident", strlen(argv[1]))) {
|
||||||
|
strncpy(rtt_ident, argv[2], sizeof(rtt_ident));
|
||||||
|
rtt_ident[sizeof(rtt_ident)-1] = '\0';
|
||||||
|
for (uint32_t i = 0; i < sizeof(rtt_ident); i++)
|
||||||
|
if (rtt_ident[i] == '_') rtt_ident[i] = ' ';
|
||||||
|
}
|
||||||
|
else if ((argc == 5) && !strncmp(argv[1], "poll", strlen(argv[1]))) {
|
||||||
|
/* set polling params */
|
||||||
|
int32_t new_max_poll_ms = atoi(argv[2]);
|
||||||
|
int32_t new_min_poll_ms = atoi(argv[3]);
|
||||||
|
int32_t new_max_poll_errs = atoi(argv[4]);
|
||||||
|
if ((new_max_poll_ms >= 0) && (new_min_poll_ms >= 0) && (new_max_poll_errs >= 0)
|
||||||
|
&& (new_max_poll_ms >= new_min_poll_ms)) {
|
||||||
|
rtt_max_poll_ms = new_max_poll_ms;
|
||||||
|
rtt_min_poll_ms = new_min_poll_ms;
|
||||||
|
rtt_max_poll_errs = new_max_poll_errs;
|
||||||
|
}
|
||||||
|
else gdb_out("how?\n");
|
||||||
|
}
|
||||||
|
else gdb_out("what?\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef PLATFORM_HAS_TRACESWO
|
#ifdef PLATFORM_HAS_TRACESWO
|
||||||
static bool cmd_traceswo(target *t, int argc, const char **argv)
|
static bool cmd_traceswo(target *t, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,9 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
#include "rtt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
enum gdb_signal {
|
enum gdb_signal {
|
||||||
GDB_SIGINT = 2,
|
GDB_SIGINT = 2,
|
||||||
@ -208,9 +211,13 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||||||
/* Wait for target halt */
|
/* Wait for target halt */
|
||||||
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
while(!(reason = target_halt_poll(cur_target, &watch))) {
|
||||||
char c = (char)gdb_if_getchar_to(0);
|
char c = (char)gdb_if_getchar_to(0);
|
||||||
if(c == '\x03' || c == '\x04')
|
if(c == '\x03' || c == '\x04') {
|
||||||
target_halt_request(cur_target);
|
target_halt_request(cur_target);
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
if (rtt_enabled) poll_rtt(cur_target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
SET_RUN_STATE(0);
|
SET_RUN_STATE(0);
|
||||||
|
|
||||||
/* Translate reason to GDB signal */
|
/* Translate reason to GDB signal */
|
||||||
@ -580,6 +587,10 @@ static void handle_v_packet(char *packet, const size_t plen)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
/* force searching rtt control block */
|
||||||
|
rtt_found = false;
|
||||||
|
#endif
|
||||||
/* Run target program. For us (embedded) this means reset. */
|
/* Run target program. For us (embedded) this means reset. */
|
||||||
if (cur_target) {
|
if (cur_target) {
|
||||||
target_set_cmdline(cur_target, cmdline);
|
target_set_cmdline(cur_target, cmdline);
|
||||||
|
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
|
@ -30,6 +30,10 @@
|
|||||||
#include "gdb_if.h"
|
#include "gdb_if.h"
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
#include "rtt_if.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "bmp_remote.h"
|
#include "bmp_remote.h"
|
||||||
#include "bmp_hosted.h"
|
#include "bmp_hosted.h"
|
||||||
#include "stlinkv2.h"
|
#include "stlinkv2.h"
|
||||||
@ -58,6 +62,9 @@ static void exit_function(void)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
rtt_if_exit();
|
||||||
|
#endif
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +117,9 @@ void platform_init(int argc, char **argv)
|
|||||||
exit(cl_execute(&cl_opts));
|
exit(cl_execute(&cl_opts));
|
||||||
else {
|
else {
|
||||||
gdb_if_init();
|
gdb_if_init();
|
||||||
|
#ifdef ENABLE_RTT
|
||||||
|
rtt_if_init();
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
136
src/platforms/stm32/rtt_if.c
Normal file
136
src/platforms/stm32/rtt_if.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
uint32_t bytes_free;
|
||||||
|
if (recv_tail <= recv_head) bytes_free = sizeof(recv_buf) - recv_head + recv_tail - 1;
|
||||||
|
else bytes_free = recv_tail - recv_head - 1;
|
||||||
|
return bytes_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
bool nak = recv_bytes_free() < 2 * CDCACM_PACKET_SIZE;
|
||||||
|
return nak;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
// not truncated
|
@ -259,6 +259,7 @@ static void usbuart_change_dma_tx_buf(void)
|
|||||||
buf_tx_act_idx ^= 1;
|
buf_tx_act_idx ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ENABLE_RTT
|
||||||
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
void usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)ep;
|
(void)ep;
|
||||||
@ -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)
|
if (TX_BUF_SIZE - buf_tx_act_sz >= CDCACM_PACKET_SIZE)
|
||||||
usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0);
|
usbd_ep_nak_set(dev, CDCACM_UART_ENDPOINT, 0);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USBUART_DEBUG
|
#ifdef USBUART_DEBUG
|
||||||
int usbuart_debug_write(const char *buf, size_t len)
|
int usbuart_debug_write(const char *buf, size_t len)
|
||||||
|
@ -99,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 usbuart_usb_out_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
(void)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++)
|
for(int i = 0; i < len; i++)
|
||||||
uart_send_blocking(USBUART, buf[i]);
|
uart_send_blocking(USBUART, buf[i]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
void usbuart_usb_in_cb(usbd_device *dev, uint8_t ep)
|
||||||
{
|
{
|
||||||
|
463
src/rtt.c
Normal file
463
src/rtt.c
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
uint32_t ram_start = r->start;
|
||||||
|
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%x\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%x\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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not truncated
|
Loading…
x
Reference in New Issue
Block a user