Giter Club home page Giter Club logo

svd2rust's Introduction

GitHub top language Minimum Supported Rust Version crates.io crates.io Released API docs Crates.io dependency status Continuous integration

svd2rust

Generate Rust register maps (structs) from SVD files

This project is developed and maintained by the Tools team.

Minimum Supported Rust Version (MSRV)

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

If you encounter compilation errors on any stable version newer than 1.65.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.

svd2rust's People

Contributors

adamgreig avatar arjanmels avatar bors[bot] avatar brandonedens avatar burrbull avatar couchand avatar cr1901 avatar craigjb avatar disasm avatar duskmoon314 avatar emilgardis avatar grossws avatar homunkulus avatar jamesmunns avatar japaric avatar jonas-schievink avatar kjetilkjeka avatar luojia65 avatar mabezdev avatar n8tlarsen avatar pellico avatar rcls avatar roblabla avatar sethp avatar shatur avatar therealprof avatar tones111 avatar wez avatar whitequark avatar yuhanliin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

svd2rust's Issues

Better error messages with parse error.

I'm trying to read the svd for nuvoton M451 found in here http://www.nuvoton.com/hq/enu/Documents/KEILSoftwarePack/Nuvoton.NuMicro_DFP.1.0.9.pack (Note, don't download via browser, use wget).
The error I get is this:

$ RUST_BACKTRACE=1 svd2rust -i M451_v1.svd 
thread 'main' panicked at '.cargo/registry/src/github.com-1ecc6299db9ec823/svd-parser-0.2.0/src/lib.rs:185 tree.get_child_text("description")', /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/option.rs:715
stack backtrace:
   1:     0x55c1576c20ea - std::sys::imp::backtrace::tracing::imp::write::h0d1aacfb8fc693ac
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
   2:     0x55c1576c45cf - std::panicking::default_hook::{{closure}}::hff87359baf5754d0
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:349
   3:     0x55c1576c41ce - std::panicking::default_hook::h936f17ca3b2b98dc
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:365
   4:     0x55c1576c4a17 - std::panicking::rust_panic_with_hook::he9c830ab830c7944
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:553
   5:     0x55c1576c48a4 - std::panicking::begin_panic::hbd10b7f500042f98
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:515
   6:     0x55c1576c47c9 - std::panicking::begin_panic_fmt::h49583ddd44614a0d
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:499
   7:     0x55c1576c4757 - rust_begin_unwind
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:475
   8:     0x55c1576ecb7d - core::panicking::panic_fmt::h3b787d0ceafd7204
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/panicking.rs:69
   9:     0x55c1576ecbed - core::option::expect_failed::hfd8d57ff225ddc33
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libcore/option.rs:715
  10:     0x55c1576a28b2 - svd_parser::Register::parse::h5423bbeb0d22c01a
  11:     0x55c1576a1790 - svd_parser::Device::parse::hbaabf4361b1d1096
  12:     0x55c1576a0868 - svd_parser::parse::h13d5d8c2807f67bc
  13:     0x55c15762bc97 - svd2rust::main::hb6cdc518cc3a4731
  14:     0x55c1576cb8aa - __rust_maybe_catch_panic
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libpanic_unwind/lib.rs:98
  15:     0x55c1576c5156 - std::rt::lang_start::ha749a62502c90792
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:434
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panic.rs:351
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/rt.rs:57
  16:     0x7fdbf8a9c290 - __libc_start_main
  17:     0x55c157628a09 - _start
  18:                0x0 - <unknown>

Which is very unhelpful. If svd-parser and svd2rust used error_chain I think this can be fixed.
The source of this error though is wierd though. I've also opened an issue in svd-parser to highlight the current try! macro defined inside it.

Get the address of a peripheral's data register?

I've looked through the doc and the code generated from my svd and I can't find a way to get the address of a peripherals data register for DMA purposes.

For example on ADC1 there is a DR register which will contain the result of conversion.

To set up DMA conversions from the ADC I believe I'll need the address of this location in memory.

I've had a look through this repo, cortex-m and cortex-m-rtfm and I can't find any way of accomplishing this.

Do you have any thoughts on how/where this should be accomplished? If you give me a steer I'm happy to make the changes and submit a PR. My first guess was adding another method alongside read/write/modify/reset called address that returns the usize address of the register calculated from the peripheral location + register offset.

Fail to compile for LPC176x5x_v0.2.svd

Hi, I'm trying to create a crate for the LPC1768 using the LPC176x5x_v0.2.svd file. The output lib file seems to have a lot of errors involving duplicate definitions. Running svd2rust as in the usage works just fine, but when I try to compile the crate, I get the following errors:

