B-L475E-IOT01A · ST-Link v2 · GDB Debug Reference

External Debug
with OpenOCD

A hands-on, step-by-step tutorial to connect, configure, and debug STM32L475 firmware from scratch using OpenOCD, STM32CubeIDE, and GDB — no prior debug-probe experience required.

STM32L475VGT6
ARM Cortex-M4 + FPU
OpenOCD v0.12.0+
SWD · 2-wire
GDB 10.x
FreeRTOS-aware
10
Modules
80
MHz Cortex-M4
1MB
Flash
6
HW Breakpoints
MOD 01

Prerequisites & Tool Installation

Install the full toolchain in the correct order to avoid PATH conflicts.

ComponentMinimumRecommended
OSWindows 10 / Ubuntu 20.04 / macOS 12Ubuntu 22.04 LTS or Windows 11
RAM4 GB8 GB+
Disk5 GB free20 GB (CubeIDE + SVD files)
USBUSB-A portUSB 3.0 for faster flash
JavaJRE 11JRE 17 (bundled with CubeIDE 1.14+)
1

Install STM32CubeIDE (includes OpenOCD + GDB)

Download STM32CubeIDE 1.14+ from st.com. The IDE ships a patched OpenOCD build and the GNU Arm toolchain — this is the fastest path to a working setup.

2

Verify the bundled OpenOCD path

# Windows default path C:\ST\STM32CubeIDE_1.14.0\STM32CubeIDE\plugins\ com.st.stm32cube.ide.mcu.externaltools.openocd.win32_2.4.0.xxx \tools\bin\openocd.exe
# Linux default path /opt/st/stm32cubeide_1.14.0/plugins/ com.st.stm32cube.ide.mcu.externaltools.openocd.linux64_2.4.0.xxx /tools/bin/openocd # OR install standalone via apt sudo apt-get install -y openocd openocd --version # expect: 0.12.0+
# macOS via Homebrew brew install open-ocd openocd --version # expect: 0.12.0+
3

Install ST-Link USB driver (Windows only)

Download STSW-LINK009 from st.com and run the installer. Linux and macOS use libusb and udev rules — no proprietary driver needed.

bash · Linux udev
# Add udev rules (allows non-root ST-Link access) sudo cp /usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger # Add your user to the plugdev group, then log out/in sudo usermod -aG plugdev $USER # Verify device is seen lsusb | grep -i stm # Expected: ID 0483:374b STMicroelectronics ST-LINK/V2.1
4

Verify GDB is available

bash
arm-none-eabi-gdb --version # Expected: GNU gdb (Arm GNU Toolchain) 12.x or newer
💡
Tip: The USB VID:PID for the on-board ST-Link v2-1 is 0483:374b. The standalone ST-Link v2 external probe uses 0483:3748.
Which USB port on the B-L475E-IOT01A connects to the ST-Link debugger? QUIZ 1
A
CN1 — USB OTG Full-Speed
B
CN7 — USB Micro-B (ST-Link)
C
CN11 — SWD external header
D
CN6 — UART bridge

MOD 02

Board Overview — B-L475E-IOT01A

Know your hardware before you debug it.

PeripheralDetails
CoreARM Cortex-M4 + FPU @ 80 MHz
Flash1 MB (2 × 512 KB banks, dual-bank erase)
SRAM128 KB SRAM1 + 32 KB SRAM2 + 16 KB SRAM3
GPIO115 I/O pins (5 V tolerant)
Timers11× 16-bit, 2× 32-bit, 2× LP timer, 2× WDG
Communication3× SPI, 6× USART, 3× I²C, 1× CAN, 1× USB OTG FS
ADC/DAC3× 12-bit ADC (5 Msps), 2× 12-bit DAC
SecurityAES-256, TRNG, PCROP, RDP, Firewall
DebugSWD, ETM trace, TPIU
SignalCN11 PinMCU PinDescription
SWCLKPin 4PA14Serial Wire Clock
SWDIOPin 2PA13Serial Wire Data I/O
NRSTPin 10NRSTReset (active low)
SWO/TDOPin 6PB3Trace output (optional ITM)
GNDPin 3/5GNDGround reference
VCCPin 13.3 VTarget voltage reference
⚠️
Warning: Never connect an external 5 V probe to VCC (pin 1). The STM32L475 is a 3.3 V device — overvoltage will permanently damage the MCU.
ℹ️
Jumper JP3 (CN11) must be closed to connect the on-board ST-Link to the target MCU. Open JP3 to use an external debug probe via the CN11 header.
📌
Key fact: The B-L475E-IOT01A's on-board ST-Link does NOT support JTAG. You must always use transport select swd in every OpenOCD config.

