Svdtools
Python package to handle vendor-supplied, often buggy SVD files.
Install / Use
/learn @rust-embedded/SvdtoolsREADME
svdtools
svdtools is a set of tools for modifying vendor-supplied, often buggy SVD
files. It can be imported as a library for use in other applications, or run
directly via the included svdtools CLI utility.
A common use case is patching vendor-supplied SVD files, then applying svd2rust to the resulting patched SVD.
This project is developed and maintained by the [Tools team][team].
Getting Started with Python version
Python 3.6 or newer is required to install and use svdtools. To install:
$ pip3 install --upgrade --user svdtools
Once installation has completed, the svd utility can be called from the command line.
An example is given in make example, which calls
svd patch example/incomplete-stm32l4x2.yaml and generates a patched SVD file
example/stm32l4x2.svd.patched.
See Device and Peripheral YAML Format for more information on creating patches.
Getting Started with Rust version
This crate is guaranteed to compile on stable Rust 1.58.0 and up. To install:
$ cargo install svdtools
Once installation has completed, the svdtools utility can be called from the command line. Command line interface is same as CLI for Python version.
Develop
To each their own, but the intended workflow is as follows:
- Setup a virtual environment via
make setup; this also installs thesvdCLI - Activate the virtual environment by running
source venv/bin/activate(or use direnv) - Iterate, running
make checkandmake fixas necessary
Device and Peripheral YAML Format
The patch specifications are in YAML, and have the following general format:
# Path to the SVD file we're targeting. Relative to this file.
# This must be included only in the device YAML file.
_svd: "../svd/STM32F0x0.svd"
# Include other YAML files. Path relative to this file.
_include:
- "../peripherals/gpio_v2.yaml"
# Alter top-level information and peripherals for this device
_modify:
version: 1.1
description: bla bla
addressUnitBits: 8
width: 32
cpu:
revision: r1p2
mpuPresent: true
# Peripherals can either live directly at this level (but other top-level
# fields will name match first)
C_ADC:
name: ADC_Common
# Or they can be inside a _peripherals block, to avoid name conflicts.
_peripherals:
FSMC:
description: Flexible static memory controller
# Multiple address blocks are supported via the addressBlocks list
# use either addressBlock or addressBlocks, but not both
addressBlocks:
- offset: 0x0
size: 0x400
usage: "ADC base registers"
- offset: 0x1000
size: 0x400
usage: "ADC extra registers"
# Add whole new peripherals to this device.
# Incredibly this feature is required.
_add:
ADC_Common:
description: ADC Common registers
groupName: ADC
baseAddress: 0x40012300
addressBlock:
offset: 0x0
size: 0x400
usage: "All ADC registers"
# Multiple address blocks are supported via the addressBlocks list
addressBlocks:
- offset: 0x0
size: 0x400
usage: "ADC base registers"
- offset: 0x1000
size: 0x400
usage: "ADC extra registers"
registers:
CSR:
description: ADC Common status register
addressOffset: 0x0
access: read-only
resetValue: 0x00000000
fields:
OVR3:
description: Overrun flag of ADC3
bitOffset: 21
bitWidth: 1
interrupts:
ADC1_2:
description: ADC global interrupt
value: 18
# A whole new peripheral can also be created as derivedFrom another peripheral.
_add:
USART3:
derivedFrom: USART1
baseAddress: "0x40004800"
interrupts:
USART3:
description: USART3 global interrupt
value: 39
# A new peripheral can have all its registers copied from another, in case
# it cannot quite be derivedFrom (e.g. some fields need different enumerated
# values) but it's otherwise almost exactly the same.
# The registers are copied but not name or address or interrupts, which are
# preserved if the target already exists.
_copy:
ADC3:
from: ADC2
# The new peripheral can also be copied from another svd file for a different
# device. This is useful when a peripheral is missing in a device but the exact
# same peripheral already exist in another device.
# When copying from another file, all fields including interrupts are copied.
_copy:
TIM1:
from: ../svd/stm32f302.svd:TIM1
# Replace peripheral registers by a 'deriveFrom'.
# This is used when e.g. UART4 and UART5 are both independently defined,
# but you'd like to make UART5 be defined as derivedFrom UART4 instead.
_derive:
# The KEY peripheral looses all its elements but 'interrupt', 'name',
# and 'baseAddress', and it is derivedFrom the VALUE peripheral.
# Peripherals that were 'deriveFrom="KEY"' are now 'deriveFrom="VALUE"'.
UART5: UART4
# Reorder the hierarchy of peripherals with 'deriveFrom'.
# This is used when e.g. I2C1 is marked as derivedFrom I2C3,
# but you'd like to swap that so that I2C3 becomes derivedFrom I2C1.
_rebase:
# The KEY peripheral steals everything but 'interrupt', 'name',
# and 'baseAddress' elements from the VALUE peripheral.
# Peripherals that were 'deriveFrom="VALUE"' are now 'deriveFrom="KEY"'.
# The VALUE peripheral is marked as derivedFrom the updated KEY.
I2C1: I2C3
# An STM32 peripheral, matches an SVD <peripheral> tag.
# Does not match any tag with derivedFrom attribute set.
"GPIO*":
# We can include other YAML files inside this peripheral
_include:
- "path/to/file.yaml"
# Alter fields on existing registers inside this peripheral
_modify:
# Rename this badly named register. Takes effect before anything else.
# Don't use wildcard matches if you are changing the name!
# We could have specified name or description or other tags to update.
GPIOB_OSPEEDR:
name: OSPEEDR
# Equivalently the register could go in a '_registers' block
_registers:
GPIOB_OSPEEDR:
name: OSPEEDR
# Change the value of an interrupt in this peripheral
_interrupts:
EXTI0:
value: 101
# Add new registers and interrupts to this peripheral.
# Entries are registers by default, which can also go inside a '_registers'
# block, or interrupts go in an '_interrupts' block.
_add:
EXAMPLER:
description: An example register
addressOffset: 0x04
access: read-write
fields:
EXR1:
description: Example field
bitOffset: 16
bitWidth: 4
_registers:
EXAMPLR2:
description: Another example register
_interrupts:
EXAMPLEI:
description: An example interrupt
value: 100
# Anywhere you can '_add' something, you can also '_delete' it.
# Wildcards are supported. The value here can be a YAML list of registers
# to delete (supported for backwards compatibility), or a YAML mapping
# of lists of registers or interrupts.
_delete:
GPIO*_EXTRAR:
_registers:
- GPIO*_EXAMPLER
_interrupts:
- USART1
# If registers have unnecessary common prefix/postfix,
# you can clean it in all registers in peripheral by:
_strip:
- "PREFIX_*_"
_strip_end:
- "_POSTFIX_"
# You can collect several same registers into one register array
# that will be represented with svd2rust as array or elements
# with one type
# Minimal version:
_array:
ARRAY*: {}
# You can also use the modifiers shown below:
_array:
ARRAY*:
name: NEW_NAME%s
_modify:
FIELD:
description: NEWDESC
OTHER_ARRAY*: {}
# If you have registers that make up a group and can be repeated,
# you can collect them into cluster like this:
_cluster:
CLUSTER%s:
FIRST_REG: {}
SECOND_REG: {}
# clusters can be expanded into individual registers. The name of the resulting register will be the cluster name, concatenated with the register name.
_expand_cluster:
- CLUSTER_ONE*
- CLUSTER_TWO*
# When expanding clusters containing more than one element (as specified by <dim>), each register will have substutute [%] in the cluster name with its index number. If the cluster has a dimIndex element, a %s in the
Related Skills
node-connect
349.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
