Skip to content

Integrate SEGGER RTT implementation #1278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [email protected]:modm-ext/segger-rtt.git
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
<!--/links-->
3 changes: 1 addition & 2 deletions examples/blue_pill_f103/rtt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion examples/blue_pill_f103/rtt/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<extends>modm:blue-pill-f103</extends>
<options>
<option name="modm:build:build.path">../../../build/blue_pill_f103/rtt</option>
<option name="modm:platform:rtt:buffer.rx">16</option>
<option name="modm:rtt:buffer.rx">16</option>
</options>
<modules>
<module>modm:platform:rtt</module>
Expand Down
3 changes: 1 addition & 2 deletions examples/blue_pill_f103/servo_pwm/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion examples/blue_pill_f103/servo_pwm/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<extends>modm:blue-pill-f103</extends>
<options>
<option name="modm:build:build.path">../../../build/blue_pill_f103/servo_pwm</option>
<option name="modm:platform:rtt:buffer.tx">128</option>
<option name="modm:rtt:buffer.tx">128</option>
</options>
<modules>
<module>modm:build:scons</module>
Expand Down
1 change: 0 additions & 1 deletion examples/nucleo_f429zi/spi_flash_fatfs/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<extends>modm:nucleo-f429zi</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_f429zi/spi_flash_fatfs</option>
<option name="modm:platform:rtt:buffer.tx">4096</option>
</options>
<modules>
<module>modm:build:scons</module>
Expand Down
3 changes: 1 addition & 2 deletions examples/nucleo_l476rg/rtt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/nucleo_l476rg/rtt/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<extends>modm:nucleo-l476rg</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_l476rg/rtt</option>
<option name="modm:platform:rtt:buffer.rx">16</option>
<option name="modm:rtt:buffer.rx">16</option>
</options>
<modules>
<module>modm:platform:rtt</module>
Expand Down
11 changes: 8 additions & 3 deletions examples/stm32f3_discovery/rtt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down
3 changes: 2 additions & 1 deletion examples/stm32f3_discovery/rtt/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<extends>modm:disco-f303vc</extends>
<options>
<option name="modm:build:build.path">../../../build/stm32f3_discovery/rtt</option>
<option name="modm:platform:rtt:buffer.rx">16</option>
<option name="modm:rtt:buffer.tx">512, 0, 1024</option>
<option name="modm:rtt:buffer.rx">16, 32, 0</option>
</options>
<modules>
<module>modm:platform:rtt</module>
Expand Down
73 changes: 73 additions & 0 deletions ext/segger/SEGGER_RTT_Conf.h.in
Original file line number Diff line number Diff line change
@@ -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 <modm/platform/device.hpp>

#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
51 changes: 51 additions & 0 deletions ext/segger/module.lb
Original file line number Diff line number Diff line change
@@ -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")
107 changes: 107 additions & 0 deletions ext/segger/module.md
Original file line number Diff line number Diff line change
@@ -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
<!-- Channel0: TX only with 256B buffer -->
<!-- Channel1: RX only with 128B buffer -->
<!-- Channel2: TX with 512B and RX with 64B buffer -->
<option name="modm:rtt:buffer.tx">256, 0, 512</option>
<option name="modm:rtt:buffer.rx">0, 128, 64</option>
```

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
1 change: 1 addition & 0 deletions ext/segger/rtt
Submodule rtt added at 267fd4
Loading
Loading