Crate svd2rust [−] [src]
Generate Rust register maps (struct
s) from SVD files
Changelog
Installation
$ cargo install svd2rust
Usage
- Get the start/base address of each peripheral's register block.
$ svd2rust -i STM32F30x.svd const GPIOA: usize = 0x48000000; const GPIOB: usize = 0x48000400; const GPIOC: usize = 0x48000800; const GPIOD: usize = 0x48000c00; const GPIOE: usize = 0x48001000; const GPIOF: usize = 0x48001400; const TSC: usize = 0x40024000; const CRC: usize = 0x40023000; const Flash: usize = 0x40022000; const RCC: usize = 0x40021000;
- Generate a register map for a single peripheral.
$ svd2rust -i STM32F30x.svd rcc | head /// Reset and clock control #[repr(C)] pub struct Rcc { /// 0x00 - Clock control register pub cr: Cr, /// 0x04 - Clock configuration register (RCC_CFGR) pub cfgr: Cfgr, /// 0x08 - Clock interrupt register (RCC_CIR) pub cir: Cir, /// 0x0c - APB2 peripheral reset register (RCC_APB2RSTR)
API
The svd2rust
generates the following API for each peripheral:
Register block
A register block "definition" as a struct
. Example below:
/// Inter-integrated circuit #[repr(C)] pub struct I2c1 { /// 0x00 - Control register 1 pub cr1: Cr1, /// 0x04 - Control register 2 pub cr2: Cr2, /// 0x08 - Own address register 1 pub oar1: Oar1, /// 0x0c - Own address register 2 pub oar2: Oar2, /// 0x10 - Timing register pub timingr: Timingr, /// 0x14 - Status register 1 pub timeoutr: Timeoutr, /// 0x18 - Interrupt and Status register pub isr: Isr, /// 0x1c - Interrupt clear register pub icr: Icr, /// 0x20 - PEC register pub pecr: Pecr, /// 0x24 - Receive data register pub rxdr: Rxdr, /// 0x28 - Transmit data register pub txdr: Txdr, }
The user has to "instantiate" this definition for each peripheral the microcontroller has. They have two choices:
static
variables. Example below:
extern "C" { // I2C1 can be accessed in read-write mode pub static mut I2C1: I2c; // whereas I2C2 can only be accessed in "read-only" mode pub static I2C1: I2c; }
here the addresses of these register blocks must be provided by a linker script:
/* layout.ld */
I2C1 = 0x40005400;
I2C2 = 0x40005800;
This has the side effect that the I2C1
and I2C2
symbols get "taken" so
no other C/Rust symbol (static
, function
, etc.) can have the same name.
- "constructor" functions. Example below:
// Base addresses of the register blocks. These are private. const I2C1: usize = 0x40005400; const I2C2: usize = 0x40005800; // NOTE(unsafe) hands out aliased `&mut-` references pub unsafe fn i2c1() -> &'static mut I2C { unsafe { &mut *(I2C1 as *mut I2c) } } pub fn i2c2() -> &'static I2C { unsafe { &*(I2C2 as *const I2c) } }
read
/ modify
/ write
Each register in the register block, e.g. the cr1
field in the I2c
struct, exposes a combination of the read
, modify
and write
methods.
Which methods exposes each register depends on whether the register is
read-only, read-write or write-only:
- read-only registers only expose the
read
method. - write-only registers only expose the
write
method. - read-write registers expose all the methods:
read
,modify
andwrite
.
This is signature of each of these methods:
(using the CR2
register as an example)
impl Cr2 { pub fn modify<F>(&mut self, f: F) where for<'w> F: FnOnce(&Cr2R, &'w mut Cr2W) -> &'w mut Cr2W { .. } pub fn read(&self) -> Cr2R { .. } pub fn write<F>(&mut self, f: F) where F: FnOnce(&mut Cr2W) -> &mut Cr2W, { .. } }
The read
method "reads" the register using a single, volatile LDR
instruction and returns a proxy Cr2R
struct that allows access to only the
readable bits (i.e. not to the reserved or write-only bits) of the CR2
register:
impl Cr2R { /// Bit 0 - Slave address bit 0 (master mode) pub fn sadd0(&self) -> bool { .. } /// Bits 1:7 - Slave address bit 7:1 (master mode) pub fn sadd1(&self) -> u8 { .. } (..) }
Usage looks like this:
// is the SADD0 bit of the CR2 register set? if i2c1.c2r.read().sadd0() { // something } else { // something else }
The write
method writes some value to the register using a single,
volatile STR
instruction. This method involves a Cr2W
struct that only
allows constructing valid states of the CR2
register.
The only constructor that Cr2W
provides is reset_value
which returns the
value of the CR2
register after a reset. The rest of Cr2W
methods are
"builder-like" and can be used to set or reset the writable bits of the
CR2
register.
impl Cr2W { /// Reset value pub fn reset_value() -> Self { Cr2W { bits: 0 } } /// Bits 1:7 - Slave address bit 7:1 (master mode) pub fn sadd1(&mut self, value: u8) -> &mut Self { .. } /// Bit 0 - Slave address bit 0 (master mode) pub fn sadd0(&mut self, value: bool) -> &mut Self { .. } }
The write
method takes a closure with signature &mut Cr2W -> &mut Cr2W
.
If the "identity closure", |w| w
, is passed then write
method will set
the CR2
register to its reset value. Otherwise, the closure specifies how
that reset value will be modified before it's written to CR2
.
Usage looks like this:
// Write to CR2, its reset value (`0x0000_0000`) but with its SADD0 and // SADD1 fields set to `true` and `0b0011110` respectively i2c1.cr2.write(|w| w.sadd0(true).sadd1(0b0011110));
Finally, the modify
method performs a single read-modify-write
operation that involves reading (LDR
) the register, modifying the fetched
value and then writing (STR
) the modified value to the register. This
method accepts a closure that specifies how the CR2
register will be
modified (the w
argument) and also provides access to the state of the
register before it's modified (the r
argument).
Usage looks like this:
// Set the START bit to 1 while KEEPING the state of the other bits intact i2c1.cr2.modify(|_, w| w.start(true)); // TOGGLE the STOP bit i2c1.cr2.modify(|r, w| w.stop(!r.stop()));