All guides
TECHNICAL GUIDE STM32 DEBUG J-LINK 2026

SEGGER J-Link
Commander & GDBServer CLI

Flash, run and inspect an STM32L4R5ZI from the terminal — JLinkExe, JLinkGDBServer + arm-none-eabi-gdb, SEGGER RTT, and scripted batch flashing.

01 J-Link toolchain and connecting with JLinkExe

A SEGGER J-Link talks to the STM32L4R5ZI Cortex-M4F core over the 2-wire SWD interface (SWDIO + SWCLK). The whole workflow — connect, flash, run, halt, read memory, stream printf — is available from the command line with no IDE. This guide targets the NUCLEO-L4R5ZI board and the STM32L4R5ZI device (2 MB dual-bank flash at 0x08000000, 640 KB SRAM at 0x20000000).

The CLI tools in the J-Link pack

The "J-Link Software and Documentation Pack" installs a family of standalone binaries. On Linux they live in /opt/SEGGER/JLink/ and are symlinked into PATH.

Binary (Linux)What it does
JLinkExeJ-Link Commander — interactive shell: connect, flash, halt, memory/register access
JLinkGDBServerGDB remote server (GUI); arm-none-eabi-gdb connects over TCP
JLinkGDBServerCLExeSame server, pure command-line (headless / CI)
JLinkRTTClientTelnet-style terminal for a running SEGGER RTT session
JLinkRTTLoggerOpens its own connection and logs one RTT channel to a file
JLinkSWOViewerDecodes SWO/ITM printf from the TRACESWO pin
JFlashExe / JFlashLiteExeScriptable production flash programmer (project + -auto)
JLinkRemoteServerExposes a J-Link over the network for remote debugging
NOTE

There is no separate binary literally called JLinkFlash in the Linux pack. Command-line flashing is done either with JLinkExe -CommandFile (Section 04) or with JFlashExe for production runs. Both are covered below.

Install and udev rules (Debian/Ubuntu)

bash — install
# Download JLink_Linux_*_x86_64.deb from segger.com/downloads/jlink
sudo dpkg -i JLink_Linux_V818_x86_64.deb

# The package installs /etc/udev/rules.d/99-jlink.rules for non-root USB access
sudo udevadm control --reload-rules && sudo udevadm trigger

# Sanity check: version banner + connected probe
JLinkExe -CommandFile /dev/null   # prints DLL version, HW version, S/N and exits

One-line connect to the target

The four things J-Link always needs are the device, the interface, the speed, and a request to actually connect. Pass them on the command line so no dialog appears:

bash — open an interactive session
JLinkExe -device STM32L4R5ZI -if SWD -speed 4000 -autoconnect 1
expected output
SEGGER J-Link Commander V8.18 (Compiled ...)
Connecting to J-Link via USB...O.K.
Firmware: J-Link ... compiled ...
Hardware version: V10.10
Device "STM32L4R5ZI" selected.
Connecting to target via SWD
Found SW-DP with ID 0x2BA01477
AP map detection skipped. AP[0]: AHB-AP (IDR: 0x24770011)
Cortex-M4 identified.
J-Link>
WHY THESE VALUES

STM32L4R5ZI is J-Link's built-in device name — it selects the correct flash loader (single- vs dual-bank, see Section 02/07). SWD is the 2-wire interface used by every Cortex-M. 4000 kHz is a safe, fast default; you can raise it to 15000 or use -speed adaptive once the link is proven stable. The SW-DP ID 0x2BA01477 and Cortex-M4 identified lines confirm the electrical connection is good.

02 J-Link Commander command reference

Inside the J-Link> prompt you drive the CPU directly. Commands are case-insensitive; most have a short alias (in parentheses below). Square brackets are optional arguments.

CommandAliasMeaning
ConnectconEstablish the debug connection (uses current device/if/speed)
SelectInterface SWDsi SWDChoose SWD (0) or JTAG (1)
Speed 4000Set SWD clock in kHz, or auto / adaptive
ResetrReset and halt the CPU
HalthStop the core
GogResume execution
Step [n]sSingle-step n instructions (halted core)
IsHaltedihReport whether the core is halted
RegsDump all Cortex-M core registers
RReg <name>Read one register, e.g. RReg PC
WReg <name> <val>Write one register
SetPC <addr>Set the program counter
Mem <addr> <n>Read n bytes, hex + ASCII dump
Mem8/Mem16/Mem32Read n items of that width
Write4 <addr> <val>w4Write a 32-bit word (also w1/w2/w8)
SaveBin <f> <addr> <n>Dump n bytes of memory to a binary file
LoadFile <f> [addr]Program flash from ELF/HEX/SREC/BIN (Section 03)
VerifyBin <f> <addr>Compare a bin file against memory
Erase [sAddr eAddr]Erase flash (whole chip or a range)
Sleep <ms>Pause the script for ms milliseconds
exec <string>Run a J-Link command string (Section 05)
ExitqClose the connection and quit