MOD 03

OpenOCD Configuration Files

Three layered config files control the debug probe, target MCU, and board-level settings.

🔌

Interface Config

Selects the debug probe — stlink.cfg. Sets transport to SWD and adapter speed.

🎯

Target Config

Describes the MCU — stm32l4x.cfg. Defines flash banks, CPU type, and reset sequence.

📋

Board Config

Your custom file that glues both configs and adds board-specific settings like GDB port and reset mode.

TCL · board-bl475e.cfg
# board-bl475e.cfg — Place in your STM32CubeIDE project root # 1. Select the ST-Link interface source [find interface/stlink.cfg] # 2. SWD transport (mandatory — no JTAG on this board) transport select swd # 3. Select STM32L4 target source [find target/stm32l4x.cfg] # 4. SWD clock speed in kHz (4 MHz is stable for ST-Link v2-1) adapter speed 4000 # 5. Reset config — NRST asserted while SWD stays active reset_config srst_nogate # 6. GDB port (default 3333) gdb_port 3333 # 7. Telnet port for OpenOCD CLI telnet_port 4444 # 8. TCL scripting port tcl_port 6666 # 9. Startup commands after OpenOCD connects init if { [catch {reset halt}] } { echo 'NOTE: Target already halted' } echo 'B-L475E-IOT01A OpenOCD ready.'
bash
# Launch OpenOCD with your custom board config openocd -f board-bl475e.cfg # Expected output: Open On-Chip Debugger 0.12.0 Info : STLINK V2J45S7 (API v2) VID:PID 0483:374B Info : Target voltage: 3.247820 Info : stm32l4x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32l4x.cpu on 3333 # Connect via Telnet in another terminal telnet localhost 4444 # Useful Telnet/TCL commands: reset halt # Reset and halt the CPU halt # Halt without reset resume # Resume execution reg # Display all core registers mdw 0x08000000 16 # 16 words from Flash start flash info 0 # Show flash bank info
💡
Managed mode: CubeIDE starts OpenOCD automatically when you launch a debug session. You only need to launch OpenOCD manually for standalone GDB sessions or scripted flashing.
What does the adapter speed 4000 line configure? QUIZ 2
A
The USB transfer speed in bytes per second
B
The SWD clock speed in kHz (4 MHz)
C
The GDB server port number
D
The MCU core frequency

MOD 04

STM32CubeIDE Debug Configuration

Configure every tab of the Debug Configuration dialog.

1

Open Debug Configurations

In CubeIDE: Run → Debug Configurations… → double-click STM32 Cortex-M C/C++ Application.

2

Main Tab

Set C/C++ Application to your Debug/MyProject.elf and Project to your project name.

3

Debugger Tab — Key Settings

FieldValueNotes
Debug ProbeST-LINK (OpenOCD)Select from dropdown
InterfaceSWDDo NOT select JTAG
SWD Frequency4000 kHzReduce to 1000 if errors
GDB port3333Must match board cfg
Enable FPU✓ checkedRequired for M4+FPU
4

Startup Tab — Initialization Commands

GDB · Startup tab
# 1. Little-endian target (required for Cortex-M) set endian little # 2. Pretty-print structs and unions set print pretty on # 3. Break on main() before user code executes tbreak main # 4. Enable FPU lazy stacking — do NOT remove for M4+FPU set $FPCCR = 0xC0000000 # 5. Reset and halt via OpenOCD monitor reset halt monitor sleep 100 # 6. Continue to the tbreak on main() continue
5

GDB Server Tab — OpenOCD Config Script

bash · config path
# Method 1 — built-in board config (simplest) ${OPENOCD_SCRIPTS}/board/st_b-l475e-iot01a.cfg # Method 2 — your custom config (full control) ${workspace_loc:/MyProject}/board-bl475e.cfg # Optional verbosity flags -d2 # verbosity level 2 (debug) -l openocd_log.txt # log to file

