ral 0.2.0

Base layer for generated libraries for specific MCUs
Documentation

Library providing basis for low level registers access This crate re-exports macros refined in ral-macro, so this is the only library needed to be defined in dependencies

Code generator

Refer to ral-gen crate documentation for code generation guidelines

DSL and produced result

Internally generates register description backed by ral

use ral::register;

register! {
    use crate_name::types::CustomType; // Use expressions for custom types used in field declarations

    #[access = "read-write"] // Optional register wide access specifier
    #[doc = "Register description"] // Optional register description
    reg0 { // Register name
        offset: 0x8, // Register offset in enclosing peripheral
        value_size: 32, // Register size, expected values are 8, 16, 32 or 64
        reset_mask: 0xFFFFFFFF, // Reset mask
        reset_value: 0x0, // Reset value
        fields: { // Optional fields
            #[doc = "Bits 16:31 - Read-only u16 field"] // Each field can have a description
            #[access = "read-only"] // Access specifier
            field5[16:16] as u16, // Field specification `<name>[<offset>:<width>] as <type>`

            #[doc = "Bits 14:15 - Write-only field"]
            #[access = "write-only"] // Only setters will be generated
            field4[14:2] as u8, // Default supported types are `u8`, `u16`, `u32` and `u64`

            #[doc = "Bits 11:13 - Read-only field"]
            #[access = "read-only"] // Only getters will be generated
            field3[11:3] as u8,

            #[doc = "Bit 10 - Boolean field"]
            #[access = "read-write"] // `read-write` is a default access specifier
            field2[10:1] as bool, // `bool` fields are also supported by default

            #[doc = "Bits 8:9 - Enum field"]
            #[access = "read-write"] // Both getters and setters will be generated
            field1[8:2] as CustomType, // Custom types are supported with limitations

            #[doc = "Bits 0:7 - Read-write by default long field"]
            field0[0:8] as u8
        }
    }
}

Above register definition will be transformed into following code

// Required uses section
use core::sync::atomic::AtomicPtr;
use core::convert::TryFrom;
use ral::{borrow_register, init_register, return_register, value_read, value_write, R, ReadableRegister, Register, VolatileCell, WritableRegister};
use crate_name::types::CustomType;
const REGISTER: AtomicPtr<VolatileCell<<Reg0 as Register>::ValueType>> = AtomicPtr::new(
    (super::BASE_ADDRESS /* Enclosing peripheral/cluster base address */ + 0x00 /* offset */) as *mut VolatileCell<<Reg0 as Register>::ValueType>,
);
///Register description
pub fn reg0() -> Option<Reg0> {
    borrow_register(&REGISTER).map(Reg0)
}
pub struct Reg0(R<u32, Reg0>);
impl Drop for Reg0 {
    fn drop(&mut self) {
        let Reg0(register) = self;
        return_register(&REGISTER, register);
    }
}
impl Register for Reg0 {
    type RegisterType = Self;
    type ValueType = u32;
    const RESET_MASK: Self::ValueType = 0xFFFF_FFFF;
    const RESET_VALUE: Self::ValueType = 0x1234_0000;
}
impl ReadableRegister for Reg0 { // Will be added if register wide access allows read
    fn get_bits(&self) -> Self::ValueType {
        self.0.get_bits()
    }

