Skip to content
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

Add support for Real Time Transfer (RTT) via OpenOCD #610

Merged
merged 4 commits into from
Apr 12, 2021
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: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ supported development boards:
[CAN](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/can/main.cpp),
[Accelerometer](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/accelerometer/main.cpp),
[Gyroscope](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/rotation/main.cpp),
[TinyUSB DFU](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/usb_dfu/main.cpp).
[TinyUSB DFU](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/usb_dfu/main.cpp),
[Logging via RTT](https://github.com/modm-io/modm/blob/develop/examples/stm32f3_discovery/rtt/main.cpp).
- STM32F4 Discovery:
[Blinky](https://github.com/modm-io/modm/blob/develop/examples/stm32f4_discovery/blink/main.cpp),
[CAN](https://github.com/modm-io/modm/blob/develop/examples/stm32f4_discovery/can/main.cpp),
Expand Down
2 changes: 1 addition & 1 deletion examples/avr/block_device_mirror/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace modm::platform;

// Create a new UART object and configure it to a baudrate of 115200
Uart0 uart;
modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull > loggerDevice(uart);
modm::IODeviceWrapper< Uart0, modm::IOBuffer::BlockIfFull > loggerDevice;

// Set all four logger streams to use the UART
modm::log::Logger modm::log::debug(loggerDevice);
Expand Down
93 changes: 93 additions & 0 deletions examples/stm32f3_discovery/rtt/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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 <modm/board.hpp>
#include <modm/processing.hpp>
#include <modm/debug.hpp>

using namespace Board;

Rtt rtt(0);
modm::IODeviceObjectWrapper< Rtt, modm::IOBuffer::DiscardIfFull > rtt_device(rtt);
// Set all four logger streams to use RTT
modm::log::Logger modm::log::debug(rtt_device);
modm::log::Logger modm::log::info(rtt_device);
modm::log::Logger modm::log::warning(rtt_device);
modm::log::Logger modm::log::error(rtt_device);

#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::INFO

/*

$ scons log-rtt
╭───OpenOCD───> Real Time Transfer
╰─────RTT────── stm32f303vct6
Info : STLINK V2J16S0 (API v2) VID:PID 0483:3748
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : rtt: Searching for control block 'modm.rtt.modm'
Info : rtt: Control block found at 0x20000c04
Info : Listening on port 9090 for rtt connections
Info
Warning
Error
loop: 0
loop: 1
loop: 2
loop: 3
loop: 4
loop: 5


Type number 0-9, then press enter to send.
The LED should blink slower or faster.

Ctrl+D to exit

*/

// ----------------------------------------------------------------------------
int
main()
{
Board::initialize();

MODM_LOG_DEBUG << "Debug" << modm::endl;
MODM_LOG_INFO << "Info" << modm::endl;
MODM_LOG_WARNING << "Warning" << modm::endl;
MODM_LOG_ERROR << "Error" << modm::endl;

uint32_t counter(0);
modm::PeriodicTimer tmr(100ms);

char data;
while (true)
{
MODM_LOG_INFO.get(data);
switch(data)
{
case '0':
tmr.restart(1s);
break;
case '1'...'9':
tmr.restart(std::chrono::milliseconds((data - '0') * 100));
break;
}
if (tmr.execute())
{
LedNorth::toggle();

MODM_LOG_INFO << "loop: " << counter++ << modm::endl;
}
}

return 0;
}
13 changes: 13 additions & 0 deletions examples/stm32f3_discovery/rtt/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<library>
<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>
</options>
<modules>
<module>modm:platform:rtt</module>
<module>modm:processing:timer</module>
<module>modm:build:scons</module>
<module>modm:debug</module>
</modules>
</library>
44 changes: 38 additions & 6 deletions src/modm/io/iodevice_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,18 @@ template< class Device, IOBuffer behavior >
class IODeviceWrapper : public IODevice
{
public:
IODeviceWrapper(const Device&) {}
IODeviceWrapper() = default;
using IODevice::write;

void
write(char c) override
{
if constexpr (behavior == IOBuffer::DiscardIfFull) {
Device::write(static_cast<uint8_t>(c));
}
else {
while(not Device::write(static_cast<uint8_t>(c))) ;
bool written;
do
{
written = Device::write(uint8_t(c));
}
while(behavior == IOBuffer::BlockIfFull and not written);
}

void
Expand All @@ -68,6 +67,39 @@ class IODeviceWrapper : public IODevice
}
};

/// @ingroup modm_io
template< class Device, IOBuffer behavior >
class IODeviceObjectWrapper : public IODevice
{
Device &device;
public:
IODeviceObjectWrapper(Device& device) : device{device} {}
using IODevice::write;

void
write(char c) override
{
bool written;
do
{
written = device.write(uint8_t(c));
}
while(behavior == IOBuffer::BlockIfFull and not written);
}

void
flush() override
{
device.flushWriteBuffer();
}

bool
read(char& c) override
{
return device.read(reinterpret_cast<uint8_t&>(c));
}
};

}

#endif // MODM_IODEVICE_WRAPPER_HPP
12 changes: 12 additions & 0 deletions src/modm/platform/core/cortex/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def common_memories(env):

- `memories`: unfiltered memory regions
- `regions`: memory region names
- `cont_ram`: largest continuous internal SRAM section

:returns: dictionary of memory properties
"""
Expand All @@ -84,9 +85,19 @@ def common_memories(env):
m["start"] = int(m["start"], 0)
m["size"] = int(m["size"], 0)

# Find largest continuous internal SRAM section
cont = []
for m in memories:
if m["name"].startswith("sram") or m["name"].startswith("ram"):
if cont and (cont[-1]["start"] + cont[-1]["size"] == m["start"]):
cont[-1]["size"] += m["size"]
else:
cont.append(dict(m))

properties = {
"memories": memories,
"regions": [m["name"] for m in memories],
"cont_ram": sorted(cont, key=lambda m: -m["size"])[0],
}
return properties

Expand Down Expand Up @@ -116,6 +127,7 @@ def common_linkerscript(env):
- `regions`: memory region names
- `ram_origin`: Lowest SRAM origin address
- `ram_origin`: Total size of all SRAM regions
- `cont_ram`: largest continuous internal SRAM section

:returns: dictionary of linkerscript properties
"""
Expand Down
9 changes: 4 additions & 5 deletions src/modm/platform/uart/cortex/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ the asynchronous trace stream.
To log the output to a file called `itm.fifo` you can call the `modm_log_itm`
command manually.

```
$ openocd -f "modm/openocd.cfg" -c "modm_log_itm itm.fifo 64000000"
```sh
openocd -f modm/openocd.cfg -c "modm_log_itm itm.fifo 64000000"
```

You can then either use `tail -f itm.fifo` to display the raw data stream
Expand All @@ -62,9 +62,8 @@ terminal

```
$ scons log-itm fcpu=64000000
.----OpenOCD--> Single Wire Viewer
'------SWO----- stm32f103rbt

╭───OpenOCD───> Single Wire Viewer
╰─────SWO────── stm32f103rbt
Hello from the SWO.
debug
info
Expand Down
42 changes: 42 additions & 0 deletions src/modm/platform/uart/rtt/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 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/.
# -----------------------------------------------------------------------------

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_set_option(
NumericOption(name="buffer.tx", description="Transmit buffer sizes",
minimum=0, maximum=2**16), default=512)
module.add_set_option(
NumericOption(name="buffer.rx", description="Receive buffer sizes",
minimum=0, maximum=2**16), default=0)

module.depends(":architecture:uart")
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")
96 changes: 96 additions & 0 deletions src/modm/platform/uart/rtt/module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 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

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<modm::platform::Rtt,
modm::IOBuffer::DiscardIfFull> rtt_device(rtt);
modm::IOStream stream(rtt_device);
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
<!-- Channel0: TX only with 256B buffer -->
<!-- Channel1: RX only with 128B buffer -->
<!-- Channel2: TX with 512B and RX with 64B buffer -->
<option name="modm:platform:rtt:buffer.tx">256, 0, 512</option>
<option name="modm:platform: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.


## 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 'modm.rtt.modm'
rtt: Control block found at 0x20001024
Listening on port 9090 for rtt connections
(gdb)
```

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 'modm.rtt.modm'
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 'modm.rtt.modm'
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).


[rtt]: http://openocd.org/doc/html/General-Commands.html#Real-Time-Transfer-_0028RTT_0029
Loading