use core::marker::PhantomData;
use crate::io::IoLoc;
use kernel::build_assert;
pub trait Register: Sized {
type Storage: Into<Self> + From<Self>;
const OFFSET: usize;
}
pub trait FixedRegister: Register {}
impl<T> IoLoc<T> for ()
where
T: FixedRegister,
{
type IoType = T::Storage;
#[inline(always)]
fn offset(self) -> usize {
T::OFFSET
}
}
impl<T> IoLoc<T> for T
where
T: FixedRegister,
{
type IoType = T::Storage;
#[inline(always)]
fn offset(self) -> usize {
T::OFFSET
}
}
pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
impl<T: FixedRegister> FixedRegisterLoc<T> {
#[inline(always)]
#[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
}
}
pub trait RegisterBase<T> {
const BASE: usize;
}
pub trait WithBase {
type BaseFamily;
#[inline(always)]
fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
where
Self: Register,
{
RelativeRegisterLoc::new()
}
}
pub trait RelativeRegister: Register + WithBase {}
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,
{
#[inline(always)]
#[expect(clippy::new_without_default)]
pub const fn new() -> Self {
Self(PhantomData, PhantomData)
}
#[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)
}
}
pub trait RegisterArray: Register {
const SIZE: usize;
const STRIDE: usize;
}
pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
impl<T: RegisterArray> RegisterArrayLoc<T> {
#[inline(always)]
pub fn new(idx: usize) -> Self {
build_assert!(idx < T::SIZE);
Self(idx, PhantomData)
}
#[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
}
}
pub trait Array {
#[inline(always)]
fn at(idx: usize) -> RegisterArrayLoc<Self>
where
Self: RegisterArray,
{
RegisterArrayLoc::new(idx)
}
#[inline(always)]
fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
where
Self: RegisterArray,
{
RegisterArrayLoc::try_new(idx)
}
}
pub trait RelativeRegisterArray: RegisterArray + WithBase {}
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,
{
#[inline(always)]
pub fn new(idx: usize) -> Self {
build_assert!(idx < T::SIZE);
Self(RelativeRegisterLoc::new(), idx)
}
#[inline(always)]
pub fn try_new(idx: usize) -> Option<Self> {
if idx < T::SIZE {
Some(Self(RelativeRegisterLoc::new(), idx))
} else {
None
}
}
}
impl<T, B> RelativeRegisterLoc<T, B>
where
T: RelativeRegisterArray,
B: RegisterBase<T::BaseFamily> + ?Sized,
{
#[inline(always)]
pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
RelativeRegisterArrayLoc::new(idx)
}
#[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
}
}
pub trait LocatedRegister {
type Value: Register;
type Location: IoLoc<Self::Value>;
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)
}
}
#[macro_export]
macro_rules! 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)* }
);
)*
};
(
@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));
};
(
@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));
};
(
@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);
};
(
@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);
};
(
@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 ]);
};
(
@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)* }
);
};
(
@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));
};
(
@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
);
};
(
@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)* }
);
};
(
@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);
};
(
@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)* });
};
(@io_base $name:ident($storage:ty) @ $offset:expr) => {
impl $crate::io::register::Register for $name {
type Storage = $storage;
const OFFSET: usize = $offset;
}
};
(@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();
};
(@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 {}
};
(@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;
}
};
(
@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 {}
};
(@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 {
#[inline(always)]
$vis const fn from_raw(value: $storage) -> Self {
Self{ inner: value }
}
#[inline(always)]
$vis const fn into_raw(self) -> $storage {
self.inner
}
}
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)
}
}
};
(@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 $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 }> {
const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
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_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_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_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 $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()
}
}
};
}