#![no_std]
pub mod riscv;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr;
use zerocopy::{FromBytes, IntoBytes};
pub use regio_macro::offset;
pub trait IoBackend {
type Base;
type Addr;
fn read_at(&self, addr: Self::Addr) -> Self::Base;
fn write_at(&self, value: Self::Base, addr: Self::Addr);
}
pub trait AtomicIoBackend: IoBackend {
fn atomic_swap_at(&self, value: Self::Base, addr: Self::Addr) -> Self::Base;
fn atomic_set_bits_at(&self, bits: Self::Base, addr: Self::Addr) -> Self::Base;
fn atomic_clear_bits_at(&self, bits: Self::Base, addr: Self::Addr) -> Self::Base;
}
pub trait Readable {}
pub trait Writable {}
pub trait FixedAddr: Register {
const ADDR: Self::Addr;
}
pub trait UnfixedAddr: Register {}
pub trait DefaultIo: Register {
type Io: Default + IoBackend<Base = Self::Base, Addr = Self::Addr>;
}
pub trait Register: From<Self::Base> + Deref<Target = Self::Base> {
type Base: Copy;
type Addr: Copy;
#[inline]
fn read_from_at<Io>(io: &Io, addr: Self::Addr) -> Self
where
Self: Readable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
Self::from(io.read_at(addr))
}
#[inline]
fn read_from<Io>(io: &Io) -> Self
where
Self: Readable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
Self::from(io.read_at(Self::ADDR))
}
#[inline]
fn read_at(addr: Self::Addr) -> Self
where
Self: Readable + UnfixedAddr + DefaultIo,
{
Self::from(Self::Io::default().read_at(addr))
}
#[inline]
fn read() -> Self
where
Self: Readable + FixedAddr + DefaultIo,
{
Self::from(Self::Io::default().read_at(Self::ADDR))
}
#[inline]
fn write_to_at<Io>(self, io: &Io, addr: Self::Addr)
where
Self: Writable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
io.write_at(*self, addr)
}
#[inline]
fn write_to<Io>(self, io: &Io)
where
Self: Writable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
{
io.write_at(*self, Self::ADDR)
}
#[inline]
fn write_at(self, addr: Self::Addr)
where
Self: Writable + UnfixedAddr + DefaultIo,
{
Self::Io::default().write_at(*self, addr)
}
#[inline]
fn write(self)
where
Self: Writable + FixedAddr + DefaultIo,
{
Self::Io::default().write_at(*self, Self::ADDR)
}
#[inline]
fn modify_with_at<Io, F>(io: &Io, mutate: F, addr: Self::Addr)
where
Self: Readable + Writable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
F: FnOnce(&mut Self),
{
let mut value = Self::read_from_at(io, addr);
mutate(&mut value);
value.write_to_at(io, addr)
}
#[inline]
fn modify_with<Io, F>(io: &Io, mutate: F)
where
Self: Readable + Writable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr>,
F: FnOnce(&mut Self),
{
let mut value = Self::read_from(io);
mutate(&mut value);
value.write_to(io)
}
#[inline]
fn modify_at<F>(mutate: F, addr: Self::Addr)
where
Self: Readable + Writable + UnfixedAddr + DefaultIo,
F: FnOnce(&mut Self),
{
let mut value = Self::read_at(addr);
mutate(&mut value);
value.write_at(addr)
}
#[inline]
fn modify<F>(mutate: F)
where
Self: Readable + Writable + FixedAddr + DefaultIo,
F: FnOnce(&mut Self),
{
let mut value = Self::read();
mutate(&mut value);
value.write()
}
#[inline]
fn atomic_swap_with_at<Io>(value: Self, io: &Io, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_swap_at(*value, addr))
}
#[inline]
fn atomic_swap_with<Io>(value: Self, io: &Io) -> Self
where
Self: Readable + Writable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_swap_at(*value, Self::ADDR))
}
#[inline]
fn atomic_swap_at(value: Self, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_swap_at(*value, addr))
}
#[inline]
fn atomic_swap(value: Self) -> Self
where
Self: Readable + Writable + FixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_swap_at(*value, Self::ADDR))
}
#[inline]
fn atomic_set_bits_with_at<Io>(bits: Self, io: &Io, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_set_bits_at(*bits, addr))
}
#[inline]
fn atomic_set_bits_with<Io>(bits: Self, io: &Io) -> Self
where
Self: Readable + Writable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_set_bits_at(*bits, Self::ADDR))
}
#[inline]
fn atomic_set_bits_at(bits: Self, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_set_bits_at(*bits, addr))
}
#[inline]
fn atomic_set_bits(bits: Self) -> Self
where
Self: Readable + Writable + FixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_set_bits_at(*bits, Self::ADDR))
}
#[inline]
fn atomic_clear_bits_with_at<Io>(bits: Self, io: &Io, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_clear_bits_at(*bits, addr))
}
#[inline]
fn atomic_clear_bits_with<Io>(bits: Self, io: &Io) -> Self
where
Self: Readable + Writable + FixedAddr,
Io: IoBackend<Base = Self::Base, Addr = Self::Addr> + AtomicIoBackend,
{
Self::from(io.atomic_clear_bits_at(*bits, Self::ADDR))
}
#[inline]
fn atomic_clear_bits_at(bits: Self, addr: Self::Addr) -> Self
where
Self: Readable + Writable + UnfixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_clear_bits_at(*bits, addr))
}
#[inline]
fn atomic_clear_bits(bits: Self) -> Self
where
Self: Readable + Writable + FixedAddr + DefaultIo<Io: AtomicIoBackend>,
{
Self::from(Self::Io::default().atomic_clear_bits_at(*bits, Self::ADDR))
}
}
#[derive(Clone, Copy, Debug)]
pub struct Offset(pub usize);
impl Deref for Offset {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
mod internal {
pub trait FitsIn<Access> {
fn widen(self) -> Access;
fn truncate(access: Access) -> Self;
}
macro_rules! fits_in {
($narrow:ty, $wide:ty) => {
impl FitsIn<$wide> for $narrow {
fn widen(self) -> $wide {
self as $wide
}
fn truncate(wide: $wide) -> Self {
wide as Self
}
}
};
}
fits_in!(u8, u8);
fits_in!(u8, u16);
fits_in!(u8, u32);
fits_in!(u8, u64);
fits_in!(u16, u16);
fits_in!(u16, u32);
fits_in!(u16, u64);
fits_in!(u32, u32);
fits_in!(u32, u64);
fits_in!(u64, u64);
}
pub struct Mmio<Reg, Access = Reg>
where
Reg: internal::FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
base: *mut Access,
size: usize,
phantom: PhantomData<Reg>,
}
impl<Reg, Access> Mmio<Reg, Access>
where
Reg: internal::FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
pub fn new(base: usize, size: usize) -> Self {
Self {
base: ptr::without_provenance_mut(base),
size,
phantom: PhantomData {},
}
}
}
impl<Reg, Access> IoBackend for Mmio<Reg, Access>
where
Reg: internal::FitsIn<Access>,
Access: FromBytes + IntoBytes,
{
type Addr = Offset;
type Base = Reg;
fn read_at(&self, offset: Offset) -> Reg {
debug_assert!(*offset < self.size);
unsafe {
let ptr = self.base.add(*offset);
Reg::truncate(ptr::read_volatile(ptr))
}
}
fn write_at(&self, value: Reg, offset: Offset) {
debug_assert!(*offset < self.size);
unsafe {
let ptr = self.base.add(*offset);
ptr::write_volatile(ptr, value.widen())
}
}
}