MOD 05

Custom Launch Groups

Chain flash → reset → debug in a single click.

ℹ️
What is a Launch Group? A meta-configuration that references existing Run/Debug configs and launches them in sequence with optional delays. Available via Run → Debug Configurations… → Launch Group.
1

Create a Flash-Only Run Config named MyProject_Flash

GDB · Run config startup
monitor reset halt load monitor reset run
2

Create a Debug Attach Config named MyProject_Debug

Use monitor reset halt and tbreak main in its Startup tab.

3

Create the Launch Group MyProject_FlashAndDebug

Go to Run → Debug Configurations → Launch Group → New.

4

Add Members

LaunchModePost-delayAdopt
MyProject_FlashRun500 ms
MyProject_DebugDebug
💡
Checking Adopt on the Debug member means CubeIDE automatically manages it in the debug perspective. Without it, the debug session runs in the background.

MOD 06

ST-Link Flash Programming

Three methods — pick the right one for your workflow.

STM32CubeProgrammer CLI

Best for CI/CD pipelines and scripted flashing. Fast and scriptable.

🔧

OpenOCD Telnet / TCL

Integrated with your debug session. Use when already connected via OpenOCD.

🚀

STM32CubeIDE Run Config

Fastest for the development loop — auto-flash on Run. Best combined with a Launch Group (Module 05).

# Alias for convenience alias cubeprog="/opt/st/stm32cubeprogrammer/bin/STM32_Programmer_CLI" # Flash .elf file via SWD at 4 MHz, verify, then reset cubeprog -c port=SWD freq=4000 -d Debug/MyProject.elf -v -rst # Flash .bin file (must specify load address) cubeprog -c port=SWD -d Debug/MyProject.bin 0x08000000 -v -rst # Erase all flash sectors cubeprog -c port=SWD -e all # Read back 256 bytes from 0x08000000 cubeprog -c port=SWD -r 0x08000000 0x100 readback.bin # Unlock RDP (erases all flash!) cubeprog -c port=SWD -ob RDP=0xAA
# Connect to running OpenOCD via Telnet telnet localhost 4444 # Flash .bin file > halt > flash write_image erase /path/to/MyProject.bin 0x08000000 bin > verify_image /path/to/MyProject.bin 0x08000000 bin > reset run # Flash .elf file (addresses from ELF headers) > halt > flash write_image erase /path/to/MyProject.elf > reset run # Non-interactive one-liner openocd -f board-bl475e.cfg \ -c "program Debug/MyProject.elf verify reset exit"
# In STM32CubeIDE → Run → Run Configurations # Startup Tab — Run Commands: monitor reset halt monitor sleep 100 load # GDB flashes the .elf monitor verify_image Debug/MyProject.elf monitor reset run disconnect quit # Add to a Launch Group (Module 05) to auto-flash before debug
⚠️
Always include the erase keyword in flash write_image erase unless you are certain the target sectors are already blank.

MOD 07

GDB Commands — Register & Memory Inspection

Essential GDB commands for inspecting a live Cortex-M4 target.

