use core::marker::PhantomData; use crate::{InvalidOffsetError, NUM_PORT_A, NUM_PORT_B}; #[cfg(feature = "vor4x")] use crate::{NUM_PORT_DEFAULT, NUM_PORT_G}; #[cfg(feature = "vor1x")] pub const BASE_ADDR: usize = 0x4000_2000; #[cfg(feature = "vor4x")] pub const BASE_ADDR: usize = 0x4001_1000; #[bitbybit::bitenum(u3)] pub enum FilterType { SysClk = 0, DirectInput = 1, FilterOneCycle = 2, FilterTwoCycles = 3, FilterThreeCycles = 4, FilterFourCycles = 5, } #[derive(Debug, PartialEq, Eq)] #[bitbybit::bitenum(u3, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FilterClkSel { SysClk = 0, Clk1 = 1, Clk2 = 2, Clk3 = 3, Clk4 = 4, Clk5 = 5, Clk6 = 6, Clk7 = 7, } #[derive(Debug, PartialEq, Eq)] #[bitbybit::bitenum(u1, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Pull { Up = 0, Down = 1, } #[derive(Debug, Eq, PartialEq)] #[bitbybit::bitenum(u2, exhaustive = true)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FunSel { Sel0 = 0b00, Sel1 = 0b01, Sel2 = 0b10, Sel3 = 0b11, } #[bitbybit::bitfield(u32)] pub struct Config { #[bit(16, rw)] io_disable: bool, #[bits(13..=14, rw)] funsel: FunSel, #[bit(12, rw)] pull_when_output_active: bool, #[bit(11, rw)] pull_enable: bool, #[bit(10, rw)] pull_dir: Pull, #[bit(9, rw)] invert_output: bool, #[bit(8, rw)] open_drain: bool, /// IEWO bit. Allows monitoring of output values. #[bit(7, rw)] input_enable_when_output: bool, #[bit(6, rw)] invert_input: bool, #[bits(3..=5, rw)] filter_sel: FilterClkSel, #[bits(0..=2, rw)] filter_type: Option, } #[derive(derive_mmio::Mmio)] #[mmio(no_ctors)] #[repr(C)] pub struct IoConfig { port_a: [Config; NUM_PORT_A], port_b: [Config; NUM_PORT_B], #[cfg(feature = "vor4x")] port_c: [Config; NUM_PORT_DEFAULT], #[cfg(feature = "vor4x")] port_d: [Config; NUM_PORT_DEFAULT], #[cfg(feature = "vor4x")] port_e: [Config; NUM_PORT_DEFAULT], #[cfg(feature = "vor4x")] port_f: [Config; NUM_PORT_DEFAULT], #[cfg(feature = "vor4x")] port_g: [Config; NUM_PORT_G], #[cfg(feature = "vor4x")] _reserved0: [u32; 0x8], #[cfg(feature = "vor4x")] #[mmio(PureRead)] clk_div_0: u32, #[cfg(feature = "vor4x")] clk_div_1: u32, #[cfg(feature = "vor4x")] clk_div_2: u32, #[cfg(feature = "vor4x")] clk_div_3: u32, #[cfg(feature = "vor4x")] clk_div_4: u32, #[cfg(feature = "vor4x")] clk_div_5: u32, #[cfg(feature = "vor4x")] clk_div_6: u32, #[cfg(feature = "vor4x")] clk_div_7: u32, #[cfg(feature = "vor4x")] _reserved1: [u32; 0x387], #[cfg(feature = "vor1x")] _reserved1: [u32; 0x3c7], #[mmio(PureRead)] /// Reset value: 0x0282_07E9 for Vorago 4x, and 0x0182_07E1 for Vorago 1x perid: u32, } static_assertions::const_assert_eq!(core::mem::size_of::(), 0x1000); impl IoConfig { pub const fn new_mmio() -> MmioIoConfig<'static> { MmioIoConfig { ptr: BASE_ADDR as *mut _, phantom: PhantomData, } } } impl MmioIoConfig<'_> { pub fn read_pin_config( &self, port: crate::Port, offset: usize, ) -> Result { if offset >= port.max_offset() { return Err(InvalidOffsetError { port, offset }); } Ok(unsafe { self.read_pin_config_unchecked(port, offset) }) } /// This function does NOT perform any bounds checking. /// /// # Safety /// /// Calling this function with an invalid offset can lead to undefined behaviour. pub unsafe fn read_pin_config_unchecked(&self, port: crate::Port, offset: usize) -> Config { match port { crate::Port::A => unsafe { self.read_port_a_unchecked(offset) }, crate::Port::B => unsafe { self.read_port_b_unchecked(offset) }, #[cfg(feature = "vor4x")] crate::Port::C => unsafe { self.read_port_c_unchecked(offset) }, #[cfg(feature = "vor4x")] crate::Port::D => unsafe { self.read_port_d_unchecked(offset) }, #[cfg(feature = "vor4x")] crate::Port::E => unsafe { self.read_port_e_unchecked(offset) }, #[cfg(feature = "vor4x")] crate::Port::F => unsafe { self.read_port_f_unchecked(offset) }, #[cfg(feature = "vor4x")] crate::Port::G => unsafe { self.read_port_g_unchecked(offset) }, } } pub fn modify_pin_config Config>( &mut self, port: crate::Port, offset: usize, f: F, ) -> Result<(), InvalidOffsetError> { if offset >= port.max_offset() { return Err(InvalidOffsetError { port, offset }); } unsafe { self.modify_pin_config_unchecked(port, offset, f) }; Ok(()) } /// This function does NOT perform any bounds checking. /// /// # Safety /// /// Calling this function with an invalid offset can lead to undefined behaviour. pub unsafe fn modify_pin_config_unchecked Config>( &mut self, port: crate::Port, offset: usize, mut f: F, ) { unsafe { let config = self.read_pin_config_unchecked(port, offset); self.write_pin_config_unchecked(port, offset, f(config)) } } pub fn write_pin_config( &mut self, port: crate::Port, offset: usize, config: Config, ) -> Result<(), InvalidOffsetError> { if offset >= port.max_offset() { return Err(InvalidOffsetError { port, offset }); } unsafe { self.write_pin_config_unchecked(port, offset, config); } Ok(()) } /// This function does NOT perform any bounds checking. /// /// # Safety /// /// Calling this function with an invalid offset can lead to undefined behaviour. pub unsafe fn write_pin_config_unchecked( &mut self, port: crate::Port, offset: usize, config: Config, ) { match port { crate::Port::A => unsafe { self.write_port_a_unchecked(offset, config) }, crate::Port::B => unsafe { self.write_port_b_unchecked(offset, config) }, #[cfg(feature = "vor4x")] crate::Port::C => unsafe { self.write_port_c_unchecked(offset, config) }, #[cfg(feature = "vor4x")] crate::Port::D => unsafe { self.write_port_d_unchecked(offset, config) }, #[cfg(feature = "vor4x")] crate::Port::E => unsafe { self.write_port_e_unchecked(offset, config) }, #[cfg(feature = "vor4x")] crate::Port::F => unsafe { self.write_port_f_unchecked(offset, config) }, #[cfg(feature = "vor4x")] crate::Port::G => unsafe { self.write_port_g_unchecked(offset, config) }, } } }