Skip to content

JosefUtbult/SVD-to-Rust-Register-Map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SVD to Rust Registry Map

The svdToRustRegisterMap script is a SVD parser for generating a register structure for Rust.

These registry maps can be used for integrating with bare metal hardware in an minimal-abstraction fashion. This should not be used if you don't know what you're doing, as this approach disregards all compile-time and run-time securities, in favour of a more manual approach. If you're not sure about this kind of stuff, you should probably be using a full-fetched HAL, such as the stm32f4xx-hal or esp-hal. Another good alternative to this method for STM32 processors is stm32ral, which supplies a Register Access Layer in the form of structs.

Usage

./svdToRustRegisterMap <input_file>.svd

This will generate a rust file with the output next to the input file.

Note

The script requires Python 3 to run.

Example

This script generates rust source code files with a structure of nestled modules. Top level modules contain all register specifications for a specific peripheral. Under each peripheral module are raw pointer addresses to each register in that peripheral in the form of const *mut <address width> pointers. These are accompanied by a module with the same name as the register, containing all the registers fields.

The following is an extract from code generated using an STM32F401 SVD file. Part of the generated source file can be found in examples/stm32f401.rs

#[rustfmt::skip]
mod peripherals {

    // ...

    pub const GPIOA_ADDR:                u32 = 0x40020000;
    pub const I2C3_ADDR:                 u32 = 0x40005C00;
    pub const I2C2_ADDR:                 u32 = 0x40005800;

    // ...
}

// ...

#[rustfmt::skip]
mod gpio_registers {

    // ...

    pub const PUPDR_ADDR:                u32 = 0x00C;
    pub const IDR_ADDR:                  u32 = 0x010;
    pub const ODR_ADDR:                  u32 = 0x014;

    // ...
}

// ...

/// General-purpose i/os
#[rustfmt::skip]
#[allow(unused)]
pub mod gpioa {
    use super::{peripherals, gpio_registers};

    // ...

    /// Gpio port pull-up/pull-down register
    /// Access: read-write
    pub const PUPDR: *mut u32 = (peripherals::GPIOA_ADDR + gpio_registers::PUPDR_ADDR) as *mut u32;
    pub mod pupdr {
        /// Port x configuration bits (y = 0..15)
        /// Bit width: 2
        pub const PUPDR15: u8 = 30;

        /// Port x configuration bits (y = 0..15)
        /// Bit width: 2
        pub const PUPDR14: u8 = 28;

        /// Port x configuration bits (y = 0..15)
        /// Bit width: 2
        pub const PUPDR13: u8 = 26;

        // ...
    }

    /// Gpio port input data register
    /// Access: read-only
    pub const IDR: *mut u32 = (peripherals::GPIOA_ADDR + gpio_registers::IDR_ADDR) as *mut u32;
    pub mod idr {
        /// Port input data (y = 0..15)
        /// Bit width: 1
        pub const IDR15: u8 = 15;

        /// Port input data (y = 0..15)
        /// Bit width: 1
        pub const IDR14: u8 = 14;

        /// Port input data (y = 0..15)
        /// Bit width: 1
        pub const IDR13: u8 = 13;

        // ...
    }

    // ...
}

These constants allow you to access for example the PUPDR register and PUPDR15 field using the full path:

gpioa::PUPDR; // PUPDR register
gpioa::pupdr::PUPDR15; // PUPDR15 field

You can read from a register using read_volatile, and mask the value with the included fields

use core::ptr::read_volatile;
unsafe { read_volatile(gpioa::PUPDR) & (0b1 << gpioa::pupdr::PUPDR15) }

You can write to a register using write_volatile and use the included fields to shift into the correct position

use core::ptr::write_volatile;
unsafe { write_volatile(gpioa::PUPDR, 0b1 << gpioa::pupdr::PUPDR15); }

And you can combine the two to set/clear fields in a register

use core::ptr::{read_volatile, write_volatile};
unsafe {
    // Set
    write_volatile(
        gpioa::PUPDR,
        read_volatile(gpioa::PUPDR) | (0b1 << gpioa::pupdr::PUPDR15)
    );

    // Clear
    write_volatile(
        gpioa::PUPDR,
        read_volatile(gpioa::PUPDR) & !(0b1 << gpioa::pupdr::PUPDR15)
    );
}

About

SVD to Rust register structure parser

Resources

License

Stars

Watchers

Forks

Contributors

Languages