GDB session
arm-none-eabi-gdb Debug/MyProject.elf (gdb) target extended-remote localhost:3333 (gdb) monitor reset halt
GDB · Registers
(gdb) info registers # all CPU registers (gdb) p/x $r0 # print r0 in hex (gdb) p/x $sp # stack pointer (gdb) p/x $pc # program counter (gdb) p/x $xPSR # Program Status Register (gdb) p/x $primask # masks all interrupts when 1 (gdb) p/x $basepri # masks IRQs at/below level (gdb) p/x $control # privilege/stack selection # FPU registers (M4 with FPU) (gdb) p/f $s0 # single-precision float (gdb) p/f $d0 # double-precision pair s0:s1 (gdb) info float # all FPU regs + FPSCR # Modify a register (dangerous with $pc!) (gdb) set $r0 = 0x1234
GDB · Peripherals
# RCC — Clock Control (base 0x40021000) (gdb) x/wx 0x40021000 # RCC_CR (gdb) x/16wx 0x40021000 # dump 16 words of RCC block # GPIOA (base 0x48000000) (gdb) x/wx 0x48000000 # GPIOA_MODER (gdb) x/wx 0x48000010 # GPIOA_IDR (input data) (gdb) x/wx 0x48000014 # GPIOA_ODR (output data) # Write to GPIOA_ODR — set bit 5 (gdb) set {int}0x48000014 = 0x00000020 # SysTick (0xE000E010) (gdb) x/4wx 0xE000E010 # SYST_CSR/RVR/CVR/CALIB # SCB Fault Status Registers (gdb) x/wx 0xE000ED28 # SCB_CFSR (Configurable Fault) (gdb) x/wx 0xE000ED2C # SCB_HFSR (HardFault) (gdb) x/wx 0xE000ED34 # SCB_MMFAR (MemManage addr) (gdb) x/wx 0xE000ED38 # SCB_BFAR (BusFault addr)
ℹ️
The STM32L475 has 6 hardware breakpoints (FPB unit) and 4 hardware watchpoints (DWT unit). Software breakpoints work only in SRAM.
GDB · Breakpoints & Watchpoints
# Hardware breakpoints (work in Flash) (gdb) hbreak *0x08001234 (gdb) hbreak HAL_GPIO_TogglePin # Software breakpoints (SRAM only) (gdb) break main (gdb) break main.c:55 # Watchpoints (gdb) watch *(uint32_t*)0x20000100 # halt on write (gdb) rwatch *(uint32_t*)0x20000100 # halt on read (gdb) awatch *(uint32_t*)0x20000100 # halt on read OR write (gdb) watch my_global_var # watch by variable name # Manage breakpoints (gdb) info breakpoints (gdb) delete # delete all (gdb) disable 2 (gdb) enable 2
GDB · Stack
(gdb) backtrace # call stack (alias: bt) (gdb) backtrace full # + local vars at each frame (gdb) frame 2 # switch to frame #2 (gdb) info locals # local variables (gdb) info args # function arguments # Manual stack walk from SP (gdb) x/32wx $sp # Check stack usage against linker symbols (gdb) p/x &_estack # top of stack (initial SP) (gdb) p/x &_sstack # bottom of stack (gdb) p (int)(&_estack) - (int)($sp) # bytes used
You need to halt execution whenever a peripheral register at 0x40021008 is read. Which GDB command do you use? QUIZ 3
A
watch *(uint32_t*)0x40021008
B
rwatch *(uint32_t*)0x40021008
C
hbreak *0x40021008
D
x/wx 0x40021008

MOD 08

RTOS-Aware Debugging

See task names and stacks instead of raw CPU threads.

TCL · board config
# Add to your board config (before init) $_TARGETNAME configure -rtos FreeRTOS # OR via GDB Startup Commands tab monitor $_TARGETNAME configure -rtos FreeRTOS
C · FreeRTOSConfig.h
#define configUSE_TRACE_FACILITY 1 // enables task tracking #define configUSE_STATS_FORMATTING 1 // for vTaskList() #define configCHECK_FOR_STACK_OVERFLOW 2 // overflow detection
GDB · RTOS
# Show all FreeRTOS tasks (gdb) info threads # Output: # Id Target Id Frame # *1 FreeRTOS Task 'IDLE' vPortSuppressTicksAndSleep() # 2 FreeRTOS Task 'main' HAL_GPIO_TogglePin() # 3 FreeRTOS Task 'comm' ulTaskNotifyTake() # Switch to a task context (gdb) thread 3 (gdb) backtrace # Inspect current TCB (gdb) p *pxCurrentTCB (gdb) p pxCurrentTCB->pcTaskName (gdb) p pxCurrentTCB->uxPriority (gdb) p pxCurrentTCB->pxTopOfStack # Stack high-water mark (low = close to overflow!) (gdb) call uxTaskGetStackHighWaterMark(task_handle) # Heap info (gdb) call xPortGetFreeHeapSize() (gdb) call xPortGetMinimumEverFreeHeapSize()
⚠️
Stack overflow: Enable configCHECK_FOR_STACK_OVERFLOW=2 and implement vApplicationStackOverflowHook(). You can set a GDB watchpoint on the stack sentinel word for instant detection.

MOD 09

Troubleshooting Reference

