Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit unions for overlapping/overloaded registers #192

Merged
merged 11 commits into from
May 9, 2018

Conversation

wez
Copy link
Contributor

@wez wez commented Mar 13, 2018

I want to get a complete working build for ATSAMD21 (rust-embedded/wg#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: #191
#16

@wez
Copy link
Contributor Author

wez commented Mar 13, 2018

I was hoping this would kick the CI but it didn't; what did I do wrong?

@jamesmunns
Copy link
Member

@wez CI isn't automatic. Bors is waiting for a reviewer. Once I get a chance to review, I can kick it off.

If you want to run the tests locally, #188 might be your easiest choice right now.

@jamesmunns
Copy link
Member

@wez - this change makes sense to me, and seems to work with my existing nrf52. I am a little worried about how we would detect overlaps (your change basically just removes the overlap check).

@Emilgardis or @japaric, any thoughts? It seems like the current "normal" behavior is to reject any overlaps generated by svd2rust.

@jamesmunns
Copy link
Member

Another possible option would be to impl #183, and make strong warnings when overlaps are detected, and/or add a command line option like --allow-overlaps (default off) that would disable overlap checks (should be applied to registers as well).

@wez
Copy link
Contributor Author

wez commented Mar 14, 2018

re: overlap detection, I started adding some logic to collect overlapping regions together so that I can emit unions for them (eg: resolve #16). I think I need to do that to make reasonable progress with the adafruit boards in the long run (eg: USB registers have host and device mode aliased into the same location), so I don't mind blocking landing this code change until I've made some more progress on the union angle.

@wez
Copy link
Contributor Author

wez commented Mar 14, 2018

I've added a commit that shows where I'm headed; the FieldRegions struct allows us to know which of the fields are overlapping so that we can choose to emit a union. I haven't done anything to actually emit a union yet, I just wanted to show my work and thinking.

It emits output like this for the atsamd21 in place of the older Ignoring message:

WARNING: overlaps for region offset=0-48. Using the first one of these:
  Some(Ident("usart")) 0-48
  Some(Ident("spi")) 0-48
  Some(Ident("i2cs")) 0-40
  Some(Ident("i2cm")) 0-48

There's some fishy looking stuff elsewhere in our interpretation of this svd file:

WARNING: overlaps for region offset=176-226. Using the first one of these:
  Some(Ident("pmux1_0")) 176-179
  Some(Ident("pmux1_1")) 177-180
  Some(Ident("pmux1_2")) 178-181
  Some(Ident("pmux1_3")) 179-182
  Some(Ident("pmux1_4")) 180-183
...

The corresponding bit of the SVD is derivedFrom PMUX0_%s and has bitfields:

       <register derivedFrom="PMUX0_%s">
          <dim>16</dim>
          <dimIncrement>0x1</dimIncrement>
          <name>PMUX1_%s</name>
          <description>Peripheral Multiplexing n - Group 1</description>
          <addressOffset>0xb0</addressOffset>
        </register>
        <register>
          <dim>16</dim>
          <dimIncrement>0x1</dimIncrement>
          <name>PMUX0_%s</name>
          <description>Peripheral Multiplexing n - Group 0</description>
          <addressOffset>0x30</addressOffset>
          <size>8</size>
          <fields>
            <field>
              <name>PMUXE</name>
              <description>Peripheral Multiplexing Even</description>
              <bitOffset>0</bitOffset>
              <bitWidth>4</bitWidth>
              ...
            </field>
            <field>
              <name>PMUXO</name>
              <description>Peripheral Multiplexing Odd</description>
              <bitOffset>4</bitOffset>
              <bitWidth>4</bitWidth>
            ...

I'm assuming that this has to do with the bit fields but it is also possible that my alternative size calculation might be to blame. Will need to dig in to this a bit more to be sure.

@jamesmunns
Copy link
Member

@wez thanks for the update! That output looks promising. Feel free to let me know if I can help with anything, or if you want me to take a look.

@Emilgardis
Copy link
Member

@wez Looks good so far! I'll make a proper review once more work has been done.

@wez
Copy link
Contributor Author

wez commented Mar 14, 2018

Thanks; I'll keep plugging away in the meantime.
The fishy bits I mentioned above are because there is an array that is derived from another. In that case expand_register uses Defaults::size for the size of the register (32 in this case) rather than resolving the correct derived value of 8. I'm going to chalk that up to rust-embedded/svd#21 and not worry so much about that right now.

@wez
Copy link
Contributor Author

wez commented Mar 14, 2018

With the commit I just added, we can now emit code like this for overlapping registers:

#[doc = "Real-Time Counter"]
pub mod rtc {
    #[doc = r" Register block"]
    #[repr(C)]
    pub struct RegisterBlock {
        u0: u0,
    }
    #[doc = r" Union container for a set of overlapping registers"]
    #[repr(C)]
    pub union u0 {
        #[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 MODE0 {
        #[doc = "0x00 - MODE0 Control"]
        pub ctrl: self::mode0::CTRL,
 ...

This code is good enough to pass cargo check running on my linux system; not the most thorough test, but shows promise.

There are a couple of TODOs/questions in here:

  • Can we do better than u0, u1 style identifiers? If so, what? One option is to try something like taking the longest common prefix of the contained fields. In the code above we could perhaps choose mode as the name. However, there's no guarantee that we find a common prefix; in my earlier comment this device has spi, i2c and usart in a union, and there's no common prefix.
  • If a RegisterBlock consists of a single field that is a union, should that entire RegisterBlock be a union?

[Edit: I went ahead and did these TODOs]

@wez wez mentioned this pull request Mar 14, 2018
@wez wez changed the title Resolve Cluster / Union size issue with ATSAMD21 boards Emit unions for overlapping/overloaded registers Mar 15, 2018
@jamesmunns jamesmunns added enhancement missing svd feature This issue is caused by a lack of support for something in the svd specification labels Mar 15, 2018
@wez wez mentioned this pull request Mar 15, 2018
@wez
Copy link
Contributor Author

wez commented Mar 15, 2018

OK, I think this is big enough and (seemingly!) working well enough state that it is ready for code review and running the CI.

Copy link
Member

@Emilgardis Emilgardis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't run this (yet) against any SVDs but I have some minor things to comment on.

let idx = self.regions.binary_search_by_key(&new_region.offset, |r| r.offset);
match idx {
Ok(idx) => {
panic!("we shouldn't exist in the vec, but are at idx {} {:#?}\n{:#?}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function should return Result<()> and this call should be made into an error. You can use error_chain's bail! macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Ok(quote! {
/// Register block
#[repr(C)]
pub struct #name {
pub #block_type #name {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this change. At first glance it seems fine but I'm not 100% on this change.

let mut min_len = usize::max_value();

for f in &self.fields {
match f.field.ident {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer an if let else here

regions.add(reg_block_field);
}

let block_is_union = regions.regions.len() == 1 && regions.regions[0].fields.len() > 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this into a function on FieldRegion instead to make it look "better"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and done!

Copy link
Member

@Emilgardis Emilgardis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a run and it looks very promising, however, the description/doc on *_union doesn't feel helpful at all. I propose that we just throw it away if they don't all match.

I also have some comments on the naming of the union.
First, the common prefix algorithm isn't correct.
Second, unions are largely much alike structs and as such should IMO be named appropriately. i.e Prefix_Union or something similar if we go by how we are currently naming our idents.

// reasonable short description, but it's good enough.
if f.description != result {
if result.len() > 0 {
result.push(' ');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This results in some wierd looking documentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Status Register overloaded Status Register
for example

impl Region {
/// Find the longest common prefix of the identifier names
/// for the fields in this region, if any.
fn common_prefix(&self) -> Option<String> {
Copy link
Member

@Emilgardis Emilgardis Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't find the longest common prefix as explained.

Take two registers fast and foo

This algorithm will say that fas is the prefix which to me doesn't sound correct [playground link]. Even wierder is that if you change the order the prefix becomes foo

I think a better algorithm would be to split each ident at a non-alpha character and calculate where the first mismatch is and use that.

I've implemented just that here (playground link).

Copy link
Member

@Emilgardis Emilgardis Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To that implementation there may be a need to add a grouping of numerical and non-alpha characters to make foo1 and foo12 have a common prefix of foo1, however, this would really depend on the context of the register because foo may very well be a valid common prefix.
Right now it outputs foo1or foo depending on which one is first. (Sorting the vec by length of str beforehand would solve this)

@Emilgardis
Copy link
Member

ping @wez

@wez
Copy link
Contributor Author

wez commented Mar 23, 2018

Do you have suggestions on naming? It's a bit difficult to move forward without some guidance on what you want to see. I can certainly fix the bug in the common prefix code but if you don't want that then there's not a lot of point :-) Thoughts?

@Emilgardis
Copy link
Member

I think PrefixUnion should work. I would appreciate some other people's opinions though. cc @japaric @jamesmunns @ryankurte

Right now unions are named prefix_union, should we change that into something else?

@jamesmunns
Copy link
Member

I'm pretty happy with {prefix}Union where {prefix} is an UpperCamelCase identifier. This seems to match Rust's standard naming scheme for Structs/Unions. It might be worth just running the regression suite against it and logging and/or grepping all the results to see what we end up with :)

btw @wez I rebased your changes against the current master (because it has the unofficial fast regression tester in it), and kicked off an unofficial CI test to see how things go. Its still running (ETA 25 minutes after I post this), but you can check it out here: https://gitlab.onevariable.com/james/svd2rust/-/jobs/93 - if either of you would like access to this repo so you can run testing there (its a beefy machine I use for builds), make an account on my gitlab server and ping me so I can give you push rights.

@jamesmunns
Copy link
Member

@wez it looks like there were some regressions on a number of the Freescale chips (but only those, it looks like). I'd suggest grabbing my rebased version of your branch and checking out the logs and output. You can use the regression tester to run single tests, and it will capture all of the output to log files for you.

@wez
Copy link
Contributor Author

wez commented Apr 19, 2018

Finally got around to looking into this; the regression tester makes this much easier!
The freescale failures are due to conflicting names for the union fields and types.
For example, dsr0 and dsr_bcr0 get lumped together into dsr_union, and dsr1 and dsr_bcr1 also get lumped into a different dsr_union.

We don't have a great way to automatically resolve such a conflict so I have a WIP that deals with this just by taking the shortest identifier from the members of the union and basing the union name off that.

So in the example I mentioned above, we produce:

    #[doc = "DMA Status Register / Byte Count Register DMA_DSR0 register."]
    #[repr(C)]
    pub union dsr0_union {
        #[doc = "0x108 - DMA Status Register / Byte Count Register"]
        pub dsr_bcr0: DSR_BCR0,
        #[doc = "0x10b - DMA_DSR0 register."]
        pub dsr0: DSR0,
    }
    #[doc = "DMA Status Register / Byte Count Register DMA_DSR1 register."]
    #[repr(C)]
    pub union dsr1_union {
        #[doc = "0x118 - DMA Status Register / Byte Count Register"]
        pub dsr_bcr1: DSR_BCR1,
        #[doc = "0x11b - DMA_DSR1 register."]
        pub dsr1: DSR1,
    }
        #[doc = "DMA Status Register / Byte Count Register DMA_DSR0 register."]
        pub dsr0: dsr0_union,
        #[doc = "DMA Status Register / Byte Count Register DMA_DSR1 register."]
        pub dsr1: dsr1_union,

Does this sound good enough? It has the nice property of guaranteeing that there are no possible conflicts in the names (assuming that the underlying svd file has no conflicting names!), and avoids doing non-trivial mapping and processing for ourselves.

In the example above you'd use dsr0.dsr0 to get at that particular variant, which feels slightly repetitive but not terrible.

I haven't tackled the casing of ${prefix}_union vs ${prefix}Union yet, but that's the easy part.

@wez
Copy link
Contributor Author

wez commented Apr 20, 2018

Rebased and force pushed. The regression tests passed (takes my laptop 2 hours to run them!) except for this one that appears unrelated; I'm assuming that riscv-rt needs to be bumped to match recent toolchain changes:

 $ cat output/si_five_e310x/cargo-check.err.log
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling vcell v0.1.0
   Compiling bare-metal v0.1.1
   Compiling riscv-rt v0.1.3
   Compiling r0 v0.2.2
   Compiling volatile-register v0.2.0
   Compiling riscv v0.1.4
error[E0259]: the name `compiler_builtins` is defined multiple times
   --> /home/wez/.cargo/registry/src/github.com-1ecc6299db9ec823/riscv-rt-0.1.3/src/lib.rs:171:1
    |
171 | extern crate compiler_builtins;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `compiler_builtins` reimported here
    |
    = note: `compiler_builtins` must be defined only once in the type namespace of this module
help: You can use `as` to change the binding name of the import
    |
171 | extern crate compiler_builtins as other_compiler_builtins;

Here's an example of the naming:

#[doc = "DMA Controller"]
pub mod dma {
    #[doc = r" Register block"]
    #[repr(C)]
    pub struct RegisterBlock {
        #[doc = "0x100 - Source Address Register"]
        pub sar0: SAR0,
        #[doc = "0x104 - Destination Address Register"]
        pub dar0: DAR0,
        #[doc = "DMA Status Register / Byte Count Register DMA_DSR0 register."]
        pub dsr0: DSR0Union,
      ...
    }
    #[doc = "DMA Status Register / Byte Count Register DMA_DSR0 register."]
    #[repr(C)]
    pub union DSR0Union {
        #[doc = "0x108 - DMA Status Register / Byte Count Register"]
        pub dsr_bcr0: DSR_BCR0,
        #[doc = "0x10b - DMA_DSR0 register."]
        pub dsr0: DSR0,
    }

(Ident::new(format!("{}Union", prefix.to_sanitized_upper_case())),
Ident::new(prefix))
}
// If we can't find a name, fall back to theregion index as a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

theregion

}

impl Region {
fn shortest_ident(&self) -> Option<String> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes for some wierd union names. However, with your example of dsr0 and dsr_bcr0 together with dsr1 and dsr_bcr1 we'd have to have this. I'd much prefer my earlier attempt #192 (comment) but that wouldn't solve things for the mentioned case.

One alternative to solve this would be to simply not support the above case, as dsr0_bcr is IMO more accurate than dsr_bcr0. This would mean that we'd have to add some patches for SVDs that have these patterns. We could also add a check (after code gen of a peripheral) for conflicting union type names to clarify that compilation will fail and recommend how to fix it.

This change would mean that for the example mentioned we would still have Dsr0Union and Dsr1Union

Example of a wierd union name (with fixed pascal case)

    #[doc = "The Prescale Register stores the Value for the prescaler. The cont event gets divided by this value"]
    #[repr(C)]
    pub union PrescaleRdUnion {
        # [ doc = "0x28 - The Prescale Register stores the Value for the prescaler. The cont event gets divided by this value" ]
        pub prescale_wr: PRESCALE_WR,
        # [ doc = "0x28 - The Prescale Register stores the Value for the prescaler. The cont event gets divided by this value" ]
        pub prescale_rd: PRESCALE_RD,
    }

Using PrescaleUnion here makes the most sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started out by integrating your code from the playground link and added some unit tests for it (there were a couple of minor issues, for example, the dsr case mentioned above would yield an empty identifier), but the conflicts were the limiting factor. It's a shame that https://github.com/rust-lang/rfcs/blob/master/text/2102-unnamed-fields.md isn't available; it only just got merged and tracked in rust-lang/rust#49804

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I think it would be a shame if we forced the user to patch the SVD file to generate something usable. I think its fine to be opinionated about naming and maintain a patched SVD, but requiring a patched SVD feels a bit heavyweight to me.

Copy link
Member

@Emilgardis Emilgardis Apr 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One solution would be to have two implementations for naming, before code gen we use the fixed common_prefix function and check that there is no name "collision" with other types and that a common prefix exists, if there aren't any problems carry on with code gen, otherwise use the current implementation (shortest_ident) for those types that are affected. I think this would satisfy us both.

if region.fields.len() > 1 && !block_is_union {
let (type_name, name) = match region.shortest_ident() {
Some(prefix) => {
(Ident::new(format!("{}Union", prefix.to_sanitized_upper_case())),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be sanitized PascalCase

@Emilgardis
Copy link
Member

cc @wez are you still working on this?

@wez
Copy link
Contributor Author

wez commented May 7, 2018

I'm waiting for feedback; it's not clear what you want to be changed.

@Emilgardis
Copy link
Member

Emilgardis commented May 7, 2018

I've spoken with @japaric and confirmed that it will work fine.

There is still one issue left to resolve which I forgot about, we will need to make unions a nightly-only feature as we are stabilizing embedded on rust (issue rust-embedded/wg#42). To fix this you should add a new opt-in flag

.arg(
            Arg::with_name("nightly_features")
                .long("nightly")
                .help("Enable features only available to nightly rustc")
)

and change the logic of register_or_cluster_block to use the current version unless the --nightly flag is not used.

You can do this easily by adding two functions register_or_cluster_block_stable and register_or_cluster_block_nightly which have the old behaviour and new behaviour, and then change register_or_cluster_block to choose which version conditionally via the flag
You'll need to also add the untagged_unions feature conditionally.

I'm sorry for the inconvenience :/

@wez
Copy link
Contributor Author

wez commented May 8, 2018

I've rebased, cherry picked in your name resolution, added the --nightly flag and threaded it through to select between the two codegen functions and force pushed to update this PR.

Is that what you had in mind?

wez added a commit to atsamd-rs/atsamd that referenced this pull request May 8, 2018
See: rust-embedded/svd2rust#192

(this also requires the commits from
https://github.com/wez/svd2rust/tree/wezlocal to make the pmux and
pincfg bits compile for gpio)
@Emilgardis
Copy link
Member

Thank you!

bors r+

bors bot added a commit that referenced this pull request May 8, 2018
192: Emit unions for overlapping/overloaded registers r=Emilgardis a=wez

I want to get a complete working build for ATSAMD21 (rust-embedded/wg#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 taking the longest common prefix of the union's alternates, or just pick an artificial name like `u1` if there was no common prefix.
* 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: #191 
#16



Co-authored-by: Wez Furlong <wez@wezfurlong.org>
Co-authored-by: Emil Gardström <emil.gardstrom@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 8, 2018

Build failed

@Emilgardis
Copy link
Member

This is due to #97, @wez could you disable the ATSAMD21G18A test again and I'll add it to svd2rust-regress instead using your patched version in a new pr.

wez and others added 10 commits May 8, 2018 08:36
I believe that this computes the correct size for a cluster that
contains overlapping registers (such as the ATSAMD21G that I've
added to the tests in this commit).

It comes at the cost of not recognizing the overlapping registers
at that stage.
I want to use this information to emit unions.  This commit deals
solely with recognizing and tracking the overlaps.
If a RegisterBlock is comprised of a single union, then the
RegisterBlock is emitted as that union.
This currently triggers for the atsamd21 for these fields:

```
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
```

the issue there is that we're missing `derivedFrom` support for
registers, and once implemented, that issue goes away.
Rather than trying to compute a smart name and hoping that there is
no conflict, this commit switches to a simpler scheme: we now take
the shortest identifier from the components of a union and take the
one that sorts earliest as the name for the union in its containing
RegisterBlock.

So long as the names of the registers in that block don't have
collisions, we are guaranteed not to collide.
@wez
Copy link
Contributor Author

wez commented May 8, 2018

force pushed with the ci/script.sh changes reverted

@Emilgardis
Copy link
Member

bors r+

bors bot added a commit that referenced this pull request May 8, 2018
192: Emit unions for overlapping/overloaded registers r=Emilgardis a=wez

I want to get a complete working build for ATSAMD21 (rust-embedded/wg#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: #191 
#16



Co-authored-by: Wez Furlong <wez@wezfurlong.org>
Co-authored-by: Emil Gardström <emil.gardstrom@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 8, 2018

Timed out

@Emilgardis
Copy link
Member

interesting, I'm not sure why bors timed out.

Merging as tests have technically been passed

@Emilgardis Emilgardis merged commit ae88a1c into rust-embedded:master May 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement missing svd feature This issue is caused by a lack of support for something in the svd specification
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants