SEGGER RTT (1)
SEGGER RTT (1)
UART is Troublesome in Target-side
Printf() is probably the most handy debug tool since I learn C. In embedded world, we used UART to direct message to PC. After learning more things these years, I began to “hate” UART with following reasons:
- First, UART is quite slow. It requires ~0.1ms to transmit 1byte @ 9600bps. When CPU is running @ 16Mhz, it takes 1600 CPU cycles to transmit 1 character.
- Typical UART hardware had only few bytes of transmit buffer which is not sufficient to hold whole strings. A handy solution is polling the hardware to transmit data. The method is unacceptable when running time critical applications such as BLE.
- Most UART had interrupt mode which raises IRQ when transmit buffer is empty. This means additional effort to bring up such mechanism. Besides, we need a ring buffer to store string-to-send.
- To have “advanced” (troublesome) solution in (3), one shall deal with UART HW which varies chip by chip.
- Besides, use UART to transmit string is quite wasteful since it takes long time to do string format (probably we need some tiny C library).
UART is More Troublesome in PC-side
In PC side, the COM port is probably extinct and we need a UART to USB IC such as FTDI-based chip. The development board in nowadays (2016~) typically had such IC. However, the solution is still not perfect for me.
When running terminal program such as “Tera-Term”, I need to setup COM-port with following parameters:
- COM port number
- # of start bits
- # of data bits
- # of end bits
- Parity bits
Among all parameters, COM port number is the most troublesome part. The port number changes as I plug the USB connector to different holes. I need to open “device manager” and find corresponding COM-port to have my port.
SEGGER RTT Introduction
Every time when I have a new development board, I always tried to bring up UART interface to see “Hello World”. I don’t like the polling method to throw the log, which is ugly and not scalable. IRQ-style log service takes time and error-prone (IRQ is always harder to implement). For my personal aesthetic, I can’t accept both solutions such that the toy (development board) is thrown away very soon.
Thanks to the ARM’s AHB-AP capability which enables debugger reading system memories without halting system. More specifically, MCU is running the application while SEGGER-JLINK could read/write system memory. This is amazingly improvement for Embedded development. Let’s see how this could solve my problem.
SEGGER-RTT implemented a ring-buffer in target side looks like:
struct {
u8 token[11];
u8 buf[1024];
u32 wptr; // MCU updates when write to buf[]
u32 rptr; // PC updates the value
} ring_buf;
MCU uses following function to put a character in the buffer, which is quite faster (<30 cycles). The speed is ~50x faster than UART-polling mode @ 9600bps.
ring_buf rtt = {
.token = "SEGGER-RTT"; // Special token enables SEGGER-JLINK to find the structure
};
rtt_write_char(char c)
{
disable_irq();
rtt.buf[rtt.wptr] = c;
rtt.wptr = (rtt.wptr+1) & (1024-1); // ring buffer management
enable_irq();
}
SEGGER-JLINK first scan MCU’s data memory via AHB-AP to find the special token “SEGGER-RTT” to locate the ring-buffer address. Then SEGGER-JLINK periodically read the RTT control data to find whether data is available:
check_target_buf()
{
u8 local_buf[32KB];
u8 *ptr = &local_buf;
u32 wptr = read_target_memory(rtt.wptr);
u32 rptr = read_target_memory(rtt.rptr);
while(wptr != rptr) {
*ptr++ = read_target_memory(rtt.buf[rptr]);
rptr = (rptr + 1) & 1023;
}
write_target_memory(rtt.rptr, rptr); // updates rptr
}
PC-side application then prints the message to some window or do required post-processing.
Compare to the fastest UART solution (UART-IRQ), both solutions need a ring buffer that application writes the message to. Write to ring buffer is quite fast and clean. However, UART requires additional MCU effort to move data from rtt.buf[] to UART hardware. In RTT solution, the data movement is done in PC & JLINK hardware which doesn’t waste MCU cycles. In this aspect, logging system overhead is less in RTT approach.
RTT is based on SWD (Serial Wire Debug) interface which only requires 2 pins {SWDIO, SWCLK}. We could reuse SWD interface to have log service. In UART solution, we need additional 2 pins {RX, TX} to have log service. One significant drawback of RTT is that we need SEGGER-JLINK (~80USD) to read log message from target board. For UART, we only need a FTDI-based chip (<10USD).
I have a standalone SEGGER-JLINK and many development boards nowadays equipped it on board. So RTT is a portable solution for me. I would recommend to use RTT in replace of UART from now.
In next article, I would introduce how to port SEGGER-RTT to the development board.
留言