use core::marker::PhantomData;
pub use crate::shared::{FifoClear, TriggerLevel};
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
pub const BASE_ADDR_0: usize = 0x4005_0000;
pub const BASE_ADDR_1: usize = 0x4005_1000;
pub const BASE_ADDR_2: usize = 0x4005_2000;
} else if #[cfg(feature = "vor4x")] {
pub const BASE_ADDR_0: usize = 0x4001_5000;
pub const BASE_ADDR_1: usize = 0x4001_5400;
pub const BASE_ADDR_2: usize = 0x4001_5800;
pub const BASE_ADDR_3: usize = 0x4001_5C00;
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
Spi0 = 0,
Spi1 = 1,
Spi2 = 2,
#[cfg(feature = "vor4x")]
Spi3 = 3,
}
impl Bank {
pub unsafe fn steal_regs(&self) -> MmioSpi<'static> {
Spi::new_mmio(*self)
}
}
#[bitbybit::bitenum(u4)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
EightBits = 0x07,
SixteenBits = 0x0f,
}
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u3, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
Id2 = 2,
Id3 = 3,
Id4 = 4,
Id5 = 5,
Id6 = 6,
Id7 = 7,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_fields(feature = "defmt"))]
pub struct Control0 {
#[bits(8..=15, rw)]
scrdv: u8,
#[bit(7, rw)]
sph: bool,
#[bit(6, rw)]
spo: bool,
#[bits(0..=3, rw)]
word_size: Option<WordSize>,
}
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
pub struct Control1 {
#[bit(11, rw)]
mtxpause: bool,
#[bit(10, rw)]
mdlycap: bool,
#[bit(9, rw)]
bm_stall: bool,
#[bit(8, rw)]
bm_start: bool,
#[bit(7, rw)]
blockmode: bool,
#[bits(4..=6, rw)]
ss: HwChipSelectId,
#[bit(3, rw)]
sod: bool,
#[bit(2, rw)]
slave_mode: bool,
#[bit(1, rw)]
enable: bool,
#[bit(0, rw)]
lbm: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Data {
#[bit(31, rw)]
bm_start_stop: bool,
#[bit(30, rw)]
bm_skipdata: bool,
#[bits(0..=15, rw)]
data: u16,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
pub struct Status {
#[bit(7, r)]
tx_trigger: bool,
#[bit(6, r)]
rx_trigger: bool,
#[bit(5, r)]
rx_data_first: bool,
#[bit(4, r)]
busy: bool,
#[bit(3, r)]
rx_full: bool,
#[bit(2, r)]
rx_not_empty: bool,
#[bit(1, r)]
tx_not_full: bool,
#[bit(0, r)]
tx_empty: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ClockPrescaler(arbitrary_int::UInt<u32, 8>);
impl ClockPrescaler {
pub const fn new(value: u8) -> Self {
ClockPrescaler(arbitrary_int::UInt::<u32, 8>::new(value as u32))
}
pub const fn value(&self) -> u8 {
self.0.value() as u8
}
}
#[bitbybit::bitfield(u32, debug, default = 0x0, defmt_bitfields(feature = "defmt"))]
pub struct InterruptControl {
#[bit(3, rw)]
tx: bool,
#[bit(2, rw)]
rx: bool,
#[bit(1, rw)]
rx_timeout: bool,
#[bit(0, rw)]
rx_overrun: bool,
}
impl InterruptControl {
pub const DISABLE_ALL: Self = Self::ZERO;
pub const ENABLE_ALL: Self = Self::builder()
.with_tx(true)
.with_rx(true)
.with_rx_timeout(true)
.with_rx_overrun(true)
.build();
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
pub struct InterruptStatus {
#[bit(3, r)]
tx: bool,
#[bit(2, r)]
rx: bool,
#[bit(1, r)]
rx_timeout: bool,
#[bit(0, r)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptClear {
#[bit(1, w)]
rx_timeout: bool,
#[bit(0, w)]
rx_overrun: bool,
}
impl InterruptClear {
pub const ALL: Self = Self::builder()
.with_rx_timeout(true)
.with_rx_overrun(true)
.build();
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
pub struct State {
#[bits(0..=7, r)]
rx_state: u8,
#[bits(8..=15, r)]
rx_fifo: u8,
#[bits(24..=31, r)]
tx_fifo: u8,
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct Spi {
ctrl0: Control0,
ctrl1: Control1,
data: Data,
#[mmio(PureRead)]
status: Status,
clkprescale: ClockPrescaler,
interrupt_control: InterruptControl,
#[mmio(PureRead)]
interrupt_status_raw: InterruptStatus,
#[mmio(PureRead)]
interrupt_status: InterruptStatus,
#[mmio(Write)]
interrupt_clear: InterruptClear,
rx_fifo_trigger: TriggerLevel,
tx_fifo_trigger: TriggerLevel,
#[mmio(Write)]
fifo_clear: FifoClear,
#[mmio(PureRead)]
state: State,
#[cfg(feature = "vor1x")]
_reserved: [u32; 0x3F2],
#[cfg(feature = "vor4x")]
_reserved: [u32; 0xF2],
#[mmio(PureRead)]
perid: u32,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x1000);
} else if #[cfg(feature = "vor4x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x400);
}
}
impl Spi {
fn new_mmio_at(base: usize) -> MmioSpi<'static> {
MmioSpi {
ptr: base as *mut _,
phantom: PhantomData,
}
}
pub fn new_mmio(bank: Bank) -> MmioSpi<'static> {
match bank {
Bank::Spi0 => Self::new_mmio_at(BASE_ADDR_0),
Bank::Spi1 => Self::new_mmio_at(BASE_ADDR_1),
Bank::Spi2 => Self::new_mmio_at(BASE_ADDR_2),
#[cfg(feature = "vor4x")]
Bank::Spi3 => Self::new_mmio_at(BASE_ADDR_2),
}
}
}