Prerequisites & Tool Installation
Install the full toolchain in the correct order to avoid PATH conflicts.
| Component | Minimum | Recommended |
|---|---|---|
| OS | Windows 10 / Ubuntu 20.04 / macOS 12 | Ubuntu 22.04 LTS or Windows 11 |
| RAM | 4 GB | 8 GB+ |
| Disk | 5 GB free | 20 GB (CubeIDE + SVD files) |
| USB | USB-A port | USB 3.0 for faster flash |
| Java | JRE 11 | JRE 17 (bundled with CubeIDE 1.14+) |
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.
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+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.
# 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.1Verify GDB is available
arm-none-eabi-gdb --version
# Expected: GNU gdb (Arm GNU Toolchain) 12.x or newer0483:374b. The standalone ST-Link v2 external probe uses 0483:3748.Board Overview — B-L475E-IOT01A
Know your hardware before you debug it.
| Peripheral | Details |
|---|---|
| Core | ARM Cortex-M4 + FPU @ 80 MHz |
| Flash | 1 MB (2 × 512 KB banks, dual-bank erase) |
| SRAM | 128 KB SRAM1 + 32 KB SRAM2 + 16 KB SRAM3 |
| GPIO | 115 I/O pins (5 V tolerant) |
| Timers | 11× 16-bit, 2× 32-bit, 2× LP timer, 2× WDG |
| Communication | 3× SPI, 6× USART, 3× I²C, 1× CAN, 1× USB OTG FS |
| ADC/DAC | 3× 12-bit ADC (5 Msps), 2× 12-bit DAC |
| Security | AES-256, TRNG, PCROP, RDP, Firewall |
| Debug | SWD, ETM trace, TPIU |
| Signal | CN11 Pin | MCU Pin | Description |
|---|---|---|---|
| SWCLK | Pin 4 | PA14 | Serial Wire Clock |
| SWDIO | Pin 2 | PA13 | Serial Wire Data I/O |
| NRST | Pin 10 | NRST | Reset (active low) |
| SWO/TDO | Pin 6 | PB3 | Trace output (optional ITM) |
| GND | Pin 3/5 | GND | Ground reference |
| VCC | Pin 1 | 3.3 V | Target voltage reference |
transport select swd in every OpenOCD config.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.
# 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.'# 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 infoadapter speed 4000 line configure? QUIZ 2STM32CubeIDE Debug Configuration
Configure every tab of the Debug Configuration dialog.
Open Debug Configurations
In CubeIDE: Run → Debug Configurations… → double-click STM32 Cortex-M C/C++ Application.
Main Tab
Set C/C++ Application to your Debug/MyProject.elf and Project to your project name.
Debugger Tab — Key Settings
| Field | Value | Notes |
|---|---|---|
| Debug Probe | ST-LINK (OpenOCD) | Select from dropdown |
| Interface | SWD | Do NOT select JTAG |
| SWD Frequency | 4000 kHz | Reduce to 1000 if errors |
| GDB port | 3333 | Must match board cfg |
| Enable FPU | ✓ checked | Required for M4+FPU |
Startup Tab — Initialization Commands
# 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()
continueGDB Server Tab — OpenOCD Config Script
# 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 fileCustom Launch Groups
Chain flash → reset → debug in a single click.
Create a Flash-Only Run Config named MyProject_Flash
monitor reset halt
load
monitor reset runCreate a Debug Attach Config named MyProject_Debug
Use monitor reset halt and tbreak main in its Startup tab.
Create the Launch Group MyProject_FlashAndDebug
Go to Run → Debug Configurations → Launch Group → New.
Add Members
| Launch | Mode | Post-delay | Adopt |
|---|---|---|---|
| MyProject_Flash | Run | 500 ms | — |
| MyProject_Debug | Debug | — | ✓ |
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 debugerase keyword in flash write_image erase unless you are certain the target sectors are already blank.GDB Commands — Register & Memory Inspection
Essential GDB commands for inspecting a live Cortex-M4 target.
arm-none-eabi-gdb Debug/MyProject.elf
(gdb) target extended-remote localhost:3333
(gdb) monitor reset halt(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# 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)# 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) 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 used0x40021008 is read. Which GDB command do you use? QUIZ 3watch *(uint32_t*)0x40021008rwatch *(uint32_t*)0x40021008hbreak *0x40021008x/wx 0x40021008RTOS-Aware Debugging
See task names and stacks instead of raw CPU threads.
# Add to your board config (before init)
$_TARGETNAME configure -rtos FreeRTOS
# OR via GDB Startup Commands tab
monitor $_TARGETNAME configure -rtos FreeRTOS#define configUSE_TRACE_FACILITY 1 // enables task tracking
#define configUSE_STATS_FORMATTING 1 // for vTaskList()
#define configCHECK_FOR_STACK_OVERFLOW 2 // overflow detection# 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()configCHECK_FOR_STACK_OVERFLOW=2 and implement vApplicationStackOverflowHook(). You can set a GDB watchpoint on the stack sentinel word for instant detection.Troubleshooting Reference
Common errors and how to fix them fast.
Check USB cable (use CN7, not CN1). Linux: verify udev rules and plugdev group. Windows: reinstall STSW-LINK009. Try a data-capable cable.
Linux udev permissions. Verify
/etc/udev/rules.d/60-openocd.rules exists and reload: sudo udevadm control --reload-rules && sudo udevadm triggerBoard not powered. Connect both USB cables. Or enable bus power via JP1 (connects 5V USB to the 3.3V LDO).
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.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=0xAAUse
flash write_image erase ... with the erase keyword. Or manually: flash erase_address pad 0x08000000 0x100000Power supply fluctuation during write. Try slower SWD speed. Also verify your firmware is ≤ 1 MB — the STM32L475 has exactly 1 MB flash.
# 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)Quick Reference Card
The commands you'll reach for every day.
haltHalt CPU executionresumeResume CPU executionreset haltReset and halt immediatelyreset runReset and run firmwarestepSingle-step one instructionregShow all core registersmdw <addr> <n>Display n words at addrmww <addr> <val>Write word at addrflash info 0Show flash bank infoflash write_image erase <f>Erase and flash fileverify_image <file>Verify flash against fileadapter speed <kHz>Change SWD speedtargetsList all debug targetspollPoll target stateinfo registersAll CPU registersp/x $r0Print r0 in hexp/x $pcPrint program counterinfo floatFPU register dumpx/wx <addr>Examine word at addressx/16wx <addr>Examine 16 wordsset {int}<addr> = <val>Write to memorybacktrace / btShow call stackinfo localsLocal vars in frameinfo threadsList RTOS tasksthread <n>Switch to task nwatch <expr>Write watchpointhbreak *<addr>Hardware breakpointmonitor reset haltReset via OpenOCDloadFlash ELF to targetcontinue / cContinue executionstepi / siStep one instructionfinishRun until function returns