---
name: jlink-debug
description: Operate SEGGER J-Link for embedded bring-up and hardware-in-the-loop loops: detect probes, flash firmware, start/stop GDB server, resolve RTT address from map files, capture RTT logs, and drive gdb in batch mode for breakpoints/step/variable inspection. Use when a user asks to burn/download firmware, run real-board debug sessions, set breakpoints, inspect memory/registers, or automate compile→flash→RTT verification on STM32/MCU projects.
---
Agent-friendly J-Link automation. All commands support --json for machine-readable output.
JLinkExe,JLinkGDBServer,JLinkRTTLoggerin PATH (SEGGER J-Link Software Pack)arm-none-eabi-gdbin PATH forgdb-batch- Python 3.9+ (no external deps)
When used in the firmware-pro2 repo, prefer these parameters:
| Param | Value |
|---|---|
--device |
ONEKEYH7 (custom device ID mapped to STM32H747XI_M7) |
--speed |
12000 |
--gdb |
/Users/wangyunlong/onekey_toolchains/arm-gnu-toolchain-15.2.rel1/bin/arm-none-eabi-gdb |
--gdb-port |
50000 |
--rtt-port |
19021 |
| ELF path | .build/arm-toolchain-debug/executables/apps/core/core.elf |
| HEX path | .build/arm-toolchain-debug/executables/apps/core/core.hex |
| Map path | .build/arm-toolchain-debug/executables/apps/core/core.map |
| RTT symbol | _SEGGER_RTT (resolved dynamically from map) |
If only one J-Link is connected, first run probe to get the serial number; pass it to all subsequent commands.
python3 scripts/jlink_agent.py probe --json
# → {"ok": true, "serial_numbers": ["801039104"], ...}python3 scripts/jlink_agent.py flash \
--device ONEKEYH7 \
--serial 801039104 \
--firmware .build/arm-toolchain-debug/executables/apps/core/core.hex \
--speed 12000 \
--jsonUses a r / h / loadfile / r / g / exit JLinkExe script — simpler and more reliable than gdb load, and leaves the CPU running.
python3 scripts/jlink_agent.py rtt-addr \
--map .build/arm-toolchain-debug/executables/apps/core/core.map \
--json
# → {"ok": true, "address": "0x24008410"}# Start (background by default)
python3 scripts/jlink_agent.py gdbserver-start \
--device ONEKEYH7 --serial 801039104 \
--gdb-port 50000 --rtt-port 19021 --speed 12000 --json
# Stop any running instance
python3 scripts/jlink_agent.py gdbserver-stop --json# Requires JLinkGDBServer to NOT be running (RTTLogger opens its own session)
python3 scripts/jlink_agent.py rtt-capture \
--device ONEKEYH7 --serial 801039104 \
--address 0x24008410 \
--out /tmp/boot.log --duration 20 --speed 12000 --jsonThen read the resulting file with the Read tool — cleaner than interactive JLinkRTTClient.
Runs arm-none-eabi-gdb in batch mode against an already-running JLinkGDBServer, executes a list of gdb commands, and returns the captured stdout/stderr as JSON. Use this for agent-driven interactive debugging — set a breakpoint, let it hit, print variables, dump registers, then exit.
Prerequisite: a JLinkGDBServer is already listening on --gdb-port. Start one via gdbserver-start first.
python3 scripts/jlink_agent.py gdb-batch \
--elf .build/arm-toolchain-debug/executables/apps/core/core.elf \
--gdb /Users/wangyunlong/onekey_toolchains/arm-gnu-toolchain-15.2.rel1/bin/arm-none-eabi-gdb \
--gdb-port 50000 \
--commands \
"break task_foreground" \
"continue" \
"info registers" \
"bt" \
"print lvgl_disp_hres" \
"print lvgl_disp_vres" \
--timeout 30 \
--jsonEach --commands arg becomes a separate -ex passed to gdb. The skill prepends target remote :<port> and monitor halt, and appends quit. So the actual gdb invocation is:
arm-none-eabi-gdb -batch -nx \
-ex "target remote :50000" \
-ex "monitor halt" \
-ex "break task_foreground" \
-ex "continue" \
-ex "info registers" \
-ex "bt" \
-ex "print lvgl_disp_hres" \
-ex "print lvgl_disp_vres" \
-ex quit \
core.elf
For longer sequences, write a gdb script file and pass it via --script:
cat > /tmp/debug.gdb <<'EOF'
break page_manager_push
commands
silent
printf "push id=%d\n", id
continue
end
continue
EOF
python3 scripts/jlink_agent.py gdb-batch \
--elf core.elf --gdb arm-none-eabi-gdb --gdb-port 50000 \
--script /tmp/debug.gdb --timeout 20 --jsonHalt and inspect current state (no breakpoint needed):
--commands "info registers" "bt" "print g_alert"The skill already halts the CPU on connect, so you don't need to add monitor halt yourself.
Hit a breakpoint and dump context:
--commands "break connect_app_wallet_create" "continue" \
"bt full" "info locals" "info registers"Step through code:
--commands "break page_manager_push" "continue" "step" "step" "bt"Inspect a variable or struct:
--commands "print lvgl_disp_hres" "print *lv_scr_act()"Read memory at an address:
--commands "x/32xw 0x24008410" # 32 words at the RTT control blockDump the framebuffer header:
--commands "x/4xw 0xD0000000" "x/4xw 0xD0200000"{
"ok": true,
"returncode": 0,
"stdout": "<full gdb output, parse for hits/values>",
"stderr": "",
"cmdline": ["arm-none-eabi-gdb", "-batch", ...]
}On timeout the skill returns {"ok": false, "timeout": true, ...} with whatever output gdb produced so far. Raise --timeout if you're waiting on a breakpoint that takes a while to hit.
ExecBuild— build the target (core_outputs)jlink-debug probe— get J-Link serialjlink-debug flash— burn.hexjlink-debug rtt-capture— grab boot logs (stop beforehand: any gdbserver)framebuffer-dump— read LCD content as PNG- If something's wrong:
jlink-debug gdbserver-startin backgroundjlink-debug gdb-batchwith breakpoints / printsjlink-debug gdbserver-stopwhen done
- Never flash without explicit board-to-SN mapping (
--serial). gdb-batchhalts the CPU on connect — your firmware will be paused until gdb exits. Pass--no-haltonly when you deliberately want to observe a running system (rare).- Only one J-Link session at a time:
rtt-capture,flash, andgdbserver-*/gdb-batchcannot run simultaneously. Stop one before starting the other. - If RTT log is empty, diagnose infra first (wrong SN, wrong RTT address, logger timing).
- Prefer JSON output in automation.
scripts/jlink_agent.py— main CLI wrapper.scripts/jlink_nfc_ab.sh— example orchestration script for one-board flash+RTT.