Common errors and how to fix them fast.

🔴
Error: open failed
Check USB cable (use CN7, not CN1). Linux: verify udev rules and plugdev group. Windows: reinstall STSW-LINK009. Try a data-capable cable.
🔴
Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Linux udev permissions. Verify /etc/udev/rules.d/60-openocd.rules exists and reload: sudo udevadm control --reload-rules && sudo udevadm trigger
🟡
Error: Target voltage: 0.000000
Board not powered. Connect both USB cables. Or enable bus power via JP1 (connects 5V USB to the 3.3V LDO).
🟡
Error: init mode failed (unable to connect)
SWD clock too fast — reduce adapter speed to 1000 or 500 kHz. Target may be in WFI/WFE with debug disabled. Try monitor reset halt while pressing the RESET button.
🔴
Error: Failed to connect (security)
RDP (Read-Out Protection) may be active. Check level: cubeprog -c port=SWD -ob displ. Unlock (erases all flash!): cubeprog -c port=SWD -ob RDP=0xAA
🟡
Error: flash memory not erased before writing
Use flash write_image erase ... with the erase keyword. Or manually: flash erase_address pad 0x08000000 0x100000
🟡
Flash verify fails / CRC mismatch
Power supply fluctuation during write. Try slower SWD speed. Also verify your firmware is ≤ 1 MB — the STM32L475 has exactly 1 MB flash.
bash · diagnostics
# Check versions openocd --version arm-none-eabi-gdb --version # Symptom: "Truncated register 33 in remote g packet" # Fix: use the GDB bundled with CubeIDE ${CUBE_IDE}/tools/bin/arm-none-eabi-gdb # OpenOCD crashes on RTOS plugin # Disable RTOS awareness or update to OpenOCD 0.12.0+ # GDB/MI protocol issues in CubeIDE # Reset debug perspective: Window → Perspective → Reset Perspective # Clear caches: File → Restart (with -clean in cubeide.ini)

MOD 10

Quick Reference Card

The commands you'll reach for every day.

haltHalt CPU execution
resumeResume CPU execution
reset haltReset and halt immediately
reset runReset and run firmware
stepSingle-step one instruction
regShow all core registers
mdw <addr> <n>Display n words at addr
mww <addr> <val>Write word at addr
flash info 0Show flash bank info
flash write_image erase <f>Erase and flash file
verify_image <file>Verify flash against file
adapter speed <kHz>Change SWD speed
targetsList all debug targets
pollPoll target state
info registersAll CPU registers
p/x $r0Print r0 in hex
p/x $pcPrint program counter
info floatFPU register dump
x/wx <addr>Examine word at address
x/16wx <addr>Examine 16 words
set {int}<addr> = <val>Write to memory
backtrace / btShow call stack
info localsLocal vars in frame
info threadsList RTOS tasks
thread <n>Switch to task n
watch <expr>Write watchpoint
hbreak *<addr>Hardware breakpoint
monitor reset haltReset via OpenOCD
loadFlash ELF to target
continue / cContinue execution
stepi / siStep one instruction
finishRun until function returns
0x0800 0000 – 0x080F FFFF
1 MB
Internal Flash (user code)
0x2000 0000 – 0x2001 FFFF
128 KB
SRAM1 (main SRAM)
0x1000 0000 – 0x1000 7FFF
32 KB
SRAM2 (retained in Standby)
0x2004 0000 – 0x2004 3FFF
16 KB
SRAM3
0x4000 0000 – 0x4000 FFFF
64 KB
APB1 — TIM, UART, SPI, I2C…
0x4001 0000 – 0x4001 FFFF
64 KB
APB2 — ADC, SDMMC, EXTI…
0x4800 0000 – 0x4800 1FFF
8 KB
AHB2 — GPIOA..GPIOH
0x1FFF 7000 – 0x1FFF 73FF
1 KB
OTP area (one-time programmable)
0x1FFF 7800 – 0x1FFF 78FF
256 B
Option bytes (RDP, BOR, WRP…)
0xE000 0000 – 0xE00F FFFF
1 MB
ARM Cortex-M4 internal (SCB, ITM, DWT…)
0xE000 E000 – 0xE000 EFFF
4 KB
System Control Space (SCB, NVIC, SysTick)