mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
sys/rtt_stdio: Support SEGGER RTT for stdin/stdout
This commit is contained in:
parent
662076e150
commit
1d6b9c6f8d
@ -333,7 +333,9 @@ ifneq (,$(filter newlib,$(USEMODULE)))
|
||||
ifeq (,$(filter newlib_syscalls_%,$(USEMODULE)))
|
||||
USEMODULE += newlib_syscalls_default
|
||||
endif
|
||||
USEMODULE += uart_stdio
|
||||
ifeq (,$(filter rtt_stdio,$(USEMODULE)))
|
||||
USEMODULE += uart_stdio
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
@ -341,6 +343,10 @@ ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
USEMODULE += random
|
||||
endif
|
||||
|
||||
ifneq (,$(filter rtt_stdio,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter uart_stdio,$(USEMODULE)))
|
||||
USEMODULE += tsrb
|
||||
endif
|
||||
|
73
sys/include/rtt_stdio.h
Normal file
73
sys/include/rtt_stdio.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Michael Andersen <m.andersen@berkeley.edu>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_rtt_stdio SEGGER RTT stdio
|
||||
* @ingroup sys
|
||||
*
|
||||
* @brief stdio init/read/write functions for SEGGER RTT. This is
|
||||
* designed to shadow the functions in uart_stdio
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
*
|
||||
* @author Michael Andersen <m.andersen@cs.berkeley.edu>
|
||||
*/
|
||||
#ifndef RTT_STDIO_H
|
||||
#define RTT_STDIO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief initialize the module. This is a noop.
|
||||
*/
|
||||
void uart_stdio_init(void);
|
||||
|
||||
/**
|
||||
* @brief read @p len bytes from stdio uart into @p buffer
|
||||
*
|
||||
* @param[out] buffer buffer to read into
|
||||
* @param[in] len nr of bytes to read
|
||||
*
|
||||
* @return nr of bytes read
|
||||
* @return <0 on error
|
||||
*/
|
||||
int uart_stdio_read(char* buffer, int len);
|
||||
|
||||
/**
|
||||
* @brief write @p len bytes from @p buffer into uart
|
||||
*
|
||||
* @param[in] buffer buffer to read from
|
||||
* @param[in] len nr of bytes to write
|
||||
*
|
||||
* @return nr of bytes written
|
||||
* @return <0 on error
|
||||
*/
|
||||
int uart_stdio_write(const char* buffer, int len);
|
||||
|
||||
/**
|
||||
* @brief enable stdin polling, at a power consumption cost. This is enabled
|
||||
* by default unless RTT_STDIO_DISABLE_STDIN is defined.
|
||||
*/
|
||||
void rtt_stdio_enable_stdin(void);
|
||||
|
||||
/**
|
||||
* @brief enable stdout blocking and free space polling. This must be done
|
||||
* with caution because if there is no RTT client attached, all
|
||||
* writes to stdout will block indefinitely. This can be enabled
|
||||
* automatically by defining RTT_STDIO_ENABLE_BLOCKING_STDOUT
|
||||
*/
|
||||
void rtt_stdio_enable_blocking_stdout(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/** @} */
|
||||
#endif /* RTT_STDIO_H */
|
1
sys/rtt_stdio/Makefile
Normal file
1
sys/rtt_stdio/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
45
sys/rtt_stdio/README.md
Normal file
45
sys/rtt_stdio/README.md
Normal file
@ -0,0 +1,45 @@
|
||||
# RTT STDIO
|
||||
|
||||
This module will allow communication using SEGGER's Real Time Terminal protocol.
|
||||
Briefly, it replaces UART stdio with a set of ringbuffers that are manipulated
|
||||
over JTAG. There are several advantages to this system. The biggest is that
|
||||
writing to stdout is extremely fast (as you are just copying to memory). This
|
||||
is useful if you are adding print statements in timing-sensitive code as part
|
||||
of debugging. The other advantage is that it frees your UART for other use
|
||||
and enables stdio on platforms that do not have a UART.
|
||||
|
||||
To use this module, add
|
||||
|
||||
```
|
||||
USEMODULE += rtt_stdio
|
||||
```
|
||||
|
||||
to your makefile. By default the module will drop bytes written to stdout if the
|
||||
buffer is full. If you know for certain that the debugger is attached, you
|
||||
can obtain lossless stdout by adding
|
||||
|
||||
```
|
||||
CFLAGS += -DRTT_STDIO_ENABLE_BLOCKING_STDOUT
|
||||
```
|
||||
|
||||
to your makefile. Note well that if you do NOT plug in the debugger and run
|
||||
the SEGGER RTT software (or compatible software) this will then lock up the
|
||||
system as it waits forever. Typically you would only define this during
|
||||
development on the lab bench.
|
||||
|
||||
If you are printing significant data out (pages a second), you can increase
|
||||
your stdout bandwidth by lowering the poll interval. The default is 50ms.
|
||||
A choice of 5ms is good during printf-heavy debugging:
|
||||
|
||||
```
|
||||
CFLAGS += -DSTDIO_POLL_INTERVAL=5000U
|
||||
```
|
||||
|
||||
SEGGER RTT supports stdin as well, and this is enabled by default. It requires
|
||||
polling the stdin ringbuffer, however, which on low duty cycle systems
|
||||
can increase the number of unnecessary wakeups from sleep. To disable stdin,
|
||||
add this to your makefile:
|
||||
|
||||
```
|
||||
CFLAGS += -DRTT_STDIO_DISABLE_STDIN
|
||||
```
|
335
sys/rtt_stdio/rtt_stdio.c
Normal file
335
sys/rtt_stdio/rtt_stdio.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Michael Andersen <m.andersen@cs.berkeley.edu>
|
||||
*
|
||||
* This file was based on SEGGER's reference implementation
|
||||
* which can be found here: https://www.segger.com/jlink-rtt.html
|
||||
* (c) 2014-2016 SEGGER Microcontroller GmbH & Co. KG
|
||||
* This implementation bore the following license notes:
|
||||
**********************************************************************
|
||||
* SEGGER MICROCONTROLLER GmbH & Co. KG *
|
||||
* Solutions for real time microcontroller applications *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* SEGGER RTT * Real Time Transfer for embedded targets *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* SEGGER strongly recommends to not make any changes *
|
||||
* to or modify the source code of this software in order to stay *
|
||||
* compatible with the RTT protocol and J-Link. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* o Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* o Redistributions in binary form must reproduce the above *
|
||||
* copyright notice, this list of conditions and the following *
|
||||
* disclaimer in the documentation and/or other materials provided *
|
||||
* with the distribution. *
|
||||
* *
|
||||
* o Neither the name of SEGGER Microcontroller GmbH & Co. KG *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup sys
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SEGGER RTT stdio implementation
|
||||
*
|
||||
* This file implements UART read/write functions, but it
|
||||
* is actually a virtual UART backed by a ringbuffer that
|
||||
* complies with SEGGER RTT. It is designed to shadow
|
||||
* uart_stdio that is used by newlib.
|
||||
*
|
||||
* @author Michael Andersen <m.andersen@cs.berkeley.edu>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <rtt_stdio.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "mutex.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
/* This parameter affects the bandwidth of both input and output. Decreasing
|
||||
it will significantly improve bandwidth at the cost of CPU time. */
|
||||
#ifndef STDIO_POLL_INTERVAL
|
||||
#define STDIO_POLL_INTERVAL 50000U
|
||||
#endif
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#ifndef STDIO_TX_BUFSIZE
|
||||
#define STDIO_TX_BUFSIZE (512)
|
||||
#endif
|
||||
|
||||
#ifndef STDIO_RX_BUFSIZE
|
||||
#define STDIO_RX_BUFSIZE (32)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief use mutex for waiting on stdin being enabled
|
||||
*/
|
||||
static mutex_t _rx_mutex = MUTEX_INIT;
|
||||
|
||||
/**
|
||||
* @brief buffer holding stdout
|
||||
*/
|
||||
static char up_buffer [STDIO_TX_BUFSIZE];
|
||||
|
||||
/**
|
||||
* @brief buffer holding stdin
|
||||
*/
|
||||
static char down_buffer [STDIO_RX_BUFSIZE];
|
||||
|
||||
/**
|
||||
* @brief flag that enables stdin polling
|
||||
*/
|
||||
static char stdin_enabled = 0;
|
||||
|
||||
/**
|
||||
* @brief flag that enables stdout blocking/polling
|
||||
*/
|
||||
static char blocking_stdout = 0;
|
||||
|
||||
/**
|
||||
* @brief SEGGER's ring buffer implementation
|
||||
*/
|
||||
typedef struct {
|
||||
const char* channel_name; /* Optional name. Standard names so far are:
|
||||
"Terminal", "VCom" */
|
||||
char* buf_ptr; /* Pointer to start of buffer */
|
||||
int32_t buf_size; /* Buffer size in bytes. Note that one byte is
|
||||
lost, as this implementation does not fill up
|
||||
the buffer in order to avoid the problem of
|
||||
being unable to distinguish between full and
|
||||
empty. */
|
||||
volatile int32_t wr_off; /* Position of next item to be written by either
|
||||
host (down-buffer) or target (up-buffer). Must
|
||||
be volatile since it may be modified by host
|
||||
(down-buffer) */
|
||||
volatile int32_t rd_off; /* Position of next item to be read by target
|
||||
(down-buffer) or host (up-buffer). Must be
|
||||
volatile since it may be modified by host
|
||||
(up-buffer) */
|
||||
int32_t flags; /* Contains configuration flags */
|
||||
} rtt_ringbuf_t;
|
||||
|
||||
/**
|
||||
* @brief RTT control block which describes the number of buffers available
|
||||
* as well as the configuration for each buffer. The struct definition
|
||||
* is fixed, as it is expected by SEGGER's software
|
||||
*/
|
||||
typedef struct {
|
||||
char sentinel[16]; /* Initialized to "SEGGER RTT" */
|
||||
int32_t max_up_buffers; /* Initialized to 1 */
|
||||
int32_t max_down_buffers; /* Initialized to 1 */
|
||||
rtt_ringbuf_t up[1]; /* Up buffers, transferring information up
|
||||
from target via debug probe to host */
|
||||
rtt_ringbuf_t down[1]; /* Down buffers, transferring information
|
||||
down from host via debug probe to target */
|
||||
} segger_rtt_cb_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The SEGGER Real-Time-Terminal control block (CB)
|
||||
*/
|
||||
static segger_rtt_cb_t rtt_cb = {
|
||||
"SEGGER RTT",
|
||||
1,
|
||||
1,
|
||||
{{ "Terminal", &up_buffer[0], sizeof(up_buffer), 0, 0, 0 }},
|
||||
{{ "Terminal", &down_buffer[0], sizeof(down_buffer), 0, 0, 0 }},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief read bytes from the down buffer. This function does not block.
|
||||
* The logic here is unmodified from SEGGER's reference, it is just
|
||||
* refactored to match code style. The string is not null terminated.
|
||||
*
|
||||
* @return the number of bytes read
|
||||
*/
|
||||
static int rtt_read(char* buf_ptr, uint16_t buf_size) {
|
||||
int16_t num_bytes_rem;
|
||||
uint16_t num_bytes_read;
|
||||
int16_t rd_off;
|
||||
int16_t wr_off;
|
||||
|
||||
rd_off = rtt_cb.down[0].rd_off;
|
||||
wr_off = rtt_cb.down[0].wr_off;
|
||||
num_bytes_read = 0;
|
||||
|
||||
/* Read from current read position to wrap-around of buffer, first */
|
||||
if (rd_off > wr_off) {
|
||||
num_bytes_rem = rtt_cb.down[0].buf_size - rd_off;
|
||||
num_bytes_rem = MIN(num_bytes_rem, (int)buf_size);
|
||||
memcpy(buf_ptr, rtt_cb.down[0].buf_ptr + rd_off, num_bytes_rem);
|
||||
num_bytes_read += num_bytes_rem;
|
||||
buf_ptr += num_bytes_rem;
|
||||
buf_size -= num_bytes_rem;
|
||||
rd_off += num_bytes_rem;
|
||||
/* Handle wrap-around of buffer */
|
||||
if (rd_off == rtt_cb.down[0].buf_size) {
|
||||
rd_off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read remaining items of buffer */
|
||||
num_bytes_rem = wr_off - rd_off;
|
||||
num_bytes_rem = MIN(num_bytes_rem, (int)buf_size);
|
||||
if (num_bytes_rem > 0) {
|
||||
memcpy(buf_ptr, rtt_cb.down[0].buf_ptr + rd_off, num_bytes_rem);
|
||||
num_bytes_read += num_bytes_rem;
|
||||
rd_off += num_bytes_rem;
|
||||
}
|
||||
if (num_bytes_read) {
|
||||
rtt_cb.down[0].rd_off = rd_off;
|
||||
}
|
||||
return num_bytes_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write bytes to the up buffer. This function does not block.
|
||||
* The logic here is unmodified from SEGGER's reference, it is just
|
||||
* refactored to match code style. The string does not need to be null
|
||||
* terminated.
|
||||
*
|
||||
* @return the number of bytes read
|
||||
*/
|
||||
int rtt_write(const char* buf_ptr, unsigned num_bytes) {
|
||||
int num_bytes_to_write;
|
||||
unsigned num_bytes_written;
|
||||
int rd_off;
|
||||
|
||||
rd_off = rtt_cb.up[0].rd_off;
|
||||
num_bytes_to_write = rd_off - rtt_cb.up[0].wr_off - 1;
|
||||
if (num_bytes_to_write < 0) {
|
||||
num_bytes_to_write += rtt_cb.up[0].buf_size;
|
||||
}
|
||||
/* If the complete data does not fit in the buffer, trim the data */
|
||||
if ((int)num_bytes > num_bytes_to_write) {
|
||||
num_bytes = num_bytes_to_write;
|
||||
}
|
||||
|
||||
/* Early out if there is nothing to do */
|
||||
if (num_bytes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write data to buffer and handle wrap-around if necessary */
|
||||
num_bytes_written = 0;
|
||||
do {
|
||||
/* May be changed by host (debug probe) in the meantime */
|
||||
rd_off = rtt_cb.up[0].rd_off;
|
||||
num_bytes_to_write = rd_off - rtt_cb.up[0].wr_off - 1;
|
||||
if (num_bytes_to_write < 0) {
|
||||
num_bytes_to_write += rtt_cb.up[0].buf_size;
|
||||
}
|
||||
/* Number of bytes that can be written until buffer wrap-around */
|
||||
num_bytes_to_write = MIN(num_bytes_to_write, (rtt_cb.up[0].buf_size -
|
||||
rtt_cb.up[0].wr_off));
|
||||
num_bytes_to_write = MIN(num_bytes_to_write, (int)num_bytes);
|
||||
memcpy(rtt_cb.up[0].buf_ptr + rtt_cb.up[0].wr_off, buf_ptr, num_bytes_to_write);
|
||||
num_bytes_written += num_bytes_to_write;
|
||||
buf_ptr += num_bytes_to_write;
|
||||
num_bytes -= num_bytes_to_write;
|
||||
rtt_cb.up[0].wr_off += num_bytes_to_write;
|
||||
if (rtt_cb.up[0].wr_off == rtt_cb.up[0].buf_size) {
|
||||
rtt_cb.up[0].wr_off = 0;
|
||||
}
|
||||
} while (num_bytes);
|
||||
return num_bytes_written;
|
||||
}
|
||||
|
||||
void uart_stdio_init(void) {
|
||||
#ifndef RTT_STDIO_DISABLE_STDIN
|
||||
stdin_enabled = 1;
|
||||
#endif
|
||||
|
||||
#ifdef RTT_STDIO_ENABLE_BLOCKING_STDOUT
|
||||
blocking_stdout = 1;
|
||||
#endif
|
||||
|
||||
/* the mutex should start locked */
|
||||
mutex_lock(&_rx_mutex);
|
||||
}
|
||||
|
||||
void rtt_stdio_enable_stdin(void) {
|
||||
stdin_enabled = 1;
|
||||
mutex_unlock(&_rx_mutex);
|
||||
}
|
||||
|
||||
void rtt_stdio_enable_blocking_stdout(void) {
|
||||
blocking_stdout = 1;
|
||||
}
|
||||
|
||||
/* The reason we have this strange logic is as follows:
|
||||
If we have an RTT console, we are powered, and so don't care
|
||||
that polling uses a lot of power. If however, we do not
|
||||
actually have an RTT console (because we are deployed on
|
||||
a battery somewhere) then we REALLY don't want to poll
|
||||
especially since we are not expecting to EVER get input. */
|
||||
int uart_stdio_read(char* buffer, int count) {
|
||||
int res = rtt_read(buffer, count);
|
||||
if (res == 0) {
|
||||
if (!stdin_enabled) {
|
||||
mutex_lock(&_rx_mutex);
|
||||
/* We only unlock when rtt_stdio_enable_stdin is called
|
||||
Note that we assume only one caller invoked this function */
|
||||
}
|
||||
uint32_t last_wakeup = xtimer_now();
|
||||
while(1) {
|
||||
xtimer_periodic_wakeup(&last_wakeup, STDIO_POLL_INTERVAL);
|
||||
res = rtt_read(buffer, count);
|
||||
if (res > 0)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int uart_stdio_write(const char* buffer, int len) {
|
||||
int written = rtt_write(buffer, len);
|
||||
uint32_t last_wakeup = xtimer_now();
|
||||
while (blocking_stdout && written < len) {
|
||||
xtimer_periodic_wakeup(&last_wakeup, STDIO_POLL_INTERVAL);
|
||||
written += rtt_write(&buffer[written], len-written);
|
||||
}
|
||||
return written;
|
||||
}
|
Loading…
Reference in New Issue
Block a user