$ cargo build
   Compiling LPC1769 v0.1.0 (file:///home/nick/Documents/LPC1769)
error[E0428]: a type named `MR` has already been defined in this module
     --> src/lib.rs:22536:5
      |
20407 | /     pub struct MR {
20408 | |         register: VolatileCell<u32>,
20409 | |     }
      | |_____- previous definition of `MR` here
...
22536 | /     pub struct MR {
22537 | |         register: VolatileCell<u32>,
22538 | |     }
      | |_____^ `MR` already defined

error[E0428]: a module named `mr` has already been defined in this module
     --> src/lib.rs:22540:5
      |
20411 | /     pub mod mr {
20412 | |         # [ doc = r" Value read from the register" ]
20413 | |         pub struct R {
20414 | |             bits: u32,
...     |
20512 | |         }
20513 | |     }
      | |_____- previous definition of `mr` here
...
22540 | /     pub mod mr {
22541 | |         # [ doc = r" Value read from the register" ]
22542 | |         pub struct R {
22543 | |             bits: u32,
...     |
22641 | |         }
22642 | |     }
      | |_____^ `mr` already defined

error[E0428]: a type named `THE_TIME_COUNTERS_AR` has already been defined in this enum
     --> src/lib.rs:28515:13
      |
28513 |             THE_TIME_COUNTERS_AR,
      |             -------------------- previous definition of `THE_TIME_COUNTERS_AR` here
28514 |             # [ doc = "The time counters are disabled so that they may be initialized." ]
28515 |             THE_TIME_COUNTERS_AR,
      |             ^^^^^^^^^^^^^^^^^^^^ `THE_TIME_COUNTERS_AR` already defined

error[E0428]: a type named `THE_CALIBRATION_COUN` has already been defined in this enum
     --> src/lib.rs:28615:13
      |
28613 |             THE_CALIBRATION_COUN,
      |             -------------------- previous definition of `THE_CALIBRATION_COUN` here
28614 |             # [ doc = "The calibration counter is enabled and counting, using the 1 Hz clock. When the calibration counter is equal to the value of the CALIBRATION register, the counter resets and repeats counting up to the value of the CALIBRATION register. See Section 30.6.4.2 and  Section 30.6.5." ]
28615 |             THE_CALIBRATION_COUN,
      |             ^^^^^^^^^^^^^^^^^^^^ `THE_CALIBRATION_COUN` already defined

error[E0428]: a type named `DISABLED` has already been defined in this enum
     --> src/lib.rs:52911:13
      |
52909 |             DISABLED,
      |             -------- previous definition of `DISABLED` here
52910 |             # [ doc = "Disabled. P0.6 pin has neither pull-up nor pull-down." ]
52911 |             DISABLED,
      |             ^^^^^^^^ `DISABLED` already defined

error[E0428]: a type named `CONFIG` has already been defined in this module
      --> src/lib.rs:135025:5
       |
132879 | /     pub struct CONFIG {
132880 | |         register: VolatileCell<u32>,
132881 | |     }
       | |_____- previous definition of `CONFIG` here
...
135025 | /     pub struct CONFIG {
135026 | |         register: VolatileCell<u32>,
135027 | |     }
       | |_____^ `CONFIG` already defined

error[E0428]: a module named `config` has already been defined in this module
      --> src/lib.rs:135029:5
       |
132883 | /     pub mod config {
132884 | |         # [ doc = r" Value read from the register" ]
132885 | |         pub struct R {
132886 | |             bits: u32,
...      |
133061 | |         }
133062 | |     }
       | |_____- previous definition of `config` here
...
135029 | /     pub mod config {
135030 | |         # [ doc = r" Value read from the register" ]
135031 | |         pub struct R {
135032 | |             bits: u32,
...      |
135566 | |         }
135567 | |     }
       | |_____^ `config` already defined

error[E0428]: a type named `THE_TIME_COUNTERS_AR` has already been defined in this enum
     --> src/lib.rs:28375:13
      |
28373 |             THE_TIME_COUNTERS_AR,
      |             -------------------- previous definition of `THE_TIME_COUNTERS_AR` here
28374 |             # [ doc = "The time counters are disabled so that they may be initialized." ]
28375 |             THE_TIME_COUNTERS_AR,
      |             ^^^^^^^^^^^^^^^^^^^^ `THE_TIME_COUNTERS_AR` already defined

error[E0428]: a type named `THE_CALIBRATION_COUN` has already been defined in this enum
     --> src/lib.rs:28469:13
      |
28467 |             THE_CALIBRATION_COUN,
      |             -------------------- previous definition of `THE_CALIBRATION_COUN` here
28468 |             # [ doc = "The calibration counter is enabled and counting, using the 1 Hz clock. When the calibration counter is equal to the value of the CALIBRATION register, the counter resets and repeats counting up to the value of the CALIBRATION register. See Section 30.6.4.2 and  Section 30.6.5." ]
28469 |             THE_CALIBRATION_COUN,
      |             ^^^^^^^^^^^^^^^^^^^^ `THE_CALIBRATION_COUN` already defined

error[E0428]: a type named `DISABLED` has already been defined in this enum
     --> src/lib.rs:52124:13
      |
52122 |             DISABLED,
      |             -------- previous definition of `DISABLED` here
52123 |             # [ doc = "Disabled. P0.6 pin has neither pull-up nor pull-down." ]
52124 |             DISABLED,
      |             ^^^^^^^^ `DISABLED` already defined

error[E0428]: a type named `ERROR_OCCURRED_DURIN` has already been defined in this enum
     --> src/lib.rs:80396:13
      |
80394 |             ERROR_OCCURRED_DURIN,
      |             -------------------- previous definition of `ERROR_OCCURRED_DURIN` here
80395 |             # [ doc = "Error occurred during receiving." ]
80396 |             ERROR_OCCURRED_DURIN,
      |             ^^^^^^^^^^^^^^^^^^^^ `ERROR_OCCURRED_DURIN` already defined

error[E0201]: duplicate definitions with name `bits`:
     --> src/lib.rs:27435:13
      |
27376 | /             pub fn bits(&self) -> u32 {
27377 | |                 self.bits
27378 | |             }
      | |_____________- previous definition of `bits` here
...
27435 | /             pub fn bits(&self) -> BITSR {
27436 | |                 BITSR::_from({
27437 | |                                  const MASK: u8 = 15;
27438 | |                                  const OFFSET: u8 = 8;
27439 | |                                  ((self.bits >> OFFSET) & MASK as u32) as u8
27440 | |                              })
27441 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `bits`:
     --> src/lib.rs:27487:13
      |
27451 | /             pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
27452 | |                 self.bits = bits;
27453 | |                 self
27454 | |             }
      | |_____________- previous definition of `bits` here
...
27487 | /             pub fn bits(&mut self) -> _BITSW {
27488 | |                 _BITSW { w: self }
27489 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_the_time_counters_ar`:
     --> src/lib.rs:28412:13
      |
28407 | /             pub fn is_the_time_counters_ar(&self) -> bool {
28408 | |                 *self == CLKENR::THE_TIME_COUNTERS_AR
28409 | |             }
      | |_____________- previous definition of `is_the_time_counters_ar` here
...
28412 | /             pub fn is_the_time_counters_ar(&self) -> bool {
28413 | |                 *self == CLKENR::THE_TIME_COUNTERS_AR
28414 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_the_calibration_coun`:
     --> src/lib.rs:28506:13
      |
28501 | /             pub fn is_the_calibration_coun(&self) -> bool {
28502 | |                 *self == CCALENR::THE_CALIBRATION_COUN
28503 | |             }
      | |_____________- previous definition of `is_the_calibration_coun` here
...
28506 | /             pub fn is_the_calibration_coun(&self) -> bool {
28507 | |                 *self == CCALENR::THE_CALIBRATION_COUN
28508 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `the_time_counters_ar`:
     --> src/lib.rs:28547:13
      |
28542 | /             pub fn the_time_counters_ar(self) -> &'a mut W {
28543 | |                 self.variant(CLKENW::THE_TIME_COUNTERS_AR)
28544 | |             }
      | |_____________- previous definition of `the_time_counters_ar` here
...
28547 | /             pub fn the_time_counters_ar(self) -> &'a mut W {
28548 | |                 self.variant(CLKENW::THE_TIME_COUNTERS_AR)
28549 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `the_calibration_coun`:
     --> src/lib.rs:28647:13
      |
28642 | /             pub fn the_calibration_coun(self) -> &'a mut W {
28643 | |                 self.variant(CCALENW::THE_CALIBRATION_COUN)
28644 | |             }
      | |_____________- previous definition of `the_calibration_coun` here
...
28647 | /             pub fn the_calibration_coun(self) -> &'a mut W {
28648 | |                 self.variant(CCALENW::THE_CALIBRATION_COUN)
28649 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_disabled`:
     --> src/lib.rs:52163:13
      |
52158 | /             pub fn is_disabled(&self) -> bool {
52159 | |                 *self == P0_06MODER::DISABLED
52160 | |             }
      | |_____________- previous definition of `is_disabled` here
...
52163 | /             pub fn is_disabled(&self) -> bool {
52164 | |                 *self == P0_06MODER::DISABLED
52165 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `disabled`:
     --> src/lib.rs:52952:13
      |
52947 | /             pub fn disabled(self) -> &'a mut W {
52948 | |                 self.variant(P0_06MODEW::DISABLED)
52949 | |             }
      | |_____________- previous definition of `disabled` here
...
52952 | /             pub fn disabled(self) -> &'a mut W {
52953 | |                 self.variant(P0_06MODEW::DISABLED)
52954 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:79905:13
      |
79878 | /             pub fn is_set(&self) -> bool {
79879 | |                 self.bit()
79880 | |             }
      | |_____________- previous definition of `is_set` here
...
79905 | /             pub fn is_set(&self) -> bool {
79906 | |                 *self == RIR::SET
79907 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:79952:13
      |
79925 | /             pub fn is_set(&self) -> bool {
79926 | |                 self.bit()
79927 | |             }
      | |_____________- previous definition of `is_set` here
...
79952 | /             pub fn is_set(&self) -> bool {
79953 | |                 *self == TI1R::SET
79954 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:79999:13
      |
79972 | /             pub fn is_set(&self) -> bool {
79973 | |                 self.bit()
79974 | |             }
      | |_____________- previous definition of `is_set` here
...
79999 | /             pub fn is_set(&self) -> bool {
80000 | |                 *self == EIR::SET
80001 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80046:13
      |
80019 | /             pub fn is_set(&self) -> bool {
80020 | |                 self.bit()
80021 | |             }
      | |_____________- previous definition of `is_set` here
...
80046 | /             pub fn is_set(&self) -> bool {
80047 | |                 *self == DOIR::SET
80048 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80093:13
      |
80066 | /             pub fn is_set(&self) -> bool {
80067 | |                 self.bit()
80068 | |             }
      | |_____________- previous definition of `is_set` here
...
80093 | /             pub fn is_set(&self) -> bool {
80094 | |                 *self == WUIR::SET
80095 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80140:13
      |
80113 | /             pub fn is_set(&self) -> bool {
80114 | |                 self.bit()
80115 | |             }
      | |_____________- previous definition of `is_set` here
...
80140 | /             pub fn is_set(&self) -> bool {
80141 | |                 *self == EPIR::SET
80142 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80187:13
      |
80160 | /             pub fn is_set(&self) -> bool {
80161 | |                 self.bit()
80162 | |             }
      | |_____________- previous definition of `is_set` here
...
80187 | /             pub fn is_set(&self) -> bool {
80188 | |                 *self == ALIR::SET
80189 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80234:13
      |
80207 | /             pub fn is_set(&self) -> bool {
80208 | |                 self.bit()
80209 | |             }
      | |_____________- previous definition of `is_set` here
...
80234 | /             pub fn is_set(&self) -> bool {
80235 | |                 *self == BEIR::SET
80236 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80281:13
      |
80254 | /             pub fn is_set(&self) -> bool {
80255 | |                 self.bit()
80256 | |             }
      | |_____________- previous definition of `is_set` here
...
80281 | /             pub fn is_set(&self) -> bool {
80282 | |                 *self == IDIR::SET
80283 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80328:13
      |
80301 | /             pub fn is_set(&self) -> bool {
80302 | |                 self.bit()
80303 | |             }
      | |_____________- previous definition of `is_set` here
...
80328 | /             pub fn is_set(&self) -> bool {
80329 | |                 *self == TI2R::SET
80330 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_set`:
     --> src/lib.rs:80375:13
      |
80348 | /             pub fn is_set(&self) -> bool {
80349 | |                 self.bit()
80350 | |             }
      | |_____________- previous definition of `is_set` here
...
80375 | /             pub fn is_set(&self) -> bool {
80376 | |                 *self == TI3R::SET
80377 | |             }
      | |_____________^ duplicate definition

error[E0201]: duplicate definitions with name `is_error_occurred_durin`:
     --> src/lib.rs:80433:13
      |
80428 | /             pub fn is_error_occurred_durin(&self) -> bool {
80429 | |                 *self == ERRDIRR::ERROR_OCCURRED_DURIN
80430 | |             }
      | |_____________- previous definition of `is_error_occurred_durin` here
...
80433 | /             pub fn is_error_occurred_durin(&self) -> bool {
80434 | |                 *self == ERRDIRR::ERROR_OCCURRED_DURIN
80435 | |             }
      | |_____________^ duplicate definition

error: aborting due to previous error(s)

error: Could not compile `LPC1769`.

To learn more, run the command again with --verbose.

I apologize if this isn't the right place to report this. Do you have any suggestions on how to fix this*, or do you know of any existing support crate for the LPC1768? Any help would be greatly appreciated!

*I wondered about simply deleting the duplicate code, but I'm worried that may cause other problems that might be harder to detect, plus that would be more of a band-aid solution.

Merge registers with the same type into an array

The code generated for the GPIO peripheral of nrf51 micros looks like this:

pub struct Gpio {
    pub out: Out,
    pub outset: Outset,
    pub outclr: Outclr,
    pub in_: In,
    pub dir: Dir,
    pub dirset: Dirset,
    pub dirclr: Dirclr,
    pub pin_cnf0: PinCnf,
    pub pin_cnf1: PinCnf,
    pub pin_cnf2: PinCnf,
    pub pin_cnf3: PinCnf,
    pub pin_cnf4: PinCnf,
    pub pin_cnf5: PinCnf,
    pub pin_cnf6: PinCnf,
    pub pin_cnf7: PinCnf,
    pub pin_cnf8: PinCnf,
    pub pin_cnf9: PinCnf,
    pub pin_cnf10: PinCnf,
    pub pin_cnf11: PinCnf,
    pub pin_cnf12: PinCnf,
    pub pin_cnf13: PinCnf,
    pub pin_cnf14: PinCnf,
    pub pin_cnf15: PinCnf,
    pub pin_cnf16: PinCnf,
    pub pin_cnf17: PinCnf,
    pub pin_cnf18: PinCnf,
    pub pin_cnf19: PinCnf,
    pub pin_cnf20: PinCnf,
    pub pin_cnf21: PinCnf,
    pub pin_cnf22: PinCnf,
    pub pin_cnf23: PinCnf,
    pub pin_cnf24: PinCnf,
    pub pin_cnf25: PinCnf,
    pub pin_cnf26: PinCnf,
    pub pin_cnf27: PinCnf,
    pub pin_cnf28: PinCnf,
    pub pin_cnf29: PinCnf,
    pub pin_cnf30: PinCnf,
    pub pin_cnf31: PinCnf,
    // some fields omitted
}

Instead, it should generate:

pub struct Gpio {
    pub out: Out,
    pub outset: Outset,
    pub outclr: Outclr,
    pub in_: In,
    pub dir: Dir,
    pub dirset: Dirset,
    pub dirclr: Dirclr,
    pub pin_cnf: [PinCnf; 32],
    // some fields omitted
}

The SVD file contains information about PinCnf being an array but the current svd2rust implementation simply unrolls the array into several struct fields instead of creating a single struct field that represents the array of registers.

Function name collision avoidance

The .svd file I use has multiple reserved bit ranges per register, leading to duplicate fn reserved() in struct impls. If that happens, those fields could either be omitted or get mangled into reserved1, reserved2, ...

Rust keywords are not escaped

I ran svd2rust over the DMA section of the EFM32LG990F256 and got the following code:

# [ doc = "DMA" ]
# [ repr ( C ) ]
pub struct Dma {
    ...
    pub if: If,
    # [ doc = "0x1004 - Interrupt Flag Set Register" ]
    pub ifs: Ifs,    # [ doc = "0x1008 - Interrupt Flag Clear Register" ]
    pub ifc: Ifc,
    # [ doc = "0x100c - Interrupt Enable register" ]
    ...

Unfortunately, this does not compile because if is a reserved keyword. I'd like to help out on the solution for this, but I'm not sure which direction you'd like to go. If you were to add a suffix or prefix to every register and field, you wouldn't have to do any special escaping and you wouldn't have special-cased names which don't follow the SVD. For example:

# [ doc = "DMA" ]
# [ repr ( C ) ]
pub struct PeripheralDma {
    ...
    pub reg_if: If,
    # [ doc = "0x1004 - Interrupt Flag Set Register" ]
    pub reg_ifs: Ifs,    # [ doc = "0x1008 - Interrupt Flag Clear Register" ]
    pub reg_ifc: Ifc,
    # [ doc = "0x100c - Interrupt Enable register" ]
    ...

The downside to that approach, is that you now have lots of repetition which hurts readability.

Optimize access to bitfields

For instance the I2C peripheral of the STM32F303 has a "32-bit" TXDR register that only contains 8 accessible bits (the other bits are reserved). In that case, svd2rust generates code like this:

# [ repr ( C ) ]
pub struct Txdr {
    register: ::volatile_register::RW<u32>,
}

impl Txdr {
    // ...

    pub fn write<F>(&mut self, f: F)
        where F: FnOnce(&mut TxdrW) -> &mut TxdrW
    {
        let mut w = TxdrW::reset_value();
        f(&mut w);
        self.register.write(w.bits);
    }
}

# [ derive ( Clone , Copy ) ]
# [ repr ( C ) ]
pub struct TxdrW {
    bits: u32,
}

impl TxdrW {
    # [ doc = r" Reset value" ]
    pub fn reset_value() -> Self {
        TxdrW { bits: 0u32 }
    }
    # [ doc = "Bits 0:7 - 8-bit transmit data" ]
    pub fn txdata(&mut self, value: u8) -> &mut Self {
        const OFFSET: u8 = 0u8;
        const MASK: u8 = 255;
        self.bits &= !((MASK as u32) << OFFSET);
        self.bits |= ((value & MASK) as u32) << OFFSET;
        self
    }
}

Which is wasteful because it performs 32-bit loads/stores when it could be doing 8-bit loads/stores. Instead svd2rust should generate code like this:

# [ repr ( C ) ]
pub struct Txdr {
    register: ::volatile_register::RW<u8>,
    padding: [u8; 3],
}

impl Txdr {
    // ...

    pub fn write<F>(&mut self, f: F)
        where F: FnOnce(&mut TxdrW) -> &mut TxdrW
    {
        let mut w = TxdrW::reset_value();
        f(&mut w);
        self.register.write(w.bits);
    }
}

# [ derive ( Clone , Copy ) ]
# [ repr ( C ) ]
pub struct TxdrW {
    bits: u8,
}

impl TxdrW {
    # [ doc = r" Reset value" ]
    pub fn reset_value() -> Self {
        TxdrW { bits: 0u32 }
    }
    # [ doc = "Bits 0:7 - 8-bit transmit data" ]
    pub fn txdata(&mut self, value: u8) -> &mut Self {
        self.bits = value;
        self
    }
}

This can be changed without affecting the generated API.

Noncontiguous register clusters result in name collisions

In the LPC178x7x.svd file (which has a bucket of other problems besides), I discovered that the PWM0 match registers are not contiguous, and will cause a name collision. To wit,

15160     <registers>
...
15759         <register>
15760             <dim>4</dim>
15761             <dimIncrement>0x4</dimIncrement>
15762             <dimIndex>0-3</dimIndex>
15763             <name>MR%s</name>
15764
15765             <description>Match Register. Match registers
15766 are continuously compared to the PWM counter in order to control PWM
15767 output edges.</description>
15768             <addressOffset>0x018</addressOffset>
15769             <access>read-write</access>
15770             <resetValue>0</resetValue>
15771             <resetMask>0xFFFFFFFF</resetMask>
15772             <fields>
15773                 <field>
15774                     <name>MATCH</name>
15775                     <description>Timer counter match value.</description>
15776                     <bitRange>[31:0]</bitRange>
15777                 </field>
15778             </fields>
15779         </register>
...
(other registers defined here)
...
16134         <register>
16135             <dim>3</dim>
16136             <dimIncrement>0x4</dimIncrement>
16137             <dimIndex>4-6</dimIndex>
16138             <name>MR%s</name>
16139
16140             <description>Match Register. Match registers
16141 are continuously compared to the PWM counter in order to control PWM
16142 output edges.</description>
16143             <addressOffset>0x040</addressOffset>
16144             <access>read-write</access>
16145             <resetValue>0</resetValue>
16146             <resetMask>0xFFFFFFFF</resetMask>
16147             <fields>
16148                 <field>
16149                     <name>MATCH</name>
16150                     <description>Timer counter match value.</description>
16151                     <bitRange>[31:0]</bitRange>
16152                 </field>
16153             </fields>
16154         </register>

will result in multiple pub mod mr and pub struct Mr statements being emitted.

#[deny(unused_imports)] errors out in certain circumstances

With my TM4C129x SVD files I currently get these two errors:

error: unused import: `cortex_m::ctxt::Context`
 --> src/lib.rs:9:9
  |
9 |     use cortex_m::ctxt::Context;
  |         ^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: #[deny(unused_imports)] implied by #[deny(warnings)]
error: unused import: `cortex_m::exception`
  --> src/lib.rs:10:9
   |
10 |     use cortex_m::exception;
   |         ^^^^^^^^^^^^^^^^^^^
   |
   = note: #[deny(unused_imports)] implied by #[deny(warnings)]

I'm not sure why this happens, perhaps because I don't have any interrupts defined?

Compilation fails on generated file with '%s'

I've tried to generate *.rs from MK70F15.xml.txt (I added 'txt' extension to satisfy github, I believe it is SVD file, I found it here).

Output file generated successfully (some warnings though):

svd2rust spi0 -i ./MK70F15.xml > src/spi0.rs
WARNING CTAR_SLAVE overlaps with another register at offset 12. Ignoring.
WARNING PUSHR_SLAVE overlaps with another register at offset 52. Ignoring.

But following error appeared on compilation:

error: expected `:`, found `%`
 --> src/spi0.rs:3:295
  |
3 | # [ repr ( C ) ] pub struct Spi0 { # [ doc = "0x00 - DSPI Module Configuration Register" ] pub mcr : Mcr , _reserved0 : [ u8 ; 4usize ] , # [ doc = "0x08 - DSPI Transfer Count Register" ] pub tcr : Tcr , # [ doc = "0x0c - DSPI Clock and Transfer Attributes Register (In Master Mode)" ] pub ctar%s : Ctar%s , _reserved1 : [ u8 ; 28usize ] , # [ doc = "0x2c - DSPI Status Register" ] pub sr : Sr , # [ doc = "0x30 - DSPI DMA/Interrupt Request Select and Enable Register" ] pub rser : Rser , # [ doc = "0x34 - DSPI PUSH TX FIFO Register In Master Mode" ] pub pushr : Pushr , # [ doc = "0x38 - DSPI POP RX FIFO Register" ] pub popr : Popr , # [ doc = "0x3c - DSPI Transmit FIFO Registers" ] pub txfr%s : Txfr%s , _reserved2 : [ u8 ; 60usize ] , # [ doc = "0x7c - DSPI Receive FIFO Registers" ] pub rxfr%s : Rxfr%s }
  |

Probably svd2rust should somehow convert '%s' in SVD into something legal for Rust.

[RFC] one type per register block

Right now, when dealing with derived peripherals, svd2rust uses type aliases:

pub mod tim2 {
    pub struct RegisterBlock { .. }
}

pub use tim2::RegisterBlock as Tim2;
pub type Tim3 = Tim2;
pub type Tim4 = Tim2;
pub type Tim5 = Tim2;

Thus all both the base peripheral and the derived peripherals have the same
type.

I propose we change svd2rust to generate a different type for each peripheral.

pub mod tim2 {
    pub struct RegisterBlock { .. }
}

pub struct Tim2 { register_block: tim2::RegisterBlock }
impl Deref for Tim2 { type Target = tim2::RegisterBlock; .. }

pub struct Tim3 { register_block: tim2::RegisterBlock }
impl Deref for Tim3 { type Target = tim2::RegisterBlock; .. }

pub struct Tim4 { register_block: tim2::RegisterBlock }
impl Deref for Tim4 { type Target = tim2::RegisterBlock; .. }

pub struct Tim5 { register_block: tim2::RegisterBlock }
impl Deref for Tim5 { type Target = tim2::RegisterBlock; .. }

The rationale is that you may want to have API that works for one peripheral,
e.g. TIM2, but not for the others. You can only enforce that if you have
different types for the peripherals. Another similar use case is APIs that
depend on some specific combinations of peripherals: PWM1 = GPIOA + TIM2, PWM2 =
GPIOB + TIM3, etc.

[Feature] Support interrupt table generation

SVD files usually contain interrupts which can be used to generate interrupt vector table.
Maybe support two modes:

  • generate full table (both events and interrupts),
  • generate only array with interrupts.

I'm not sure how it should look like, so this issue is just to fix absence of such feature. If I get some spare time, I'll try to experiment with it.

[RFC] API tailored for specific microcontrollers

Original issue: japaric/stm32f103xx#9

The problem

SVD files usually describe a family of microcontrollers and contain
information about all the peripherals any member of the family could have.
When svd2rust produces a device crate from a SVD file it produces an API to
access every single of these peripherals.

The problem is that the lower density members of a family are likely to contain
less peripherals than the set of peripherals described by the SVD file. As a
result using the device crate with such devices lets you access nonexistent
peripherals.

As a concrete example the stm32f103xx crate exposes an API for the TIM6 and
TIM7 peripherals (basic timers) but these peripherals are not available on the
STM32F103C8 microcontroller. So if you write an application for that
microcontroller using the stm32f103xx crate you may end up using those timers
without realizing they are not available. Worst part is that the program won't
crash -- it won't hit an exception -- but rather it will likely have undefined
behavior (writes are no-op and reads return junk values or zero)

Possible solutions

Constraints

  • AFAIS SVD files contain no information about which peripherals are present on
    device X. So this information will have to be supplied by a human and most
    likely we won't be able to add this information to the SVD file.

Cargo features

One of the ideas brought up in the original issue was to encode the presence of
each peripheral through a Cargo feature and then have one Cargo feature per
microcontroller. That microcontroller feature would enable all the peripherals,
through their features, that are present on that microcontroller. Example:

# Cargo.toml
[features]
TIM2 = []
TIM3 = []
TIM4 = []
TIM6 = []
TIM7 = []
stm32f103c8 = ["TIM2", "TIM3", "TIM4"]
stm32f103vg = ["TIM2", "TIM3", "TIM4", "TIM6", "TIM7"]

The device crate would make use #[cfg] attributes like this:

#[cfg(feature = "TIM2")]
pub const TIM2: Peripheral<TIM2> = ..;

#[cfg(feature = "TIM3")]
pub const TIM3: Peripheral<TIM3> = ..;

to prevent exposing APIs not available to a certain microcontroller.

As you know Cargo features are additive so there's nothing stopping
you from enabling more than one microcontroller feature at the same time, even
by mistake (e.g. a dependency enables one microcontroller feature and another
dependency enables a different one). In those cases we can raise an error in the
device crate like this:

#[allow(dead_code)]
#[cfg(feature = "stm32f103c8")]
const ERROR: &str = "feature stm32f103c8 is enabled";

#[allow(dead_code)]
#[cfg(feature = "stm32f103vg")]
const ERROR: &str = "feature stm32f103vg is enabled";

If more than one microcontroller feature is enabled this will raise a name
collision error.

Library crates that depend on the device crate can write device specific APIs
like this:

extern crate stm32f103xx;

#[cfg(feature = "TIM2")]
fn foo(tim2: &stm32f103xx::TIM2) { .. }

#[cfg(feature = "TIM3")]
fn bar(tim2: &stm32f103xx::TIM3) { .. }

Testing a library crate that depends on a device crate for the different devices
that the device crate supports is as simple as calling the Cargo command with
different --feature arguments:

$ cargo check --feature stm32f103c8
$ cargo check --feature stm32f103vg

Upsides

This is straightforward to implement in svd2rust.

Downsides

Due to the additive nature of Cargo features it seems to be easy to break the
device selection mechanism: it's just enough that a dependency directly enables
a peripheral feature:

# Cargo.toml
[package]
name = "application"

[dependencies.stm32f103xx]
features = ["stm32f103c8"]
version = "0.1.0"

[dependencies]
# this crate depends on the stm32f103xx crate and directly enables its TIM6
# feature, but this peripheral is not available on the stm32f103c8
# microcontroller
foo = "0.1.0"

This problem can be avoided if library crates never enable any feature of the
device crate but there's no mechanism to enforce this so discipline would be
required.

--cfg device=

Another approach is to not use Cargo features at all but to directly use
#[cfg] attributes and the --cfg rustc flag. With this approach the device
crate would look like this:

#[cfg(any(device = "stm32f103c8", device = "stm32f103vg"))]
pub const TIM2: Peripheral<TIM2> = ..;

#[cfg(any(device = "stm32f103c8", device = "stm32f103vg"))]
pub const TIM3: Peripheral<TIM3> = ..;

// ..

#[cfg(device = "stm32f103vg")]
pub const TIM6: Peripheral<TIM6> = ..;

Application crates that depend on the device crate can then pick one specific
device or other using the --cfg flag:

$ RUSTFLAGS='--cfg device="stm32f103vg"' cargo build

Library crates would require #[cfg] attributes similar to the ones used in the
device crate:

extern crate stm32f103xx;

#[cfg(any(device = "stm32f103c8", device = "stm32f103vg"))]
fn foo(tim2: &stm32f103xx::TIM2) { .. }

#[cfg(device = "stm32f103vg")]
fn bar(tim6: &stm32f103xx::TIM6) { .. }

Enabling more than one device cfg seems hard to do by mistake but an error can
be raised in the device crate like this:

#[allow(dead_code)]
#[cfg(device = "stm32f103c8")]
const ERROR: &str = "feature stm32f103c8 is enabled";

#[allow(dead_code)]
#[cfg(device = "stm32f103vg")]
const ERROR: &str = "feature stm32f103vg is enabled";

The more common error scenario is that people will likely forget to pass the
--cfg device= flag. In that case a helpful error can be raised in the device
crate:

#[cfg(not(any(device = "stm32f103c8", device = "stm32f103vg")))]
const ERROR: &str = "No device selected! Add `--cfg device=something` to `RUSTFLAGS`";

Downsides

Implementing this is hard and would require teaching svd2rust to parse a file
that maps a device to the peripherals it has.

Writing library crates is tedious as it requires checking which peripheral is
available for each specific device the device crate supports.

RUSTFLAGS is not the first thing that comes to people's mind when they think
about configuring dependencies.

one crate per device

Another approach is to not solve this in svd2rust. Instead we can create device
specific SVD files from a more generic one, and then generate one device crate
for each of those files. This means that instead of a generic stm32f103xx crate
we would have several crates: stm32f103c8, stm32f103vg, etc.

Downsides

Lots of duplicated code.

More work would likely be required to write crates that abstract over device
specific details. Mainly because stm32f103c8::TIM2 and stm32f103vg::TIM2 are
not the same type.

re-exports

Yet another approach is to have device specific crates but that only include
re-exports of a more generic device crate. For instance:

// crate: stm32f103c8
extern crate stm32f103xx;

pub use stm32f103xx::{..,TIM2,TIM3,TIM4};
// crate: stm32f103vg
extern crate stm32f103xx;

pub use stm32f103xx::{..,TIM2,TIM3,TIM4,TIM5,TIM6};

Downsides

Unless the application crate directly depends on a device specific crate some
intermediate library crate will end up looking like this:

// Looks familiar?
#[cfg(..)]
extern crate stm32f103c8;

#[cfg(..)]
extern crate stm32f103vg;

Unresolved questions

  • Is there a simpler alternative?

cc @protomors

Add Debug trait implementation

It would be very helpful to have a Debug traits implemented for register structs. Currently if I am debugging some code or interested in how things work I simply add something like this to memory map crate. It will be mainly useful for read and memory block structs, but some may find it usefull for write structs too.

Make `bits` pub?

I've got 4 LEDs that I can manipulate individually. My first try looks like this:

fn toggle_led(led: u8) {
    match led {
        1 => 
            gpio_port().not[2].write(|notw| notw.notp1(true)),
        2 => 
            gpio_port().not[2].write(|notw| notw.notp2(true)),
        3 => 
            gpio_port().not[2].write(|notw| notw.notp8(true)),
        4 => 
            gpio_port().not[5].write(|notw| notw.notp26(true)),
        _ => (),
    }
}

It's hard to parameterize over notp1, notp2, etc. In this case I'd like to sacrifice the pretty abstraction and manipulate any bit of self.bits.

support <alternateGroup> in registers?

The TM4C1230C3PM.svd file defines the MCS register in the I2C0 peripheral like this:

        <register>
          <name>MCS</name>
          <description>I2C Master Control/Status</description>
          <addressOffset>0x00000004</addressOffset>
          <fields>
            <field>
              <name>I2C_MCS_RUN</name>
              <description>I2C Master Enable</description>
              <bitRange>[0:0]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_START</name>
              <description>Generate START</description>
              <bitRange>[1:1]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_ADRACK</name>
              <description>Acknowledge Address</description>
              <bitRange>[2:2]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_ACK</name>
              <description>Data Acknowledge Enable</description>
              <bitRange>[3:3]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_ARBLST</name>
              <description>Arbitration Lost</description>
              <bitRange>[4:4]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_IDLE</name>
              <description>I2C Idle</description>
              <bitRange>[5:5]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_CLKTO</name>
              <description>Clock Timeout Error</description>
              <bitRange>[7:7]</bitRange>
            </field>
          </fields>
        </register>
        <register>
          <name>MCS</name>
          <description>I2C Master Control/Status</description>
          <alternateGroup>I2C0_ALT</alternateGroup>
          <addressOffset>0x00000004</addressOffset>
          <fields>
            <field>
              <name>I2C_MCS_BUSY</name>
              <description>I2C Busy</description>
              <bitRange>[0:0]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_ERROR</name>
              <description>Error</description>
              <bitRange>[1:1]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_STOP</name>
              <description>Generate STOP</description>
              <bitRange>[2:2]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_DATACK</name>
              <description>Acknowledge Data</description>
              <bitRange>[3:3]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_HS</name>
              <description>High-Speed Enable</description>
              <bitRange>[4:4]</bitRange>
            </field>
            <field>
              <name>I2C_MCS_BUSBSY</name>
              <description>Bus Busy</description>
              <bitRange>[6:6]</bitRange>
            </field>
          </fields>
        </register>

This makes svd2rust generate two sets of MCS registers which makes the generated code not compilable. For some reason this doesn't the "overlapping" code that emits a warning about the overlap and only produces code for the first set of bitfields. Perhaps that's because the two overlapping registers have the same name?

According to the SVD specification having these two registers with the same name is valid because one of the has a <alternateGroup> element. This what the specification says about <alternateGroup>:

alternateGroup
Specifies a group name associated with all alternate register that have the same name. At the same time, it indicates that there is a register definition allocating the same absolute address in the address space.

What actually this SVD file meant to convey is that when writing to the MCS register only the fields HS, ACK, STOP, START, RUN can be used, but when reading the MCS regsiter then it contains the fields CLKTO, BUSBSY, IDLE, ARBLST, DATACK, ADRACK, ERROR, BUSY. Of course the SVD file doesn't do this right because it says nothing about read vs write mode; also the bit ranges are wrong ([6:6] = 0-bit field).

I think these TM4C fiels may be busted. @whitequark you have worked with these micros using dslite2svd. Do the SVD files generated from dslite ones make use of this alternateGroup feature? Also how do those SVD files deal with these registers with different read / write modes?

"overloaded" registers

File: STM32F30x.svd
Peripheral: TIM2

The SVD file specifies that two registers exist at offset 0x1C: CCMR2_Input and CCMR2_Output. The thing is that this register can be used differently depending on whether the TIM2 peripheral is configured in Output Compare Mode or in Input Compare Mode. By differently, I mean that the bits of this register have different meaning depending on the compare mode.

I'm not sure how to model this. We could use a union here but that's not stable. For now, I think, I'm just going to pick one register and ignore the other but emit a warning.

More extensive testing

Once #43 lands, I think we should be ready to test generating all the peripherals that a SVD contains. svd2rust -i $svd would give us the full list of peripheral then we can use svd2rust -i $svd $peripheral for each peripheral in that list.

We could even repeat this approach for several SVD files in the cmsis-svd repo. We should be careful to not overdo ourselves and end up with unreasonable test times though.

the `modify` method should probably expose the readable bits of the register

Possible implementation:

impl Cr1 {
    pub fn modify<F>(&mut self, f: F)
        where F: FnOnce(&Cr1R, &mut Cr1W) -> &mut Cr1W
    {
        let bits = self.register.read();
        let mut w = Cr1W { bits: bits };
        let r = Cr1R { bits: bits }
        f(&r, &mut w);
        self.register.write(rw.bits);
    }
}

Usage:

// Toggle the SADD0 bit
i2c1.cr1.modify(|r, w| w.sadd0(!r.sadd0()));

[MSP430] The generated code is not as efficient as in C.

A very simple code that just sets some bits and clears some bits:

    P1OUT |= RED_LED;
    P1OUT &= ~GRN_LED;
    port_1_2.p1out.modify(|_, w| w.p0().set_bit()
                                  .p6().clear_bit());

Will produce only 2 instructions in C

    c150:	d2 d3 21 00 	bis.b	#1,	&0x0021	;r3 As==01
    c154:	f2 f0 bf ff 	and.b	#65471,	&0x0021	;#0xffbf
    c158:	21 00 

But it has 4 instructions in Rust:

    c0e0:	7d 40 be ff 	mov.b	#65470,	r13	;#0xffbe
    c0e4:	5d f2 21 00 	and.b	&0x0021,r13	;0x0021
    c0e8:	5d d3       	bis.b	#1,	r13	;r3 As==01
    c0ea:	c2 4d 21 00 	mov.b	r13,	&0x0021	;

This is what clang emits in llvm-ir, maybe we can do something similar

  %3 = load volatile i8, i8* @P1OUT, align 1, !tbaa !1
  %4 = or i8 %3, 1
  store volatile i8 %4, i8* @P1OUT, align 1, !tbaa !1
  %5 = load volatile i8, i8* @P1OUT, align 1, !tbaa !1
  %6 = and i8 %5, -65
  store volatile i8 %6, i8* @P1OUT, align 1, !tbaa !1

I can get the same behavior if I do the operations separately, but it's too verbose(

    port_1_2.p1out.modify(|_, w| w.p0().set_bit());
    port_1_2.p1out.modify(|_, w| w.p6().clear_bit());

"bits" is a valid field name

In that case the method to access the bitfield proxy:

peripheral.register.read().bits() // BitsR
peripheral.register.read().bits().is_variant() // bool

is ambiguous with the register method to get its raw bits

peripheral.register.read().bits()  // e.g. u8

This happens in the SKEAZN84.svd file.

Support for MSP430

It is possible to use the current release of svd2rust to generate device crate for msp430,
but you need to manually fix some things in generated code:

  1. Remove default perfipherals (#117)
  2. Replace all uses of cortex_m crate with msp430
  3. Replace all extern "C" with extern "msp430-interrupt"
  4. Add #![feature(abi_msp430_interrupt)] declaration

The proposal is to add --arch=msp430 command line flag to svd2rust, that will affect the generated code to make this steps unnecessary.

You can find an SVD file and an example of generated code in msp430g2553 device crate.

Remove #[inline(always)]

Since I absolutely love clippy, I also wanted to run it on my embedded experiments but essentially failed to do so because I ran into tons of:

error: you have declared `#[inline(always)]` on `bit`. This is usually a bad idea

warnings and so I decided to look into whether that provides any benefit to the generated code at all and surprise it does not provide any benefit at all:

When compiling in release mode (with lto on) the only difference is that main is not inlined into reset_handler which is actually a positive since it makes debugging a whole lot easier if you have a trivially named main function you can put your breakpoint on.

In debug mode it has a slightly larger impact of around 20% but that seems to be mostly coming other skipped inlining opportunities rather any of the functions marked als always inline not being inline anymore which can probably offset by adjusting the inlining target, if necessary.

But most importantly to me, this allows me to run clippy just fine:

warning: equality checks against true are unnecessary
  --> examples/flash_systick_hsi48.rs:30:16
   |
30 |             if rcc.cr2.read().hsi48rdy().bit() == true {
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `rcc.cr2.read().hsi48rdy().bit()`
   |
   = note: #[warn(bool_comparison)] on by default
   = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#bool_comparison

Woohoo!

svd2rust omits registers if the register Vec isn't sorted by address_offset

This occurs for example on the SAI component of the stm32f7 (svd file):

<peripheral>
      <name>SAI1</name>
      <description>Serial audio interface</description>
      <groupName>SAI</groupName>
      <baseAddress>0x40015800</baseAddress>
      <addressBlock>…</addressBlock>
      <interrupt>…</interrupt>
      <registers>
        <register>
          <name>BCR1</name>
          <displayName>BCR1</displayName>
          <description>BConfiguration register 1</description>
          <addressOffset>0x24</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x00000040</resetValue>
          <fields>…</fields>
        </register>
        …
        <register>
          <name>ACR1</name>
          <displayName>ACR1</displayName>
          <description>AConfiguration register 1</description>
          <addressOffset>0x4</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x00000040</resetValue>
          <fields>…</fields>
        </register>
        …

svd2rust doesn't emit any field for ACR1 since the addressOffset is smaller than the offset of the previous BCR1.

`reset` method as a short-hand for `write(|w| w)`

At first glance, it's not immediately clear that tim7.sr.write(|w| w) sets the value of the register to its default reset value. I think that tim7.sr.reset() may be slightly clearer in that regard.

Enum names collisions inside a register

Thanks @japaric for the amazing volume and quality of embedded work you're doing for the Rust community.

I'm working on rust-lpc43xx, and the LPC43xx SVD describes several fields in a register using the same <enumeratedValues> name, "ENUM". See for instance Timer0 peripheral's fields MR0I and MR0R. There are a dozen or so other examples for Timer0 alone, and a quick scan of the SVD indicates they use "ENUM" for all their <enumeratedValues> -- hundreds of examples.

I can think of a few ways to address this:

  1. Name the enum struct using a concatenation of the field name and enumeratedValues name. This could result in longer names when referencing the enum, with the field name repeated in the enum struct name.
  2. Find a way to scope the enum inside the field struct, or some other namespace. This seems a bit tidier, but not sure you can declare an enum inside a struct?
  3. Ignore the enumeratedValues name altogether, if it's common practice for it to always be "ENUM", or if a field can only have one <enumeratedValues> block. I can't speak to whether those assumptions are true or not...

I think I understand the svd2rust code well enough that I could attempt to make any of the three changes outlined above. But I'm hoping somebody with a more informed opinion can speak up and help decide which is the best choice.

v0.5.1 Output invalid and all on one line with STM32F41x.svd

I ran rustup run nightly cargo install svd2rust a couple of days ago using rustc 1.18.0-nightly (40feadb96 2017-03-31) and then ran svd2rust -i STM32F41x.svd > stm32f415.rs with STM32F41x.svd.

The output was a rust file with all the code on one line:

$ wc stm32f415.rs
       1 2284030 7837650 stm32f415.rs

Trying to rustfmt the svd2rust output gives an error:

$ rustfmt < stm32f415.rs 2>&1 | less
error: expected `:`, found `}`
 --> stdin:1:31576`

Am I correct in thinking the output should have been properly pretty printed (not a giant 7.8M single line)? Is there anything I could look into to figure out why this is happening?

Large generated `lib` file is unusable by development tools such as intellij-rust

Due to the huge contiguous file size of the lib.rs generated output, intellij-rust (and possibly other development help tools) is unable to parse the library for intelligent completion support. I've been able to manually fix this by breaking top-level modules into their own files (you can see an example here), but this is a tedious process that involves fixing scopes by inserting new use statements manually, etc.

I think it'd be helpful if svd2rust had an option to output top-level modules (or further, perhaps every module) to individual module files for easier navigation and completion.

Odd formatting for output

I build svd2rust, using rust 1.12.1, with

cargo install svd2rust

If I run it with the Kinetis MKE06Z4.svd file, I get this output

impl PidrW { # [ doc = r" Reset value" ] pub fn reset_value ( ) -> Self { PidrW { bits : 4294967295u32 } } # [ doc = "Bits 0:31 - Port Input Disable" ] pub fn pid ( & mut self , value : u32 ) -> & mut Self { const OFFSET : u8 = 0u8 ; const MASK : u32 = 4294967295 ; self . bits &= ! ( ( MASK as u32 ) << OFFSET ) ; self . bits |= ( ( value & MASK ) as u32 ) << OFFSET ; self } }

I can fix the #[] pragmas manually and then rustfmt it back into shape, but it's a bit weird.

OS is Ubuntu 14.04 for x86-64.

EnumeratedValue RESERVED has no <value> field

$ curl -LO https://raw.githubusercontent.com/posborne/cmsis-svd/master/data/NXP/LPC176x5x_v0.2.svd
$ svd2rust --version
svd2rust 0.7.2 ( )
$ svd2rust -i LPC176x5x_v0.2.svd
error: EnumeratedValue RESERVED has no <value> field
note: run with `RUST_BACKTRACE=1` for a backtrace

I don't believe a value is needed, as a RESERVED EnumeratedValue can be ignored safely when generating the public API.

register of size 1?

File: MB9AF10xN.svd

Contents:

        <register>
          <name>WDG_RIS</name>
          <description>Hardware Watchdog Timer Interrupt Status Register</description>
          <addressOffset>0x10</addressOffset>
          <size>1</size>
          <access>read-only</access>
          <resetValue>0xFF</resetValue>
          <resetMask>0x00</resetMask>
          <!-- FIELDS -->
          <fields>
            <!-- FIELD "RIS" -->
            <field>
              <name>RIS</name>
              <description>Hardware watchdog interrupt status bit</description>
              <lsb>0</lsb>
              <msb>0</msb>
              <access>read-only</access>
            </field>
          </fields>
        </register>

Produces:

        impl R {
            # [ doc = r" Value of the register as raw bits" ]
            # [ inline ( always ) ]
            pub fn bits(&self) -> bool {
                self.bits
            }
            # [ doc = "Bit 0 - Hardware watchdog interrupt status bit" ]
            # [ inline ( always ) ]
            pub fn ris(&self) -> RISR {
                let bits = {
                    const MASK: bool = true;
                    const OFFSET: u8 = 0;
                    ((self.bits >> OFFSET) & MASK as bool) != 0
                };
                RISR { bits }
            }
        }

Compiler error:

error[E0369]: binary operation `>>` cannot be applied to type `bool`
    --> src/lib.rs:6496:22
     |
6496 |                     ((self.bits >> OFFSET) & MASK as bool) != 0
     |                      ^^^^^^^^^^^^^^^^^^^^^
     |
     = note: an implementation of `std::ops::Shr` might be missing for `bool`

error: aborting due to previous error(s)

The problem I see that the SVD file declares the register has having a size of 1. I'm not sure if that allowed by the SVD format (the website appears to be down ATM) but I'd expect it to only allow sizes multiple of 8 (bytes bits). Note that we are talking about registers; bitfields can have any size (smaller than the size of their register).

Anyhow the current implementation maps a size of 1 to a bool so the bits field of R is bool which can be shifted to the right by OFFSET thus the compiler error.

cc @whitequark fallout from #84

EDIT I meant bits not bytes.

Registers with multiple reserved fields causes duplicate definitions

Running the tool on this svd file generates code that does not compile.

The error message reads:

error[E0201]: duplicate definitions with name `reserved`:
      --> src/lib.rs:156931:13
       |
156881 | /             pub fn reserved(&self) -> ReservedR {
156882 | |                 let bits = {
156883 | |                     const MASK: u8 = 1;
156884 | |                     const OFFSET: u8 = 0;
...      |
156887 | |                 ReservedR { bits }
156888 | |             }
       | |_____________- previous definition of `reserved` here
...
156931 | /             pub fn reserved(&self) -> ReservedR {
156932 | |                 let bits = {
156933 | |                     const MASK: u32 = 134217727;
156934 | |                     const OFFSET: u8 = 5;
...      |
156937 | |                 ReservedR { bits }
156938 | |             }
       | |_____________^ duplicate definition

This happens as a lot of registers in the svd file has multiple reserved fields.

I see two different solutions to this problem:

  1. Edit the SVD file, appending a unique string to each conflicting RESERVED field.
  2. Change svd2rust, by special casing registers with multiple reserved fields such that unique field names are generated. i.e. RESERVED1, RESERVED2

What do you think?

An alternative write/modify interface

Thanks for the great library!

I have some problems with the generated modify function. It leads to very verbose code when we want to change a single field. For example, let's assume that we want to change the mode of GPIO A pin 0 to 1:

gpioa.moder.modify(|r,w| w.moder0(1).moder1(r.moder1()).moder2(r.moder2())
    .moder3(r.moder3()).[].moder15(r.moder15()));

Why not generate a modify function like that:

gpioa.moder.modify(|m| m.moder0(1));

The m would be a write instance that has all values set to the current value (instead of the reset value).

With such a modify function, we could replace the write function with a combination of modify and a new reset function. So instead of:

gpioa.moder.write(|w| w.moder0(0));

we would write

gpioa.moder.reset().modify(|m| m.moder0(0));

Which – in my opinion – expresses the intent in a clearer way.

support <access>read-writeOnce</access>

The code generated from the file STM32L063x.svd doesn't compile. Because the read-writeOnce access mode is not supported by svd2rust the DAC_SR register has no access methods:

# [ doc = "Digital-to-analog converter" ]
pub mod dac {
    // ..
    # [ doc = "status register" ]
    pub struct Sr {
        register: VolatileCell<u32>,
    }
    # [ doc = "status register" ]
    pub mod sr {
        impl super::Sr {}
    }
}

And you run into an unused field warning which becomes an error because of #![deny(warnings)].

Very repetitious output when deriving enumeratedValues

As I've said elsewhere, I'm working on adding enumeratedValues to the SVD for the MCU I'm using. Since there are a lot of fields that use the same enumerations, I'm reusing them using e.g. <enumeratedValues derivedFrom="GPIOA.MODER.MODER15.MODE" /> in each field.

However, when inspecting the output, I noticed this results in a lot of duplicated code. Most of it seems to be due to the separate write proxies for each field, with each proxy defining all the variant methods, despite their implementation being the exact same each time.

This isn't a huge problem, and I understand why the separate write proxy objects are necessary (each field has a different offset), but it seems like there should be a less boilerplate-y way to accomplish the same thing? The first thing that comes to mind is generics with numerical arguments, but those aren't a part of Rust yet, unfortunately. I'm fairly new to Rust, though, so maybe someone has a better idea?

Some of this would be ameliorated by #44 but that wouldn't stop the duplication entirely.

Problematic enum names are passed through unchecked

Hi,

I've been tinkering with the SVD for the STM32F334x microcontrollers, adding enums to make the generated API more useful. At some point, rustfmt started throwing errors and it took me a little bit of effort to figure out why. It turned out I'd named an enum value push-pull, which isn't a valid Rust identifier.

While it makes sense that this breaks, and it was trivial to fix, it would be helpful if svd2rust recognised this sort of error in the SVD file, rather than generating output that then results in a non-obvious compiler error. A similar check could have caught the duplicate enum value names in #112 too.

I'm pretty new to Rust but I'll try to have a look at the code to see how easy this would be to implement. Just thought I'd make an issue first to see what other people think and prevent duplicate effort. :)

test the MSP430 backend

#120 added support for the MSP430 architecture but didn't add any test. We should add a few MSP430 SVD files to our test suite. The problem is where do we get them. There's no database like the Cortex-M one.

@pftbest @cr1901 @awygle You all have have MSP430 micros -- I hope you all have different ones. Any plans for making a SVD file database of MSP430 devices?

Reset value should not be required

The SVD spec does not require reset values to be present. This happens in real data, e.g. on TM4C series it looks like all writable registers are always initialized to 0.

Fields `Not_Used` and `NOT_USED` map to the same type

File: MK61F15.svd

Contents:

          <fields>
            <field>
              <name>Not_Used</name>
              <description>Reserved</description>
              <bitOffset>0</bitOffset>
              <bitWidth>16</bitWidth>
              <access>read-only</access>
            </field>
            <field>
              <name>NOT_USED</name>
              <description>Reserved</description>
              <bitOffset>16</bitOffset>
              <bitWidth>16</bitWidth>
              <access>read-only</access>
            </field>
          </fields>

Error:

error[E0428]: a type named `NOT_USEDR` has already been defined in this module
      --> src/lib.rs:530278:9
       |
530267 | /         pub struct NOT_USEDR {
530268 | |             bits: u16,
530269 | |         }
       | |_________- previous definition of `NOT_USEDR` here
...
530278 | /         pub struct NOT_USEDR {
530279 | |             bits: u16,
530280 | |         }
       | |_________^ `NOT_USEDR` already defined

The read proxy for these two fields, Not_Used and NOT_USED, is NOT_USEDR. I don't know if this is something we want to support. This SVD file is also kind wrong because it should be using the keyword RESERVED for reserved bitfields then svd2rust will simply had ignored the bitfields.

Should we support this or flag the SVD file as being broken?

cc @whitequark

support clusters

Example:

File: ATSAMD21E15A.svd

Contents:

    <peripheral>
      <name>SERCOM0</name>
      <version>2.0.0</version>
      <description>Serial Communication Interface 0</description>
      <groupName>SERCOM</groupName>
      <prependToName>SERCOM_</prependToName>
      <baseAddress>0x42000800</baseAddress>
      <addressBlock>
        <offset>0</offset>
        <size>0x40</size>
        <usage>registers</usage>
      </addressBlock>
      <interrupt>
        <name>SERCOM0</name>
        <value>9</value>
      </interrupt>
      <registers>
       <cluster>
        <name>I2CM</name>
        <description>I2C Master Mode</description>
        <headerStructName>SercomI2cm</headerStructName>
        <addressOffset>0x0</addressOffset>
        <register>
          <name>ADDR</name>
          <!-- .. -->
       </cluster> <!-- SercomI2cm -->
       <cluster>
        <name>I2CS</name>
        <description>I2C Slave Mode</description>
        <alternateCluster>I2CM</alternateCluster>
        <headerStructName>SercomI2cs</headerStructName>
        <addressOffset>0x0</addressOffset>
        <register>
          <name>ADDR</name>
          <!-- .. -->

These clusters seem to be non-functional. They only add more structure (namespaces) to a register block. The above is supposed to generate something like this:

pub struct RegisterBlock {
    pub i2cm: I2CM,
    pub i2cs: I2CS,
    ..
}

pub struct I2CM {
    pub addr: ADDR,
    ..
}

pub struct I2CS {
    pub addr: ADDR,
    ..
}

The "flat" version would have resulted in a name collision between I2CM.ADDR and I2CS.ADDR.

We currently don't generate anything at all for clusters. For instance the above SERCOM0 peripheral is totally omitted in the output because it appears to have 0 registers as clusters are ignored.

Common traits for identical IP blocks

I want to be able to write generic functions that can operate on any instance of the same peripheral across a vendor's product line, or any instance of the same peripheral within a given chip.

For a concrete example: as far as I know, ST Micro uses the same bxCAN IP on every chip in the STM32 line that has a CAN peripheral, and on some chips they have multiple copies of the bxCAN peripheral. So I'd like to be able to write generic code that uses a bxCAN peripheral, independent of which STM32 product you're building for and which instance of the peripheral you're using.

I think a lot of the relevant information can be inferred directly from the .svd files, by checking whether two peripherals have identical register definitions, ignoring reset values. But I can't decide how much manual intervention makes sense.

What are your thoughts?

Add Support for Ranged Write Constraints

Some svd files have type information embedded in them with the enumeratedValues, but many do not and it is reasonable for the maintainer of an svd2rust memory map to provide these for making a safe and usable interface. Would it be possible to take in a second file of some sort which can associate a Peripheral + Register + Field to a type or inject enumeratedValues? I could see some desire to just say it is safe to write a boolean value to this register, or it is safe to write a u8 to this register without specifying an enum.

The main goal would be to allow for SVDs and Maintainer based meta-data to be maintained separately so that manufacturer provided svd files can be used without editing them directly (which makes it hard to figure out what you added and what was provided for). This also means that as manufacturers update their svd files with more information (if they ever do) the maintainer can more easily update without having to edit the svd file again.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.