macro_rules! const_assert {
($cond:expr, $msg:expr) => {
const _: () = {
#[allow(dead_code, clippy::unit_arg)]
const ASSERT: () = if !$cond {
panic!($msg)
};
};
};
}
#[allow(unused_macros)]
macro_rules! ptr_aligned {
($ptr:expr) => {
const_assert!($ptr as usize % 4 == 0, "MMIO pointer must be 4-byte aligned");
};
}
const MMIO_LOW: usize = 0x4000_0000;
const MMIO_HIGH: usize = 0x5704_0000;
const_assert!(
0x4401_0000 >= MMIO_LOW && 0x4401_2000 <= MMIO_HIGH,
"UART region (UART0@0x4401_0000..UART2@0x4401_2000) out of MMIO range"
);
const_assert!(0x4000_6000 >= MMIO_LOW && 0x4000_6100 <= MMIO_HIGH, "WDT region (0x4000_6000) out of MMIO range");
const_assert!(0x4800_0000 >= MMIO_LOW && 0x4800_0100 <= MMIO_HIGH, "SFC base out of MMIO range");
const_assert!(0x4A00_0000 >= MMIO_LOW && 0x4A00_0000 <= MMIO_HIGH, "DMA base out of MMIO range");
const_assert!(0x4410_0000 >= MMIO_LOW && 0x4411_4000 <= MMIO_HIGH, "Crypto base out of MMIO range");
use crate::clock::PERIPHERAL_COUNT;
const_assert!(crate::soc::ws63::SYSTEM_CLOCK_HZ == 240_000_000, "SYSTEM_CLOCK_HZ must be 240MHz (CPU/PLL clock)");
const_assert!(
crate::soc::ws63::TIMER_CLOCK_HZ.is_multiple_of(1_000_000) && crate::soc::ws63::TIMER_CLOCK_HZ >= 1_000_000,
"TIMER_CLOCK_HZ must be a whole number of MHz so us->ticks is exact"
);
const MAX_SAFE_TIMER_US: u64 = u32::MAX as u64 / (crate::soc::ws63::TIMER_CLOCK_HZ as u64 / 1_000_000);
const_assert!(MAX_SAFE_TIMER_US > 17_000_000, "Timer max safe period must cover at least 17 seconds");
#[derive(Debug, Clone, Copy)]
pub struct PeripheralIndex(u8);
impl PeripheralIndex {
#[allow(clippy::missing_safety_doc)]
pub const unsafe fn new_unchecked(idx: u8) -> Self {
PeripheralIndex(idx)
}
pub const fn get(&self) -> usize {
self.0 as usize
}
}
impl TryFrom<usize> for PeripheralIndex {
type Error = ();
fn try_from(idx: usize) -> Result<Self, ()> {
if idx < PERIPHERAL_COUNT { Ok(PeripheralIndex(idx as u8)) } else { Err(()) }
}
}
#[derive(Debug, Clone, Copy)]
pub struct GpioPinIndex(#[allow(dead_code)] u8);
impl GpioPinIndex {
pub const fn new(pin: u8) -> Option<Self> {
if pin < crate::soc::ws63::GPIO_COUNT as u8 { Some(GpioPinIndex(pin)) } else { None }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_peripheral_index_bounds() {
assert!(PeripheralIndex::try_from(0).is_ok());
assert!(PeripheralIndex::try_from(16).is_ok());
assert!(PeripheralIndex::try_from(17).is_err());
}
#[test]
fn test_gpio_pin_bounds() {
assert!(GpioPinIndex::new(0).is_some());
assert!(GpioPinIndex::new(18).is_some());
assert!(GpioPinIndex::new(19).is_none());
}
#[test]
fn test_soc_constants_consistency() {
use crate::soc::ws63;
assert_eq!(ws63::SYSTEM_CLOCK_HZ, 240_000_000);
assert_eq!(ws63::TIMER_COUNT, 3);
assert_eq!(ws63::PWM_CHANNEL_COUNT, 8);
assert_eq!(ws63::DMA_CHANNEL_COUNT, 4);
assert_eq!(ws63::SPI_COUNT, 2);
assert_eq!(ws63::UART_COUNT, 3);
assert_eq!(ws63::I2C_COUNT, 2);
assert_eq!(ws63::GPIO_COUNT, 19);
assert_eq!(ws63::ULP_GPIO_COUNT, 8);
assert_eq!(ws63::LSADC_CHANNEL_COUNT, 6);
}
#[test]
fn test_max_safe_timer_us() {
let ticks_per_us = crate::soc::ws63::TIMER_CLOCK_HZ as u64 / 1_000_000;
let max_safe: u64 = u32::MAX as u64 / ticks_per_us;
assert!(max_safe > 17_000_000); let overflow: u64 = crate::soc::ws63::TIMER_CLOCK_HZ as u64 * (max_safe + 1) / 1_000_000;
assert!(overflow > u32::MAX as u64); }
#[test]
fn test_pwm_channel_count_fits_u8() {
assert!(crate::soc::ws63::PWM_CHANNEL_COUNT <= 8);
}
#[test]
fn test_dma_channel_bound_check() {
assert!(crate::soc::ws63::DMA_CHANNEL_COUNT == 4);
for ch in 0u8..4 {
assert!(ch < crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
}
assert!(4u8 >= crate::soc::ws63::DMA_CHANNEL_COUNT as u8);
}
}