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;
+}