1macro_rules! const_assert {
16 ($cond:expr, $msg:expr) => {
17 const _: () = {
18 #[allow(dead_code, clippy::unit_arg)]
19 const ASSERT: () = if !$cond {
20 panic!($msg)
21 };
22 };
23 };
24}
25
26#[allow(unused_macros)]
28macro_rules! ptr_aligned {
29 ($ptr:expr) => {
30 const_assert!($ptr as usize % 4 == 0, "MMIO pointer must be 4-byte aligned");
31 };
32}
33
34const MMIO_LOW: usize = 0x4000_0000;
38const MMIO_HIGH: usize = 0x5704_0000; const_assert!(
42 0x4401_0000 >= MMIO_LOW && 0x4401_2000 <= MMIO_HIGH,
43 "UART region (UART0@0x4401_0000..UART2@0x4401_2000) out of MMIO range"
44);
45const_assert!(0x4000_6000 >= MMIO_LOW && 0x4000_6100 <= MMIO_HIGH, "WDT region (0x4000_6000) out of MMIO range");
46const_assert!(0x4800_0000 >= MMIO_LOW && 0x4800_0100 <= MMIO_HIGH, "SFC base out of MMIO range");
47const_assert!(0x4A00_0000 >= MMIO_LOW && 0x4A00_0000 <= MMIO_HIGH, "DMA base out of MMIO range");
48const_assert!(0x4410_0000 >= MMIO_LOW && 0x4411_4000 <= MMIO_HIGH, "Crypto base out of MMIO range");
49
50use crate::clock::PERIPHERAL_COUNT;
52
53const_assert!(
56 crate::soc::ws63::SYSTEM_CLOCK_HZ == 240_000_000,
57 "SYSTEM_CLOCK_HZ must be 240MHz — timer tick calculations assume this"
58);
59const MAX_SAFE_TIMER_US: u64 = u32::MAX as u64 / 240;
61const_assert!(MAX_SAFE_TIMER_US > 17_000_000, "Timer max safe period must cover at least 17 seconds");
62
63#[derive(Debug, Clone, Copy)]
68pub struct PeripheralIndex(u8);
69
70impl PeripheralIndex {
71 #[allow(clippy::missing_safety_doc)]
73 pub const unsafe fn new_unchecked(idx: u8) -> Self {
74 PeripheralIndex(idx)
75 }
76
77 pub const fn get(&self) -> usize {
78 self.0 as usize
79 }
80}
81
82impl TryFrom<usize> for PeripheralIndex {
83 type Error = ();
84 fn try_from(idx: usize) -> Result<Self, ()> {
85 if idx < PERIPHERAL_COUNT { Ok(PeripheralIndex(idx as u8)) } else { Err(()) }
86 }
87}
88
89#[derive(Debug, Clone, Copy)]
91pub struct GpioPinIndex(#[allow(dead_code)] u8);
92
93impl GpioPinIndex {
94 pub const fn new(pin: u8) -> Option<Self> {
95 if pin < crate::soc::ws63::GPIO_COUNT as u8 { Some(GpioPinIndex(pin)) } else { None }
96 }
97}
98
99#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_peripheral_index_bounds() {
107 assert!(PeripheralIndex::try_from(0).is_ok());
108 assert!(PeripheralIndex::try_from(16).is_ok());
109 assert!(PeripheralIndex::try_from(17).is_err());
110 }
111
112 #[test]
113 fn test_gpio_pin_bounds() {
114 assert!(GpioPinIndex::new(0).is_some());
115 assert!(GpioPinIndex::new(18).is_some());
116 assert!(GpioPinIndex::new(19).is_none());
117 }
118
119 #[test]
120 fn test_soc_constants_consistency() {
121 use crate::soc::ws63;
122 assert_eq!(ws63::SYSTEM_CLOCK_HZ, 240_000_000);
123 assert_eq!(ws63::TIMER_COUNT, 3);
124 assert_eq!(ws63::PWM_CHANNEL_COUNT, 8);
125 assert_eq!(ws63::DMA_CHANNEL_COUNT, 4);
126 assert_eq!(ws63::SPI_COUNT, 2);
127 assert_eq!(ws63::UART_COUNT, 3);
128 assert_eq!(ws63::I2C_COUNT, 2);
129 assert_eq!(ws63::GPIO_COUNT, 19);
130 assert_eq!(ws63::ULP_GPIO_COUNT, 8);
131 assert_eq!(ws63::LSADC_CHANNEL_COUNT, 6);
132 }
133
134 #[test]
135 fn test_max_safe_timer_us() {
136 let max_safe: u64 = u32::MAX as u64 / 240;
138 assert!(max_safe > 17_000_000); let overflow: u64 = 240_000_000u64 * (max_safe as u64 + 1) / 1_000_000;
140 assert!(overflow > u32::MAX as u64); }
142
143 #[test]
144 fn test_pwm_channel_count_fits_u8() {
145 assert!(crate::soc::ws63::PWM_CHANNEL_COUNT <= 8);
147 }
148
149 #[test]
150 fn test_dma_channel_bound_check() {
151 assert!(crate::soc::ws63::DMA_CHANNEL_COUNT == 4);
153 for ch in 0u8..4 {
154 assert!(ch < crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
155 }
156 assert!(4u8 >= crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
157 }
158}