Skip to content

enum based API #52

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

Closed
wants to merge 4 commits into from
Closed

enum based API #52

wants to merge 4 commits into from

Conversation

japaric
Copy link
Member

@japaric japaric commented Jan 29, 2017

Auto-generated API for reads looks like this:

impl Out {
    pub fn read(&self) -> OutR {
        OutR { bits: self.register.read() }
    }

    ..
}

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

impl OutR {
    #[doc = "Bit 0 - Pin 0."]
    pub fn pin0(&self) -> OutRPin0 {
        const MASK: u32 = 1;
        const OFFSET: u8 = 0u8;
        OutRPin0::from((self.bits >> OFFSET) & MASK)
    }
    
    ..
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum OutRPin0 {
    #[ doc = "Pin driver is low." ]
    Low,
    #[ doc = "Pin driver is high." ]
    High,
}

impl OutRPin0 {
    #[inline(always)]
    fn from(value: u32) -> Self {
        match value {
            0u32 => OutRPin0::Low,
            1u32 => OutRPin0::High,
            _ => unreachable!(),
        }
    }
}

The unreachable!() will get optimized away when compiling in release mode.

In the case where a bitfield is e.g. 2 bits wide but the SVD file lists less than 4 values / variants, svd2rust will create hidden reserved variants:

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PinCnfRPull {
    #[doc = "No pull."]
    Disabled,
    #[doc = "Pulldown on pin."]
    Pulldown,
    #[doc(hidden)]
    _Reserved10,
    #[doc = "Pullup on pin."]
    Pullup,
}

In the above example, the value 0b10 was not listed in the SVD.


Auto-generated API for writes looks like this:

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

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

impl OutW {
    #[doc = "Bit 0 - Pin 0."]
    pub fn pin0(&mut self, value: OutWPin0) -> &mut Self {
        const MASK: u32 = 1;
        const OFFSET: u8 = 0u8;
        self.bits &= !((MASK as u32) << OFFSET);
        self.bits |= value.bits() << OFFSET;
        self
    }
    
    ..
}


#[derive(Clone, Copy, Eq, PartialEq)]
pub enum OutWPin0 {
    # [ doc = "Pin driver is low." ]
    Low,
    # [ doc = "Pin driver is high." ]
    High,
}

impl OutWPin0 {
    # [ inline ( always ) ]
    fn bits(&self) -> u32 {
        match *self {
            OutWPin0::Low => 0u32,
            OutWPin0::High => 1u32,
        }
    }
}

The modify API remains unchanged.

To get an idea of what actually using this API looks like, check this PR.

cc @brandonedens

Jorge Aparicio added 2 commits January 28, 2017 22:34
To support SVD like this one

``` xml
<peripheral>
  <name>GPIOA</name>
  ..
  <registers>
    <register>
      <name>MODER</name>
      ..
      <fields>
        <field>
          <name>MODER15</name>
          <description>Port x configuration bits (y =
          0..15)</description>
          <bitOffset>30</bitOffset>
          <bitWidth>2</bitWidth>
          <access>read-write</access>
          <enumeratedValues>
            <name>Moder</name>
            <usage>read-write</usage>
            <enumeratedValue>
              <name>InputMode</name>
              <description>00: Input (reset state)</description>
              <value>0</value>
            </enumeratedValue>
            <enumeratedValue>
              <name>OutputMode</name>
              <description>01: General purpose output mode</description>
              <value>1</value>
            </enumeratedValue>
            <enumeratedValue>
              <name>AlternateMode</name>
              <description>10: Alternate function mode</description>
              <value>2</value>
            </enumeratedValue>
            <enumeratedValue>
              <name>AnalogMode</name>
              <description>11: Analog mode</description>
              <value>3</value>
            </enumeratedValue>
          </enumeratedValues>
        </field>
        <field>
          <name>MODER14</name>
          <description>Port x configuration bits (y =
          0..15)</description>
          <bitOffset>28</bitOffset>
          <bitWidth>2</bitWidth>
          <enumeratedValues derivedFrom="MODER15">
          </enumeratedValues>
        </field>
```

The derivedFrom value has to match the name of an *field* that's in the same
register
@japaric japaric mentioned this pull request Jan 29, 2017
@japaric
Copy link
Member Author

japaric commented Jan 29, 2017

I don't really like having to import enums to be able to perform writes to registers, which is a very commond operation. I have in mind a more ergonomic API that would look like this:

#![no_main]
#![no_std]

extern crate nrf51822;

use nrf51822::peripheral;
use nrf51822::led::Led;

#[no_mangle]
pub fn main() -> ! {
    let timer0 = unsafe { peripheral::timer0_mut() };

    timer0.mode.write(|w| w.mode().timer());

    timer0.bitmode.write(|w| w.bitmode()._24bit());

    // prescaler = 2 ^ 4 = 16
    timer0.prescaler.write(|w| w.prescaler(4));

    timer0.shorts.modify(|_, w| w.compare0_clear().enabled());

    // reset the timer after 1_000_000 ticks
    // NOTE clock frequency = 16 MHz
    timer0.cc0.write(1_000_000);

    // start the timer
    timer0.tasks_start.write(1);

    let mut on = true;
    loop {
        if on {
            Led.on();
        } else {
            Led.off();
        }

        on = !on;

        while timer0.events_compare0.read() == 0 {}
        timer0.events_compare0.write(0);
    }
}

When used in the blinky example. Note that no imports are necessary in this case. The variant / value of a bitfield is picked using a method. The read API would remain unchanged in this scenario.

I have already prototyped this API "by hand". I'll implement this different API generation and post another PR tomorrow.

instead of enumerating them from 0 to N
@japaric
Copy link
Member Author

japaric commented Jan 29, 2017

I have already prototyped this API "by hand". I'll implement this different API generation and post another PR tomorrow.

See #53.

@japaric
Copy link
Member Author

japaric commented Feb 4, 2017

Closing in favor of #53.

@japaric japaric closed this Feb 4, 2017
@japaric japaric deleted the enum branch April 11, 2017 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant