use core::marker::PhantomData;
use core::ptr::NonNull;
#[derive(Debug)]
#[repr(transparent)]
pub struct MmioAddr<T> {
ptr: NonNull<T>,
_phantom: PhantomData<T>,
}
impl<T> MmioAddr<T> {
pub const unsafe fn new(addr: usize) -> Self {
debug_assert!(addr != 0, "MmioAddr::new called with zero address");
let ptr = unsafe { NonNull::new_unchecked(addr as *mut T) };
Self {
ptr,
_phantom: PhantomData,
}
}
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub fn raw(&self) -> usize {
self.ptr.as_ptr() as usize
}
}
unsafe impl<T> Send for MmioAddr<T> {}
unsafe impl<T> Sync for MmioAddr<T> {}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[repr(transparent)]
pub struct PhysAddr(u32);
pub const SDRAM_BANK2_BASE: u32 = 0xD000_0000;
pub const SDRAM_BANK_STRIDE: u32 = 0x0080_0000;
pub const SDRAM_BANK_COUNT: u8 = 4;
impl PhysAddr {
#[inline]
pub const fn new(addr: u32) -> Self {
Self(addr)
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
#[inline]
pub const fn offset(self, delta: u32) -> Self {
Self(self.0.saturating_add(delta))
}
#[inline]
pub const fn sdram_bank(self) -> Option<u8> {
let base = SDRAM_BANK2_BASE;
let stride = SDRAM_BANK_STRIDE;
let count = SDRAM_BANK_COUNT as u32;
if self.0 < base {
return None;
}
let offset = self.0 - base;
let bank = offset / stride;
if bank >= count {
return None;
}
Some(bank as u8)
}
#[inline]
pub unsafe fn as_mut_slice<'a>(self, len: usize) -> &'a mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.0 as *mut u8, len) }
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum AddrError {
Misaligned {
addr: u32,
required: usize,
},
InvalidAlignment(usize),
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(transparent)]
pub struct DmaAddr(u32);
impl DmaAddr {
#[inline]
pub fn from_phys(p: PhysAddr, align: usize) -> Result<Self, AddrError> {
if align == 0 || !align.is_power_of_two() {
return Err(AddrError::InvalidAlignment(align));
}
let addr = p.raw();
if (addr as usize) & (align - 1) != 0 {
return Err(AddrError::Misaligned {
addr,
required: align,
});
}
Ok(Self(addr))
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sdram_bank_zero_is_bank_zero() {
assert_eq!(PhysAddr::new(SDRAM_BANK2_BASE).sdram_bank(), Some(0));
}
#[test]
fn sdram_bank_one_is_bank_one() {
let a = SDRAM_BANK2_BASE + SDRAM_BANK_STRIDE;
assert_eq!(PhysAddr::new(a).sdram_bank(), Some(1));
}
#[test]
fn sdram_bank_offset_within_bank_still_counts() {
let a = SDRAM_BANK2_BASE + SDRAM_BANK_STRIDE + 0x1234;
assert_eq!(PhysAddr::new(a).sdram_bank(), Some(1));
}
#[test]
fn address_below_sdram_has_no_bank() {
assert_eq!(PhysAddr::new(0x2000_0000).sdram_bank(), None);
}
#[test]
fn address_above_sdram_array_has_no_bank() {
let a = SDRAM_BANK2_BASE + SDRAM_BANK_STRIDE * (SDRAM_BANK_COUNT as u32);
assert_eq!(PhysAddr::new(a).sdram_bank(), None);
}
#[test]
fn dma_addr_rejects_misaligned() {
let p = PhysAddr::new(SDRAM_BANK2_BASE + 2);
assert!(matches!(
DmaAddr::from_phys(p, 4),
Err(AddrError::Misaligned { required: 4, .. })
));
}
#[test]
fn dma_addr_accepts_aligned() {
let p = PhysAddr::new(SDRAM_BANK2_BASE);
let d = DmaAddr::from_phys(p, 4).unwrap();
assert_eq!(d.raw(), SDRAM_BANK2_BASE);
}
#[test]
fn dma_addr_rejects_non_power_of_two_alignment() {
let p = PhysAddr::new(SDRAM_BANK2_BASE);
assert!(matches!(
DmaAddr::from_phys(p, 3),
Err(AddrError::InvalidAlignment(3))
));
}
#[test]
fn phys_addr_offset_is_saturating() {
let p = PhysAddr::new(u32::MAX - 3);
assert_eq!(p.offset(10).raw(), u32::MAX);
}
#[test]
fn mmio_addr_round_trips_address() {
let m: MmioAddr<u32> = unsafe { MmioAddr::new(0x4002_1C00) };
assert_eq!(m.raw(), 0x4002_1C00);
assert_eq!(m.as_ptr() as usize, 0x4002_1C00);
}
}