Back to Home

RTT CLI for Cortex-M via J-Link SWD

The article describes the integration of CLI shell via J-Link RTT on ARM Cortex-M. Initialization code, command processing, and response sending are provided. Viewer setup, TCP access, and comparison with UART are discussed.

CLI via RTT: SWD shell for ARM without UART
Advertisement 728x90

Setting Up a CLI Shell via J-Link RTT for ARM Cortex-M

To implement a CLI over SWD without UART, use the SEGGER_RTT library. Download the source files: SEGGER_RTT.h (API, 253 lines), SEGGER_RTT.c (implementation, 1751 lines), SEGGER_RTT_printf.c (printf, 516 lines), SEGGER_RTT_Conf.h (configuration, 333 lines).

Start by initializing the zero RTT channel:

void SeggerInit() {
    SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
    SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);    
    SEGGER_RTT_SetTerminal(0); 
}

bool segger_rtt_init_custom(void) {
    bool res = true;
    SeggerInit();
    return res;
}

Call SeggerInit() at firmware startup to configure non-blocking transmit and receive buffers.

Google AdInline article slot

Processing Incoming Commands

Periodically poll the channel via SEGGER_RTT_Read in the main loop or a timer:

bool segger_rtt_proc_one(uint8_t num) {
    bool res = false;
    LOG_PARN(SEGGER_RTT, "RTT%u,Proc", num);
    SeggerRttHandle_t* Node = SeggerRttGetNode(num);
    if(Node) {
        if(Node->RxBuffer) {
            if(Node->buffer_size) {
                unsigned rx_byte = SEGGER_RTT_Read(Node->BufferIndex, 
                                                   Node->RxBuffer, 
                                                   Node->buffer_size);
                if(rx_byte) {
                    res = segger_rtt_writer(num);
                    LOG_DEBUG(SEGGER_RTT,"RxData:[%s]=[%s],rxByteCnt:%u Bytes", 
                              ArrayToStr(Node->RxBuffer,rx_byte), 
                              ArrayToAsciiStr(Node->RxBuffer,rx_byte), 
                              rx_byte);
                    res = cli_process_data(Node->cli_num, 
                                           Node->RxBuffer, 
                                           rx_byte);
                    memset(Node->RxBuffer, 0, rx_byte);
                }
            }
        }
    }
    return res;
}

The function returns the number of bytes received. Pass the data to the CLI handler: cli_process_data().

Sending Responses from Firmware

To transmit strings, use SEGGER_RTT_Write. Implement functions for putc/puts and a FIFO buffer:

Google AdInline article slot
void segger_rtt1_puts(void* stream_ptr, const char* str, int32_t len) {
    SeggerRttHandle_t* Node = SeggerRttGetNode(1);
    if(Node) {
        if(str) {
            if(len) {
                unsigned tx_cnt = SEGGER_RTT_Write(Node->BufferIndex,
                                                   (void* )  str,
                                                   (unsigned) len);
                (void)tx_cnt;
            }
        }
    }
}

void segger_rtt1_putc(void* stream_ptr, char ch) {
    SeggerRttHandle_t* Node = SeggerRttGetNode(1);
    if(Node) {
        unsigned tx_cnt = SEGGER_RTT_Write(Node->BufferIndex,
                                           (void* )  &ch,
                                           (unsigned)  1);
        (void)tx_cnt;
    }
}

Extract data from the Tx FIFO and write it to the RTT channel. This ensures asynchronous transmission without data loss.

  • FIFO Advantages: Buffering prevents overflow during peak loads.
  • Monitoring: Log tx_cnt to debug any missed transmissions.
  • Scaling: Support for multiple channels (num > 0).

Configuring J-Link RTT Viewer on PC

Install the SEGGER J-Link package. Run JLinkRTTViewer.exe from C:\Program Files\SEGGER\JLink_V834.

Configure the connection:

Google AdInline article slot
  • Select SWD or JTAG.
  • Specify the target device (ARM Cortex-M).
  • Set the speed (up to megahertz).
  • Confirm the connection.

Enter commands in Terminal 0 — supports color highlighting. If FIFO overflows, edit SEGGER_RTT_Conf.h to increase buffer sizes.

Alternative Access via TCP

RTT Viewer opens a socket on 127.0.0.1:19021 (check with netstat -ano | grep 19021).

Connect using PuTTY or Tera Term:

  • Host: 19021
  • Type: TCP

This emulates a UART terminal. Works remotely over a WiFi network.

Advantages and Limitations of RTT CLI

Advantages:

  • Parallel operation with step-by-step debugging.
  • No ELF file required: flash and pass to J-Link.
  • Instant transmission at SWD speeds.
  • Copy-paste logs with highlighting.
  • Multiple terminals.

Disadvantages:

  • Dependency on J-Link (not for AVR/ESP32/RISC-V).
  • Disconnection on MCU reset — requires manual reconnection.
  • Separate input window (solved via TCP).
  • No support for \r in Viewer (available in Tera Term).

| Aspect | RTT via SWD | UART |

|--------|-------------|------|

| Speed | MHz SWD | 115200+ |

| Pins | 3-4 | 2+ |

| Debugging | Parallel | No |

| Reset | Disconnection | Stable |

Key Points

  • Initialize RTT before main() for early logging.
  • Use NO_BLOCK_SKIP to avoid hangs.
  • Monitor FIFO overflow via tx/rx_cnt logs.
  • TCP 19021 solves UI Viewer issues.
  • Suitable for Cortex-M with J-Link support.

— Editorial Team

Advertisement 728x90

Read Next