    fn read(&mut self) -> &mut Self::RegisterType {
        self.0.read();
        self
    }
}
impl WritableRegister for Reg0 { // Will be added if register wide access allows write
    fn set_bits(&mut self, bits: Self::ValueType) -> &mut Self::RegisterType {
        self.0.set_bits(bits);
        self
    }
    fn reset(&mut self) -> &mut Self::RegisterType {
        self.set_bits(Self::RESET_VALUE)
    }
    fn write(&mut self) -> &mut Self::RegisterType {
        self.0.write();
        self
    }
}
impl Reg0 { // Will be added if any fields specified
    ///Bits 16:31 - Read-only u16 field
    #[inline]
    pub fn get_field5(&self) -> u16 {
        value_read!(self, 0x0000FFFFu32, 16) as u16
        // Will further expand into
        // ((self.0.get_bits() >> 16) & 0x0000FFFFu32) as u16
    }
    ///Bits 14:15 - Write-only field
    #[inline]
    pub fn set_field4(&mut self, value: u8) -> &mut Self {
        value_write!(self, 0x00000003u32, 14, value as <Self as Register>::ValueType);
        // Will further expand into
        // self.0.set_bits(
        //     (self.0.get_bits() & !(0x00000003u32 << 14))
        //         | ((value as <Self as Register>::ValueType & 0x00000003u32) << 14),
        // );
        self
    }
    ///Bits 11:13 - Read-only field
    #[inline]
    pub fn get_field3(&self) -> u8 {
        value_read!(self, 0x00000007u32, 11) as u8
        // Will further expand into
        // ((self.0.get_bits() >> 11) & 0x00000007u32) as u8
    }
    ///Bit 10 - Boolean field
    #[inline]
    pub fn is_field2_set(&self) -> bool {
        value_read!(self, 0x00000001u32, 10) == 1
        // Will further expand into
        // ((self.0.get_bits() >> 10) & 0x00000001u32) == 1
    }
    ///Bit 10 - Boolean field
    #[inline]
    pub fn set_field2_value(&mut self, value: bool) -> &mut Self {
        value_write!(self, 0x00000001u32, 10, value as <Self as Register>::ValueType);
        // Will further expand into
        // self.0.set_bits(
        //     (self.0.get_bits() & !(0x00000001u32 << 10))
        //         | ((value as <Self as Register>::ValueType & 0x00000001u32) << 10),
        // );
        self
    }
    ///Bit 10 - Boolean field
    #[inline]
    pub fn set_field2(&mut self) -> &mut Self {
        self.set_field2_value(true)
    }
    ///Bit 10 - Boolean field
    #[inline]
    pub fn unset_field2(&mut self) -> &mut Self {
        self.set_field2_value(false)
    }
    ///Bits 8:9 - Enum field
    #[inline]
    pub fn get_field1(
        &self,
    ) -> Result<CustomType, <CustomType as TryFrom<<Self as Register>::ValueType>>::Error> {
        <CustomType as TryFrom<<Self as Register>::ValueType>>::try_from(value_read!(self, 0x00000003u32, 8))
        // Will further expand into
        // <CustomType as TryFrom<<Self as Register>::ValueType>>::try_from(
        //     (self.0.get_bits() >> 8) & 0x00000003u32,
        // )
    }
    ///Bits 8:9 - Enum field
    #[inline]
    pub fn set_field1(
        &mut self,
        value: CustomType,
    ) -> Result<&mut Self, <<Self as Register>::ValueType as TryFrom<CustomType>>::Error>
    {
        value_write!(self, 0x00000003u32, 8, <<Self as Register>::ValueType as TryFrom<#ty>>::try_from(value)?);
        // Will further expand into
        // self.0.set_bits(
        //     (self.0.get_bits() & !(0x00000003u32 << 8))
        //         | ((<<Self as Register>::ValueType as TryFrom<CustomType>>::try_from(value)?
        //             & 0x00000003u32) << 8),
        // );
        Ok(self)
    }
    ///Bits 0:7 - Read-write by default long field
    #[inline]
    pub fn get_field0(&self) -> u8 {
        value_read!(self, 0x000000FFu32, 0) as u8
        // Will further expand into
        // ((self.0.get_bits() >> 0) & 0x000000FFu32) as u8
    }
    ///Bits 0:7 - Read-write by default long field
    #[inline]
    pub fn set_field0(&mut self, value: u8) -> &mut Self {
        value_write!(self, 0x000000FFu32, 0, value as <Self as Register>::ValueType);
        // Will further expand into
        // self.0.set_bits(
        //     (self.0.get_bits() & !(0x000000FFu32 << 0))
        //         | ((value as <Self as Register>::ValueType & 0x000000FFu32) << 0),
        // );
        self
    }
}

Proposed modules structure

Enclosing peripheral module can look like this

#![doc = "Peripheral description"]

mod reg0;
pub use reg0::*;

const BASE_ADDRESS: usize = 0x0000_0000; // Should be actual peripheral base address

Peripherals module should look like this

pub mod peripheral;

And finally lib.rs should look like this

#![doc = "Device description"]

pub mod peripherals;

So the recommended module hierarchy is

src/
├── lib.rs
└── peripherals/
    ├── mod.rs
    └── peripheral/
        ├── mod.rs
        ├── reg0.rs
        ├── reg1.rs
        └── cluster/
            ├── mod.rs
            ├── reg2.rs
            ├── reg3.rs
            └── reg4.rs

The layout also includes optional cluster module, which can look like this

#![doc = "Cluster description"]

mod reg2;
pub use reg2::*;
mod reg3;
pub use reg3::*;
mod reg4;
pub use reg4::*;

const BASE_ADDRESS: usize = super::BASE_ADDRESS + 0x40; // Enclosing peripheral/cluster base address plus offset

Requirenments to custom types

For read access you must implement TryFrom<u32> for CustomType or From<u32> for CustomType if your register is 32-bit For write access you have to implement TryFrom<CustomType> for u32 or From<CustomType> for u32 respectively

How to use resulting library

use device_crate::peripherals::peripheral;

let mut reg0 = peripheral::reg0()
        .unwrap() // Borrow the register
        .read(); // Load current data stored in register
let field3 = reg0.get_field3(); // Read `u8` field

let field2 = reg0.is_field2_set(); // Read `bool` field
let field1: CustomType = reg0.get_field1().unwrap(); // Read field represented by `CustomType`
reg0
    .set_field0(if field2 {
        reg0.get_field5() as u8
    } else {
        0
    }) // Set `u8` field
    .unset_field2() // Unset `bool` field
    .set_field4(field3 + 10) // Set 'u8' field
    .set_field1(if field1 == Two { One } else { Three }).unwrap() // Set `CustomType` field
    .write(); // Finally write result to hardware register