Koen De Vleeschauwer
3 years ago
committed by
Rachel Mant
13 changed files with 1198 additions and 1 deletions
@ -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) |
@ -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 |
@ -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
|
@ -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