svd2rust

repository·master·Indexed 21 days ago

https://github.com/rust-embedded/svd2rust

A 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.

Tokens
7.7K
Snippets
20
Records
36
Agent score
70%

What's inside svd2rust

  1. Use svd2rust-regress for regression testing

    master

    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:

    1. Creates a new crate in output/<chip> with architecture-specific dependencies.
    2. Downloads the .svd file for the chip.
    3. Runs svd2rust to generate output/<chip>/src/lib.rs.
    4. Runs cargo check to verify the generated project builds successfully.
  2. Run svd2rust-regress with filters

    master

    You can filter which tests are executed by combining filters. Note that filters can be combined but not repeated.

    Common filtering tasks:

    • Filter by architecture: Use tests --architecture <arch> (e.g., riscv).
    • Filter by chip name: Use -c <chip_name>.
    • Filter by manufacturer: Use -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 Fujitsu
  3. Set up preconditions for svd2rust-regress

    master

    Before running svd2rust-regress, ensure the following requirements are met:

    1. svd2rust binary: By default, the tool assumes you have already built 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.
    2. rustfmt: If you intend to use the --format flag, you must have rustfmt version > v0.4.0 installed. You can install it via rustup:
      rustup component add rustfmt-preview
  4. Use svd2rust-regress for local testing

    master
    If you are modifying 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.
  5. How to use the Peripheral API

    master

    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-only: read()
    • Write-only: write()
    • 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()));
  6. Use enumeratedValues for enhanced type safety

    master

    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 => { .. },
    }
  7. Customize identifier naming with IdentFormat

    master

    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");
  8. Configure identifier formatting with `--ident-format`

    master

    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': snake
    • suffix: A string to append to the identifier.
  9. Configure Cortex-M target generation

    master

    When 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.x
    • cortex-m >=v0.7.6
    • cortex-m-rt >=v0.6.13 (with device feature enabled if rt is enabled)
    • vcell >=v0.1.2

    Example 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"]