A full inspection session

Reset the core, look at registers, read the vector table (the first word at 0x08000000 is the initial stack pointer; the second is the reset vector), poke a RAM word, then run.

J-Link Commander — interactive
# started with: JLinkExe -device STM32L4R5ZI -if SWD -speed 4000 -autoconnect 1
J-Link> r                        # reset + halt
Reset delay: 0 ms
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ ...
J-Link> regs
R0 = 0x00000000, R1 = 0x00000000, R2 = 0x00000000, R3 = 0x00000000
...
SP(R13)= 0x2009FFE8, LR(R14)= 0xFFFFFFFF, PC(R15)= 0x08000B34
XPSR = 0x01000000
J-Link> mem32 0x08000000 2      # initial SP, then Reset_Handler address
08000000 = 2009FFE8 08000B35
J-Link> w4 0x20000000 0xDEADBEEF   # write a 32-bit word to SRAM1
J-Link> mem32 0x20000000 1
20000000 = DEADBEEF
J-Link> savebin vectors.bin 0x08000000 0x400
Opening binary file for writing... [vectors.bin]
Reading 1024 bytes from addr 0x08000000 into file...O.K.
J-Link> g                        # resume
J-Link> h                        # halt again — PC now inside your main loop
J-Link> exit
TIP

The low bit of the Reset_Handler value (08000B35) is the Thumb bit — Cortex-M always executes Thumb, so vector table entries are odd. The actual instruction address is 0x08000B34, which is exactly the PC shown by regs.

03 Flashing firmware: loadfile, erase, verify

LoadFile is the workhorse. It auto-detects the format from the extension and programs the correct addresses. J-Link erases only the sectors it touches and verifies programmed data with a CRC as it goes.

File typeCommandAddress source
ELF (.elf/.axf)loadfile fw.elfLoad addresses from ELF program headers
Intel HEX (.hex)loadfile fw.hexAddresses embedded in the HEX records
Motorola SREC (.srec)loadfile fw.srecAddresses embedded in the SREC records
Raw binary (.bin)loadfile fw.bin 0x08000000You must supply the address
J-Link Commander — program + verify
J-Link> r
J-Link> halt
J-Link> loadfile firmware.hex
Downloading file [firmware.hex]...
J-Link: Flash download: Bank 0 @ 0x08000000: 1 range affected (65536 bytes)
J-Link: Flash download: Total: 1.204s (Prepare: 0.041s, Compare: 0.010s,
        Erase: 0.352s, Program & Verify: 0.760s, Restore: 0.039s)
O.K.
J-Link> # For a raw .bin the base address is mandatory:
J-Link> loadfile firmware.bin 0x08000000
J-Link> # Independent read-back verify of a .bin against flash:
J-Link> verifybin firmware.bin 0x08000000
Verifying data ... O.K.
J-Link> r
J-Link> g

Erasing

J-Link Commander — erase
J-Link> erase                          # full-chip erase of internal flash
J-Link> erase 0x08000000 0x08010000    # erase only the first 64 KB range
DUAL-BANK FLASH

The STM32L4R5 has 2 MB of flash split into bank 1 (0x08000000) and bank 2 (0x08100000) when the DUALBANK option byte is 1 (the factory default). Since J-Link V7.86a the pack ships two loaders and picks the right one automatically from the device name — you do not normally configure this. Only if you changed DUALBANK yourself should you check that the loader matches, or images near the 1 MB boundary can mis-program.

04 Scripting JLinkExe with command files

For CI and production you want JLinkExe to run unattended. A command file is a plain text file with one Commander command per line. Pass it with -CommandFile (or its alias -CommanderScript).

flash.jlink — command file
// One command per line. // and # start comments.
device STM32L4R5ZI
si SWD
speed 4000
connect
r
halt
erase
loadfile firmware.hex
verifybin firmware.hex
r
g
exit
bash — run the script
# Device/interface can live in the file OR on the CLI. -ExitOnError makes
# JLinkExe stop (and return non-zero) the moment a command fails. -NoGui 1
# suppresses every dialog so it never blocks on a headless machine.
JLinkExe -NoGui 1 -ExitOnError 1 -CommandFile flash.jlink

