From 39f3a9d1484e8febef76da4ff1ef76de11446826 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sun, 27 Jul 2025 22:19:00 +0200 Subject: [PATCH 1/3] [ext] Add SEGGER RTT module --- .gitmodules | 3 + ext/segger/SEGGER_RTT_Conf.h.in | 73 ++++++++++++++++++++++ ext/segger/module.lb | 51 +++++++++++++++ ext/segger/module.md | 107 ++++++++++++++++++++++++++++++++ ext/segger/rtt | 1 + ext/segger/rtt_modm_port.cpp.in | 93 +++++++++++++++++++++++++++ 6 files changed, 328 insertions(+) create mode 100644 ext/segger/SEGGER_RTT_Conf.h.in create mode 100644 ext/segger/module.lb create mode 100644 ext/segger/module.md create mode 160000 ext/segger/rtt create mode 100644 ext/segger/rtt_modm_port.cpp.in diff --git a/.gitmodules b/.gitmodules index 72de297697..e207c1807f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -49,3 +49,6 @@ [submodule "ext/nlohmann/json"] path = ext/nlohmann/json url = https://github.com/modm-ext/json-partial.git +[submodule "ext/segger/rtt"] + path = ext/segger/rtt + url = git@github.com:modm-ext/segger-rtt.git diff --git a/ext/segger/SEGGER_RTT_Conf.h.in b/ext/segger/SEGGER_RTT_Conf.h.in new file mode 100644 index 0000000000..16984f5d94 --- /dev/null +++ b/ext/segger/SEGGER_RTT_Conf.h.in @@ -0,0 +1,73 @@ +/********************************************************************* +* (c) 1995 - 2020 SEGGER Microcontroller GmbH * +* * +* All rights reserved. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* condition is met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this condition and the following disclaimer. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +*********************************************************************/ + +#ifndef SEGGER_RTT_CONF_H +#define SEGGER_RTT_CONF_H + +/// @ingroup modm_rtt +/// @{ + +#define SEGGER_RTT_MAX_NUM_UP_BUFFERS ({{ options["buffer.tx"] | length }}) +#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS ({{ options["buffer.rx"] | length }}) + +#define BUFFER_SIZE_UP ({{ options["buffer.tx"][0] }}) +#define BUFFER_SIZE_DOWN ({{ options["buffer.rx"][0] }}) + + +#ifndef SEGGER_RTT_ASM + +#include + +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +# define SEGGER_RTT_MAX_INTERRUPT_PRIORITY ((1ul << __NVIC_PRIO_BITS) - 1ul) +#endif + +// Non-standard useful functions +unsigned SEGGER_RTT_GetBytesInDownBuffer(unsigned BufferIndex); + +// RTT lock configuration for GCC +#if (defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_8M_BASE__)) + + #define SEGGER_RTT_LOCK() { const unsigned int _SEGGER_RTT__LockState = __get_PRIMASK(); \ + __disable_irq(); + #define SEGGER_RTT_UNLOCK() __set_PRIMASK(_SEGGER_RTT__LockState); } + +#elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)) + + #define SEGGER_RTT_LOCK() { const unsigned int _SEGGER_RTT__LockState = __get_BASEPRI(); \ + __set_BASEPRI_MAX(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); + #define SEGGER_RTT_UNLOCK() __set_BASEPRI(_SEGGER_RTT__LockState); } + #define RTT_USE_ASM 1 + +#endif + +#endif // SEGGER_RTT_ASM + +/// @} + +#endif // SEGGER_RTT_CONF_H diff --git a/ext/segger/module.lb b/ext/segger/module.lb new file mode 100644 index 0000000000..ea1994b4d7 --- /dev/null +++ b/ext/segger/module.lb @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2025, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":rtt" + module.description = FileReader("module.md") + + +def prepare(module, options): + module.add_list_option( + NumericOption(name="buffer.tx", description="Transmit buffer sizes", + minimum=0), default=512) + module.add_list_option( + NumericOption(name="buffer.rx", description="Receive buffer sizes", + minimum=0), default=0) + + return options[":target"].has_driver("core:cortex-m*") + + +def validate(env): + if len(env["buffer.tx"]) != len(env["buffer.rx"]): + raise ValidateException("There must be the same number of TX buffers as RX buffers!") + if not env["buffer.tx"]: + raise ValidateException("There must be at least one TX buffer or RX buffer!") + + +def build(env): + env.collect(":build:path.include", "modm/ext/rtt") + env.outbasepath = "modm/ext/rtt" + env.substitutions = { + "buffer_tx": env["buffer.tx"], + "buffer_rx": env["buffer.rx"], + "with_printf": env.has_module(":printf") + } + + env.template("SEGGER_RTT_Conf.h.in") + env.template("rtt_modm_port.cpp.in") + + env.copy("rtt/RTT/SEGGER_RTT.h", dest="SEGGER_RTT.h") + env.copy("rtt/RTT/SEGGER_RTT.c", dest="SEGGER_RTT.c") + if "m0" not in env[":target"].get_driver("core")["type"]: + env.copy("rtt/RTT/SEGGER_RTT_ASM_ARMv7M.S", dest="SEGGER_RTT_ASM.S") diff --git a/ext/segger/module.md b/ext/segger/module.md new file mode 100644 index 0000000000..8d8b6f62a5 --- /dev/null +++ b/ext/segger/module.md @@ -0,0 +1,107 @@ +# Real Time Transfer (RTT) + +This module implements the [RTT protocol](https://www.segger.com/jlink-rtt.html) +compatible with JLink and OpenOCD. RTT works by placing multiple ring-buffers in +RAM which the debugger read and writes using background memory accesses via +SWD. It therefore works on any Cortex-M device without extra pins except for +the SWDCLK and SWDIO. + +You can define the number of channels and their buffer size by setting the +`buffer.tx` and `buffer.rx` set options. Note that you can define *multiple* +buffer sizes indexed by channel. Here is an example of three channels: + +```xml + + + + + +``` + +You can set the buffer size to `0` if you don't want to use this channel +direction. This won't allocate a buffer and save a little RAM. + +The [API reference is available here](https://kb.segger.com/RTT#API_functions). + + +## Accessing Data + +[OpenOCD has built-in support for RTT][rtt] and modm generates a config that +opens each RTT channel as a TCP port starting at 9090 (ie. 9090=channel 0, +9091=channel 1, etc). + +```sh +openocd -f modm/openocd.cfg -c modm_rtt +``` + +You can also call this from inside GDB via the `monitor` command: + +``` +(gdb) monitor modm_rtt +rtt: Searching for control block 'SEGGER RTT' +rtt: Control block found at 0x20001024 +Listening on port 9090 for rtt connections +(gdb) continue& +``` + +You can then use netcat to connect to one or multiple streams: + +```sh +stty -icanon -echo +nc localhost 9090 +stty sane +``` + +Note that this connection does not halt the target, you should therefore be able +to use this at any point during program execution. + +A simple RTT client is integrated into the build system generator, however, +it can only connect to one stream at a time (disconnect with Ctrl+C). + +``` + $ scons log-rtt +╭───OpenOCD───> Real Time Transfer +╰─────RTT────── stm32f103rbt +Info : rtt: Searching for control block 'SEGGER RTT' +Info : rtt: Control block found at 0x20000008 +Listening on port 9090 for rtt connections +loop 51 +loop 52 +loop 53 +^C + + $ make log-rtt channel=0 +Info : rtt: Searching for control block 'SEGGER RTT' +Info : rtt: Control block found at 0x20000008 +Listening on port 9090 for rtt connections +loop 58 +loop 60 +loop 61 +``` + +If you want to use this as a proper communication channel with a custom protocol +you should implement the OpenOCD config yourself (with different ports). + +You can also use JLink to access the RTT data, which may be significantly faster +than OpenOCD if the debug probe has hardware support for the protocol. Note the +port JLink uses is 19021 (channel 0) and 19022 (channel 1), etc. + +``` + $ scons log-rtt-jlink +╭────JLink────> Real Time Transfer +╰─────RTT────── stm32l476rgt6 + +SEGGER J-Link - Real time terminal output +RTT Demo on Nucleo-64 +loop: 0 +loop: 1 +loop: 2 +loop: 3 +loop: 4 +loop: 5 +loop: 6 +loop: 7 +``` + + +[rtt]: http://openocd.org/doc/html/General-Commands.html#Real-Time-Transfer-_0028RTT_0029 diff --git a/ext/segger/rtt b/ext/segger/rtt new file mode 160000 index 0000000000..267fd43b4f --- /dev/null +++ b/ext/segger/rtt @@ -0,0 +1 @@ +Subproject commit 267fd43b4f5b173c193f8560fcc49a9189028fb2 diff --git a/ext/segger/rtt_modm_port.cpp.in b/ext/segger/rtt_modm_port.cpp.in new file mode 100644 index 0000000000..b8a3c14dac --- /dev/null +++ b/ext/segger/rtt_modm_port.cpp.in @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +%# +%% for index in range(buffer_tx[1:] | length) +%% if buffer_tx[index+1] +static uint8_t tx_data_buffer_{{loop.index}}[{{buffer_tx[index+1]}}]; +%% endif +%% if buffer_rx[index+1] +static uint8_t rx_data_buffer_{{loop.index}}[{{buffer_rx[index+1]}}]; +%% endif +%% endfor +%# +__attribute__((constructor(1000))) +static void modm_rtt_init() +{ + SEGGER_RTT_Init(); +%% for index in range(buffer_tx[1:] | length) +%% if buffer_tx[index+1] + SEGGER_RTT_ConfigUpBuffer({{ loop.index }}, "tx{{loop.index}}", tx_data_buffer_{{loop.index}}, + sizeof(tx_data_buffer_{{loop.index}}), SEGGER_RTT_MODE_NO_BLOCK_TRIM); +%% endif +%% if buffer_rx[index+1] + SEGGER_RTT_ConfigDownBuffer({{ loop.index }}, "rx{{loop.index}}", rx_data_buffer_{{loop.index}}, + sizeof(rx_data_buffer_{{loop.index}}), SEGGER_RTT_MODE_NO_BLOCK_TRIM); +%% endif +%% endfor +} +%# +/********************************************************************* +* +* SEGGER_RTT_GetBytesInBuffer() +* +* Function description +* Returns the number of bytes currently used in the down buffer. +* +* Parameters +* BufferIndex Index of the down buffer. +* +* Return value +* Number of bytes that are used in the buffer. +*/ +unsigned SEGGER_RTT_GetBytesInDownBuffer(unsigned BufferIndex) { + unsigned RdOff; + unsigned WrOff; + unsigned r; + volatile SEGGER_RTT_CB* pRTTCB; + // + // Avoid warnings regarding volatile access order. It's not a problem + // in this case, but dampen compiler enthusiasm. + // + // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + pRTTCB = (volatile SEGGER_RTT_CB*)((uintptr_t)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); + RdOff = pRTTCB->aDown[BufferIndex].RdOff; + WrOff = pRTTCB->aDown[BufferIndex].WrOff; + if (RdOff <= WrOff) { + r = WrOff - RdOff; + } else { + r = pRTTCB->aDown[BufferIndex].SizeOfBuffer - (WrOff - RdOff); + } + return r; +} +%# +%% if with_printf +#include + +static void output_gadget(char c, void* BufferIndex) +{ + SEGGER_RTT_PutCharSkipNoLock((unsigned) BufferIndex, c); +} + +int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) +{ + va_list ParamList; + va_start(ParamList, sFormat); + const int retval = vfctprintf(output_gadget, (void*) BufferIndex, sFormat, ParamList); + va_end(ParamList); + return retval; +} + +int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) +{ + return vfctprintf(output_gadget, (void*) BufferIndex, sFormat, *pParamList); +} +%% endif From 42109c7a6460e7e27063d1fc03d2a64772674c1b Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 9 Aug 2025 19:36:54 +0200 Subject: [PATCH 2/3] [rtt] Refactor to use the SEGGER RTT implementation --- README.md | 2 + examples/blue_pill_f103/rtt/main.cpp | 3 +- examples/blue_pill_f103/rtt/project.xml | 2 +- examples/blue_pill_f103/servo_pwm/main.cpp | 3 +- examples/blue_pill_f103/servo_pwm/project.xml | 2 +- .../nucleo_f429zi/spi_flash_fatfs/project.xml | 1 - examples/nucleo_l476rg/rtt/main.cpp | 3 +- examples/nucleo_l476rg/rtt/project.xml | 2 +- examples/stm32f3_discovery/rtt/main.cpp | 11 +- examples/stm32f3_discovery/rtt/project.xml | 3 +- src/modm/platform/uart/rtt/module.lb | 27 ++-- src/modm/platform/uart/rtt/module.md | 120 ++------------ src/modm/platform/uart/rtt/rtt.cpp.in | 153 ------------------ src/modm/platform/uart/rtt/rtt.hpp | 130 +++++++++++++++ src/modm/platform/uart/rtt/rtt.hpp.in | 82 ---------- tools/build_script_generator/cmake/module.md | 8 +- tools/build_script_generator/make/module.md | 8 +- tools/build_script_generator/module.lb | 4 +- tools/build_script_generator/openocd.cfg.in | 10 +- tools/build_script_generator/scons/module.md | 8 +- tools/modm_tools/jlink.py | 11 +- tools/modm_tools/openocd.py | 13 +- 22 files changed, 192 insertions(+), 414 deletions(-) delete mode 100644 src/modm/platform/uart/rtt/rtt.cpp.in create mode 100644 src/modm/platform/uart/rtt/rtt.hpp delete mode 100644 src/modm/platform/uart/rtt/rtt.hpp.in diff --git a/README.md b/README.md index 685c5a41e0..a9c292b79e 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git - [printf][]: Small printf implementation. - [Nanopb][]: Embedded Protocol Buffers. - [LVGL][]: Embedded Graphics Library. + - [RTT][]: Segger Real-Time Transport. ## Microcontrollers @@ -972,4 +973,5 @@ and [many more contributors][contributors]. [printf]: https://github.com/eyalroz/printf [Nanopb]: https://github.com/nanopb/nanopb [LVGL]: https://lvgl.io +[RTT]: https://kb.segger.com/RTT diff --git a/examples/blue_pill_f103/rtt/main.cpp b/examples/blue_pill_f103/rtt/main.cpp index dccf0da86e..c0050de225 100644 --- a/examples/blue_pill_f103/rtt/main.cpp +++ b/examples/blue_pill_f103/rtt/main.cpp @@ -16,8 +16,7 @@ using namespace Board; -Rtt rtt(0); -modm::IODeviceObjectWrapper< Rtt, modm::IOBuffer::DiscardIfFull > rtt_device(rtt); +modm::IODeviceWrapper< Rtt<0>, modm::IOBuffer::DiscardIfFull > rtt_device; // Set all four logger streams to use RTT modm::log::Logger modm::log::debug(rtt_device); modm::log::Logger modm::log::info(rtt_device); diff --git a/examples/blue_pill_f103/rtt/project.xml b/examples/blue_pill_f103/rtt/project.xml index 14f1111ebd..f11b26eda2 100644 --- a/examples/blue_pill_f103/rtt/project.xml +++ b/examples/blue_pill_f103/rtt/project.xml @@ -2,7 +2,7 @@ modm:blue-pill-f103 - + modm:platform:rtt diff --git a/examples/blue_pill_f103/servo_pwm/main.cpp b/examples/blue_pill_f103/servo_pwm/main.cpp index 921a8cd7db..912ceeea76 100644 --- a/examples/blue_pill_f103/servo_pwm/main.cpp +++ b/examples/blue_pill_f103/servo_pwm/main.cpp @@ -14,8 +14,7 @@ using namespace Board; -Rtt rtt(0); -modm::IODeviceObjectWrapper< Rtt, modm::IOBuffer::DiscardIfFull > rtt_device(rtt); +modm::IODeviceWrapper< Rtt<0>, modm::IOBuffer::DiscardIfFull > rtt_device; // Set all four logger streams to use RTT modm::log::Logger modm::log::debug(rtt_device); modm::log::Logger modm::log::info(rtt_device); diff --git a/examples/blue_pill_f103/servo_pwm/project.xml b/examples/blue_pill_f103/servo_pwm/project.xml index 8c62ce0783..fdb75e8a3e 100644 --- a/examples/blue_pill_f103/servo_pwm/project.xml +++ b/examples/blue_pill_f103/servo_pwm/project.xml @@ -2,7 +2,7 @@ modm:blue-pill-f103 - + modm:build:scons diff --git a/examples/nucleo_f429zi/spi_flash_fatfs/project.xml b/examples/nucleo_f429zi/spi_flash_fatfs/project.xml index 09f0b93edb..f9d5b32888 100644 --- a/examples/nucleo_f429zi/spi_flash_fatfs/project.xml +++ b/examples/nucleo_f429zi/spi_flash_fatfs/project.xml @@ -2,7 +2,6 @@ modm:nucleo-f429zi - modm:build:scons diff --git a/examples/nucleo_l476rg/rtt/main.cpp b/examples/nucleo_l476rg/rtt/main.cpp index c8cb45f189..a90d5d8b68 100644 --- a/examples/nucleo_l476rg/rtt/main.cpp +++ b/examples/nucleo_l476rg/rtt/main.cpp @@ -15,8 +15,7 @@ using namespace Board; -Rtt rtt(0); -modm::IODeviceObjectWrapper< Rtt, modm::IOBuffer::DiscardIfFull > rtt_device(rtt); +modm::IODeviceWrapper< Rtt<0>, modm::IOBuffer::DiscardIfFull > rtt_device; modm::IOStream rtt_stream(rtt_device); #undef MODM_LOG_LEVEL diff --git a/examples/nucleo_l476rg/rtt/project.xml b/examples/nucleo_l476rg/rtt/project.xml index aa853f1a1c..38fd4ff1a4 100644 --- a/examples/nucleo_l476rg/rtt/project.xml +++ b/examples/nucleo_l476rg/rtt/project.xml @@ -2,7 +2,7 @@ modm:nucleo-l476rg - + modm:platform:rtt diff --git a/examples/stm32f3_discovery/rtt/main.cpp b/examples/stm32f3_discovery/rtt/main.cpp index 2a4a6d4368..b68994e075 100644 --- a/examples/stm32f3_discovery/rtt/main.cpp +++ b/examples/stm32f3_discovery/rtt/main.cpp @@ -15,8 +15,7 @@ using namespace Board; -Rtt rtt(0); -modm::IODeviceObjectWrapper< Rtt, modm::IOBuffer::DiscardIfFull > rtt_device(rtt); +modm::IODeviceWrapper< Rtt<0>, modm::IOBuffer::DiscardIfFull > rtt_device; // Set all four logger streams to use RTT modm::log::Logger modm::log::debug(rtt_device); modm::log::Logger modm::log::info(rtt_device); @@ -85,7 +84,13 @@ main() { LedNorth::toggle(); - MODM_LOG_INFO << "loop: " << counter++ << modm::endl; + MODM_LOG_INFO << "loop: " << counter << modm::endl; + counter++; + } + // loopback: read from channel 1, output on channel 2 + if (uint8_t value; SEGGER_RTT_ReadNoLock(1, &value, 1)) + { + SEGGER_RTT_WriteNoLock(2, &value, 1); } } diff --git a/examples/stm32f3_discovery/rtt/project.xml b/examples/stm32f3_discovery/rtt/project.xml index 1964b37101..258a9459ba 100644 --- a/examples/stm32f3_discovery/rtt/project.xml +++ b/examples/stm32f3_discovery/rtt/project.xml @@ -2,7 +2,8 @@ modm:disco-f303vc - + + modm:platform:rtt diff --git a/src/modm/platform/uart/rtt/module.lb b/src/modm/platform/uart/rtt/module.lb index 2c6ebd4be8..1da757a6ff 100644 --- a/src/modm/platform/uart/rtt/module.lb +++ b/src/modm/platform/uart/rtt/module.lb @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# Copyright (c) 2021, Niklas Hauser +# Copyright (c) 2021, 2025, Niklas Hauser # # This file is part of the modm project. # @@ -14,29 +14,22 @@ def init(module): module.name = ":platform:rtt" module.description = FileReader("module.md") + def prepare(module, options): if not options[":target"].has_driver("core:cortex-m*"): return False - module.add_list_option( - NumericOption(name="buffer.tx", description="Transmit buffer sizes", - minimum=0, maximum="64Ki"), default=512) - module.add_list_option( - NumericOption(name="buffer.rx", description="Receive buffer sizes", - minimum=0, maximum="64Ki"), default=0) + module.add_alias( + Alias(name="buffer.tx", destination=":rtt:buffer.tx", + description="Moved to the `modm:rtt` module")) + module.add_alias( + Alias(name="buffer.rx", destination=":rtt:buffer.rx", + description="Moved to the `modm:rtt` module")) - module.depends(":architecture:uart") + module.depends(":architecture:uart", ":architecture:fiber", ":rtt") return True -def validate(env): - if len(env["buffer.tx"]) != len(env["buffer.rx"]): - raise ValidateException("There must be the same number of TX buffers as RX buffers!") def build(env): env.outbasepath = "modm/src/modm/platform/rtt" - env.substitutions = { - "buffer_tx": env["buffer.tx"], - "buffer_rx": env["buffer.rx"], - } - env.template("rtt.hpp.in") - env.template("rtt.cpp.in") + env.copy("rtt.hpp") diff --git a/src/modm/platform/uart/rtt/module.md b/src/modm/platform/uart/rtt/module.md index 922dff3f8a..dfa792e9d0 100644 --- a/src/modm/platform/uart/rtt/module.md +++ b/src/modm/platform/uart/rtt/module.md @@ -1,116 +1,22 @@ -# Real Time Transfer (RTT) - -This module implements the RTT protocol compatible with OpenOCD. RTT works by -placing multiple ring-buffers in RAM which the debugger read and writes using -background memory accesses via SWD. It therefore works on any Cortex-M devices -without extra pins except for the SWDCLK and SWDIO. - -See https://www.segger.com/jlink-rtt.html +# UART over RTT The RTT channels are exposed as a UART interface which you can use like so: ```cpp -modm::platform::Rtt rtt(/* channel= */0); -// Access data directly via UART interface: eg. loop back -if (uint8_t data; rtt.read(data)) { rtt.write(data); } -// Or wrap into an IOStream for printing -modm::IODeviceObjectWrapper rtt_device(rtt); +// Access data via static methods +using Rtt = modm::platform::Rtt<0>; +// Create a software loopback +if (uint8_t data; Rtt::read(data)) { Rtt::write(data); } + +// Wrap into an IOStream +modm::IODeviceWrapper rtt_device; modm::IOStream stream(rtt_device); +// Writing data stream << "Hello World" << modm::endl; -// Reading is more annoying -char data; -stream.get(data); -if (data != modm::IOStream::eof) { /* process new data */ } -``` - -You can define the number of channels and their buffer size by setting the -`buffer.tx` and `buffer.rx` set options. Note that you can define *multiple* -buffer sizes indexed by channel. Here is an example of three channels: - -```xml - - - - - -``` - -You can set the buffer size to `0` if you don't want to use this channel -direction. This won't allocate a buffer and save a little RAM. - -## Accessing Data - -[OpenOCD has built-in support for RTT][rtt] and modm generates a config that -opens each RTT channel as a TCP port starting at 9090 (ie. 9090=channel 0, -9091=channel 1, etc). - -```sh -openocd -f modm/openocd.cfg -c modm_rtt -``` - -You can also call this from inside GDB via the `monitor` command: - -``` -(gdb) monitor modm_rtt -rtt: Searching for control block 'SEGGER RTT' -rtt: Control block found at 0x20001024 -Listening on port 9090 for rtt connections -(gdb) continue& +// Reading data from an IOStream +if (char data; stream.get(data), data != modm::IOStream::eof) +{ /* process new data */ } ``` -You can then use for example `telnet 127.0.0.1 9090` to connect to the stream. - -Note that this connection does not halt the target, you should therefore be able -to use this at any point during program execution. - -A simple telnet client is integrated into the build system generator, however, -it can only connect to one stream at a time (disconnect with Ctrl+D). - -``` - $ scons log-rtt -╭───OpenOCD───> Real Time Transfer -╰─────RTT────── stm32f103rbt -Info : rtt: Searching for control block 'SEGGER RTT' -Info : rtt: Control block found at 0x20000008 -Listening on port 9090 for rtt connections -loop 51 -loop 52 -loop 53 -^D - - $ make log-rtt channel=0 -Info : rtt: Searching for control block 'SEGGER RTT' -Info : rtt: Control block found at 0x20000008 -Listening on port 9090 for rtt connections -loop 58 -loop 60 -loop 61 -``` - -If you want to use this as a proper communication channel with a custom protocol -you should implement the OpenOCD config yourself (with different ports). - -You can also use JLink to access the RTT data, which may be significantly faster -than OpenOCD if the debug probe has hardware support for the protocol. - -``` - $ scons log-rtt-jlink -╭────JLink────> Real Time Transfer -╰─────RTT────── stm32l476rgt6 - -SEGGER J-Link V7.84f - Real time terminal output -RTT Demo on Nucleo-64 -loop: 0 -loop: 1 -loop: 2 -loop: 3 -loop: 4 -loop: 5 -loop: 6 -loop: 7 -``` - - -[rtt]: http://openocd.org/doc/html/General-Commands.html#Real-Time-Transfer-_0028RTT_0029 +To configure the number and size of RTT channels, see the `modm:rtt` module. diff --git a/src/modm/platform/uart/rtt/rtt.cpp.in b/src/modm/platform/uart/rtt/rtt.cpp.in deleted file mode 100644 index c3fa8a88a4..0000000000 --- a/src/modm/platform/uart/rtt/rtt.cpp.in +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2021, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include "rtt.hpp" -#include - -namespace modm::platform -{ - -struct RttBuffer -{ - const char* name; - uint8_t* const buffer; - const uint32_t size; - volatile uint32_t head; - volatile uint32_t tail; - const uint32_t flags; - - bool write(uint8_t data) - { - const uint32_t rhead{head}; - const uint32_t rtail{tail}; - const uint32_t rhead_next{(rhead + 1) % size}; - if (not size or rhead_next == rtail) return false; - buffer[rhead] = data; - head = rhead_next; - return true; - } - bool read(uint8_t &data) - { - const uint32_t rhead{head}; - const uint32_t rtail{tail}; - if (not size or rtail == rhead) return false; - data = buffer[rtail]; - tail = (rtail + 1) % size; - return true; - } - bool isEmpty() const { return (head == tail); } - uint32_t getSize() const - { - const uint32_t rhead{head}; - const uint32_t rtail{tail}; - return ((rhead > rtail) ? 0 : size) + rhead - rtail; - } -} modm_packed; - -%% for size in buffer_tx -%% if size -static uint8_t tx_data_buffer_{{loop.index0}}[{{size}}]; -%% endif -%% endfor -%% for size in buffer_rx -%% if size -static uint8_t rx_data_buffer_{{loop.index0}}[{{size}}]; -%% endif -%% endfor -%# -struct RttControlBlock -{ - const char identifier[16]; - const int32_t tx_buffer_count; - const int32_t rx_buffer_count; - RttBuffer tx_buffers[{{ buffer_tx | length }}]; - RttBuffer rx_buffers[{{ buffer_rx | length }}]; -} modm_packed; - -// Explicitly constructed as constinit to force *only* copying via .data section. -// This prevents the identifier leaking into the stack and being -// found by OpenOCD accidentally instead of the real RTT control block. -static constinit RttControlBlock rtt_control{ - "SEGGER RTT", - {{ buffer_tx | length }}, - {{ buffer_rx | length }}, - { -%% for size in buffer_tx - {"tx{{loop.index0}}", {% if size %}tx_data_buffer_{{loop.index0}}{% else %}nullptr{% endif %}, {{size}}, 0,0,0 }, -%% endfor - },{ -%% for size in buffer_rx - {"rx{{loop.index0}}", {% if size %}rx_data_buffer_{{loop.index0}}{% else %}nullptr{% endif %}, {{size}}, 0,0,0 }, -%% endfor - } -}; - - -Rtt::Rtt(uint8_t channel) -: tx_buffer(rtt_control.tx_buffers[std::min(channel, {{ buffer_tx | length - 1 }})]), - rx_buffer(rtt_control.rx_buffers[std::min(channel, {{ buffer_rx | length - 1 }})]) -{ -} - -bool -Rtt::write(uint8_t data) -{ - return tx_buffer.write(data); -} - -std::size_t -Rtt::write(const uint8_t *data, std::size_t length) -{ - std::size_t sent = 0; - for (; sent < length; sent++) - if (not write(*data++)) - return sent; - return sent; -} - -bool -Rtt::isWriteFinished() -{ - return tx_buffer.isEmpty(); -} - -std::size_t -Rtt::transmitBufferSize() -{ - return tx_buffer.getSize(); -} - -bool -Rtt::read(uint8_t& data) -{ - return rx_buffer.read(data); -} - -std::size_t -Rtt::read(uint8_t *data, std::size_t length) -{ - uint32_t i = 0; - for (; i < length; ++i) - if (not rx_buffer.read(*data++)) - return i; - return i; -} - -std::size_t -Rtt::receiveBufferSize() -{ - return rx_buffer.getSize(); -} - - - -} // namespace modm::platform diff --git a/src/modm/platform/uart/rtt/rtt.hpp b/src/modm/platform/uart/rtt/rtt.hpp new file mode 100644 index 0000000000..bf11e2fcf8 --- /dev/null +++ b/src/modm/platform/uart/rtt/rtt.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021, 2025, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once +#include +#include +#include + +namespace modm::platform +{ + +/** + * Real Time Transfer (RTT) Uart Interface + * + * @author Niklas Hauser + * @ingroup modm_platform_rtt + */ +template +class Rtt : public ::modm::Uart +{ + static_assert(Channel < SEGGER_RTT_MAX_NUM_UP_BUFFERS, + "Channel index too large for RTT configuration!"); +public: + inline void + static writeBlocking(uint8_t data) + { + modm::this_fiber::poll([&]{ return write(data); }); + } + + inline void + static writeBlocking(const uint8_t *data, std::size_t length) + { + while (true) + { + const std::size_t written = write(data, length); + length -= written; + if (length == 0) return; + data += written; + modm::this_fiber::yield(); + } + } + + inline void + static flushWriteBuffer() + { + modm::this_fiber::poll([&]{ return isWriteFinished(); }); + } + + inline bool + static write(uint8_t data) + { + return SEGGER_RTT_PutCharSkipNoLock(Channel, data); + } + + inline std::size_t + static write(const uint8_t *data, std::size_t length) + { + if (length == 0) return 0; + if (not SEGGER_RTT_WriteSkipNoLock(Channel, data, length)) + { + const std::size_t available = std::min(SEGGER_RTT_GetAvailWriteSpace(Channel), length); + if (available and SEGGER_RTT_WriteSkipNoLock(Channel, data, available)) + return available; + } + return 0; + } + + inline bool + static isWriteFinished() + { + return SEGGER_RTT_HASDATA_UP(Channel) == 0; + } + + std::size_t + static transmitBufferSize() + { + return SEGGER_RTT_GetBytesInBuffer(Channel); + } + + inline std::size_t + static discardTransmitBuffer() + { + // cannot do it safely due to race conditions with the debugger + return 0; + } + + inline bool + static read(uint8_t &data) + { + return read(&data, 1) == 1; + } + + std::size_t + static read(uint8_t *data, std::size_t length) + { + return SEGGER_RTT_ReadNoLock(Channel, data, length); + } + + inline std::size_t + static receiveBufferSize() + { + return SEGGER_RTT_GetBytesInDownBuffer(Channel); + } + + inline std::size_t + static discardReceiveBuffer() + { + uint8_t buffer[32]; + std::size_t length{0}; + while (size_t size = read(buffer, 32)) length += size; + return length; + } + + inline bool + static hasError() + { return false; } + + inline void + static clearError() {} +}; + +} // namespace modm::platform diff --git a/src/modm/platform/uart/rtt/rtt.hpp.in b/src/modm/platform/uart/rtt/rtt.hpp.in deleted file mode 100644 index aa6b8f895d..0000000000 --- a/src/modm/platform/uart/rtt/rtt.hpp.in +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#pragma once -#include - -namespace modm::platform -{ - -struct RttBuffer; - -/** - * Real Time Transfer (RTT) Uart Interface - * - * @author Niklas Hauser - * @ingroup modm_platform_rtt - */ -class Rtt : public ::modm::Uart -{ - RttBuffer& tx_buffer; - RttBuffer& rx_buffer; - -public: - Rtt(uint8_t channel); - - inline void - writeBlocking(uint8_t data) - { while(not write(data)) ; } - - inline void - writeBlocking(const uint8_t *data, std::size_t length) - { while (length--) writeBlocking(*data++); } - - inline void - flushWriteBuffer() {} - - bool - write(uint8_t data); - - std::size_t - write(const uint8_t *data, std::size_t length); - - bool - isWriteFinished(); - - std::size_t - transmitBufferSize(); - - inline std::size_t - discardTransmitBuffer() - { return 0; } - - bool - read(uint8_t &data); - - std::size_t - read(uint8_t *data, std::size_t length); - - std::size_t - receiveBufferSize(); - - inline std::size_t - discardReceiveBuffer() - { return 0; } - - inline bool - hasError() - { return false; } - - inline void - clearError() {} -}; - -} // namespace modm::platform diff --git a/tools/build_script_generator/cmake/module.md b/tools/build_script_generator/cmake/module.md index 4f4b3ee7c1..c3640b68bf 100644 --- a/tools/build_script_generator/cmake/module.md +++ b/tools/build_script_generator/cmake/module.md @@ -157,8 +157,8 @@ Outputs ITM channel 0 via JLinkSWOViewer. - option `MODM_RTT_CHANNEL=0` as integer. -Configures OpenOCD in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures OpenOCD in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) See the `modm:platform:rtt` module for details how to use RTT for data transfer. @@ -168,8 +168,8 @@ See the `modm:platform:rtt` module for details how to use RTT for data transfer. - option `MODM_RTT_CHANNEL=0` as integer. -Configures JLink in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures JLink in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) [cmake]: http://cmake.org diff --git a/tools/build_script_generator/make/module.md b/tools/build_script_generator/make/module.md index 74ca00a653..ea7052313d 100644 --- a/tools/build_script_generator/make/module.md +++ b/tools/build_script_generator/make/module.md @@ -479,8 +479,8 @@ CPU frequency. make log-rtt [channel={int}] ``` -Configures OpenOCD in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures OpenOCD in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) ``` @@ -503,8 +503,8 @@ See the `modm:platform:rtt` module for details how to use RTT for data transfer. make log-rtt-jlink [channel={int}] ``` -Configures JLink in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures JLink in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) diff --git a/tools/build_script_generator/module.lb b/tools/build_script_generator/module.lb index 8a538dce42..34644ec938 100644 --- a/tools/build_script_generator/module.lb +++ b/tools/build_script_generator/module.lb @@ -254,11 +254,11 @@ def post_build(env): env.substitutions["vector_table_size"] = vector_table["vector_table_size"] env.template("gdbinit.in") - has_rtt = env.has_module(":platform:rtt") + has_rtt = env.has_module(":rtt") env.substitutions["has_rtt"] = has_rtt if has_rtt: env.substitutions["main_ram"] = linkerscript.get("cont_ram", {"start": 0x20000000, "size": 4096}) - env.substitutions["rtt_channels"] = len(env.get(":platform:rtt:buffer.tx", [])) + env.substitutions["rtt_channels"] = len(env.get(":rtt:buffer.tx", [])) env.template("openocd.cfg.in") diff --git a/tools/build_script_generator/openocd.cfg.in b/tools/build_script_generator/openocd.cfg.in index 58b29d4616..dc0401b46d 100644 --- a/tools/build_script_generator/openocd.cfg.in +++ b/tools/build_script_generator/openocd.cfg.in @@ -19,19 +19,17 @@ proc modm_itm_log { OUTPUT F_CPU {BAUDRATE 2000000} } { proc modm_program { SOURCE } { program $SOURCE verify -%% if has_rtt - # Zero SRAM to prevent OpenOCD from leaking RTT identifier into memory - mww 0x{{"%0x" % main_ram.start}} 0 {{main_ram.size // 4}} -%% endif reset run shutdown } %% if has_rtt -proc modm_rtt { {POLLING 1} {CHANNELS {{rtt_channels}}} } { - rtt setup 0x{{"%0x" % main_ram.start}} {{main_ram.size}} "SEGGER RTT" +proc modm_rtt { {POLLING 1} {CHANNELS {{rtt_channels}}} {IDENTIFIER "SEGGER RTT"} } { + puts [format "Searching \[0x{{"%0x" % main_ram.start}}, 0x{{"%0x" % (main_ram.start + main_ram.size)}}\]"] + rtt setup 0x{{"%0x" % main_ram.start}} {{main_ram.size}} $IDENTIFIER rtt start rtt polling_interval $POLLING + rtt channels for {set i 0} {$i < $CHANNELS} {incr i} { rtt server start [expr {9090 + $i}] $i } diff --git a/tools/build_script_generator/scons/module.md b/tools/build_script_generator/scons/module.md index f0653c60f4..b11848e125 100644 --- a/tools/build_script_generator/scons/module.md +++ b/tools/build_script_generator/scons/module.md @@ -563,8 +563,8 @@ CPU frequency. scons log-rtt [channel={int}] ``` -Configures OpenOCD in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures OpenOCD in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) ``` @@ -589,8 +589,8 @@ See the `modm:platform:rtt` module for details how to use RTT for data transfer. scons log-rtt-jlink [channel={int}] ``` -Configures JLink in RTT mode to output the chosen channel (default 0) via a -simple telnet client. Disconnect with Ctrl+D. +Configures JLink in RTT mode to output the chosen channel (default 0) via +netcat. Stop listening with Ctrl+C. (\* *only ARM Cortex-M targets*) diff --git a/tools/modm_tools/jlink.py b/tools/modm_tools/jlink.py index f956db5909..a7d005cdb1 100644 --- a/tools/modm_tools/jlink.py +++ b/tools/modm_tools/jlink.py @@ -98,19 +98,10 @@ def itm(device, baudrate=None): def rtt(backend, channel=0): - try: - import telnetlib3 - except ImportError: - print("Please upgrade modm: pip3 install -U modm") - import telnetlib as telnetlib3 # Start JLinkGDBServer in the background with backend.scope(): time.sleep(0.5) - with telnetlib3.Telnet("localhost", 19021) as tn: - try: - tn.interact() - except KeyboardInterrupt: - pass + subprocess.call(f"stty -icanon -echo; nc localhost {19021 + channel}; stty sane", shell=True) # ----------------------------------------------------------------------------- diff --git a/tools/modm_tools/openocd.py b/tools/modm_tools/openocd.py index 134e18623b..595aaeb606 100644 --- a/tools/modm_tools/openocd.py +++ b/tools/modm_tools/openocd.py @@ -125,20 +125,11 @@ def itm(backend, fcpu, baudrate=None): pass def rtt(backend, channel=0): - try: - import telnetlib3 - except ImportError: - print("Please upgrade modm: pip3 install -U modm") - import telnetlib as telnetlib3 - backend.commands.append("modm_rtt") # Start OpenOCD in the background + backend.commands.append("modm_rtt") with backend.scope(): time.sleep(0.5) - with telnetlib3.Telnet("localhost", 9090+channel) as tn: - try: - tn.interact() - except KeyboardInterrupt: - pass + subprocess.call(f"stty -icanon -echo; nc localhost {9090 + channel}; stty sane", shell=True) # ----------------------------------------------------------------------------- From fae764386965cea6906d7991ca405970834085c8 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 9 Aug 2025 20:03:26 +0200 Subject: [PATCH 3/3] [core] Add RTT control block address into interrupt vector table --- src/modm/platform/core/cortex/module.lb | 1 + src/modm/platform/core/cortex/vectors.c.in | 22 ++++++++++++--------- tools/build_script_generator/openocd.cfg.in | 11 +++++++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index e54730ec9c..be2442aac5 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -352,6 +352,7 @@ def build(env): "with_assert": env.has_module(":architecture:assert"), "with_fpu": env.get("float-abi", "soft") != "soft", "with_multicore": env.has_module(":platform:multicore"), + "with_rtt": env.has_module(":rtt"), "with_msplim": sum(c.isnumeric() for c in core) == 2, "enable_icache": env.get("enable_icache", False), "enable_dcache": enable_dcache, diff --git a/src/modm/platform/core/cortex/vectors.c.in b/src/modm/platform/core/cortex/vectors.c.in index 907b064e19..94be49a48a 100644 --- a/src/modm/platform/core/cortex/vectors.c.in +++ b/src/modm/platform/core/cortex/vectors.c.in @@ -25,13 +25,15 @@ void HardFault_Handler(void) __attribute__((weak, alias("Undefined_Handler") {{ ("void " ~ vector_table[pos] ~ "(void)") | lbuild.pad(47)}}__attribute__((weak, alias("Undefined_Handler"))); %% endif %% endfor - +%# // ---------------------------------------------------------------------------- typedef void (* const FunctionPointer)(void); -// defined in the linkerscript -extern uint32_t __main_stack_top[]; - +extern uint32_t __main_stack_top[]; // from linkerscript +%% if with_rtt +extern uint32_t _SEGGER_RTT; // from modm:rtt +%% endif +%# // Define the vector table modm_section(".vector_rom") FunctionPointer vectorsRom[] = @@ -41,11 +43,13 @@ FunctionPointer vectorsRom[] = NMI_Handler, // -14: Non Maskable Interrupt handler HardFault_Handler, // -13: hard fault handler %% for pos in range(4 - 16, highest_irq) -%% if pos in vector_table +%% if pos in vector_table {{ (vector_table[pos] ~ ",") | lbuild.pad(39) }}// {{ (pos|string).rjust(3) }} -%% else +%% elif with_rtt and pos == -8 + (FunctionPointer)(((uint32_t)&_SEGGER_RTT) + 2ul), +%% else Undefined_Handler, // {{ (pos|string).rjust(3) }} -%% endif +%% endif %% endfor }; %% if vector_table_location == "ram" @@ -53,7 +57,7 @@ FunctionPointer vectorsRom[] = modm_section(".vector_ram") FunctionPointer vectorsRam[sizeof(vectorsRom) / sizeof(FunctionPointer)]; %% endif - +%# // ---------------------------------------------------------------------------- // Ignore redeclaration of interrupt handlers in vendor headers #pragma GCC diagnostic push @@ -64,7 +68,7 @@ FunctionPointer vectorsRam[sizeof(vectorsRom) / sizeof(FunctionPointer)]; %% if with_assert #include %% endif - +%# void Undefined_Handler(void) { %% if with_assert diff --git a/tools/build_script_generator/openocd.cfg.in b/tools/build_script_generator/openocd.cfg.in index dc0401b46d..250984aa66 100644 --- a/tools/build_script_generator/openocd.cfg.in +++ b/tools/build_script_generator/openocd.cfg.in @@ -25,8 +25,15 @@ proc modm_program { SOURCE } { %% if has_rtt proc modm_rtt { {POLLING 1} {CHANNELS {{rtt_channels}}} {IDENTIFIER "SEGGER RTT"} } { - puts [format "Searching \[0x{{"%0x" % main_ram.start}}, 0x{{"%0x" % (main_ram.start + main_ram.size)}}\]"] - rtt setup 0x{{"%0x" % main_ram.start}} {{main_ram.size}} $IDENTIFIER + set hint [mrw 0x08000020] + if {$hint & 0x2} { + set hint [expr {$hint & ~0x2}] + puts [format "Info : Searching 0x%08x from IRQ table" $hint] + rtt setup $hint 16 $IDENTIFIER + } else { + puts [format "Info : Searching \[0x{{"%0x" % main_ram.start}}, 0x{{"%0x" % (main_ram.start + main_ram.size)}}\]"] + rtt setup 0x{{"%0x" % main_ram.start}} {{main_ram.size}} $IDENTIFIER + } rtt start rtt polling_interval $POLLING rtt channels