use crate::peripherals::{CldoCrg, SysCtl0};
use crate::soc::chip::SYSTEM_CLOCK_HZ;
fn sys_ctl0_regs() -> &'static crate::soc::pac::sys_ctl0::RegisterBlock {
unsafe { &*SysCtl0::ptr() }
}
fn cmu_regs() -> &'static crate::soc::pac::cmu::RegisterBlock {
unsafe { &*crate::soc::pac::Cmu::ptr() }
}
fn cldo_crg_regs() -> &'static crate::soc::pac::cldo_crg::RegisterBlock {
unsafe { &*CldoCrg::ptr() }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TcxoFreq {
MHz24 = 24_000_000,
MHz40 = 40_000_000,
}
impl TcxoFreq {
pub fn detect() -> Self {
if sys_ctl0_regs().hw_ctl().read().refclk_freq_status().bit_is_clear() {
TcxoFreq::MHz40
} else {
TcxoFreq::MHz24
}
}
pub const fn hz(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PllStatus {
Locked,
Unlocked,
}
fn pll_is_locked() -> bool {
cmu_regs().excep_ro_rg().read().fnpll_lock_grm().bit_is_set()
}
fn wait_pll_lock(retry_count: u32, retry_delay_us: u32) -> PllStatus {
for _ in 0..retry_count {
if pll_is_locked() {
return PllStatus::Locked;
}
let cycles = TcxoFreq::detect().hz() as u64 * retry_delay_us as u64 / 1_000_000;
for _ in 0..cycles / 3 {
core::hint::spin_loop();
}
}
PllStatus::Unlocked
}
#[derive(Debug, Clone, Copy)]
pub struct SystemClocks {
pub cpu_clk: u32,
pub pclk: u32,
pub tcxo_freq: TcxoFreq,
pub pll_locked: bool,
}
impl SystemClocks {
pub const fn assumed() -> Self {
Self { cpu_clk: SYSTEM_CLOCK_HZ, pclk: SYSTEM_CLOCK_HZ, tcxo_freq: TcxoFreq::MHz40, pll_locked: true }
}
}
pub fn init_clocks(_sys_ctl0: &SysCtl0<'_>, _cldo_crg: &CldoCrg<'_>) -> SystemClocks {
let tcxo_freq = TcxoFreq::detect();
let cmu = cmu_regs();
let cldo = cldo_crg_regs();
cmu.cmu_new_cfg1().write(|w| w.cpu_div_flash_rstn_sync().set_bit());
for _ in 0..tcxo_freq.hz() / 1_000_000 / 3 {
core::hint::spin_loop(); }
cmu.cmu_new_cfg1().write(|w| w.cpu_div_flash_rstn_sync().set_bit().cpu_div_flash_rstn().set_bit());
cldo.clk_sel().modify(|_, w| w.flash_clk_sel().set_bit());
cldo.cken_ctl1().modify(|_, w| unsafe { w.uart_cken().bits(0) });
cldo.clk_sel().modify(|_, w| w.uart0_clk_sel().set_bit().uart1_clk_sel().set_bit().uart2_clk_sel().set_bit());
cldo.cken_ctl1().modify(|_, w| unsafe { w.uart_cken().bits(0b111) });
cldo.clk_sel().modify(|_, w| w.spi_clk_sel().set_bit());
let pll_locked = match wait_pll_lock(30, 1000) {
PllStatus::Locked => true,
PllStatus::Unlocked => false,
};
SystemClocks {
cpu_clk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
pclk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
tcxo_freq,
pll_locked,
}
}
pub fn probe_clocks() -> SystemClocks {
let tcxo_freq = TcxoFreq::detect();
let pll_locked = pll_is_locked();
SystemClocks {
cpu_clk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
pclk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
tcxo_freq,
pll_locked,
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
use super::*;
#[test]
fn test_tcxo_freq_values() {
assert_eq!(TcxoFreq::MHz24.hz(), 24_000_000);
assert_eq!(TcxoFreq::MHz40.hz(), 40_000_000);
}
#[test]
fn test_default_clocks() {
let c = SystemClocks::assumed();
assert_eq!(c.cpu_clk, 240_000_000);
assert_eq!(c.pclk, 240_000_000);
assert!(c.pll_locked);
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use super::{SYSTEM_CLOCK_HZ, TcxoFreq};
use proptest::prelude::*;
fn pll_wait_cycles(tcxo_hz: u32, delay_us: u32) -> u64 {
let cycles = tcxo_hz as u64 * delay_us as u64 / 1_000_000;
cycles / 3
}
fn flash_delay_iters(tcxo_hz: u32) -> u32 {
tcxo_hz / 1_000_000 / 3
}
fn resolved_clk(pll_locked: bool, tcxo: TcxoFreq) -> u32 {
if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo.hz() }
}
fn clk_sel_word(init: u32) -> u32 {
let mut sel = init;
sel |= 1 << 18; sel |= (1 << 1) | (1 << 2) | (1 << 3); sel |= 1 << 6; sel
}
fn gate_word_final(init: u32) -> u32 {
let mut gate = init;
gate &= !((1 << 18) | (1 << 19) | (1 << 20)); gate |= (1 << 18) | (1 << 19) | (1 << 20); gate
}
proptest! {
#[test]
fn pll_wait_cycles_never_panics(tcxo in any::<u32>(), delay in any::<u32>()) {
let _ = pll_wait_cycles(tcxo, delay);
}
#[test]
fn pll_wait_cycles_monotonic_in_delay(tcxo in any::<u32>(), a in any::<u32>(), b in any::<u32>()) {
let (lo, hi) = if a <= b { (a, b) } else { (b, a) };
prop_assert!(pll_wait_cycles(tcxo, hi) >= pll_wait_cycles(tcxo, lo));
}
#[test]
fn pll_wait_cycles_known_boundaries(_dummy in any::<u8>()) {
prop_assert_eq!(pll_wait_cycles(TcxoFreq::MHz24.hz(), 1000), 24_000_000u64 * 1000 / 1_000_000 / 3);
prop_assert_eq!(pll_wait_cycles(TcxoFreq::MHz40.hz(), 1000), 40_000_000u64 * 1000 / 1_000_000 / 3);
prop_assert_eq!(pll_wait_cycles(TcxoFreq::MHz24.hz(), 1000), 8000);
prop_assert_eq!(pll_wait_cycles(TcxoFreq::MHz40.hz(), 1000), 13333);
}
#[test]
fn flash_delay_iters_never_panics(tcxo in any::<u32>()) {
let _ = flash_delay_iters(tcxo);
}
#[test]
fn flash_delay_iters_known(_dummy in any::<u8>()) {
prop_assert_eq!(flash_delay_iters(TcxoFreq::MHz24.hz()), 8); prop_assert_eq!(flash_delay_iters(TcxoFreq::MHz40.hz()), 13); }
#[test]
fn resolved_clk_is_pll_or_tcxo(locked in any::<bool>(), use40 in any::<bool>()) {
let tcxo = if use40 { TcxoFreq::MHz40 } else { TcxoFreq::MHz24 };
let r = resolved_clk(locked, tcxo);
if locked {
prop_assert_eq!(r, SYSTEM_CLOCK_HZ);
} else {
prop_assert_eq!(r, tcxo.hz());
prop_assert!(r == 24_000_000 || r == 40_000_000);
}
}
#[test]
fn clk_sel_sets_only_documented_bits(init in any::<u32>()) {
const SET: u32 = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 6) | (1 << 18);
let out = clk_sel_word(init);
prop_assert_eq!(out & SET, SET);
prop_assert_eq!(out & !SET, init & !SET);
prop_assert_eq!(out & init, init);
}
#[test]
fn gate_word_round_trips_other_bits(init in any::<u32>()) {
const GATE: u32 = (1 << 18) | (1 << 19) | (1 << 20);
let out = gate_word_final(init);
prop_assert_eq!(out & GATE, GATE); prop_assert_eq!(out & !GATE, init & !GATE); }
#[test]
fn tcxo_hz_is_valid(use40 in any::<bool>()) {
let f = if use40 { TcxoFreq::MHz40 } else { TcxoFreq::MHz24 };
let hz = f.hz();
prop_assert!(hz == 24_000_000 || hz == 40_000_000);
prop_assert_eq!(hz, f as u32); }
}
}