A robust wrapper with exit-code checking

flash.sh — production wrapper
#!/usr/bin/env bash
set -euo pipefail

DEVICE="STM32L4R5ZI"
IMAGE="${1:-firmware.hex}"

# Build a command file on the fly so the image path is a variable
SCRIPT="$(mktemp)"
cat > "$SCRIPT" <<EOF
device $DEVICE
si SWD
speed 4000
connect
r
halt
erase
loadfile $IMAGE
r
g
exit
EOF

# -ExitOnError 1 is what makes the return code trustworthy
if JLinkExe -NoGui 1 -ExitOnError 1 -CommandFile "$SCRIPT"; then
    echo "FLASH OK: $IMAGE -> $DEVICE"
else
    rc=$?
    echo "FLASH FAILED (rc=$rc)" >&2
    rm -f "$SCRIPT"
    exit "$rc"
fi
rm -f "$SCRIPT"
JFlashExe FOR PRODUCTION

For high-volume programming with serial-number injection and blank-check, JFlashExe is the purpose-built tool. It runs a saved .jflash project non-interactively:

bash — JFlashExe batch mode
JFlashExe -openprj stm32l4r5.jflash -open firmware.hex -auto -exit
# -auto = erase+program+verify in one go; -exit closes when done.
# Exit code 0 = success; check $? in your build script.

05 JLinkGDBServer and arm-none-eabi-gdb

For source-level debugging, JLinkGDBServer bridges the J-Link to a TCP port; then any GDB (arm-none-eabi-gdb) attaches with target remote. The server owns the probe; GDB just speaks the remote protocol.

Default ports

PortPurpose
2331GDB remote protocol — target remote :2331
2332SWO raw output
2333Telnet / semihosting printf terminal

Key server options

OptionEffect
-device STM32L4R5ZITarget device (required; skips the dialog)
-if SWDInterface: SWD or JTAG
-speed 4000Clock in kHz (or adaptive)
-port 2331Override the GDB port
-swoport 2332 / -telnetport 2333Override SWO / Telnet ports
-singlerunExit after the GDB client disconnects (great for CI)
-noguiNo window; log to stdout
-haltHalt the core on attach (default)
-strictAbort on any configuration error
-SelectEmuBySN <sn>Pick a specific probe when several are plugged in
-settingsfile / -jlinkscriptfileLoad a settings or J-Link script file
terminal A — start the server
# GUI-less variant is JLinkGDBServerCLExe; identical options.
JLinkGDBServerCLExe -device STM32L4R5ZI -if SWD -speed 4000 \
                    -port 2331 -singlerun -nogui
# ...
# Connecting to target...Connected to target
# Waiting for GDB connection...
terminal B — drive it with GDB
arm-none-eabi-gdb firmware.elf

(gdb) target remote localhost:2331
(gdb) monitor reset            # J-Link-specific: reset + halt
(gdb) load                     # flash the ELF over the debug link
(gdb) monitor semihosting enable
(gdb) break main
(gdb) monitor reset
(gdb) continue                 # runs to main()

Reusable .gdbinit

.gdbinit — auto-connect + flash
# Run: arm-none-eabi-gdb -x .gdbinit firmware.elf
target remote localhost:2331
monitor speed 4000
monitor reset
load
monitor semihosting enable
break main
monitor reset
# 'continue' left out so you land at the prompt ready to inspect
monitor commandEffect
monitor resetReset and halt the core
monitor halt / monitor goStop / resume
monitor speed 4000Change SWD clock live
monitor flash download = 1Let load program flash (default on)
monitor semihosting enableRoute printf/SYS_WRITE0 to the Telnet port
monitor reg <name>Read a core register
monitor RTTStartBegin RTT auto-detection (see Section 06)

06 SEGGER RTT: RTTClient and RTTLogger

Real-Time Transfer (RTT) is SEGGER's zero-instrumentation-overhead printf: your firmware writes into a ring buffer in SRAM, and J-Link reads it out over SWD while the CPU keeps running — no UART, no SWO pin, sub-microsecond cost per message. J-Link locates the buffer by scanning RAM for the "SEGGER RTT" ID string in a control block.

Firmware side — complete, compilable

Add SEGGER's SEGGER_RTT.c, SEGGER_RTT_printf.c and the RTT/ + Config/ headers to your build (they ship inside the J-Link pack under Samples/RTT/). This bare-metal example uses direct register access — no HAL — and blinks NUCLEO-L4R5ZI's green LED on PC7 while streaming counters over RTT channel 0.

