| // Licensed under the Apache-2.0 license |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| //! AST10x0 SPI monitor (SPIPF) low-level register access. |
| //! |
| |
| use ast1060_pac as device; |
| use core::cell::UnsafeCell; |
| use core::marker::PhantomData; |
| |
| /// SPI monitor controller identifier. |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub enum SpiMonitorController { |
| Spim0 = 0, |
| Spim1 = 1, |
| Spim2 = 2, |
| Spim3 = 3, |
| } |
| |
| impl SpiMonitorController { |
| /// Returns the SPIPF register block base address for the controller. |
| #[must_use] |
| pub const fn base_address(self) -> usize { |
| match self { |
| Self::Spim0 => SPIPF1_BASE, |
| Self::Spim1 => SPIPF2_BASE, |
| Self::Spim2 => SPIPF3_BASE, |
| Self::Spim3 => SPIPF4_BASE, |
| } |
| } |
| |
| /// Returns the PAC register block pointer for this controller. |
| #[must_use] |
| pub const fn ptr(self) -> *const device::spipf::RegisterBlock { |
| match self { |
| Self::Spim0 => device::Spipf::ptr(), |
| Self::Spim1 => device::Spipf1::ptr(), |
| Self::Spim2 => device::Spipf2::ptr(), |
| Self::Spim3 => device::Spipf3::ptr(), |
| } |
| } |
| } |
| |
| /// SPIPF1 base address (used by SPIM0). |
| pub const SPIPF1_BASE: usize = 0x7E79_1000; |
| /// SPIPF2 base address (used by SPIM1). |
| pub const SPIPF2_BASE: usize = 0x7E79_2000; |
| /// SPIPF3 base address (used by SPIM2). |
| pub const SPIPF3_BASE: usize = 0x7E79_3000; |
| /// SPIPF4 base address (used by SPIM3). |
| pub const SPIPF4_BASE: usize = 0x7E79_4000; |
| |
| /// Size of each SPIPF register block. |
| pub const SPIPF_REG_SIZE: usize = 0x1000; |
| |
| /// Spin iterations for the `sw_reset` pulse hold. Targets ~5 us at 200 MHz; |
| /// exact timing is uncritical for the reset to latch. |
| const SW_RESET_SPIN_COUNT: u32 = 5 * 200; |
| |
| /// Safe wrapper around a SPI monitor register block. |
| pub struct SpiMonitorRegisters { |
| base: *const device::spipf::RegisterBlock, |
| _not_sync: PhantomData<UnsafeCell<()>>, // Prevent Sync, allow Send. |
| } |
| |
| impl SpiMonitorRegisters { |
| /// Create a register accessor from a raw register block pointer. |
| /// |
| /// # Safety |
| /// Caller must ensure: |
| /// - `base` points to a valid SPIPF register block. |
| /// - Only one mutable owner accesses this hardware instance. |
| pub const unsafe fn new(base: *const device::spipf::RegisterBlock) -> Self { |
| Self { |
| base, |
| _not_sync: PhantomData, |
| } |
| } |
| |
| /// Create a register accessor for a specific SPI monitor controller. |
| /// |
| /// # Safety |
| /// Caller must ensure exclusive ownership of the selected controller. |
| pub const unsafe fn new_for_controller(controller: SpiMonitorController) -> Self { |
| // SAFETY: Caller upholds exclusive-access requirement. |
| unsafe { Self::new(controller.ptr()) } |
| } |
| |
| #[inline] |
| fn regs(&self) -> &device::spipf::RegisterBlock { |
| // SAFETY: Constructor ensures pointer validity and ownership discipline. |
| unsafe { &*self.base } |
| } |
| |
| /// SPIPF000: Common control register. |
| pub fn read_ctrl(&self) -> u32 { |
| self.regs().spipf000().read().bits() |
| } |
| |
| pub fn write_ctrl(&self, value: u32) { |
| self.regs().spipf000().write(|w| unsafe { w.bits(value) }); |
| } |
| |
| pub fn modify_ctrl<F>(&self, f: F) |
| where |
| F: FnOnce(&mut u32), |
| { |
| self.regs().spipf000().modify(|r, w| { |
| let mut bits = r.bits(); |
| f(&mut bits); |
| // SAFETY: Callback is responsible for valid register image. |
| unsafe { w.bits(bits) } |
| }); |
| } |
| |
| /// Enable or disable the monitor filter (SPIPF000 `enbl_filter_fn`, bit 2). |
| pub fn set_filter_enable(&self, enable: bool) { |
| self.regs() |
| .spipf000() |
| .modify(|_, w| w.enbl_filter_fn().bit(enable)); |
| } |
| |
| /// Enable or disable single-bit passthrough (SPIPF000 |
| /// `enbl_single_bit_passthrough`, bit 0); passthrough bypasses the filter. |
| pub fn set_single_passthrough(&self, enable: bool) { |
| self.regs() |
| .spipf000() |
| .modify(|_, w| w.enbl_single_bit_passthrough().bit(enable)); |
| } |
| |
| /// Software-reset the monitor: pulse SPIPF000 `sweng_rst` (bit 15) high then |
| /// low. Clears status and logs while preserving the programmed policy. |
| /// `sweng_rst` is not self-clearing, so it is driven low explicitly after a |
| /// brief hold. |
| pub fn sw_reset(&self) { |
| self.regs() |
| .spipf000() |
| .modify(|_, w| w.sweng_rst().bit(true)); |
| for _ in 0..SW_RESET_SPIN_COUNT { |
| core::hint::spin_loop(); |
| } |
| self.regs() |
| .spipf000() |
| .modify(|_, w| w.sweng_rst().bit(false)); |
| } |
| |
| /// SPIPF004: Secondary control/config register. |
| pub fn read_ctrl2(&self) -> u32 { |
| self.regs().spipf004().read().bits() |
| } |
| |
| pub fn write_ctrl2(&self, value: u32) { |
| self.regs().spipf004().write(|w| unsafe { w.bits(value) }); |
| } |
| |
| /// SPIPF07C: Lock/status register. |
| pub fn read_lock_status(&self) -> u32 { |
| self.regs().spipf07c().read().bits() |
| } |
| |
| pub fn write_lock_status(&self, value: u32) { |
| self.regs().spipf07c().write(|w| unsafe { w.bits(value) }); |
| } |
| |
| /// SPIPFWT[n]: Allow-command table entry. |
| pub fn read_allow_cmd_slot(&self, index: usize) -> u32 { |
| self.regs().spipfwt(index).read().bits() |
| } |
| |
| pub fn write_allow_cmd_slot(&self, index: usize, value: u32) { |
| self.regs() |
| .spipfwt(index) |
| .write(|w| unsafe { w.bits(value) }); |
| } |
| |
| /// SPIPFWA[n]: Address filter table entry. |
| pub fn read_addr_filter_slot(&self, index: usize) -> u32 { |
| self.regs().spipfwa(index).read().bits() |
| } |
| |
| pub fn write_addr_filter_slot(&self, index: usize, value: u32) { |
| self.regs() |
| .spipfwa(index) |
| .write(|w| unsafe { w.bits(value) }); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Violation log registers |
| // |
| // TODO: confirm SPIPF register offsets for log control from the AST10x0 |
| // datasheet once available. Offsets below are placeholders consistent with |
| // known SPIPF register map patterns. |
| // ----------------------------------------------------------------------- |
| |
| /// Current violation log write index (number of entries written so far). |
| /// |
| /// Maps to the SPIPF log index register (placeholder offset 0x080). |
| pub fn read_log_idx_reg(&self) -> u32 { |
| // SAFETY: raw offset read within the known SPIPF register block page. |
| unsafe { |
| let ptr = (self.base as *const u8).add(0x080) as *const u32; |
| core::ptr::read_volatile(ptr) |
| } |
| } |
| |
| /// Maximum violation log capacity in bytes. |
| /// |
| /// Maps to the SPIPF log size register (placeholder offset 0x084). |
| pub fn read_log_max_sz(&self) -> u32 { |
| // SAFETY: same as above. |
| unsafe { |
| let ptr = (self.base as *const u8).add(0x084) as *const u32; |
| core::ptr::read_volatile(ptr) |
| } |
| } |
| |
| /// Base address of the violation log RAM region. |
| /// |
| /// Returns a `usize` suitable for casting to `*const u32` by the caller. |
| /// Maps to the SPIPF log RAM address register (placeholder offset 0x088). |
| pub fn log_ram_base_addr(&self) -> usize { |
| // SAFETY: same as above. |
| unsafe { |
| let ptr = (self.base as *const u8).add(0x088) as *const u32; |
| core::ptr::read_volatile(ptr) as usize |
| } |
| } |
| } |