Skip to content

SVD Register Decoding

Without SVD, reading a peripheral register gives you a raw hex value — say, 0x44310000 from GPIOA’s CRL register. With SVD loaded, mcjtag decodes that into named fields: MODE0=0b01 (10 MHz output), CNF0=0b01 (open-drain), MODE1=0b11 (50 MHz output), and so on. No datasheet lookup required.

CMSIS System View Description (SVD) is an XML format defined by ARM that describes every memory-mapped peripheral on a microcontroller. Each SVD file contains:

  • Peripherals — named blocks like GPIOA, USART1, RCC, TIM1
  • Registers — named registers within each peripheral, with their addresses
  • Bitfields — individual fields within each register, with widths, offsets, and descriptions

Chip vendors provide SVD files for their devices, usually alongside their SDK or IDE packages.

STMicroelectronics — The community-maintained cmsis-svd-data repository has SVD files for most STM32 families. Look in the data/STMicro directory.

Terminal window
# Example: download the STM32F103 SVD
curl -LO https://raw.githubusercontent.com/cmsis-svd/cmsis-svd-data/main/data/STMicro/STM32F103xx.svd

Nordic Semiconductor — SVD files are bundled with the nRF5 SDK and nRF Connect SDK. Also available in the cmsis-svd-data repository under data/Nordic.

Other vendors — Check your chip vendor’s SDK download page. SVD files are typically found alongside CMSIS packs, IDE project templates, or device support packages. The file extension is .svd.

Option 1: Environment variable (auto-loaded on startup)

Section titled “Option 1: Environment variable (auto-loaded on startup)”
Terminal window
export OPENOCD_SVD=/path/to/STM32F103xx.svd

When mcjtag starts and connects to OpenOCD, it automatically loads the SVD file. This is the simplest approach — set it in your .env or MCP client config and forget about it.

Terminal window
claude mcp add mcjtag -- env OPENOCD_SVD=/path/to/STM32F103xx.svd OPENOCD_CONFIG=/path/to/config.cfg uvx mcjtag
svd_inspect(svd_path="/path/to/STM32F103xx.svd")

When called with svd_path, the tool loads the file and then returns the list of available peripherals. Subsequent calls can omit svd_path — the file stays loaded for the session.

After loading, call with no arguments to see what is available:

svd_inspect()

Returns a list of all peripherals defined in the SVD file — typically GPIOA through GPIOG, USART1-3, TIM1-4, RCC, FLASH, DMA1, ADC1, SPI1, I2C1, and the system peripherals (SCB, NVIC, SysTick, etc.). Each peripheral name is a string you pass to subsequent calls.

svd_inspect(peripheral="GPIOA")

This reads every register in the peripheral from the live target and decodes all bitfields. The response contains:

  • peripheral — the name you requested
  • registers — a dictionary of register name to decoded result

Each register entry includes:

  • name — e.g., “CRL”
  • address — the memory-mapped address, e.g., “0x40010800”
  • raw_value — the 32-bit hex value read from hardware, e.g., “0x44310000”
  • fields — a list of decoded bitfields, each with:
    • name — field name from the SVD, e.g., “MODE0”
    • value — the extracted value (integer)
    • width — field width in bits
    • offset — bit offset within the register
    • description — text description from the SVD

Progress is reported as each register is read and decoded, which is helpful for peripherals with many registers (some timer peripherals have 20+).

If you only care about one register, specify both the peripheral and register name:

svd_inspect(peripheral="RCC", register="CR")

This reads and decodes only that register, which is faster than decoding the entire peripheral.

Workflow: “Why isn’t my UART working?”

Section titled “Workflow: “Why isn’t my UART working?””

A common debugging scenario: you set up USART1 in firmware, but nothing comes out on the TX pin. Here is how to diagnose it with SVD decoding.

The peripheral clock must be enabled before the peripheral will respond. On STM32, USART1 is on the APB2 bus.

svd_inspect(peripheral="RCC", register="APB2ENR")

Look for the USART1EN bitfield. If its value is 0, the clock is disabled and the peripheral is dead. Your firmware forgot to enable it (or the init function never ran).

USART1 TX is typically PA9, RX is PA10 (check your chip’s datasheet for the actual pin mapping).

svd_inspect(peripheral="GPIOA")

For STM32F1 (which uses a different GPIO model than F4), check:

  • CRH register — controls pins PA8-PA15
  • PA9 (MODE9 + CNF9) should be configured as alternate function push-pull output
  • PA10 (MODE10 + CNF10) should be configured as input (floating or pull-up)

For STM32F4 and newer (which use the AFR model):

  • MODER — PA9 should be “alternate function” mode (0b10)
  • AFRH — PA9’s alternate function should be set to AF7 (USART1)
svd_inspect(peripheral="USART1")

Key fields to check:

RegisterFieldWhat to look for
CR1UEUSART enable — must be 1
CR1TETransmitter enable — must be 1
CR1REReceiver enable — must be 1 (if you need RX)
BRRDIV_Mantissa, DIV_FractionBaud rate divisor — calculate from APB clock
SRTXETX data register empty — 1 means ready to send
SRTCTransmission complete — 1 means last byte fully shifted out

If UE=0, the peripheral is not enabled at all. If TE=0, the transmitter is off. If BRR is 0 or a wildly wrong value, the baud rate is misconfigured.

This same pattern — clock, pins, peripheral config — applies to debugging any peripheral. SPI not working? Check SPI clock enable, GPIO AF settings, then the SPI CR1/CR2 registers.

mcjtag also exposes SVD data as MCP resources, which the LLM can poll without making explicit tool calls:

  • jtag://svd/peripherals — list all loaded peripherals
  • jtag://svd/{peripheral} — decoded register values for a specific peripheral

Resources are read-only snapshots. Use the svd_inspect tool when you need to load an SVD file or want progress reporting during decode.