main.c — register-level, CMSIS + RTT
/* Build with: arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16
 *   -mfloat-abi=hard -DSTM32L4R5xx -O2 main.c SEGGER_RTT.c SEGGER_RTT_printf.c
 *   startup_stm32l4r5xx.s -T STM32L4R5ZITx_FLASH.ld -o firmware.elf          */
#include <stdint.h>
#include "stm32l4r5xx.h"   /* CMSIS device header from STM32Cube_FW_L4 */
#include "SEGGER_RTT.h"    /* from the J-Link pack: Samples/RTT/       */

/* NUCLEO-L4R5ZI user LEDs: LD1 green = PC7, LD2 blue = PB7, LD3 red = PB14 */
#define LED_PIN   7U

static void led_init(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;              /* clock GPIOC        */
    GPIOC->MODER  &= ~(3U << (LED_PIN * 2));           /* clear mode bits    */
    GPIOC->MODER  |=  (1U << (LED_PIN * 2));           /* 01 = general output*/
}

static void delay(volatile uint32_t n) { while (n--) __asm volatile("nop"); }

int main(void)
{
    led_init();
    SEGGER_RTT_Init();
    SEGGER_RTT_WriteString(0, "STM32L4R5ZI: RTT channel 0 is up\r\n");

    uint32_t i = 0;
    while (1) {
        GPIOC->ODR ^= (1U << LED_PIN);                /* toggle the LED     */
        SEGGER_RTT_printf(0, "tick %u  ODR=0x%08X\r\n",
                          (unsigned)i++, (unsigned)GPIOC->ODR);
        delay(1000000);
    }
}

Host side — two ways to read it

Option A — JLinkRTTClient. Keep any J-Link connection open (a running JLinkExe or JLinkGDBServer), then attach a terminal. Narrowing the search range makes detection instant and reliable on the 640 KB SRAM:

terminal A — connection that hosts RTT
JLinkExe -device STM32L4R5ZI -if SWD -speed 4000 -autoconnect 1
# Restrict the RAM scan to the 640 KB SRAM block (fast, deterministic):
J-Link> exec SetRTTSearchRanges 0x20000000 0xA0000
J-Link> g
# If you know the exact control-block address (e.g. from the .map file):
J-Link> exec SetRTTAddr 0x20000000
terminal B — the RTT terminal
JLinkRTTClient
# ###RTT Client: Connected...
# STM32L4R5ZI: RTT channel 0 is up
# tick 0  ODR=0x00000080
# tick 1  ODR=0x00000000
# tick 2  ODR=0x00000080

Option B — JLinkRTTLogger. A self-contained tool: it opens its own connection (no Commander needed) and streams one channel to a file — ideal for long unattended captures.

bash — log channel 0 to a file
JLinkRTTLogger -Device STM32L4R5ZI -If SWD -Speed 4000 \
               -RTTChannel 0 rtt_channel0.log

# Point it straight at the control block to skip the RAM search:
JLinkRTTLogger -Device STM32L4R5ZI -If SWD -Speed 4000 \
               -RTTAddress 0x20000000 -RTTChannel 0 rtt_channel0.log
Tool / optionMeaning
JLinkRTTClient -RTTTelnetPort <p>Terminal client; default telnet port 19021
JLinkRTTClient -LocalEcho OffDisable local echo of typed input
JLinkRTTLogger -RTTChannel <n>Which up-channel to record (0 = default log)
JLinkRTTLogger -RTTAddress <a>Exact control-block address (skip search)
exec SetRTTSearchRanges <a> <len>Limit the RAM scan; here 0x20000000 0xA0000
exec SetRTTAddr <a>Pin the control block; fastest, most reliable

07 Registers, memory and SWD/SWO pin reference

Reference tables you will reach for constantly: the Cortex-M4 core registers that regs/monitor reg print, the STM32L4R5 debug pins to wire, and the memory map that anchors your mem, loadfile and RTT addresses.

Cortex-M4 core registers

