1261 lines
40 KiB
Rust
1261 lines
40 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Macro to define register layout and accessors.
|
|
//!
|
|
//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for
|
|
//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such
|
|
//! type comes with its own field accessors that can return an error if a field's value is invalid.
|
|
//!
|
|
//! Note: most of the items in this module are public so they can be referenced by the macro, but
|
|
//! most are not to be used directly by users. Outside of the `register!` macro itself, the only
|
|
//! items you might want to import from this module are [`WithBase`] and [`Array`].
|
|
//!
|
|
//! # Simple example
|
|
//!
|
|
//! ```no_run
|
|
//! use kernel::io::register;
|
|
//!
|
|
//! register! {
|
|
//! /// Basic information about the chip.
|
|
//! pub BOOT_0(u32) @ 0x00000100 {
|
|
//! /// Vendor ID.
|
|
//! 15:8 vendor_id;
|
|
//! /// Major revision of the chip.
|
|
//! 7:4 major_revision;
|
|
//! /// Minor revision of the chip.
|
|
//! 3:0 minor_revision;
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
|
|
//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
|
|
//! least significant bits of the type.
|
|
//!
|
|
//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
|
|
//! getter method, which is named after them. They also have setter methods prefixed with `with_`
|
|
//! for runtime values and `with_const_` for constant values. All setters return the updated
|
|
//! register value.
|
|
//!
|
|
//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
|
|
//! `?=>` syntaxes.
|
|
//!
|
|
//! If present, doc comments above register or fields definitions are added to the relevant item
|
|
//! they document (the register type itself, or the field's setter and getter methods).
|
|
//!
|
|
//! Note that multiple registers can be defined in a single `register!` invocation. This can be
|
|
//! useful to group related registers together.
|
|
//!
|
|
//! Here is how the register defined above can be used in code:
|
|
//!
|
|
//!
|
|
//! ```no_run
|
|
//! use kernel::{
|
|
//! io::{
|
|
//! register,
|
|
//! Io,
|
|
//! IoLoc,
|
|
//! },
|
|
//! num::Bounded,
|
|
//! };
|
|
//! # use kernel::io::Mmio;
|
|
//! # register! {
|
|
//! # pub BOOT_0(u32) @ 0x00000100 {
|
|
//! # 15:8 vendor_id;
|
|
//! # 7:4 major_revision;
|
|
//! # 3:0 minor_revision;
|
|
//! # }
|
|
//! # }
|
|
//! # fn test(io: &Mmio<0x1000>) {
|
|
//! # fn obtain_vendor_id() -> u8 { 0xff }
|
|
//!
|
|
//! // Read from the register's defined offset (0x100).
|
|
//! let boot0 = io.read(BOOT_0);
|
|
//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
|
|
//!
|
|
//! // Update some fields and write the new value back.
|
|
//! let new_boot0 = boot0
|
|
//! // Constant values.
|
|
//! .with_const_major_revision::<3>()
|
|
//! .with_const_minor_revision::<10>()
|
|
//! // Runtime value.
|
|
//! .with_vendor_id(obtain_vendor_id());
|
|
//! io.write_reg(new_boot0);
|
|
//!
|
|
//! // Or, build a new value from zero and write it:
|
|
//! io.write_reg(BOOT_0::zeroed()
|
|
//! .with_const_major_revision::<3>()
|
|
//! .with_const_minor_revision::<10>()
|
|
//! .with_vendor_id(obtain_vendor_id())
|
|
//! );
|
|
//!
|
|
//! // Or, read and update the register in a single step.
|
|
//! io.update(BOOT_0, |r| r
|
|
//! .with_const_major_revision::<3>()
|
|
//! .with_const_minor_revision::<10>()
|
|
//! .with_vendor_id(obtain_vendor_id())
|
|
//! );
|
|
//!
|
|
//! // Constant values can also be built using the const setters.
|
|
//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
|
|
//! .with_const_major_revision::<3>()
|
|
//! .with_const_minor_revision::<10>();
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! For more extensive documentation about how to define registers, see the
|
|
//! [`register!`](kernel::io::register!) macro.
|
|
|
|
use core::marker::PhantomData;
|
|
|
|
use crate::io::IoLoc;
|
|
|
|
use kernel::build_assert;
|
|
|
|
/// Trait implemented by all registers.
|
|
pub trait Register: Sized {
|
|
/// Backing primitive type of the register.
|
|
type Storage: Into<Self> + From<Self>;
|
|
|
|
/// Start offset of the register.
|
|
///
|
|
/// The interpretation of this offset depends on the type of the register.
|
|
const OFFSET: usize;
|
|
}
|
|
|
|
/// Trait implemented by registers with a fixed offset.
|
|
pub trait FixedRegister: Register {}
|
|
|
|
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
|
|
/// passing a [`FixedRegister`] value.
|
|
impl<T> IoLoc<T> for ()
|
|
where
|
|
T: FixedRegister,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
T::OFFSET
|
|
}
|
|
}
|
|
|
|
/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
|
|
/// as an [`IoLoc`].
|
|
impl<T> IoLoc<T> for T
|
|
where
|
|
T: FixedRegister,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
T::OFFSET
|
|
}
|
|
}
|
|
|
|
/// Location of a fixed register.
|
|
pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
|
|
|
|
impl<T: FixedRegister> FixedRegisterLoc<T> {
|
|
/// Returns the location of `T`.
|
|
#[inline(always)]
|
|
// We do not implement `Default` so we can be const.
|
|
#[expect(clippy::new_without_default)]
|
|
pub const fn new() -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<T> IoLoc<T> for FixedRegisterLoc<T>
|
|
where
|
|
T: FixedRegister,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
T::OFFSET
|
|
}
|
|
}
|
|
|
|
/// Trait providing a base address to be added to the offset of a relative register to obtain
|
|
/// its actual offset.
|
|
///
|
|
/// The `T` generic argument is used to distinguish which base to use, in case a type provides
|
|
/// several bases. It is given to the `register!` macro to restrict the use of the register to
|
|
/// implementors of this particular variant.
|
|
pub trait RegisterBase<T> {
|
|
/// Base address to which register offsets are added.
|
|
const BASE: usize;
|
|
}
|
|
|
|
/// Trait implemented by all registers that are relative to a base.
|
|
pub trait WithBase {
|
|
/// Family of bases applicable to this register.
|
|
type BaseFamily;
|
|
|
|
/// Returns the absolute location of this type when using `B` as its base.
|
|
#[inline(always)]
|
|
fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
|
|
where
|
|
Self: Register,
|
|
{
|
|
RelativeRegisterLoc::new()
|
|
}
|
|
}
|
|
|
|
/// Trait implemented by relative registers.
|
|
pub trait RelativeRegister: Register + WithBase {}
|
|
|
|
/// Location of a relative register.
|
|
///
|
|
/// This can either be an immediately accessible regular [`RelativeRegister`], or a
|
|
/// [`RelativeRegisterArray`] that needs one additional resolution through
|
|
/// [`RelativeRegisterLoc::at`].
|
|
pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>);
|
|
|
|
impl<T, B> RelativeRegisterLoc<T, B>
|
|
where
|
|
T: Register + WithBase,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
{
|
|
/// Returns the location of a relative register or register array.
|
|
#[inline(always)]
|
|
// We do not implement `Default` so we can be const.
|
|
#[expect(clippy::new_without_default)]
|
|
pub const fn new() -> Self {
|
|
Self(PhantomData, PhantomData)
|
|
}
|
|
|
|
// Returns the absolute offset of the relative register using base `B`.
|
|
//
|
|
// This is implemented as a private const method so it can be reused by the [`IoLoc`]
|
|
// implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].
|
|
#[inline]
|
|
const fn offset(self) -> usize {
|
|
B::BASE + T::OFFSET
|
|
}
|
|
}
|
|
|
|
impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
|
|
where
|
|
T: RelativeRegister,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
RelativeRegisterLoc::offset(self)
|
|
}
|
|
}
|
|
|
|
/// Trait implemented by arrays of registers.
|
|
pub trait RegisterArray: Register {
|
|
/// Number of elements in the registers array.
|
|
const SIZE: usize;
|
|
/// Number of bytes between the start of elements in the registers array.
|
|
const STRIDE: usize;
|
|
}
|
|
|
|
/// Location of an array register.
|
|
pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
|
|
|
|
impl<T: RegisterArray> RegisterArrayLoc<T> {
|
|
/// Returns the location of register `T` at position `idx`, with build-time validation.
|
|
#[inline(always)]
|
|
pub fn new(idx: usize) -> Self {
|
|
build_assert!(idx < T::SIZE);
|
|
|
|
Self(idx, PhantomData)
|
|
}
|
|
|
|
/// Attempts to return the location of register `T` at position `idx`, with runtime validation.
|
|
#[inline(always)]
|
|
pub fn try_new(idx: usize) -> Option<Self> {
|
|
if idx < T::SIZE {
|
|
Some(Self(idx, PhantomData))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> IoLoc<T> for RegisterArrayLoc<T>
|
|
where
|
|
T: RegisterArray,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
T::OFFSET + self.0 * T::STRIDE
|
|
}
|
|
}
|
|
|
|
/// Trait providing location builders for [`RegisterArray`]s.
|
|
pub trait Array {
|
|
/// Returns the location of the register at position `idx`, with build-time validation.
|
|
#[inline(always)]
|
|
fn at(idx: usize) -> RegisterArrayLoc<Self>
|
|
where
|
|
Self: RegisterArray,
|
|
{
|
|
RegisterArrayLoc::new(idx)
|
|
}
|
|
|
|
/// Returns the location of the register at position `idx`, with runtime validation.
|
|
#[inline(always)]
|
|
fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
|
|
where
|
|
Self: RegisterArray,
|
|
{
|
|
RegisterArrayLoc::try_new(idx)
|
|
}
|
|
}
|
|
|
|
/// Trait implemented by arrays of relative registers.
|
|
pub trait RelativeRegisterArray: RegisterArray + WithBase {}
|
|
|
|
/// Location of a relative array register.
|
|
pub struct RelativeRegisterArrayLoc<
|
|
T: RelativeRegisterArray,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
>(RelativeRegisterLoc<T, B>, usize);
|
|
|
|
impl<T, B> RelativeRegisterArrayLoc<T, B>
|
|
where
|
|
T: RelativeRegisterArray,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
{
|
|
/// Returns the location of register `T` from the base `B` at index `idx`, with build-time
|
|
/// validation.
|
|
#[inline(always)]
|
|
pub fn new(idx: usize) -> Self {
|
|
build_assert!(idx < T::SIZE);
|
|
|
|
Self(RelativeRegisterLoc::new(), idx)
|
|
}
|
|
|
|
/// Attempts to return the location of register `T` from the base `B` at index `idx`, with
|
|
/// runtime validation.
|
|
#[inline(always)]
|
|
pub fn try_new(idx: usize) -> Option<Self> {
|
|
if idx < T::SIZE {
|
|
Some(Self(RelativeRegisterLoc::new(), idx))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].
|
|
impl<T, B> RelativeRegisterLoc<T, B>
|
|
where
|
|
T: RelativeRegisterArray,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
{
|
|
/// Returns the location of the register at position `idx`, with build-time validation.
|
|
#[inline(always)]
|
|
pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
|
|
RelativeRegisterArrayLoc::new(idx)
|
|
}
|
|
|
|
/// Returns the location of the register at position `idx`, with runtime validation.
|
|
#[inline(always)]
|
|
pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
|
|
RelativeRegisterArrayLoc::try_new(idx)
|
|
}
|
|
}
|
|
|
|
impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
|
|
where
|
|
T: RelativeRegisterArray,
|
|
B: RegisterBase<T::BaseFamily> + ?Sized,
|
|
{
|
|
type IoType = T::Storage;
|
|
|
|
#[inline(always)]
|
|
fn offset(self) -> usize {
|
|
self.0.offset() + self.1 * T::STRIDE
|
|
}
|
|
}
|
|
|
|
/// Trait implemented by items that contain both a register value and the absolute I/O location at
|
|
/// which to write it.
|
|
///
|
|
/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
|
|
pub trait LocatedRegister {
|
|
/// Register value to write.
|
|
type Value: Register;
|
|
/// Full location information at which to write the value.
|
|
type Location: IoLoc<Self::Value>;
|
|
|
|
/// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
|
|
/// operation.
|
|
fn into_io_op(self) -> (Self::Location, Self::Value);
|
|
}
|
|
|
|
impl<T> LocatedRegister for T
|
|
where
|
|
T: FixedRegister,
|
|
{
|
|
type Location = FixedRegisterLoc<Self::Value>;
|
|
type Value = T;
|
|
|
|
#[inline(always)]
|
|
fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
|
|
(FixedRegisterLoc::new(), self)
|
|
}
|
|
}
|
|
|
|
/// Defines a dedicated type for a register, including getter and setter methods for its fields and
|
|
/// methods to read and write it from an [`Io`](kernel::io::Io) region.
|
|
///
|
|
/// This documentation focuses on how to declare registers. See the [module-level
|
|
/// documentation](mod@kernel::io::register) for examples of how to access them.
|
|
///
|
|
/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of
|
|
/// registers, and relative arrays of registers.
|
|
///
|
|
/// ## Fixed offset registers
|
|
///
|
|
/// These are the simplest kind of registers. Their location is simply an offset inside the I/O
|
|
/// region. For instance:
|
|
///
|
|
/// ```ignore
|
|
/// register! {
|
|
/// pub FIXED_REG(u16) @ 0x80 {
|
|
/// ...
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
|
|
///
|
|
/// These registers' location can be built simply by referencing their name:
|
|
///
|
|
/// ```no_run
|
|
/// use kernel::{
|
|
/// io::{
|
|
/// register,
|
|
/// Io,
|
|
/// },
|
|
/// };
|
|
/// # use kernel::io::Mmio;
|
|
///
|
|
/// register! {
|
|
/// FIXED_REG(u32) @ 0x100 {
|
|
/// 16:8 high_byte;
|
|
/// 7:0 low_byte;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test(io: &Mmio<0x1000>) {
|
|
/// let val = io.read(FIXED_REG);
|
|
///
|
|
/// // Write from an already-existing value.
|
|
/// io.write(FIXED_REG, val.with_low_byte(0xff));
|
|
///
|
|
/// // Create a register value from scratch.
|
|
/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
|
|
///
|
|
/// // The location of fixed offset registers is already contained in their type. Thus, the
|
|
/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
|
|
/// io.write((), val2);
|
|
///
|
|
/// // Or, the single-argument `Io::write_reg` can be used.
|
|
/// io.write_reg(val2);
|
|
/// # }
|
|
///
|
|
/// ```
|
|
///
|
|
/// It is possible to create an alias of an existing register with new field definitions by using
|
|
/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
|
|
/// the context:
|
|
///
|
|
/// ```no_run
|
|
/// use kernel::io::register;
|
|
///
|
|
/// register! {
|
|
/// /// Scratch register.
|
|
/// pub SCRATCH(u32) @ 0x00000200 {
|
|
/// 31:0 value;
|
|
/// }
|
|
///
|
|
/// /// Boot status of the firmware.
|
|
/// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
|
|
/// 0:0 completed;
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
|
|
/// its own `completed` field.
|
|
///
|
|
/// ## Relative registers
|
|
///
|
|
/// Relative registers can be instantiated several times at a relative offset of a group of bases.
|
|
/// For instance, imagine the following I/O space:
|
|
///
|
|
/// ```text
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// | |
|
|
/// 0x100--->+------------CPU0-------------+
|
|
/// | |
|
|
/// 0x110--->+-----------------------------+
|
|
/// | CPU_CTL |
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// | |
|
|
/// | |
|
|
/// 0x200--->+------------CPU1-------------+
|
|
/// | |
|
|
/// 0x210--->+-----------------------------+
|
|
/// | CPU_CTL |
|
|
/// +-----------------------------+
|
|
/// | ... |
|
|
/// +-----------------------------+
|
|
/// ```
|
|
///
|
|
/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
|
|
/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
|
|
/// them twice and would prefer a way to select which one to use from a single definition.
|
|
///
|
|
/// This can be done using the `Base + Offset` syntax when specifying the register's address:
|
|
///
|
|
/// ```ignore
|
|
/// register! {
|
|
/// pub RELATIVE_REG(u32) @ Base + 0x80 {
|
|
/// ...
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// This creates a register with an offset of `0x80` from a given base.
|
|
///
|
|
/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
|
|
/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
|
|
/// this register needs to implement `RegisterBase<Base>`.
|
|
///
|
|
/// The location of relative registers can be built using the [`WithBase::of`] method to specify
|
|
/// its base. All relative registers implement [`WithBase`].
|
|
///
|
|
/// Here is the above layout translated into code:
|
|
///
|
|
/// ```no_run
|
|
/// use kernel::{
|
|
/// io::{
|
|
/// register,
|
|
/// register::{
|
|
/// RegisterBase,
|
|
/// WithBase,
|
|
/// },
|
|
/// Io,
|
|
/// },
|
|
/// };
|
|
/// # use kernel::io::Mmio;
|
|
///
|
|
/// // Type used to identify the base.
|
|
/// pub struct CpuCtlBase;
|
|
///
|
|
/// // ZST describing `CPU0`.
|
|
/// struct Cpu0;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
|
|
/// const BASE: usize = 0x100;
|
|
/// }
|
|
///
|
|
/// // ZST describing `CPU1`.
|
|
/// struct Cpu1;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
|
|
/// const BASE: usize = 0x200;
|
|
/// }
|
|
///
|
|
/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
|
|
/// register! {
|
|
/// /// CPU core control.
|
|
/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
|
|
/// 0:0 start;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test(io: Mmio<0x1000>) {
|
|
/// // Read the status of `Cpu0`.
|
|
/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
|
|
///
|
|
/// // Stop `Cpu0`.
|
|
/// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());
|
|
/// # }
|
|
///
|
|
/// // Aliases can also be defined for relative register.
|
|
/// register! {
|
|
/// /// Alias to CPU core control.
|
|
/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
|
|
/// /// Start the aliased CPU core.
|
|
/// 1:1 alias_start;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test2(io: Mmio<0x1000>) {
|
|
/// // Start the aliased `CPU0`, leaving its other fields untouched.
|
|
/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// ## Arrays of registers
|
|
///
|
|
/// Some I/O areas contain consecutive registers that share the same field layout. These areas can
|
|
/// be defined as an array of identical registers, allowing them to be accessed by index with
|
|
/// compile-time or runtime bound checking:
|
|
///
|
|
/// ```ignore
|
|
/// register! {
|
|
/// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
|
|
/// ...
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
|
|
/// register is separated from its neighbor by 4 bytes.
|
|
///
|
|
/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
|
|
/// each other.
|
|
///
|
|
/// A location for a register in a register array is built using the [`Array::at`] trait method.
|
|
/// All arrays of registers implement [`Array`].
|
|
///
|
|
/// ```no_run
|
|
/// use kernel::{
|
|
/// io::{
|
|
/// register,
|
|
/// register::Array,
|
|
/// Io,
|
|
/// },
|
|
/// };
|
|
/// # use kernel::io::Mmio;
|
|
/// # fn get_scratch_idx() -> usize {
|
|
/// # 0x15
|
|
/// # }
|
|
///
|
|
/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
|
|
/// register! {
|
|
/// /// Scratch registers.
|
|
/// pub SCRATCH(u32)[64] @ 0x00000080 {
|
|
/// 31:0 value;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test(io: &Mmio<0x1000>)
|
|
/// # -> Result<(), Error>{
|
|
/// // Read scratch register 0, i.e. I/O address `0x80`.
|
|
/// let scratch_0 = io.read(SCRATCH::at(0)).value();
|
|
///
|
|
/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
|
|
/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
|
|
///
|
|
/// // This is out of bounds and won't build.
|
|
/// // let scratch_128 = io.read(SCRATCH::at(128)).value();
|
|
///
|
|
/// // Runtime-obtained array index.
|
|
/// let idx = get_scratch_idx();
|
|
/// // Access on a runtime index returns an error if it is out-of-bounds.
|
|
/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
|
|
///
|
|
/// // Alias to a specific register in an array.
|
|
/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
|
|
/// register! {
|
|
/// /// Firmware exit status code.
|
|
/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
|
|
/// 7:0 status;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let status = io.read(FIRMWARE_STATUS).status();
|
|
///
|
|
/// // Non-contiguous register arrays can be defined by adding a stride parameter.
|
|
/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
|
|
/// // registers of the two declarations below are interleaved.
|
|
/// register! {
|
|
/// /// Scratch registers bank 0.
|
|
/// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
|
|
/// 31:0 value;
|
|
/// }
|
|
///
|
|
/// /// Scratch registers bank 1.
|
|
/// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
|
|
/// 31:0 value;
|
|
/// }
|
|
/// }
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// ## Relative arrays of registers
|
|
///
|
|
/// Combining the two features described in the sections above, arrays of registers accessible from
|
|
/// a base can also be defined:
|
|
///
|
|
/// ```ignore
|
|
/// register! {
|
|
/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
|
|
/// ...
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Like relative registers, they implement the [`WithBase`] trait. However the return value of
|
|
/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the
|
|
/// [`at`](RelativeRegisterLoc::at) method.
|
|
///
|
|
/// ```no_run
|
|
/// use kernel::{
|
|
/// io::{
|
|
/// register,
|
|
/// register::{
|
|
/// RegisterBase,
|
|
/// WithBase,
|
|
/// },
|
|
/// Io,
|
|
/// },
|
|
/// };
|
|
/// # use kernel::io::Mmio;
|
|
/// # fn get_scratch_idx() -> usize {
|
|
/// # 0x15
|
|
/// # }
|
|
///
|
|
/// // Type used as parameter of `RegisterBase` to specify the base.
|
|
/// pub struct CpuCtlBase;
|
|
///
|
|
/// // ZST describing `CPU0`.
|
|
/// struct Cpu0;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu0 {
|
|
/// const BASE: usize = 0x100;
|
|
/// }
|
|
///
|
|
/// // ZST describing `CPU1`.
|
|
/// struct Cpu1;
|
|
/// impl RegisterBase<CpuCtlBase> for Cpu1 {
|
|
/// const BASE: usize = 0x200;
|
|
/// }
|
|
///
|
|
/// // 64 per-cpu scratch registers, arranged as a contiguous array.
|
|
/// register! {
|
|
/// /// Per-CPU scratch registers.
|
|
/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
|
|
/// 31:0 value;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> {
|
|
/// // Read scratch register 0 of CPU0.
|
|
/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
|
|
///
|
|
/// // Write the retrieved value into scratch register 15 of CPU1.
|
|
/// io.write(WithBase::of::<Cpu1>().at(15), scratch);
|
|
///
|
|
/// // This won't build.
|
|
/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();
|
|
///
|
|
/// // Runtime-obtained array index.
|
|
/// let scratch_idx = get_scratch_idx();
|
|
/// // Access on a runtime index returns an error if it is out-of-bounds.
|
|
/// let cpu0_scratch = io.read(
|
|
/// CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
|
|
/// ).value();
|
|
/// # Ok(())
|
|
/// # }
|
|
///
|
|
/// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
|
|
/// register! {
|
|
/// /// Per-CPU firmware exit status code.
|
|
/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
|
|
/// 7:0 status;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// // Non-contiguous relative register arrays can be defined by adding a stride parameter.
|
|
/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
|
|
/// // registers of the two declarations below are interleaved.
|
|
/// register! {
|
|
/// /// Scratch registers bank 0.
|
|
/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
|
|
/// 31:0 value;
|
|
/// }
|
|
///
|
|
/// /// Scratch registers bank 1.
|
|
/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
|
|
/// 31:0 value;
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> {
|
|
/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! register {
|
|
// Entry point for the macro, allowing multiple registers to be defined in one call.
|
|
// It matches all possible register declaration patterns to dispatch them to corresponding
|
|
// `@reg` rule that defines a single register.
|
|
(
|
|
$(
|
|
$(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
|
|
$([ $size:expr $(, stride = $stride:expr)? ])?
|
|
$(@ $($base:ident +)? $offset:literal)?
|
|
$(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
|
|
{ $($fields:tt)* }
|
|
)*
|
|
) => {
|
|
$(
|
|
$crate::register!(
|
|
@reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
|
|
$(@ $($base +)? $offset)?
|
|
$(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
|
|
{ $($fields)* }
|
|
);
|
|
)*
|
|
};
|
|
|
|
// All the rules below are private helpers.
|
|
|
|
// Creates a register at a fixed offset of the MMIO space.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(@io_base $name($storage) @ $offset);
|
|
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
|
|
};
|
|
|
|
// Creates an alias register of fixed offset register `alias` with its own fields.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(
|
|
@io_base $name($storage) @
|
|
<$alias as $crate::io::register::Register>::OFFSET
|
|
);
|
|
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
|
|
};
|
|
|
|
// Creates a register at a relative offset from a base address provider.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(@io_base $name($storage) @ $offset);
|
|
$crate::register!(@io_relative $vis $name($storage) @ $base);
|
|
};
|
|
|
|
// Creates an alias register of relative offset register `alias` with its own fields.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(
|
|
@io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET
|
|
);
|
|
$crate::register!(@io_relative $vis $name($storage) @ $base);
|
|
};
|
|
|
|
// Creates an array of registers at a fixed offset of the MMIO space.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
|
|
[ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
|
|
) => {
|
|
::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
|
|
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(@io_base $name($storage) @ $offset);
|
|
$crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]);
|
|
};
|
|
|
|
// Shortcut for contiguous array of registers (stride == size of element).
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(
|
|
$(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
|
|
@ $offset { $($fields)* }
|
|
);
|
|
};
|
|
|
|
// Creates an alias of register `idx` of array of registers `alias` with its own fields.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
|
|
{ $($fields:tt)* }
|
|
) => {
|
|
::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
|
|
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(
|
|
@io_base $name($storage) @
|
|
<$alias as $crate::io::register::Register>::OFFSET
|
|
+ $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
|
|
);
|
|
$crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
|
|
};
|
|
|
|
// Creates an array of registers at a relative offset from a base address provider.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
|
|
[ $size:expr, stride = $stride:expr ]
|
|
@ $base:ident + $offset:literal { $($fields:tt)* }
|
|
) => {
|
|
::kernel::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
|
|
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(@io_base $name($storage) @ $offset);
|
|
$crate::register!(
|
|
@io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset
|
|
);
|
|
};
|
|
|
|
// Shortcut for contiguous array of relative registers (stride == size of element).
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
|
|
@ $base:ident + $offset:literal { $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(
|
|
$(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
|
|
@ $base + $offset { $($fields)* }
|
|
);
|
|
};
|
|
|
|
// Creates an alias of register `idx` of relative array of registers `alias` with its own
|
|
// fields.
|
|
(
|
|
@reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
|
|
=> $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
|
|
) => {
|
|
::kernel::static_assert!($idx < <$alias as $crate::io::register::RegisterArray>::SIZE);
|
|
|
|
$crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
|
|
$crate::register!(
|
|
@io_base $name($storage) @
|
|
<$alias as $crate::io::register::Register>::OFFSET +
|
|
$idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
|
|
);
|
|
$crate::register!(@io_relative $vis $name($storage) @ $base);
|
|
};
|
|
|
|
// Generates the bitfield for the register.
|
|
//
|
|
// `#[allow(non_camel_case_types)]` is added since register names typically use
|
|
// `SCREAMING_CASE`.
|
|
(
|
|
@bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
|
|
) => {
|
|
$crate::register!(@bitfield_core
|
|
#[allow(non_camel_case_types)]
|
|
$(#[$attr])* $vis $name $storage
|
|
);
|
|
$crate::register!(@bitfield_fields $vis $name $storage { $($fields)* });
|
|
};
|
|
|
|
// Implementations shared by all registers types.
|
|
(@io_base $name:ident($storage:ty) @ $offset:expr) => {
|
|
impl $crate::io::register::Register for $name {
|
|
type Storage = $storage;
|
|
|
|
const OFFSET: usize = $offset;
|
|
}
|
|
};
|
|
|
|
// Implementations of fixed registers.
|
|
(@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => {
|
|
impl $crate::io::register::FixedRegister for $name {}
|
|
|
|
$(#[$attr])*
|
|
$vis const $name: $crate::io::register::FixedRegisterLoc<$name> =
|
|
$crate::io::register::FixedRegisterLoc::<$name>::new();
|
|
};
|
|
|
|
// Implementations of relative registers.
|
|
(@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => {
|
|
impl $crate::io::register::WithBase for $name {
|
|
type BaseFamily = $base;
|
|
}
|
|
|
|
impl $crate::io::register::RelativeRegister for $name {}
|
|
};
|
|
|
|
// Implementations of register arrays.
|
|
(@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => {
|
|
impl $crate::io::register::Array for $name {}
|
|
|
|
impl $crate::io::register::RegisterArray for $name {
|
|
const SIZE: usize = $size;
|
|
const STRIDE: usize = $stride;
|
|
}
|
|
};
|
|
|
|
// Implementations of relative array registers.
|
|
(
|
|
@io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]
|
|
@ $base:ident + $offset:literal
|
|
) => {
|
|
impl $crate::io::register::WithBase for $name {
|
|
type BaseFamily = $base;
|
|
}
|
|
|
|
impl $crate::io::register::RegisterArray for $name {
|
|
const SIZE: usize = $size;
|
|
const STRIDE: usize = $stride;
|
|
}
|
|
|
|
impl $crate::io::register::RelativeRegisterArray for $name {}
|
|
};
|
|
|
|
// Defines the wrapper `$name` type and its conversions from/to the storage type.
|
|
(@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
|
|
$(#[$attr])*
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
$vis struct $name {
|
|
inner: $storage,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
/// Creates a bitfield from a raw value.
|
|
#[inline(always)]
|
|
$vis const fn from_raw(value: $storage) -> Self {
|
|
Self{ inner: value }
|
|
}
|
|
|
|
/// Turns this bitfield into its raw value.
|
|
///
|
|
/// This is similar to the [`From`] implementation, but is shorter to invoke in
|
|
/// most cases.
|
|
#[inline(always)]
|
|
$vis const fn into_raw(self) -> $storage {
|
|
self.inner
|
|
}
|
|
}
|
|
|
|
// SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
|
|
unsafe impl ::pin_init::Zeroable for $name {}
|
|
|
|
impl ::core::convert::From<$name> for $storage {
|
|
#[inline(always)]
|
|
fn from(val: $name) -> $storage {
|
|
val.into_raw()
|
|
}
|
|
}
|
|
|
|
impl ::core::convert::From<$storage> for $name {
|
|
#[inline(always)]
|
|
fn from(val: $storage) -> $name {
|
|
Self::from_raw(val)
|
|
}
|
|
}
|
|
};
|
|
|
|
// Definitions requiring knowledge of individual fields: private and public field accessors,
|
|
// and `Debug` implementation.
|
|
(@bitfield_fields $vis:vis $name:ident $storage:ty {
|
|
$($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
|
|
$(?=> $try_into_type:ty)?
|
|
$(=> $into_type:ty)?
|
|
;
|
|
)*
|
|
}
|
|
) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
$(
|
|
$crate::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
|
|
$crate::register!(
|
|
@public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
|
|
$(?=> $try_into_type)?
|
|
$(=> $into_type)?
|
|
);
|
|
)*
|
|
}
|
|
|
|
$crate::register!(@debug $name { $($field;)* });
|
|
};
|
|
|
|
// Private field accessors working with the exact `Bounded` type for the field.
|
|
(
|
|
@private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
$vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
|
|
$vis const [<$field:upper _MASK>]: $storage =
|
|
((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
|
|
$vis const [<$field:upper _SHIFT>]: u32 = $lo;
|
|
);
|
|
|
|
::kernel::macros::paste!(
|
|
fn [<__ $field>](self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
|
|
// Left shift to align the field's MSB with the storage MSB.
|
|
const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
|
|
// Right shift to move the top-aligned field to bit 0 of the storage.
|
|
const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
|
|
|
|
// Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
|
|
// output type.
|
|
let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
|
|
self.inner << ALIGN_TOP
|
|
);
|
|
val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
|
|
}
|
|
|
|
const fn [<__with_ $field>](
|
|
mut self,
|
|
value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
|
|
) -> Self
|
|
{
|
|
const MASK: $storage = <$name>::[<$field:upper _MASK>];
|
|
const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>];
|
|
|
|
let value = value.get() << SHIFT;
|
|
self.inner = (self.inner & !MASK) | value;
|
|
|
|
self
|
|
}
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields infallibly (`=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:literal:$lo:literal $field:ident => $into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) -> $into_type
|
|
{
|
|
self.[<__ $field>]().into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields fallibly (`?=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
Result<
|
|
$try_into_type,
|
|
<$try_into_type as ::core::convert::TryFrom<
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
>>::Error
|
|
>
|
|
{
|
|
self.[<__ $field>]().try_into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $try_into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields not converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
{
|
|
self.[<__ $field>]()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the compile-time constant `VALUE`."]
|
|
#[inline(always)]
|
|
$vis const fn [<with_const_ $field>]<const VALUE: $storage>(self) -> Self {
|
|
self.[<__with_ $field>](
|
|
::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
|
|
)
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> Self
|
|
where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
|
|
#[inline(always)]
|
|
$vis fn [<try_with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> ::kernel::error::Result<Self>
|
|
where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
|
|
{
|
|
Ok(
|
|
self.[<__with_ $field>](
|
|
value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
|
|
)
|
|
)
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// `Debug` implementation.
|
|
(@debug $name:ident { $($field:ident;)* }) => {
|
|
impl ::kernel::fmt::Debug for $name {
|
|
fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
|
|
f.debug_struct(stringify!($name))
|
|
.field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.inner))
|
|
$(
|
|
.field(stringify!($field), &self.$field())
|
|
)*
|
|
.finish()
|
|
}
|
|
}
|
|
};
|
|
}
|