diff --git a/Makefile.dep b/Makefile.dep index 5519a2da325af92249944a91cf76f5ad83bfb23a..4f8b3329e85095c11deb90df2dd9368064226af7 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -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 diff --git a/sys/include/rtt_stdio.h b/sys/include/rtt_stdio.h new file mode 100644 index 0000000000000000000000000000000000000000..2ce337420eed28aa53f44163ab3f71d81efd421e --- /dev/null +++ b/sys/include/rtt_stdio.h @@ -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 */ diff --git a/sys/rtt_stdio/Makefile b/sys/rtt_stdio/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/sys/rtt_stdio/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/rtt_stdio/README.md b/sys/rtt_stdio/README.md new file mode 100644 index 0000000000000000000000000000000000000000..abd868f2a67e1246453301e5dde37457da1b5ce6 --- /dev/null +++ b/sys/rtt_stdio/README.md @@ -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 +``` diff --git a/sys/rtt_stdio/rtt_stdio.c b/sys/rtt_stdio/rtt_stdio.c new file mode 100644 index 0000000000000000000000000000000000000000..cd3370a0a5195019ff662c4b97b07331f6ca8ea8 --- /dev/null +++ b/sys/rtt_stdio/rtt_stdio.c @@ -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; +}