RegisterRole
R0–R12General-purpose (R0–R3, R12 are caller-saved / argument regs)
SP (R13)Stack pointer — banked as MSP / PSP
LR (R14)Link register — return address; EXC_RETURN in an ISR
PC (R15)Program counter
XPSRProgram status (APSR flags + IPSR exception# + EPSR Thumb bit)
MSP / PSPMain / Process stack pointers
PRIMASK / FAULTMASK / BASEPRIInterrupt-masking registers
CONTROLPrivilege level, stack selection, FPU active bit
S0–S31 / FPSCRSingle-precision FPU registers (Cortex-M4F)

STM32L4R5 debug pins (default SWJ-DP)

SignalPinAFNotes
SWDIOPA13AF0SWD data — do not remap on the debug port
SWCLKPA14AF0SWD clock
SWO / TRACESWOPB3AF0ITM/SWO printf and trace (also JTDO)
NRSTNRSTOptional hardware reset line
JTDIPA15AF0Only if using 4-wire JTAG
NJTRSTPB4AF0Only if using 4-wire JTAG
NOTE

After reset the SWJ-DP has SWDIO/SWCLK enabled automatically, so J-Link can always attach on a blank chip. RTT does not use SWO/PB3 at all — it rides the SWD data lines. Reserve PB3 only if you additionally want ITM/SWO printf via JLinkSWOViewer.

STM32L4R5ZI memory map (essentials)

RegionBaseSize
Flash bank 10x080000001 MB
Flash bank 2 (DUALBANK=1)0x081000001 MB
SRAM10x20000000192 KB
SRAM20x2003000064 KB (also aliased at 0x10000000)
SRAM30x20040000384 KB — total SRAM = 640 KB, contiguous to 0x2009FFFF
OCTOSPI mapped flash0x90000000up to 256 MB (external QSPI/OSPI)
Peripherals0x40000000APB/AHB registers
Cortex-M4 PPB (SCS/ITM/DWT)0xE0000000Debug/trace core registers

The full 640 KB SRAM is one contiguous block 0x20000000–0x2009FFFF, which is exactly the SetRTTSearchRanges 0x20000000 0xA0000 range used in Section 06.

08 Gotchas and common mistakes

The failures that eat the most time are rarely the tool — they are the device name, the interface state, or a mismatched port. Work down this list first.

Wrong / missing device nameIf you pass a generic name (or none) J-Link loads no flash loader and loadfile silently does nothing useful. Always use the exact built-in name STM32L4R5ZI — this is what selects the correct dual-bank loader.
.bin without an addressloadfile firmware.bin programs to address 0 and fails. Raw binaries carry no address — you must write loadfile firmware.bin 0x08000000. ELF/HEX/SREC carry their own addresses.
Trusting the exit code without -ExitOnErrorBy default JLinkExe can return 0 even after a failed command. In scripts always add -ExitOnError 1 (and -NoGui 1) so the return code is meaningful and no dialog blocks a headless run.
Connect fails on a sleeping / low-power targetIf the STM32 is in a deep low-power mode or holds the debug pins, SWD can drop. Use -speed adaptive, lower the speed, or configure connect under reset (exec EnableEraseAllFlashBanks is unrelated — for connect issues use ResetType / connect-under-reset). A held NRST also blocks attach.
GDB "connection refused"The GDB port is 2331, not the Telnet 2333. Confirm the server printed "Waiting for GDB connection" and that -singlerun did not already close it after a previous session.
Two owners of one probeA running JLinkGDBServer and a separate JLinkRTTLogger both try to open the J-Link and collide. Use one owner: read RTT through the GDB server (monitor RTTStart + JLinkRTTClient) or through a single JLinkExe — not two independent connections.
RTT prints nothingThe control block must exist in RAM before J-Link can find it — the CPU has to have run past SEGGER_RTT_Init()/the first RTT write. Let it Go, and narrow the hunt with exec SetRTTSearchRanges 0x20000000 0xA0000 or pin it with SetRTTAddr.
Speed too high for the wiringLong jumper leads or a breadboard cannot hold 15 MHz SWD. Symptoms are intermittent Cannot read from AP errors. Drop to -speed 4000 or lower, then raise once stable.
Readout protection (RDP)If RDP level 1 is set, flash reads/verify fail. J-Link can regress RDP to level 0, but that mass-erases the device. Never set RDP level 2 during development — it is permanent and locks out the debug port forever.
Permission denied on the USB deviceIf the udev rule did not install, you get a USB error as a normal user. Reinstall the .deb (or copy 99-jlink.rules), then udevadm control --reload-rules && udevadm trigger, and re-plug the probe.

The 30-second workflow

  • Flash once: JLinkExe -device STM32L4R5ZI -if SWD -speed 4000 -NoGui 1 -ExitOnError 1 -CommandFile flash.jlink
  • Debug: JLinkGDBServerCLExe -device STM32L4R5ZI -if SWD -speed 4000 -singlerun then arm-none-eabi-gdb -x .gdbinit firmware.elf
  • Logs: add SEGGER_RTT_printf(0, ...), run, attach JLinkRTTClient (or capture with JLinkRTTLogger)