What is svd2rust?
masterstructs) from SVD (System View Description) files. It is primarily used in embedded development to provide type-safe access to microcontroller peripherals.repository·master·Indexed 21 days ago
https://github.com/rust-embedded/svd2rustA code generation tool that converts SVD (System View Description) files into type-safe Rust register maps (structs) for embedded development. It supports multiple target architectures including cortex-m, msp430, riscv, xtensa-lx, mips, and avr. The tool provides customizable identifier naming via themes and specific configuration options for RISC-V ISA and AVR Configuration Change Protection (CCP). Version 0.37.1 requires stable Rust 1.81.0 or higher.
structs) from SVD (System View Description) files. It is primarily used in embedded development to provide type-safe access to microcontroller peripherals.svd2rust-regress is a helper program designed to test changes against svd2rust by running it against multiple chips in parallel using rayon.
For each chip/SVD tested, the tool performs the following workflow:
output/<chip> with architecture-specific dependencies..svd file for the chip.svd2rust to generate output/<chip>/src/lib.rs.cargo check to verify the generated project builds successfully.You can filter which tests are executed by combining filters. Note that filters can be combined but not repeated.
Common filtering tasks:
tests --architecture <arch> (e.g., riscv).-c <chip_name>.-m <manufacturer_name>.Note: Commands must be run from the ci/svd2rust-regress folder.
# Run all RiscV tests
cargo regress tests --architecture riscv
# Run against any chip named MB9AF12xK
cargo regress test -c MB9AF12xK
# Run against specifically the Fujitsu MB9AF12xK
cargo regress test -c MB9AF12xK -m FujitsuBefore running svd2rust-regress, ensure the following requirements are met:
svd2rust in the root of the repository in --release mode. If you have built it elsewhere, you must specify the path to the binary using the appropriate option.--format flag, you must have rustfmt version > v0.4.0 installed. You can install it via rustup:rustup component add rustfmt-previewsvd2rust is guaranteed to compile on stable Rust version 1.81.0 and higher. If you experience compilation errors on stable versions newer than 1.81.0, you should report the issue.svd2rust and want to ensure your changes do not break existing functionality, you can use svd2rust-regress. This is a helper program designed for regression testing changes against the main svd2rust tool. Refer to the svd2rust-regress documentation for specific usage instructions.Peripherals are modeled as singletons. To access them, you must obtain an instance using the Peripherals::take() method. This method returns an Option; it returns Some(peripherals) on the first call and None on subsequent calls.
Accessing Peripherals:
let mut peripherals = stm32f30x::Peripherals::take().unwrap();
peripherals.GPIOA.odr().write(|w| unsafe { w.bits(1) });Bypassing Singletons (Unsafe):
If you need to implement safe higher-level abstractions, you can bypass the singleton property using ptr() or steal() methods on peripheral types. Note: This is unsafe.
// Example of using ptr() to access a peripheral without take()
unsafe { (*GPIOA::ptr()).idr().read().bits() }Register Access Patterns:
Each peripheral proxy dereferences to a RegisterBlock. Registers expose different methods based on their access permissions:
read()write()read(), write(), and modify()The modify API:
Performs a single read-modify-write operation. It provides a closure with access to both the current register state (r) and a writable proxy (w).
// Toggle the STOP bit while keeping other bits unchanged
i2c1.cr2().modify(|r, w| w.stop().bit(!r.stop().bit()));If your SVD file includes <enumeratedValues>, the generated API provides enums for bitfield values, making code more readable and safer.
Reading Enums:
You can match on the variant or use convenience methods like .is_input().
// Match on variant
match gpioa.dir().read().pin0().variant() {
gpioa::dir::PIN0_A::Input => { .. },
gpioa::dir::PIN0_A::Output => { .. },
}
// Use convenience method
if gpioa.dir().read().pin0().is_input() { .. }Writing Enums:
You can use .variant() to pick a value from the enum, or use convenience methods.
// Using variant
gpioa.dir().write(|w| w.pin0().variant(gpio::dir::PIN0_A::Output));
// Using convenience method
gpioa.dir().write(|w| w.pin0().output());match gpioa.dir().read().pin0().variant() {
gpioa::dir::PIN0_A::Input => { .. },
gpioa::dir::PIN0_A::Output => { .. },
}You can control the casing, prefix, and suffix of generated identifiers using IdentFormat. Identifiers can be parsed from a string using the format prefix:case:suffix.
Supported cases:
constant (or c, upper)pascal (or p, type)snake (or s, lower)unchanged (or svd, `"")Example string formats:
MY_PREFIX:pascal:MY_SUFFIX -> Prefix MY_PREFIX, PascalCase, Suffix MY_SUFFIX.MY_PREFIX:snake -> Prefix MY_PREFIX, SnakeCase, no suffix.constant -> ConstantCase, no prefix, no suffix.// Parsing an identifier format string
let format = IdentFormat::parse("PRE:pascal:POST").unwrap();
// Programmatic construction
let format = IdentFormat::default()
.pascal_case()
.prefix("My")
.suffix("R");You can customize how identifiers (registers, fields, etc.) are formatted using the -f or --ident-format flag. This allows you to override default naming conventions for specific types.
Format Syntax:
-f <type>:<prefix>:<case>:<suffix>
Parameters:
type: The identifier type (e.g., names found in the IdentFormats theme).prefix: A string to prepend to the identifier.case: The casing style. Allowed values are:'' (empty string): unchanged'p': pascal'c': constant's': snakesuffix: A string to append to the identifier.You can install the svd2rust command line tool using cargo install.
$ cargo install svd2rustWhen targeting cortex-m, svd2rust generates three files:
build.rs: A build script to locate device.x.device.x: A linker script that weakly aliases interrupt handlers to DefaultHandler.lib.rs: The generated peripheral API.All three files must be included in the same device crate. The crate must provide an opt-in rt feature and depend on the following crates:
critical-section v1.xcortex-m >=v0.7.6cortex-m-rt >=v0.6.13 (with device feature enabled if rt is enabled)vcell >=v0.1.2Example Cargo.toml for Cortex-M:
[dependencies]
critical-section = { version = "1.0", optional = true }
cortex-m-types = "0.1"
cortex-m-rt = { version = "0.6.13", optional = true }
vcell = "0.1.2"
[features]
rt = ["cortex-m-rt/device"]