Generate Rust register maps (`struct`s) from SVD files

Last update: May 22, 2022

crates.io crates.io

svd2rust

Generate Rust register maps (structs) from SVD files

This project is developed and maintained by the Tools team.

Documentation

API

Minimum Supported Rust Version (MSRV)

The generated code is guaranteed to compile on stable Rust 1.51.0 and up.

If you encounter compilation errors on any stable version newer than 1.51.0, please open an issue.

Testing Locally

svd2rust-regress is a helper program for regression testing changes against svd2rust. This tool can be used locally to check modifications of svd2rust locally before submitting a PR.

Check out the svd2rust-regress README for information on how to use this tool.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Code of Conduct

Contribution to this crate is organized under the terms of the Rust Code of Conduct, the maintainer of this crate, the Tools team, promises to intervene to uphold that code of conduct.

GitHub

https://github.com/japaric/svd2rust
Comments
  • 1. Automatically generate feature gates for all peripherals

    This PR adds cfg(feature = "...") gates for all the peripherals that are generated. It means that if people only want to use certain peripherals, that they don't need to compile all of them which can take a very long time.

    This PR is not yet finished (I want to see CI though :wink: ) – ideally there should also be a Cargo.toml skeleton that's generated or even just a .txt file which includes all available feature flags for users to expose in the Cargo.toml.

    Reviewed by spacekookie at 2018-09-18 12:51
  • 2. [RFC] Peripherals as scoped singletons

    Motivation

    Today, in svd2rust and cortex-m APIs, peripherals are modeled as global singletons that require some synchronization, e.g. a critical section like interrupt::free, to be modified.

    The consequences of this choice is that (a) driver APIs are not ergonomic as one would expect, see below:

    use stm32f103xx::TIM6;
    
    // Periodic timer
    struct Timer<'a>(&'a TIM6);
    
    impl<'a> Timer<'a> { .. }
    
    fn main() {
        interrupt::free(|cs| {
            let tim6 = TIM6.borrow(cs);
            let timer = Timer(tim);
            timer.init(..);
            // ..
            timer.resume();
        });
    }
    
    interrupt!(TIM6, tim6);
    
    fn tim6() {
        interrupt::free(|cs| {
            let tim6 = TIM6.borrow(cs);
            let timer = Timer(tim);
            timer.clear_update_flag();
            // ..
        });
    }
    

    Here the Timer abstraction has to be reconstructed in each execution context. One would prefer to instantiate Timer as a static variable and then use that from each execution context. However, that's not possible with this Timer struct because of the non-static lifetime of the inner field. (It is possible to remove the inner field from Timer at the expense of having a critical section per method call -- which has worse performance than the non-static lifetime approach).

    Even worst is that (b) driver abstractions can be easily broken due to the global visibility property of peripherals. This means that there's no way to e.g. make sure that TIM6 is only used as a Timer in main and tim6. Nothing prevents you, or some other crate author, from silently using TIM6 in other execution context -- TIM6 doesn't even have to appear as a function argument because it's always in scope. This issue not only breaks abstractions; you can also have race conditions on TIM6 -- yes, even with interrupt::free you can have race conditions and that's totally memory safe per Rust definition -- if Timer is being used in other execution contexts and you don't know about it.

    So, what can we do to fix these issues? We can remove the root of all these problems: global visibility.

    This RFC is proposing to go from global singletons to scoped singletons. Instead of having peripheral singletons with global visibility you'll have to explicitly import a peripheral singleton into scope, into the current execution context. Because we are talking about singletons here you can only import singleton P into scope (execution context) S once. IOW, if you imported P into an execution context S then you can't import P into another scope S'.

    This RFC not only addresses the problems (a) and (b) mentioned above; it also helps us tackle the problem of compile time checked configuration -- more about that later on.

    Detailed design

    Zero sized proxies that represent a peripheral register block, as shown below, will be added to cortex-m and svd2rust generated crates.

    pub struct GPIOA { _marker: PhantomData<*const ()> }
    
    // Peripherals are `Send` but *not* `Sync`
    unsafe impl Send for GPIOA {}
    
    impl Deref for GPIOA {
        type Target = gpioa::RegisterBlock;
    
        fn deref(&self) -> &gpio::RegisterBlock { /* .. */ }
    }
    

    These proxies will be impossible to directly instantiate. Instead there will be a guarded function that returns all the proxies only once.

    pub struct Peripherals {
        pub GPIOA: GPIOA,
        pub GPIOB: GPIOB,
        // ..
    }
    
    impl Peripherals {
        pub fn all() -> Option<Self> {
            interrupt::free(|_| unsafe {
                static mut USED = false;
    
                if USED {
                    None
                } else {
                    USED = true;
                    Some(Peripherals { .. })
                }
            })
        }
    }
    

    The user will be able to access the proxies like this:

    fn main() {
        let p = Peripherals::all().unwrap(); // first call: OK
        //let p = Peripherals::all().unwrap(); // a second call would panic!
    
        p.GPIOA.bsrr.write(|w| /* .. */);
        p.GPIOB.bsrr.write(|w| /* .. */);
    }
    

    Thus the proxies are singletons: there can only exist a single instance of each of them during the whole lifetime of the program. The proxies are also scoped, in this case they are tied to main, so they are not visible to other execution contexts, unless they are explicitly moved into another execution context.

    Zero cost

    An unsafe, unguarded variant of Peripherals::all will also be provided:

    static mut USED = false;
    
    impl Peripherals {
        pub fn all() -> Option<Self> {
            interrupt::free(|_| unsafe { /* .. */ })
        }
    
        // NOTE for safety this function can only be called once and before `Peripherals::all` is called
        pub unsafe fn _all() -> Self {
            USED = true;
            Peripherals { .. }
        }
    }
    

    When only the unsafe variant is used the cost of having scoped singletons becomes zero:

    fn main() {
        let p = unsafe { Peripherals::_all() };
    
        p.GPIOA.bssr.write(|w| /* .. */)
    }
    

    This program has the same cost as using a global, unsynchronized GPIOA register block (which is what you see in C HALs).

    Sidestepping the proxy

    Each peripheral will provide a static method, ptr, that returns a raw pointer into the register block:

    impl GPIOA {
        pub fn ptr() -> *const gpioa::RegisterBlock {
            0x4001_0800 as *const _
        }
    }
    

    This is useful for implementing safe APIs that perform "unsynchronized" reads on registers that have no side effect when read:

    impl DWT {
        /// Reads the cycle counter
        // NOTE this is a static method and doesn't require an instance of `DWT`
        pub fn cyccnt() -> u32  {
            unsafe { (*DWT::ptr()).cyccnt.read() }
        }
    }
    
    // time something
    let before = DWT::cyccnt();
    // ..
    let after = DWT::cyccnt();
    let elapsed = after.wrapping_sub(before);
    

    Enabling new APIs

    Scoped singletons effectively give move semantics to peripherals. This enables richer, type safer APIs than what can be expressed with the current peripheral API. Let's see some examples:

    (you can find some initial experiments with these APIs in the singletons branch of the f3 repository)

    Type state as a contract

    Digital I/O pins can be configured as inputs, outputs or to some special functionality like serial, SPI or I2C. In some cases you want to configure a pin to operate in a certain mode for the duration of the whole program; that is you don't want the pin to be re-configured by mistake.

    Type state is a good way to encode this property: a type is used to encode the state of an object. To transition the object from a state to another it needs to be moved so that the previous state can no longer be used.

    Here's a tentative GPIO API that uses type state:

    use blue_pill::GpioExt;
    
    let p = Peripherals::all().unwrap();
    
    // from `GpioExt`: `fn pins(self) -> gpioa::Pins`
    // `Pins` is a struct that contains all the pins. Each pin is (also) a singleton (!).
    let pins = p.GPIOA.pins();
    
    // `fn as_output(self) -> Output<Self>`
    // `as_output` configures a pin as an output pin
    let pa0 = pins.PA0.as_output();
    
    // `fn as_input(self) -> Input<Self>`
    // `as_input` configures a pin as an input pin
    let pa1 = pins.PA1.as_input();
    
    // `Output::high(&mut self)`
    // set PA0 high (to 3.3V)
    pa0.high();
    
    // `Input::is_high(&self) -> bool`
    if pa1.is_high() {
        // ..
    }
    
    // this would cause a compile error because `PA0` is not in the `Input` state
    // pa0.is_high();
    
    // this would cause a compile error because `PA1` is not in the `Output` state
    // pa1.high();
    
    // this would cause a compile error because `GPIOA` was consumed by the `pins` call
    // p.GPIOA.moder.modify(|_, w| /* configure PA0 as an input */)
    

    Here the Input and Output newtypes are used to encode the type state. The most important part here is that GPIOA is consumed to generate the individual pins and thus it can't no longer be used to configure the pins -- which would break Input / Output contract of "this pin can only be used as an input for the rest of the program".

    Compile time pin allocation

    On modern microcontrollers a single pin can be configured to have one of many functionalities (Serial, PWM, I2C, etc.). This lets vendor pack more peripherals in a microcontroller without increasing the number of pins.

    A problem that arises from this flexibility is that one might wrongly configure two, or more, peripherals to use the same pin. With move semantics you can construct an API that ensures that this can't happen:

    let p = Peripherals::all().unwrap();
    let pins = p.GPIOA.pins();
    
    // use PA9 and PA10 for the USART1 serial port
    
    // NOTE consumes `pa9`
    let tx = pa9.as_usart1_tx();
    
    // NOTE consumes `pa10`
    let rx = pa10.as_usart1_rx();
    
    // `Serial::new` in turn consumes `tx` and `rx`
    let serial = Serial::new(p.USART1, (tx, rx), 115_200.bps());
    
    // this would error because `pa9` was consumed above
    //let ch2 = pa9.as_tim1_ch2();
    //let pwm = Pwm::new(p.TIM1, ch2, 10.khz());
    

    Here we have high level abstractions like Serial consume the pins they are going to use. This way once one such abstraction is created no other abstraction can't use any of the pins the first one is using.

    Non overlapping register access

    The "split" technique used for GPIO pins can also be used to split a peripherals in "parts" that (a) modify different registers and (b) modify non overlapping parts of a same register. This comes in handy with peripherals like the DMA which usually interacts with several other peripherals.

    Vendors usually design their DMA peripherals so that even if the settings related to different channels are stored in a single register that register can be modified atomically using e.g. "clear flag" bits (no RMW operation required to clear a flag). If the vendor doesn't provide such mechanism bit banding can probably be used in its place, if the device has support for it.

    RTFM protects peripherals at the register block level. Without move semantics, to clear "transfer complete" interrupt flags from two interrupts handlers running at different priorities you would need a lock in the lowest priority handler:

    // priority = 2
    fn dma1_channel1(r: DMA1_CHANNEL1::Resources) {
        r.DMA1.lock(|dma1| {
            // clear the transfer complete flag for this channel
            dma1.ifcr.write(|w| w.ctcif1().set_bit());
            // ..
        });
    }
    
    // priority = 3
    fn dma1_channel2(r: DMA1_CHANNEL2::Resources) {
        let dma1 = r.DMA1;
        // clear the transfer complete flag for this channel
        dma1.ifcr.write(|w| w.ctcif2().set_bit())
        // ..
    }
    

    But with move semantics you can split the DMA in channels and assign exclusive access to each channel to each interrupt handler:

    fn init(p: init::Peripherals) -> init::LateResourceValues {
        let channels = p.DMA1.split();
    
        // ..
    
        init::LateResourceValues { CH1: channels.1, CH2: channels.2 }
    }
    
    // priority = 2
    // resources = [CH1]
    fn dma1_channel1(r: DMA1_CHANNEL1::Resources) {
        // clear the transfer complete flag for this channel
        r.CH1.clear_transfer_complete_flag();
    }
    
    // priority = 3
    // resources = [CH2]
    fn dma1_channel2(r: DMA1_CHANNEL2::Resources) {
        // clear the transfer complete flag for this channel
        r.CH2.clear_transfer_complete_flag();
    }
    

    Thus no locking is needed. Each channel will operate on a non-overlapping portion of the shared IFCR register.

    Configuration freezing

    In some cases you want to configure the core and peripherals clocks to operate at certain frequencies during initialization and then make sure that these frequencies are not changed later at runtime.

    Again, move semantics can help here by "discarding" the peripheral in charge of clock configuration once the clock has been configured:

    use blue_pill::RccExt;
    
    let p = Peripherals::all().unwrap();
    
    // .. use `p.RCC`, or a higher level API based on it, to configure the clocks ..
    
    // from `RccExt`: `fn clocks(self) -> Clocks`
    // `clocks` contains information about the operating frequency of the peripheral buses
    let clocks = p.RCC.freeze();
    
    // To compute USART1 internal prescalers and achieve the desired baud rate,
    // information about the current clock configuration is required.
    // `clocks` provides that information
    let serial = Serial::new(p.USART1, (tx, rx), &clocks, 115_200.bps());
    

    Here, once the clock is configured, its configuration gets frozen by consuming / discarding the RCC peripheral. With this ... move the clock frequencies can no longer be changed. Freezing RCC returns a Clocks struct that contains the frequency of every peripheral bus. This information is required to properly configure the operating frequency of each peripheral so it's passed to peripherals' init functions.

    Drawbacks

    Regression when not using RTFM

    And you still want to use interrupts.

    RTFM supports moving runtime initialized resources into tasks (interrupts) at zero, or very little, cost but if you are not using RTFM then a static variable is required to share a peripheral between e.g. main and an interrupt handler. Putting a peripheral singleton in a static variable means making it globally visible which means you have global singletons again, and all their disadvantages, but with worse performance (because an Option and a RefCell are needed, see below) than what you get with today's API.

    With this RFC:

    // you want to share `GPIOA` between `main` and `exti0`
    // but this makes it global so any other execution context can also use it
    static GPIOA: Mutex<RefCell<Option<GPIOA>>> = Mutex::new(RefCell::new(None));
    
    fn main() {
        // initialize `GPIOA`
        let p = Peripherals::all().unwrap();
        interrupt::free(move |cs| {
            *GPIOA.borrow(cs).borrow_mut() = Some(p.GPIOA);
        });
    
        loop {
            interrupt::free(|cs| {
                let gpioa = GPIOA.borrow(cs).borrow().as_ref().unwrap();
    
                // ..
            });
    
            // ..
        }
    }
    
    interrupt!(EXTI0, exti0);
    
    fn exit0() {
        interrupt::free(|cs| {
            let gpioa = GPIOA.borrow(cs).borrow().as_ref().unwrap();
    
            // ..
        });
    }
    

    With today's API:

    // you don't have the RefCell + Option overhead but you still have global visibility
    
    fn main() {
        loop {
            interrupt::free(|cs| {
                let gpioa = GPIOA.borrow(cs);
    
                // ..
            });
    
            // ..
        }
    }
    
    interrupt!(EXTI0, exti0);
    
    fn exit0() {
        interrupt::free(|cs| {
            let gpioa = GPIOA.borrow(cs);
    
            // ..
        });
    }
    

    Compare this to RTFM + this RFC:

    app! {
        resources: {
             static GPIOA: GPIOA;
        },
    
        idle: {
            resources: [GPIOA],
        },
    
        tasks: {
            EXTI0: {
                path: exti0,
                resources: [GPIOA],
            },
        },
    }
    
    fn init(p: init::Peripherals) -> init::LateResourceValues {
        init::LateResourceValues {
            GPIOA: p.GPIOA,
        }
    }
    
    fn idle(t: &mut Threshold, r: idle::Resources) -> ! {
        loop {
            r.GPIOA.claim(threshold, |gpioa| {
                // do stuff with `r.GPIOA`
            });
    
            // ..
        }
    }
    
    fn exti0(r: EXTI0::Resources) {
         // do stuff with `r.GPIOA`
    }
    

    No Mutex, no RefCell, no Option and no global visibility. Plus only idle needs a lock to access GPIOA. Without this RFC, even with RTFM, it's possible to access GPIOA from an execution context that's not idle or exti0 due to this issue / bug / hole: japaric/cortex-m-rtfm#13.

    It breaks the world

    Breaking changes in cortex-m, svd2rust and cortex-m-rtfm are required to implement this.

    Unresolved questions

    • Should we have finer grained access to peripherals as in a GPIOA::take() that returns Option<GPIOA> (in addition to Peripherals::all() -- one would invalidate the other)?

    • The unsafe variant, Peripherals::_all, needs a better name.

    Implementation bits

    Note that the implementation is a bit crude at this point. Expect bugs. Still, I'm posting now to get feedback and to allow others to experiment.

    • The f3 repo contains a high level API, and examples that use it, based on this RFC.
    • japaric/cortex-m#65
    • japaric/svd2rust#158
    • japaric/cortex-m-rtfm#50

    cc @pftbest @thejpster @therealprof @hannobraun @nagisa @kjetilkjeka


    EDIT: I realized that it may not be obvious how the RFC solves problem (a) so here's an example:

    Let's say that you want to use a serial port in idle and in some interrupt handler. You know that idle only needs to use the transmitter half the serial port and the interrupt handler only needs to use the receiver part.

    app! {
        resources: {
            static TX: Tx;
            static RX: Rx;
        },
    
        idle: {
            resources: [TX],
        },
    
        tasks: {
            EXTI0: {
                path: exti0,
                resources: [RX],
            },
        },
    }
    
    fn init(p: init::Peripherals) -> init::LateResourceValues {
        // consumes `GPIOA`
        let pins = p.GPIOA.pins();
    
        // consumes `PA9`
        let pa9 = pins.PA9.as_usart1_tx();
    
        // consumes `PA10`
        let pa10 = pins.PA10.as_usart1_rx();
    
        // consumes `USART1`, `pa9` and `pa10`
        let serial: Serial = Serial::new(p.USART1, (pa9, pa10), 115_200.bps());
    
        // consumes `serial`
        let (tx, rx): (Tx, Rx) = serial.split();
    
        // initializes the resources `TX` and `RX`
        init::LateResourceValues {
            TX: tx,
            RX: rx,
        }
    }
    
    fn idle(_t: &mut Threshold, r: idle::Resources) -> ! {
        let tx: &mut Tx = r.TX;
    
        loop {
            // do stuff with `tx`
        }
    }
    
    fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
        let rx: &mut Rx = r.RX;
    
        // do stuff with `rx`
    }
    

    With all the moves in init you are sure that:

    • No task or crate (dependency) can reconfigure the pins through GPIOA, because it (GPIOA) was created (cf. the init::Peripherals argument of init) and consumed in init.

    • No task or crate (dependency) can reconfigure or drive the pins PA9 and PA10, because both pins were created and consumed in init.

    • No task or crate (dependency) can do serial I/O through USART1 because it (USART1) was created and consumed in init.

    • No task or crate (dependency) other than idle can do serial writes because only idle has access to Tx.

    • No task or crate (dependency) other than exti0 can do serial reads because only exti0 has access to Rx.

    Without this RFC you can't have any of these guarantees because the svd2rust API lets you use GPIOA, USART1 in any execution context so even a function foo with signature fn() (note: no arguments) can reconfigure the pins or start a serial operation.

    Reviewed by japaric at 2017-11-21 16:19
  • 3. Emit unions for overlapping/overloaded registers

    I want to get a complete working build for ATSAMD21 (https://github.com/rust-lang-nursery/embedded-wg/issues/61) so I'm taking a stab at that.

    • Introduced a FieldRegion helper to reason about overlapping regions
    • If overlaps are detected, a union container is emitted
    • If the only item in a RegisterBlock is a union, that RegisterBlock is emitted as a union
    • Otherwise: we generate a name for the union field by either taking the shortest common prefix of the union's alternates or the shortest register name (depending on type name conflicts). If that doesn't work just pick an artificial name like u1.
    • If the starting address offset of elements in a union are not all the same, we don't have a way to emit padding for them today. We will emit a warning (and bad code) in that case (example below). The one example of this I see in ATSAMD21 is due to missing derivedFrom support for registers; we're currently generating bad code for these anyway. I have resolved in another branch that I'll turn into a PR once this one is landed.
    WARNING: field Some(Ident("pmux1_1")) has different offset 177 than its union container 176
    WARNING: field Some(Ident("pmux1_2")) has different offset 178 than its union container 176
    

    Examples:

    #[doc = "Real-Time Counter"]
    pub mod rtc {
        #[doc = r" Register block"]
        #[repr(C)]
        pub union RegisterBlock {
            #[doc = "0x00 - Clock/Calendar with Alarm"]
            pub mode2: MODE2,
            #[doc = "0x00 - 16-bit Counter with Two 16-bit Compares"]
            pub mode1: MODE1,
            #[doc = "0x00 - 32-bit Counter with Single 32-bit Compare"]
            pub mode0: MODE0,
        }
    
        #[doc = r" Register block"]
        #[repr(C)]
        pub struct USART {
            #[doc = "0x00 - USART Control A"]
            pub ctrla: self::usart::CTRLA,
            #[doc = "0x04 - USART Control B"]
            pub ctrlb: self::usart::CTRLB,
            _reserved2: [u8; 4usize],
            #[doc = "USART Baud Rate"]
            pub baud: baud_union,
          ...
        }
        #[doc = "USART Baud Rate"]
        #[repr(C)]
        pub union baud_union {
            #[doc = "0x0c - USART Baud Rate"]
            pub baud_usartfp_mode: self::usart::BAUD_USARTFP_MODE,
            #[doc = "0x0c - USART Baud Rate"]
            pub baud_fracfp_mode: self::usart::BAUD_FRACFP_MODE,
            #[doc = "0x0c - USART Baud Rate"]
            pub baud_frac_mode: self::usart::BAUD_FRAC_MODE,
            #[doc = "0x0c - USART Baud Rate"]
            pub baud: self::usart::BAUD,
        }
    

    Refs: https://github.com/japaric/svd2rust/issues/191 https://github.com/japaric/svd2rust/issues/16

    Reviewed by wez at 2018-03-13 15:04
  • 4. Ignore non snake case types that can exist in SVD files

    Hi! :wave: Thanks for this awesome project!

    I've run into a build error generating bindings for the psoc6-pac. Here's a example of one of the many build failures:

    error: structure field `data_list_sent_update__status` should have a snake case name
       --> src/ble.rs:187:9
        |
    187 |     pub data_list_sent_update__status: self::blell::DATA_LIST_SENT_UPDATE__STATUS,
        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `data_list_sent_update_status`
    

    This issue is caused by #![deny(warnings)] and should be fixed by adding the #![allow(non_snake_case)] exception. There may be alternative fixes, I'm open to suggestions!

    Reviewed by mvertescher at 2019-11-12 14:04
  • 5. Remove bare-metal dependency from generated crates

    The only dependency on bare-metal was the implementation of the Nr trait which has been removed from version 1.0.0 (rust-embedded/bare-metal#32).

    Closes #453

    Signed-off-by: Daniel Egger [email protected]

    Reviewed by therealprof at 2020-07-05 12:58
  • 6. Add initial Cluster support

    Add cluster support to svd2rust. I am calling it initial, because I have only tested it against the nRF52 svd file.

    Note, this is based on https://github.com/japaric/svd2rust/pull/180, so I would prefer that PR be merged first. If that PR is rejected, I can instead rebase these changes on japaric/master (just let me know).

    Edit: All changes relevant to clusters are squashed into the last commit of this PR, so if you want to see the relevant changes, check out https://github.com/japaric/svd2rust/pull/182/commits/6de6de760c535e6f60eed4acb41e30db1458f33c.

    This is based on the work of @brandonedens earlier https://github.com/japaric/svd2rust/pull/149, and I believe would close https://github.com/japaric/svd2rust/issues/107 and related issues.

    CC @japaric @emilgardis @therealprof

    Edit 2: Here are some handy references:

    Reviewed by jamesmunns at 2018-02-27 14:33
  • 7. enum based API (alternative write API)

    This PR is like #52 but with an alternative write API.

    The generated write API looks like this:

    #[derive(Clone, Copy)]
    #[repr(C)]
    pub struct DirW {
        bits: u32,
    }
    
    pub struct DirWPin0<'a> {
        register: &'a mut DirW,
    }
    
    impl<'a> DirWPin0<'a> {
        pub fn input(self) -> &'a mut DirW {
            const MASK: u32 = 1;
            const OFFSET: u8 = 0u8;
            self.register.bits &= !((MASK as u32) << OFFSET);
            self.register.bits |= 0u32 << OFFSET;
            self.register
        }
    
        pub fn output(self) -> &'a mut DirW {
            const MASK: u32 = 1;
            const OFFSET: u8 = 0u8;
            self.register.bits &= !((MASK as u32) << OFFSET);
            self.register.bits |= 1u32 << OFFSET;
            self.register
        }
    }
    
    impl DirW {
        #[doc = "Bit 0 - Pin 0."]
        pub fn pin0(&mut self) -> DirWPin0 {
            DirWPin0 { register: self }
        }
        
        ..
    }
    

    Instead of exposing a method that takes an enum to modify a bitfield of a register (register.bitfield(Enum::VariantA)), like in #52, this API exposes a method that returns a proxy struct whose methods let you pick a value for the bitfield (register.bitfield().variant_a()) and this method returns back the register so you don't lose the chain-ability: register.bitfield1().variant_a().bitfield2().variant_b().

    Here's the blinky example from #52 ported to this alternative write API.

    cc @brandonedens

    Reviewed by japaric at 2017-01-29 17:16
  • 8. MSP430 Improvements

    This PR brings MSP430 PAC generation up to parity with Cortex M PAC generation. Among other things, a device.x file is now generated for each MSP430 PAC, and the interrupt! macro has been replaced with an attribute macro from the not-yet-published msp430-rt v0.2.0.

    The remaining changes are docs and cleanups of the generated code; a number of features have become stable. Only abi_msp430_interrupt is not stable yet, and I have a workaround for this that I've not written yet.

    Switching to an interrupt attribute macro is a breaking change, so I incremented the "major" version number.

    Reviewed by cr1901 at 2019-12-23 04:30
  • 9. WIP: Register derivedFrom and svd updates

    This PR extends https://github.com/rust-embedded/svd2rust/pull/193 (edit: and https://github.com/rust-embedded/svd2rust/pull/190), and also brings support for the changes made in svd-parser, primarily understanding the RegisterCluster type.

    Reviewed by jamesmunns at 2018-11-17 21:06
  • 10. Simplify Interrupt::nr

    Make the Interrupt enum descriminant match the interrupt nr directly. This allows simplifying Interrupt::nr from being a complicated match to a simple cast to an u8.

    According to #370, this should lead to a code reduction of as much as 304 bytes.

    Fixes #211 Fixes #370

    Reviewed by roblabla at 2019-12-24 17:48
  • 11. Generate `bool` accessors for single-bit fields

    This makes using such fields significantly more ergonomic, as previously they would have been u8s, leading to unnecessary conversion and silent truncation in downstream code.

    The bool writers are never unsafe because it is meaningless to have a writable field that only admits one (or worse, zero) valid values.

    Reviewed by whitequark at 2017-04-30 04:14
  • 12. Enum typename considering name in enumeratedValues

    svd2rust has a good mechanism to parse enumeratedValues into Rust enums. However, current code would pick name of the first field the enum resides, and use pub type to re-export it into other names.

    SVD files provide name field in enumeratedValues, this commit make use of them to name the Rust enums it generated. After this commit, more information of SVD file is considered in output pac crate. Users may also discover a significant drop in amount of types, which will speed up rustdoc generation and crate compilation.

    Before this pull request:

    图片

    After this pull request:

    Note that INTERRUPT_ENABLE_A comes from enumeration name InterruptEnable, but not register name interrupt_enable. 图片

    SVD file for this example:

                <field>
                  <name>fifo_error_enable</name>
                  <description>Transmit or receive FIFO error interrupt enable</description>
                  <lsb>29</lsb>
                  <msb>29</msb>
                  <writeConstraint>
                    <useEnumeratedValues>true</useEnumeratedValues>
                  </writeConstraint>
                  <enumeratedValues>
                    <name>InterruptEnable</name>
                    <enumeratedValue>
                      <name>enable</name>
                      <description>Enable interrupt</description>
                      <value>1</value>
                    </enumeratedValue>
                    <enumeratedValue>
                      <name>disable</name>
                      <description>Disable interrupt</description>
                      <value>0</value>
                    </enumeratedValue>
                  </enumeratedValues>
                </field>
                <field>
                  <name>arbitrate_lost_enable</name>
                  <description>Arbitration lost interrupt enable</description>
                  <lsb>28</lsb>
                  <msb>28</msb>
                  <writeConstraint>
                    <useEnumeratedValues>true</useEnumeratedValues>
                  </writeConstraint>
                  <enumeratedValues derivedFrom="InterruptEnable" />
                </field>
                <field>
                  <name>not_acknowledged_enable</name>
                  <description>Not-acknowledged response interrupt enable</description>
                  <lsb>27</lsb>
                  <msb>27</msb>
                  <writeConstraint>
                    <useEnumeratedValues>true</useEnumeratedValues>
                  </writeConstraint>
                  <enumeratedValues derivedFrom="InterruptEnable" />
                </field>
    <!-- and all other fields in this register -->
    
    Reviewed by luojia65 at 2022-05-21 08:05
  • 13. Support multi-level derive

    Some SVDs in CI (Fujitsu and Spansion, for ex. https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/Spansion/MB9BF36xx.svd) contains registers with sequences of deriveFrom

            <register derivedFrom="DDR1">
              <name>PDIR1</name>
              <description>Port input data register 1</description>
              <addressOffset>0x304</addressOffset>
            </register>
    ...
            <register derivedFrom="PFR1">
              <name>DDR1</name>
              <description>Port input/output direction setting register 1</description>
              <addressOffset>0x204</addressOffset>
            </register>
    

    Now this is processed in incorrect way.

    Reviewed by burrbull at 2022-05-10 16:07
  • 14. The ability to obtain offset values for the modifiable bit fields of registers

    To be able to manipulation of peripheral registers through bit-banding, the values of the offset of the modifiable bit fields in the registers are required.

    For example, there are functions for bit-banding here: https://docs.rs/stm32f1xx-hal/0.9.0/stm32f1xx_hal/bb/index.html

    Implementation variant:

    pub mod perih_name {
       ...
       pub mod reg_name {
          ...
          pub const field_name_OFFSET: u8 = ...;
          ...
       }
       ...
    }
    

    Example of an implementation variant (stm32f1):

    pub mod rcc {
       ...
       pub mod apb2enr {
          ...
          pub const USART1EN_OFFSET: u8 = 14;
          ...
          impl<'a> USART1EN_W<'a> {
             ...
             pub fn bit(self, value: bool) -> &'a mut W {
                self.w.bits = (self.w.bits & !(0x01 << USART1EN_OFFSET)) | ((value as u32 & 0x01) << USART1EN_OFFSET);
                self.w
             }
          }
          ...
          impl R {
             ...
             pub fn usart1en(&self) -> USART1EN_R {
                USART1EN_R::new(((self.bits >> USART1EN_OFFSET) & 0x01) != 0)
             }
             ...
          }
          ...
       }
       ...
    }
    

    Usage example:

    unsafe {
       use stm32f1xx_hal::{self as hal, pac};
       hal::bb::set(&(*pac::RCC::PTR).apb2enr, pac::rcc::apb2enr::USART1EN_OFFSET);
    }
    
    Reviewed by AndreySmirnov81 at 2022-03-14 18:19
  • 15. Add --mark_range flag

    This flag allows implementing a marker trait for registers in specific memory addresses. These can then be used to implement methods on registers. One such example is providing atomic access to only some of the registers directly in the PAC. Currently this is done in HAL, but this loses the ability to use writer proxies. It is also finicky to use raw register addresses. Moreover this allows keeping atomic and non-atomic usage in HAL more similar.

    For an example on how to use this, see my fork of rp2040-pac. The most interesting changes regarding the use reside in

    Even though I have an example only for the RP2040, I believe that other architectures and devices could also benefit from this. For example on SMT32 MCUs this could be used to implement a nicer API for bit-banding.

    Addresses #535

    Reviewed by henkkuli at 2022-01-28 22:15
  • 16. svd2rust fails parsing Atmel SAML22 SYNCBUSY register

    Hi folks,

    I have the SVD for a SAML22N18A ("Atmel SAML22 Series Device Support (1.2.77)" on http://packs.download.atmel.com) and this error:

    $ svd2rust -i ATSAML22N18A.svd 
    [INFO  svd2rust] Parsing device from SVD file
    [ERROR svd2rust] Error parsing SVD file
        
        Caused by:
            0: In device `ATSAML22N18A`
            1: In peripheral `GCLK`
            2: In register `SYNCBUSY`
            3: Parsing field `GENCTRL0` at 5375:13
            4: `EnumeratedValue error: Value 2 out of range 0..2
    

    The register is described like this in SVD:

            <register>
              <name>SYNCBUSY</name>
              <description>Synchronization Busy</description>
              <addressOffset>0x04</addressOffset>
              <size>32</size>
              <access>read-only</access>
              <fields>
                <field>
                  <name>SWRST</name>
                  <description>Software Reset Synchroniation Busy bit</description>
                  <bitOffset>0</bitOffset>
                  <bitWidth>1</bitWidth>
                  <access>read-only</access>
                </field>
                <field>
                  <name>GENCTRL0</name>
                  <description>Generic Clock Generator Control 0 Synchronization Busy bits</description>
                  <bitOffset>2</bitOffset>
                  <bitWidth>1</bitWidth>
                  <access>read-only</access>
                  <enumeratedValues>
                    <name>GENCTRL0Select</name>
                    <enumeratedValue>
                      <name>GCLK0</name>
                      <description>Generic clock generator 0</description>
                      <value>0x1</value>
                    </enumeratedValue>
                    <enumeratedValue>
                      <name>GCLK1</name>
                      <description>Generic clock generator 1</description>
                      <value>0x2</value>
                    </enumeratedValue>
                    <enumeratedValue>
                      <name>GCLK2</name>
                      <description>Generic clock generator 2</description>
                      <value>0x4</value>
                    </enumeratedValue>
                    <enumeratedValue>
                      <name>GCLK3</name>
                      <description>Generic clock generator 3</description>
                      <value>0x8</value>
                    </enumeratedValue>
                    <enumeratedValue>
                      <name>GCLK4</name>
                      <description>Generic clock generator 4</description>
                      <value>0x10</value>
                    </enumeratedValue>
                  </enumeratedValues>
                </field>
                [repeats for GENCTRL1-4]
    

    and like this in the datasheet: saml22-syncbusy

    So it seems like those enumerated values are meant for the entire 5-bit field of GENCTRLs, but it's been written in a way that looks like out of range values to svd2rust. Removing the enumerated values allows the parsing to complete without error. I'm new at this, so I'm not sure: is this something svd2rust should handle, or is it just a weird SVD?

    Reviewed by gtank at 2021-12-20 20:56
This crate allows writing a struct in Rust and have it derive a struct of arrays layed out in memory according to the arrow format.

Arrow2-derive - derive for Arrow2 This crate allows writing a struct in Rust and have it derive a struct of arrays layed out in memory according to th

May 11, 2022
A simple script (in Rust lang) to create HTML from SVD
A simple script (in Rust lang) to create HTML from SVD

A simple script to create HTML from an SVD file This is a simple script written in Rust language to create a single HTML file from an SVD file. It's r

May 8, 2022
Register access crate for AVR microcontrollers

avr-device Auto-generated wrappers around registers for AVR microcontrollers. Usage Add the following to Cargo.toml: [dependencies.avr-device] version

Apr 30, 2022
A proc-macro to get Vec from struct and vise versa

byteme A proc-macro to convert a struct into Vec and back by implemeting From trait on the struct. The conversion is Big Endian by default. We have ma

Apr 14, 2022
A set of bison skeleton files that can be used to generate a Bison grammar that is written in Rust.

rust-bison-skeleton A set of bison skeleton files that can be used to generate a Bison grammar that is written in Rust. Technically it's more like a B

Mar 15, 2022
Rust macro that uses GPT3 codex to generate code at compiletime

gpt3_macro Rust macro that uses GPT3 codex to generate code at compiletime. Just describe what you want the function to do and (optionally) define a f

May 11, 2022
Generate bindings to use Rust code in Qt and QML
Generate bindings to use Rust code in Qt and QML

Rust Qt Binding Generator This code generator gets you started quickly to use Rust code from Qt and QML. In other words, it helps to create a Qt based

May 27, 2022
Generate short, memorable phrases for throw-away names.

Generates three-word phrases of the form intensifier-adjective-noun, just like GitHub default repo names.

Dec 25, 2021
Generate a THIRDPARTY file with all licenses in a cargo project.

cargo-bundle-licenses Bundle all third-party licenses into a single file. NOTE This tools is not a lawyer and no guarantee of correctness can be made

May 18, 2022
Generate alerts for when metrics/recordings become absent

prometheus-absent-data-alert-rule-generator prometheus-absent-data-alert-rule-generator is a tool to generate alerts for missing data in time-series t

May 17, 2022
Used to generate and compare bounded timestamps.
Used to generate and compare bounded timestamps.

ClockBound Summary: ClockBound allows you to generate and compare bounded timestamps that include accumulated error as reported from the local chronyd

May 3, 2022
Generate an HTML page based on a Notion document

Notion Generator Generate an HTML page based on a Notion document! Still a bit of a work in progress, but I am about to actually use it for some actua

May 15, 2022
Generate enum from a trait, with converters between them
Generate enum from a trait, with converters between them

Derive macro for Rust that turns traits into enums, providing tools for calling funtions over channels

May 16, 2022
Rust crate which provides direct access to files within a Debian archive

debarchive This Rust crate provides direct access to files within a Debian archive. This crate is used by our debrep utility to generate the Packages

Dec 18, 2021
Rust crate for reading SER files used in astrophotography

Rust crate for reading SER files used in astrophotography.

Oct 4, 2021
Rust library to scan files and expand multi-file crates source code as a single tree

syn-file-expand This library allows you to load full source code of multi-file crates into a single syn::File. Features: Based on syn crate. Handling

Apr 7, 2022
Concatenate Amazon S3 files remotely using flexible patterns

S3 Concat This tool has been migrated into s3-utils, please use that crate for future updates. A small utility to concatenate files in AWS S3. Designe

May 1, 2022
Czkawka is a simple, fast and easy to use app to remove unnecessary files from your computer.
Czkawka is a simple, fast and easy to use app to remove unnecessary files from your computer.

Multi functional app to find duplicates, empty folders, similar images etc.

May 25, 2022
Like jq, but for HTML. Uses CSS selectors to extract bits content from HTML files.

Like jq, but for HTML. Uses CSS selectors to extract bits content from HTML files. Mozilla's MDN has a good reference for CSS selector syntax.

May 21, 2022