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;
#[access = "read-write"] #[doc = "Register description"] reg0 { offset: 0x8, value_size: 32, reset_mask: 0xFFFFFFFF, reset_value: 0x0, fields: { #[doc = "Bits 16:31 - Read-only u16 field"] #[access = "read-only"] field5[16:16] as u16,
#[doc = "Bits 14:15 - Write-only field"]
#[access = "write-only"] field4[14:2] as u8,
#[doc = "Bits 11:13 - Read-only field"]
#[access = "read-only"] field3[11:3] as u8,
#[doc = "Bit 10 - Boolean field"]
#[access = "read-write"] field2[10:1] as bool,
#[doc = "Bits 8:9 - Enum field"]
#[access = "read-write"] field1[8:2] as CustomType,
#[doc = "Bits 0:7 - Read-write by default long field"]
field0[0:8] as u8
}
}
}
Above register definition will be transformed into following code
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 + 0x00 ) as *mut VolatileCell<<Reg0 as Register>::ValueType>,
);
pub fn reg0() -> Option<Reg0> {
borrow_register(®ISTER).map(Reg0)
}
pub struct Reg0(R<u32, Reg0>);
impl Drop for Reg0 {
fn drop(&mut self) {
let Reg0(register) = self;
return_register(®ISTER, 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 { 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 { 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 { #[inline]
pub fn get_field5(&self) -> u16 {
value_read!(self, 0x0000FFFFu32, 16) as u16
}
#[inline]
pub fn set_field4(&mut self, value: u8) -> &mut Self {
value_write!(self, 0x00000003u32, 14, value as <Self as Register>::ValueType);
self
}
#[inline]
pub fn get_field3(&self) -> u8 {
value_read!(self, 0x00000007u32, 11) as u8
}
#[inline]
pub fn is_field2_set(&self) -> bool {
value_read!(self, 0x00000001u32, 10) == 1
}
#[inline]
pub fn set_field2_value(&mut self, value: bool) -> &mut Self {
value_write!(self, 0x00000001u32, 10, value as <Self as Register>::ValueType);
self
}
#[inline]
pub fn set_field2(&mut self) -> &mut Self {
self.set_field2_value(true)
}
#[inline]
pub fn unset_field2(&mut self) -> &mut Self {
self.set_field2_value(false)
}
#[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))
}
#[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)?);
Ok(self)
}
#[inline]
pub fn get_field0(&self) -> u8 {
value_read!(self, 0x000000FFu32, 0) as u8
}
#[inline]
pub fn set_field0(&mut self, value: u8) -> &mut Self {
value_write!(self, 0x000000FFu32, 0, value as <Self as Register>::ValueType);
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() .read(); let field3 = reg0.get_field3();
let field2 = reg0.is_field2_set(); let field1: CustomType = reg0.get_field1().unwrap(); reg0
.set_field0(if field2 {
reg0.get_field5() as u8
} else {
0
}) .unset_field2() .set_field4(field3 + 10) .set_field1(if field1 == Two { One } else { Three }).unwrap() .write();