rtt
This commit is contained in:
parent
738ac96e57
commit
0a0f5a9dd7
|
@ -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"
|
||||
|
|
|
@ -95,6 +95,15 @@ VPATH += platforms/common
|
|||
CFLAGS += -Iplatforms/common
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_RTT), 1)
|
||||
CFLAGS += -DENABLE_RTT
|
||||
SRC += rtt.c rtt_if.c
|
||||
endif
|
||||
|
||||
ifdef RTT_IDENT
|
||||
CFLAGS += -DRTT_IDENT=$(RTT_IDENT)
|
||||
endif
|
||||
|
||||
OBJ = $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SRC)))
|
||||
|
||||
$(TARGET): include/version.h $(OBJ)
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
#include "version.h"
|
||||
#include "serialno.h"
|
||||
|
||||
#ifdef ENABLE_RTT
|
||||
#include "rtt.h"
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_TRACESWO
|
||||
# include "traceswo.h"
|
||||
#endif
|
||||
|
@ -56,6 +60,9 @@ static bool cmd_target_power(target *t, int argc, const char **argv);
|
|||
static bool cmd_traceswo(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
static bool cmd_heapinfo(target *t, int argc, const char **argv);
|
||||
#ifdef ENABLE_RTT
|
||||
static bool cmd_rtt(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
#if defined(PLATFORM_HAS_DEBUG) && (PC_HOSTED == 0)
|
||||
static bool cmd_debug_bmp(target *t, int argc, const char **argv);
|
||||
#endif
|
||||
|
@ -74,6 +81,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 ...)" },
|
||||
|
@ -410,6 +420,93 @@ static bool cmd_target_power(target *t, int argc, const char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_RTT
|
||||
const char* onoroffstr[2] = {"off", "on"};
|
||||
static const char* onoroff(bool bval) {
|
||||
return bval ? onoroffstr[1] : onoroffstr[0];
|
||||
}
|
||||
|
||||
static bool cmd_rtt(target *t, int argc, const char **argv)
|
||||
{
|
||||
(void)t;
|
||||
if ((argc == 1) || ((argc == 2) && !strncmp(argv[1], "enabled", strlen(argv[1])))) {
|
||||
rtt_enabled = true;
|
||||
rtt_found = false;
|
||||
}
|
||||
else if ((argc == 2) && !strncmp(argv[1], "disabled", strlen(argv[1]))) {
|
||||
rtt_enabled = false;
|
||||
rtt_found = false;
|
||||
}
|
||||
else if ((argc == 2) && !strncmp(argv[1], "status", strlen(argv[1]))) {
|
||||
gdb_outf("rtt: %s found: %s ident: ",
|
||||
onoroff(rtt_enabled), rtt_found ? "yes" : "no");
|
||||
if (rtt_ident[0] == '\0')
|
||||
gdb_out("off");
|
||||
else
|
||||
gdb_outf("\"%s\"", rtt_ident);
|
||||
gdb_outf(" halt: %s", onoroff(target_no_background_memory_access(t)));
|
||||
gdb_out(" channels: ");
|
||||
if (rtt_auto_channel) gdb_out("auto ");
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||
if (rtt_channel[i].is_enabled) gdb_outf("%d ", i);
|
||||
gdb_outf("\nmax poll ms: %u min poll ms: %u max errs: %u\n",
|
||||
rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs);
|
||||
}
|
||||
else if ((argc >= 2) && !strncmp(argv[1], "channel", strlen(argv[1]))) {
|
||||
/* mon rtt channel switches to auto rtt channel selection
|
||||
mon rtt channel number... selects channels given */
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++)
|
||||
rtt_channel[i].is_enabled = false;
|
||||
if (argc == 2) {
|
||||
rtt_auto_channel = true;
|
||||
} else {
|
||||
rtt_auto_channel = false;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
int chan = atoi(argv[i]);
|
||||
if ((chan >= 0) && (chan < MAX_RTT_CHAN))
|
||||
rtt_channel[chan].is_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((argc == 2) && !strncmp(argv[1], "ident", strlen(argv[1]))) {
|
||||
rtt_ident[0] = '\0';
|
||||
}
|
||||
else if ((argc == 2) && !strncmp(argv[1], "poll", strlen(argv[1])))
|
||||
gdb_outf("%u %u %u\n", rtt_max_poll_ms, rtt_min_poll_ms, rtt_max_poll_errs);
|
||||
else if ((argc == 2) && !strncmp(argv[1], "cblock", strlen(argv[1]))) {
|
||||
gdb_outf("cbaddr: 0x%x\n", rtt_cbaddr);
|
||||
gdb_out("ch ena cfg i/o buf@ size head@ tail@ flg\n");
|
||||
for (uint32_t i = 0; i < MAX_RTT_CHAN; i++) {
|
||||
gdb_outf("%2d %c %c %s 0x%08x %5d 0x%08x 0x%08x %d\n",
|
||||
i, rtt_channel[i].is_enabled ? 'y' : 'n', rtt_channel[i].is_configured ? 'y' : 'n',
|
||||
rtt_channel[i].is_output ? "out" : "in ", rtt_channel[i].buf_addr, rtt_channel[i].buf_size,
|
||||
rtt_channel[i].head_addr, rtt_channel[i].tail_addr, rtt_channel[i].flag);
|
||||
}
|
||||
}
|
||||
else if ((argc == 3) && !strncmp(argv[1], "ident", strlen(argv[1]))) {
|
||||
strncpy(rtt_ident, argv[2], sizeof(rtt_ident));
|
||||
rtt_ident[sizeof(rtt_ident)-1] = '\0';
|
||||
for (uint32_t i = 0; i < sizeof(rtt_ident); i++)
|
||||
if (rtt_ident[i] == '_') rtt_ident[i] = ' ';
|
||||
}
|
||||
else if ((argc == 5) && !strncmp(argv[1], "poll", strlen(argv[1]))) {
|
||||
/* set polling params */
|
||||
int32_t new_max_poll_ms = atoi(argv[2]);
|
||||
int32_t new_min_poll_ms = atoi(argv[3]);
|
||||
int32_t new_max_poll_errs = atoi(argv[4]);
|
||||
if ((new_max_poll_ms >= 0) && (new_min_poll_ms >= 0) && (new_max_poll_errs >= 0)
|
||||
&& (new_max_poll_ms >= new_min_poll_ms)) {
|
||||
rtt_max_poll_ms = new_max_poll_ms;
|
||||
rtt_min_poll_ms = new_min_poll_ms;
|
||||
rtt_max_poll_errs = new_max_poll_errs;
|
||||
}
|
||||
else gdb_out("how?\n");
|
||||
}
|
||||
else gdb_out("what?\n");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_HAS_TRACESWO
|
||||
static bool cmd_traceswo(target *t, int argc, const char **argv)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
@ -193,6 +196,9 @@ int gdb_main_loop(struct target_controller *tc, bool in_syscall)
|
|||
if((c == '\x03') || (c == '\x04')) {
|
||||
target_halt_request(cur_target);
|
||||
}
|
||||
#ifdef ENABLE_RTT
|
||||
if (rtt_enabled) poll_rtt(cur_target);
|
||||
#endif
|
||||
}
|
||||
SET_RUN_STATE(0);
|
||||
|
||||
|
@ -518,6 +524,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);
|
||||
|
|
|
@ -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
|
|
@ -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 <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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
|
@ -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…
Reference in New Issue