diff --git a/UsingRTT.md b/UsingRTT.md new file mode 100644 index 00000000..578b86ee --- /dev/null +++ b/UsingRTT.md @@ -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) diff --git a/driver/99-blackmagic.rules b/driver/99-blackmagic.rules index f8e749b3..e8bfecc4 100644 --- a/driver/99-blackmagic.rules +++ b/driver/99-blackmagic.rules @@ -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" diff --git a/src/Makefile b/src/Makefile index a35387e1..1eec2895 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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) diff --git a/src/command.c b/src/command.c index 99096345..2f7df860 100644 --- a/src/command.c +++ b/src/command.c @@ -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) { diff --git a/src/gdb_main.c b/src/gdb_main.c index 7e84d24c..56c49b04 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -35,6 +35,9 @@ #include "command.h" #include "crc32.h" #include "morse.h" +#ifdef ENABLE_RTT +#include "rtt.h" +#endif enum gdb_signal { GDB_SIGINT = 2, @@ -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); diff --git a/src/include/rtt.h b/src/include/rtt.h new file mode 100644 index 00000000..7d8f04ad --- /dev/null +++ b/src/include/rtt.h @@ -0,0 +1,34 @@ +#ifndef RTT_H +#define RTT_H +#include + +#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 diff --git a/src/include/rtt_if.h b/src/include/rtt_if.h new file mode 100644 index 00000000..711be5ea --- /dev/null +++ b/src/include/rtt_if.h @@ -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 diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index 84a76c66..ff6865a3 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -30,6 +30,10 @@ #include "gdb_if.h" #include +#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; } } diff --git a/src/platforms/hosted/rtt_if.c b/src/platforms/hosted/rtt_if.c new file mode 100644 index 00000000..b303a583 --- /dev/null +++ b/src/platforms/hosted/rtt_if.c @@ -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 +#include +#include +#include + +/* maybe rewrite this as tcp server */ + +#ifndef WIN32 +#include + +/* 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 diff --git a/src/platforms/stm32/rtt_if.c b/src/platforms/stm32/rtt_if.c new file mode 100644 index 00000000..27130a2a --- /dev/null +++ b/src/platforms/stm32/rtt_if.c @@ -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 +#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 diff --git a/src/platforms/stm32/usbuart.c b/src/platforms/stm32/usbuart.c index db336f5f..e5f6ce54 100644 --- a/src/platforms/stm32/usbuart.c +++ b/src/platforms/stm32/usbuart.c @@ -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) diff --git a/src/platforms/tm4c/usbuart.c b/src/platforms/tm4c/usbuart.c index da821986..23946847 100644 --- a/src/platforms/tm4c/usbuart.c +++ b/src/platforms/tm4c/usbuart.c @@ -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) { diff --git a/src/rtt.c b/src/rtt.c new file mode 100644 index 00000000..82336a4f --- /dev/null +++ b/src/rtt.c @@ -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