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!(crate::soc::ws63::SYSTEM_CLOCK_HZ == 240_000_000, "SYSTEM_CLOCK_HZ must be 240MHz (CPU/PLL clock)");
56const_assert!(
58 crate::soc::ws63::TIMER_CLOCK_HZ.is_multiple_of(1_000_000) && crate::soc::ws63::TIMER_CLOCK_HZ >= 1_000_000,
59 "TIMER_CLOCK_HZ must be a whole number of MHz so us->ticks is exact"
60);
61const MAX_SAFE_TIMER_US: u64 = u32::MAX as u64 / (crate::soc::ws63::TIMER_CLOCK_HZ as u64 / 1_000_000);
63const_assert!(MAX_SAFE_TIMER_US > 17_000_000, "Timer max safe period must cover at least 17 seconds");
64
65#[derive(Debug, Clone, Copy)]
70pub struct PeripheralIndex(u8);
71
72impl PeripheralIndex {
73 #[allow(clippy::missing_safety_doc)]
75 pub const unsafe fn new_unchecked(idx: u8) -> Self {
76 PeripheralIndex(idx)
77 }
78
79 pub const fn get(&self) -> usize {
80 self.0 as usize
81 }
82}
83
84impl TryFrom<usize> for PeripheralIndex {
85 type Error = ();
86 fn try_from(idx: usize) -> Result<Self, ()> {
87 if idx < PERIPHERAL_COUNT { Ok(PeripheralIndex(idx as u8)) } else { Err(()) }
88 }
89}
90
91#[derive(Debug, Clone, Copy)]
93pub struct GpioPinIndex(#[allow(dead_code)] u8);
94
95impl GpioPinIndex {
96 pub const fn new(pin: u8) -> Option<Self> {
97 if pin < crate::soc::ws63::GPIO_COUNT as u8 { Some(GpioPinIndex(pin)) } else { None }
98 }
99}
100
101#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_peripheral_index_bounds() {
109 assert!(PeripheralIndex::try_from(0).is_ok());
110 assert!(PeripheralIndex::try_from(16).is_ok());
111 assert!(PeripheralIndex::try_from(17).is_err());
112 }
113
114 #[test]
115 fn test_gpio_pin_bounds() {
116 assert!(GpioPinIndex::new(0).is_some());
117 assert!(GpioPinIndex::new(18).is_some());
118 assert!(GpioPinIndex::new(19).is_none());
119 }
120
121 #[test]
122 fn test_soc_constants_consistency() {
123 use crate::soc::ws63;
124 assert_eq!(ws63::SYSTEM_CLOCK_HZ, 240_000_000);
125 assert_eq!(ws63::TIMER_COUNT, 3);
126 assert_eq!(ws63::PWM_CHANNEL_COUNT, 8);
127 assert_eq!(ws63::DMA_CHANNEL_COUNT, 4);
128 assert_eq!(ws63::SPI_COUNT, 2);
129 assert_eq!(ws63::UART_COUNT, 3);
130 assert_eq!(ws63::I2C_COUNT, 2);
131 assert_eq!(ws63::GPIO_COUNT, 19);
132 assert_eq!(ws63::ULP_GPIO_COUNT, 8);
133 assert_eq!(ws63::LSADC_CHANNEL_COUNT, 6);
134 }
135
136 #[test]
137 fn test_max_safe_timer_us() {
138 let ticks_per_us = crate::soc::ws63::TIMER_CLOCK_HZ as u64 / 1_000_000;
141 let max_safe: u64 = u32::MAX as u64 / ticks_per_us;
142 assert!(max_safe > 17_000_000); let overflow: u64 = crate::soc::ws63::TIMER_CLOCK_HZ as u64 * (max_safe + 1) / 1_000_000;
144 assert!(overflow > u32::MAX as u64); }
146
147 #[test]
148 fn test_pwm_channel_count_fits_u8() {
149 assert!(crate::soc::ws63::PWM_CHANNEL_COUNT <= 8);
151 }
152
153 #[test]
154 fn test_dma_channel_bound_check() {
155 assert!(crate::soc::ws63::DMA_CHANNEL_COUNT == 4);
157 for ch in 0u8..4 {
158 assert!(ch < crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
159 }
160 assert!(4u8 >= crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
161 }
162}