use core::arch::asm;
use core::marker::PhantomData;
#[cfg(feature = "rp2040")]
use core::sync::atomic::AtomicU16;
use core::sync::atomic::{AtomicU32, Ordering};
use pac::clocks::vals::*;
use crate::gpio::{AnyPin, SealedPin};
use crate::pac::common::{RW, Reg};
use crate::{Peri, pac, reset};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockError {
PllLockTimedOut,
InvalidPllParameters,
UnexpectedCoreVoltageRead,
}
struct Clocks {
xosc: AtomicU32,
sys: AtomicU32,
reference: AtomicU32,
pll_sys: AtomicU32,
pll_usb: AtomicU32,
usb: AtomicU32,
adc: AtomicU32,
rosc: AtomicU32,
peri: AtomicU32,
#[cfg(feature = "rp2040")]
rtc: AtomicU16,
}
static CLOCKS: Clocks = Clocks {
xosc: AtomicU32::new(0),
sys: AtomicU32::new(0),
reference: AtomicU32::new(0),
pll_sys: AtomicU32::new(0),
pll_usb: AtomicU32::new(0),
usb: AtomicU32::new(0),
adc: AtomicU32::new(0),
rosc: AtomicU32::new(0),
peri: AtomicU32::new(0),
#[cfg(feature = "rp2040")]
rtc: AtomicU16::new(0),
};
#[repr(u8)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PeriClkSrc {
Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _,
PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _,
PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _,
Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _,
}
#[cfg(feature = "rp2040")]
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CoreVoltage {
V0_80 = 0b0000,
V0_85 = 0b0110,
V0_90 = 0b0111,
V0_95 = 0b1000,
V1_00 = 0b1001,
V1_05 = 0b1010,
V1_10 = 0b1011,
V1_15 = 0b1100,
V1_20 = 0b1101,
V1_25 = 0b1110,
V1_30 = 0b1111,
}
#[cfg(feature = "_rp235x")]
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CoreVoltage {
V0_55 = 0b00000,
V0_60 = 0b00001,
V0_65 = 0b00010,
V0_70 = 0b00011,
V0_75 = 0b00100,
V0_80 = 0b00101,
V0_85 = 0b00110,
V0_90 = 0b00111,
V0_95 = 0b01000,
V1_00 = 0b01001,
V1_05 = 0b01010,
V1_10 = 0b01011,
V1_15 = 0b01100,
V1_20 = 0b01101,
V1_25 = 0b01110,
V1_30 = 0b01111,
}
impl CoreVoltage {
fn recommended_bod(self) -> u8 {
#[cfg(feature = "rp2040")]
match self {
CoreVoltage::V0_80 => 0b0100, CoreVoltage::V0_85 => 0b0101, CoreVoltage::V0_90 => 0b0110, CoreVoltage::V0_95 => 0b0111, CoreVoltage::V1_00 => 0b1000, CoreVoltage::V1_05 => 0b1000, CoreVoltage::V1_10 => 0b1001, CoreVoltage::V1_15 => 0b1010, CoreVoltage::V1_20 => 0b1011, CoreVoltage::V1_25 => 0b1100, CoreVoltage::V1_30 => 0b1101, }
#[cfg(feature = "_rp235x")]
match self {
CoreVoltage::V0_55 => 0b00001, CoreVoltage::V0_60 => 0b00010, CoreVoltage::V0_65 => 0b00011, CoreVoltage::V0_70 => 0b00011, CoreVoltage::V0_75 => 0b00100, CoreVoltage::V0_80 => 0b00101, CoreVoltage::V0_85 => 0b00110, CoreVoltage::V0_90 => 0b00110, CoreVoltage::V0_95 => 0b00111, CoreVoltage::V1_00 => 0b01000, CoreVoltage::V1_05 => 0b01000, CoreVoltage::V1_10 => 0b01001, CoreVoltage::V1_15 => 0b01001, CoreVoltage::V1_20 => 0b01010, CoreVoltage::V1_25 => 0b01010, CoreVoltage::V1_30 => 0b01011, }
}
}
#[non_exhaustive]
pub struct ClockConfig {
pub rosc: Option<RoscConfig>,
pub xosc: Option<XoscConfig>,
pub ref_clk: RefClkConfig,
pub sys_clk: SysClkConfig,
pub peri_clk_src: Option<PeriClkSrc>,
pub usb_clk: Option<UsbClkConfig>,
pub adc_clk: Option<AdcClkConfig>,
#[cfg(feature = "rp2040")]
pub rtc_clk: Option<RtcClkConfig>,
pub core_voltage: CoreVoltage,
pub voltage_stabilization_delay_us: Option<u32>,
}
impl Default for ClockConfig {
fn default() -> Self {
Self {
rosc: None,
xosc: None,
ref_clk: RefClkConfig {
src: RefClkSrc::Rosc,
div: 1,
},
sys_clk: SysClkConfig {
src: SysClkSrc::Rosc,
div_int: 1,
div_frac: 0,
},
peri_clk_src: None,
usb_clk: None,
adc_clk: None,
#[cfg(feature = "rp2040")]
rtc_clk: None,
core_voltage: CoreVoltage::V1_10,
voltage_stabilization_delay_us: None,
}
}
}
impl ClockConfig {
pub fn crystal(crystal_hz: u32) -> Self {
Self {
rosc: Some(RoscConfig {
hz: 6_500_000,
range: RoscRange::Medium,
drive_strength: [0; 8],
div: 16,
}),
xosc: Some(XoscConfig {
hz: crystal_hz,
sys_pll: Some(PllConfig {
refdiv: 1,
fbdiv: 125,
#[cfg(feature = "rp2040")]
post_div1: 6,
#[cfg(feature = "_rp235x")]
post_div1: 5,
post_div2: 2,
}),
usb_pll: Some(PllConfig {
refdiv: 1,
fbdiv: 120,
post_div1: 6,
post_div2: 5,
}),
delay_multiplier: 128,
}),
ref_clk: RefClkConfig {
src: RefClkSrc::Xosc,
div: 1,
},
sys_clk: SysClkConfig {
src: SysClkSrc::PllSys,
div_int: 1,
div_frac: 0,
},
peri_clk_src: Some(PeriClkSrc::Sys),
usb_clk: Some(UsbClkConfig {
src: UsbClkSrc::PllUsb,
div: 1,
phase: 0,
}),
adc_clk: Some(AdcClkConfig {
src: AdcClkSrc::PllUsb,
div: 1,
phase: 0,
}),
#[cfg(feature = "rp2040")]
rtc_clk: Some(RtcClkConfig {
src: RtcClkSrc::PllUsb,
div_int: 1024,
div_frac: 0,
phase: 0,
}),
core_voltage: CoreVoltage::V1_10, voltage_stabilization_delay_us: None,
}
}
pub fn rosc() -> Self {
Self {
rosc: Some(RoscConfig {
hz: 140_000_000,
range: RoscRange::High,
drive_strength: [0; 8],
div: 1,
}),
xosc: None,
ref_clk: RefClkConfig {
src: RefClkSrc::Rosc,
div: 1,
},
sys_clk: SysClkConfig {
src: SysClkSrc::Rosc,
div_int: 1,
div_frac: 0,
},
peri_clk_src: Some(PeriClkSrc::Rosc),
usb_clk: None,
adc_clk: Some(AdcClkConfig {
src: AdcClkSrc::Rosc,
div: 3,
phase: 0,
}),
#[cfg(feature = "rp2040")]
rtc_clk: Some(RtcClkConfig {
src: RtcClkSrc::Rosc,
div_int: 2986,
div_frac: 171,
phase: 0,
}),
core_voltage: CoreVoltage::V1_10, voltage_stabilization_delay_us: None,
}
}
pub fn system_freq(hz: u32) -> Result<Self, ClockError> {
const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
#[cfg(feature = "rp2040")]
if hz == 125_000_000 {
return Ok(config);
}
#[cfg(feature = "_rp235x")]
if hz == 150_000_000 {
return Ok(config);
}
let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;
if let Some(xosc) = &mut config.xosc {
xosc.sys_pll = Some(sys_pll_params);
}
#[cfg(feature = "rp2040")]
{
config.core_voltage = match hz {
freq if freq > 133_000_000 => CoreVoltage::V1_15,
_ => CoreVoltage::V1_10, };
}
#[cfg(feature = "_rp235x")]
{
config.core_voltage = match hz {
_ => CoreVoltage::V1_10, };
}
Ok(config)
}
#[cfg(feature = "rp2040")]
pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self {
assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters");
let mut config = Self::default();
config.xosc = Some(XoscConfig {
hz: xosc_hz,
sys_pll: Some(pll_config),
usb_pll: Some(PllConfig {
refdiv: 1,
fbdiv: 120,
post_div1: 6,
post_div2: 5,
}),
delay_multiplier: 128,
});
config.ref_clk = RefClkConfig {
src: RefClkSrc::Xosc,
div: 1,
};
config.sys_clk = SysClkConfig {
src: SysClkSrc::PllSys,
div_int: 1,
div_frac: 0,
};
config.core_voltage = core_voltage;
config.peri_clk_src = Some(PeriClkSrc::Sys);
config.usb_clk = Some(UsbClkConfig {
src: UsbClkSrc::PllUsb,
div: 1,
phase: 0,
});
config.adc_clk = Some(AdcClkConfig {
src: AdcClkSrc::PllUsb,
div: 1,
phase: 0,
});
config.rtc_clk = Some(RtcClkConfig {
src: RtcClkSrc::PllUsb,
div_int: 1024,
div_frac: 0,
phase: 0,
});
config
}
}
#[repr(u16)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RoscRange {
Low = pac::rosc::vals::FreqRange::LOW.0,
Medium = pac::rosc::vals::FreqRange::MEDIUM.0,
High = pac::rosc::vals::FreqRange::HIGH.0,
TooHigh = pac::rosc::vals::FreqRange::TOOHIGH.0,
}
pub struct RoscConfig {
pub hz: u32,
pub range: RoscRange,
pub drive_strength: [u8; 8],
pub div: u16,
}
pub struct XoscConfig {
pub hz: u32,
pub sys_pll: Option<PllConfig>,
pub usb_pll: Option<PllConfig>,
pub delay_multiplier: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct PllConfig {
pub refdiv: u8,
pub fbdiv: u16,
pub post_div1: u8,
pub post_div2: u8,
}
impl PllConfig {
pub fn output_frequency(&self, input_hz: u32) -> u32 {
let ref_freq = input_hz / self.refdiv as u32;
let vco_freq = ref_freq * self.fbdiv as u32;
vco_freq / ((self.post_div1 * self.post_div2) as u32)
}
pub fn is_valid(&self, input_hz: u32) -> bool {
if self.refdiv < 1 || self.refdiv > 63 {
return false;
}
if self.fbdiv < 16 || self.fbdiv > 320 {
return false;
}
if self.post_div1 < 1 || self.post_div1 > 7 {
return false;
}
if self.post_div2 < 1 || self.post_div2 > 7 {
return false;
}
if self.post_div2 > self.post_div1 {
return false;
}
let ref_freq = input_hz / self.refdiv as u32;
if ref_freq < 5_000_000 || ref_freq > 800_000_000 {
return false;
}
let vco_freq = ref_freq * self.fbdiv as u32;
vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000
}
}
pub struct RefClkConfig {
pub src: RefClkSrc,
pub div: u8,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RefClkSrc {
Xosc,
Rosc,
PllUsb,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SysClkSrc {
Ref,
PllSys,
PllUsb,
Rosc,
Xosc,
}
pub struct SysClkConfig {
pub src: SysClkSrc,
#[cfg(feature = "rp2040")]
pub div_int: u32,
#[cfg(feature = "rp2040")]
pub div_frac: u8,
#[cfg(feature = "_rp235x")]
pub div_int: u16,
#[cfg(feature = "_rp235x")]
pub div_frac: u16,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbClkSrc {
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _,
}
pub struct UsbClkConfig {
pub src: UsbClkSrc,
pub div: u8,
pub phase: u8,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcClkSrc {
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _,
}
pub struct AdcClkConfig {
pub src: AdcClkSrc,
pub div: u8,
pub phase: u8,
}
#[repr(u8)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "rp2040")]
pub enum RtcClkSrc {
PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _,
}
#[cfg(feature = "rp2040")]
pub struct RtcClkConfig {
pub src: RtcClkSrc,
pub div_int: u32,
pub div_frac: u8,
pub phase: u8,
}
fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
const PLL_SYS_REFDIV: u8 = 1;
let reference_freq = input_hz as u64 / PLL_SYS_REFDIV as u64;
for fbdiv in (16..=320).rev() {
let vco_freq = reference_freq * fbdiv;
if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 {
continue;
}
for post_div1 in (1..=7).rev() {
for post_div2 in (1..=post_div1).rev() {
let out_freq = vco_freq / (post_div1 * post_div2);
if out_freq == target_hz as u64 && (vco_freq % (post_div1 * post_div2) == 0) {
return Some(PllConfig {
refdiv: PLL_SYS_REFDIV,
fbdiv: fbdiv as u16,
post_div1: post_div1 as u8,
post_div2: post_div2 as u8,
});
}
}
}
}
let mut best_config = None;
let mut min_diff = u32::MAX;
for fbdiv in (16..=320).rev() {
let vco_freq = reference_freq * fbdiv;
if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 {
continue;
}
for post_div1 in (1..=7).rev() {
for post_div2 in (1..=post_div1).rev() {
let out_freq = (vco_freq / (post_div1 * post_div2) as u64) as u32;
let diff = if out_freq > target_hz {
out_freq - target_hz
} else {
target_hz - out_freq
};
if diff < min_diff {
min_diff = diff;
best_config = Some(PllConfig {
refdiv: PLL_SYS_REFDIV,
fbdiv: fbdiv as u16,
post_div1: post_div1 as u8,
post_div2: post_div2 as u8,
});
}
}
}
}
best_config
}
pub(crate) unsafe fn init(config: ClockConfig) {
let mut peris = reset::ALL_PERIPHERALS;
peris.set_io_qspi(false);
peris.set_pads_qspi(false);
peris.set_pll_sys(false);
peris.set_pll_usb(false);
peris.set_usbctrl(false);
peris.set_syscfg(false);
reset::reset(peris);
let c = pac::CLOCKS;
c.clk_sys_resus_ctrl()
.write_value(pac::clocks::regs::ClkSysResusCtrl(0));
c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
#[cfg(feature = "rp2040")]
while c.clk_sys_selected().read() != 1 {}
#[cfg(feature = "_rp235x")]
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1) {}
c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));
#[cfg(feature = "rp2040")]
while c.clk_ref_selected().read() != 1 {}
#[cfg(feature = "_rp235x")]
while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
let mut peris = reset::Peripherals(0);
peris.set_pll_sys(true);
peris.set_pll_usb(true);
reset::reset(peris);
reset::unreset_wait(peris);
let rosc_freq = match config.rosc {
Some(config) => configure_rosc(config),
None => 0,
};
CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
{
let voltage = config.core_voltage;
#[cfg(feature = "rp2040")]
let vreg = pac::VREG_AND_CHIP_RESET;
#[cfg(feature = "_rp235x")]
let vreg = pac::POWMAN;
let current_vsel = vreg.vreg().read().vsel();
let target_vsel = voltage as u8;
if target_vsel != current_vsel {
#[cfg(feature = "rp2040")]
vreg.vreg().modify(|w| w.set_vsel(target_vsel));
#[cfg(feature = "_rp235x")]
vreg.vreg().modify(|w| {
w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); w.set_vsel(target_vsel);
*w
});
let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
match voltage {
CoreVoltage::V1_15 => 1000, CoreVoltage::V1_20 | CoreVoltage::V1_25 | CoreVoltage::V1_30 => 2000, _ => 0, }
});
if settling_time_us != 0 {
let cycles_per_us = rosc_freq / 1_000_000;
let delay_cycles = settling_time_us * cycles_per_us;
cortex_m::asm::delay(delay_cycles);
}
#[cfg(feature = "rp2040")]
vreg.bod().write(|w| {
w.set_vsel(voltage.recommended_bod());
w.set_en(true); });
#[cfg(feature = "_rp235x")]
vreg.bod().write(|w| {
w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); w.set_vsel(voltage.recommended_bod());
w.set_en(true); });
}
}
let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc {
Some(config) => {
start_xosc(config.hz, config.delay_multiplier);
let pll_sys_freq = match config.sys_pll {
Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
Ok(freq) => freq,
Err(e) => panic!("Failed to configure PLL_SYS: {:?}", e),
},
None => 0,
};
let pll_usb_freq = match config.usb_pll {
Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
Ok(freq) => freq,
Err(e) => panic!("Failed to configure PLL_USB: {:?}", e),
},
None => 0,
};
(config.hz, pll_sys_freq, pll_usb_freq)
}
None => (0, 0, 0),
};
CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed);
CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed);
CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed);
let (ref_src, ref_aux, clk_ref_freq) = {
use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src};
let div = config.ref_clk.div as u32;
assert!(div >= 1 && div <= 4);
match config.ref_clk.src {
RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div),
RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div),
RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div),
}
};
assert!(clk_ref_freq != 0);
CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed);
c.clk_ref_ctrl().write(|w| {
w.set_src(ref_src);
w.set_auxsrc(ref_aux);
});
#[cfg(feature = "rp2040")]
while c.clk_ref_selected().read() != (1 << ref_src as u32) {}
#[cfg(feature = "_rp235x")]
while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {}
c.clk_ref_div().write(|w| {
w.set_int(config.ref_clk.div);
});
#[cfg(feature = "rp2040")]
pac::WATCHDOG.tick().write(|w| {
w.set_cycles((clk_ref_freq / 1_000_000) as u16);
w.set_enable(true);
});
#[cfg(feature = "_rp235x")]
{
let cycle_count = clk_ref_freq / 1_000_000;
pac::TICKS.timer0_cycles().write(|w| w.0 = cycle_count);
pac::TICKS.timer0_ctrl().write(|w| w.set_enable(true));
pac::TICKS.watchdog_cycles().write(|w| w.0 = cycle_count);
pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true));
}
let (sys_src, sys_aux, clk_sys_freq) = {
use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src};
let (src, aux, freq) = match config.sys_clk.src {
SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq),
SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq),
SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq),
SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq),
SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq),
};
let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64;
(src, aux, ((freq as u64 * 256) / div) as u32)
};
assert!(clk_sys_freq != 0);
CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
if sys_src != ClkSysCtrlSrc::CLK_REF {
c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
#[cfg(feature = "rp2040")]
while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {}
#[cfg(feature = "_rp235x")]
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {}
}
c.clk_sys_ctrl().write(|w| {
w.set_auxsrc(sys_aux);
w.set_src(sys_src);
});
#[cfg(feature = "rp2040")]
while c.clk_sys_selected().read() != (1 << sys_src as u32) {}
#[cfg(feature = "_rp235x")]
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}
c.clk_sys_div().write(|w| {
w.set_int(config.sys_clk.div_int);
w.set_frac(config.sys_clk.div_frac);
});
let mut peris = reset::ALL_PERIPHERALS;
if let Some(src) = config.peri_clk_src {
c.clk_peri_ctrl().write(|w| {
w.set_enable(true);
w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _));
});
let peri_freq = match src {
PeriClkSrc::Sys => clk_sys_freq,
PeriClkSrc::PllSys => pll_sys_freq,
PeriClkSrc::PllUsb => pll_usb_freq,
PeriClkSrc::Rosc => rosc_freq,
PeriClkSrc::Xosc => xosc_freq,
};
assert!(peri_freq != 0);
CLOCKS.peri.store(peri_freq, Ordering::Relaxed);
} else {
peris.set_spi0(false);
peris.set_spi1(false);
peris.set_uart0(false);
peris.set_uart1(false);
CLOCKS.peri.store(0, Ordering::Relaxed);
}
if let Some(conf) = config.usb_clk {
c.clk_usb_div().write(|w| w.set_int(conf.div));
c.clk_usb_ctrl().write(|w| {
w.set_phase(conf.phase);
w.set_enable(true);
w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
});
let usb_freq = match conf.src {
UsbClkSrc::PllUsb => pll_usb_freq,
UsbClkSrc::PllSys => pll_sys_freq,
UsbClkSrc::Rosc => rosc_freq,
UsbClkSrc::Xosc => xosc_freq,
};
assert!(usb_freq != 0);
assert!(conf.div >= 1 && conf.div <= 4);
CLOCKS.usb.store(usb_freq / conf.div as u32, Ordering::Relaxed);
} else {
peris.set_usbctrl(false);
CLOCKS.usb.store(0, Ordering::Relaxed);
}
if let Some(conf) = config.adc_clk {
c.clk_adc_div().write(|w| w.set_int(conf.div));
c.clk_adc_ctrl().write(|w| {
w.set_phase(conf.phase);
w.set_enable(true);
w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
});
let adc_in_freq = match conf.src {
AdcClkSrc::PllUsb => pll_usb_freq,
AdcClkSrc::PllSys => pll_sys_freq,
AdcClkSrc::Rosc => rosc_freq,
AdcClkSrc::Xosc => xosc_freq,
};
assert!(adc_in_freq != 0);
assert!(conf.div >= 1 && conf.div <= 4);
CLOCKS.adc.store(adc_in_freq / conf.div as u32, Ordering::Relaxed);
} else {
peris.set_adc(false);
CLOCKS.adc.store(0, Ordering::Relaxed);
}
#[cfg(feature = "rp2040")]
if let Some(conf) = config.rtc_clk {
c.clk_rtc_div().write(|w| {
w.set_int(conf.div_int);
w.set_frac(conf.div_frac);
});
c.clk_rtc_ctrl().write(|w| {
w.set_phase(conf.phase);
w.set_enable(true);
w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
});
let rtc_in_freq = match conf.src {
RtcClkSrc::PllUsb => pll_usb_freq,
RtcClkSrc::PllSys => pll_sys_freq,
RtcClkSrc::Rosc => rosc_freq,
RtcClkSrc::Xosc => xosc_freq,
};
assert!(rtc_in_freq != 0);
assert!(config.sys_clk.div_int <= 0x1000000);
CLOCKS.rtc.store(
((rtc_in_freq as u64 * 256) / (conf.div_int as u64 * 256 + conf.div_frac as u64)) as u16,
Ordering::Relaxed,
);
} else {
peris.set_rtc(false);
CLOCKS.rtc.store(0, Ordering::Relaxed);
}
#[cfg(feature = "_rp235x")]
{
peris.set_hstx(false);
}
reset::unreset_wait(peris);
}
fn configure_rosc(config: RoscConfig) -> u32 {
let p = pac::ROSC;
p.freqa().write(|w| {
w.set_passwd(pac::rosc::vals::Passwd::PASS);
w.set_ds0(config.drive_strength[0]);
w.set_ds1(config.drive_strength[1]);
w.set_ds2(config.drive_strength[2]);
w.set_ds3(config.drive_strength[3]);
});
p.freqb().write(|w| {
w.set_passwd(pac::rosc::vals::Passwd::PASS);
w.set_ds4(config.drive_strength[4]);
w.set_ds5(config.drive_strength[5]);
w.set_ds6(config.drive_strength[6]);
w.set_ds7(config.drive_strength[7]);
});
p.div().write(|w| {
w.set_div(pac::rosc::vals::Div(config.div + pac::rosc::vals::Div::PASS.0));
});
p.ctrl().write(|w| {
w.set_enable(pac::rosc::vals::Enable::ENABLE);
w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16));
});
config.hz
}
pub fn rosc_freq() -> u32 {
CLOCKS.rosc.load(Ordering::Relaxed)
}
pub fn xosc_freq() -> u32 {
CLOCKS.xosc.load(Ordering::Relaxed)
}
pub fn pll_sys_freq() -> u32 {
CLOCKS.pll_sys.load(Ordering::Relaxed)
}
pub fn pll_usb_freq() -> u32 {
CLOCKS.pll_usb.load(Ordering::Relaxed)
}
pub fn clk_sys_freq() -> u32 {
CLOCKS.sys.load(Ordering::Relaxed)
}
pub fn clk_ref_freq() -> u32 {
CLOCKS.reference.load(Ordering::Relaxed)
}
pub fn clk_peri_freq() -> u32 {
CLOCKS.peri.load(Ordering::Relaxed)
}
pub fn clk_usb_freq() -> u32 {
CLOCKS.usb.load(Ordering::Relaxed)
}
pub fn clk_adc_freq() -> u32 {
CLOCKS.adc.load(Ordering::Relaxed)
}
#[cfg(feature = "rp2040")]
pub fn clk_rtc_freq() -> u16 {
CLOCKS.rtc.load(Ordering::Relaxed)
}
pub fn core_voltage() -> Result<CoreVoltage, ClockError> {
#[cfg(feature = "rp2040")]
{
let vreg = pac::VREG_AND_CHIP_RESET;
let vsel = vreg.vreg().read().vsel();
match vsel {
0b0000 => Ok(CoreVoltage::V0_80),
0b0110 => Ok(CoreVoltage::V0_85),
0b0111 => Ok(CoreVoltage::V0_90),
0b1000 => Ok(CoreVoltage::V0_95),
0b1001 => Ok(CoreVoltage::V1_00),
0b1010 => Ok(CoreVoltage::V1_05),
0b1011 => Ok(CoreVoltage::V1_10),
0b1100 => Ok(CoreVoltage::V1_15),
0b1101 => Ok(CoreVoltage::V1_20),
0b1110 => Ok(CoreVoltage::V1_25),
0b1111 => Ok(CoreVoltage::V1_30),
_ => Err(ClockError::UnexpectedCoreVoltageRead),
}
}
#[cfg(feature = "_rp235x")]
{
let vreg = pac::POWMAN;
let vsel = vreg.vreg().read().vsel();
match vsel {
0b00000 => Ok(CoreVoltage::V0_55),
0b00001 => Ok(CoreVoltage::V0_60),
0b00010 => Ok(CoreVoltage::V0_65),
0b00011 => Ok(CoreVoltage::V0_70),
0b00100 => Ok(CoreVoltage::V0_75),
0b00101 => Ok(CoreVoltage::V0_80),
0b00110 => Ok(CoreVoltage::V0_85),
0b00111 => Ok(CoreVoltage::V0_90),
0b01000 => Ok(CoreVoltage::V0_95),
0b01001 => Ok(CoreVoltage::V1_00),
0b01010 => Ok(CoreVoltage::V1_05),
0b01011 => Ok(CoreVoltage::V1_10),
0b01100 => Ok(CoreVoltage::V1_15),
0b01101 => Ok(CoreVoltage::V1_20),
0b01110 => Ok(CoreVoltage::V1_25),
0b01111 => Ok(CoreVoltage::V1_30),
_ => Err(ClockError::UnexpectedCoreVoltageRead),
}
}
}
fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;
pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
pac::XOSC.ctrl().write(|w| {
w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
w.set_enable(pac::xosc::vals::Enable::ENABLE);
});
while !pac::XOSC.status().read().stable() {}
}
#[inline(always)]
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {
let ref_freq = input_freq / config.refdiv as u32;
assert!(config.fbdiv >= 16 && config.fbdiv <= 320);
assert!(config.post_div1 >= 1 && config.post_div1 <= 7);
assert!(config.post_div2 >= 1 && config.post_div2 <= 7);
assert!(config.post_div2 <= config.post_div1);
assert!(config.refdiv >= 1 && config.refdiv <= 63);
assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000);
let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);
assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
p.pwr().write(|w| {
w.set_pd(true); w.set_vcopd(true); w.set_postdivpd(true); w.set_dsmpd(true); *w
});
cortex_m::asm::delay(10);
p.cs().write(|w| w.set_refdiv(config.refdiv as _));
p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv));
p.pwr().write(|w| {
w.set_pd(false); w.set_vcopd(false); w.set_postdivpd(true); w.set_dsmpd(true); *w
});
let mut timeout = 1_000_000;
while !p.cs().read().lock() {
timeout -= 1;
if timeout == 0 {
return Err(ClockError::PllLockTimedOut);
}
}
p.prim().write(|w| {
w.set_postdiv1(config.post_div1);
w.set_postdiv2(config.post_div2);
});
p.pwr().modify(|w| {
w.set_postdivpd(false); *w
});
cortex_m::asm::delay(100);
Ok(vco_freq / ((config.post_div1 * config.post_div2) as u32))
}
pub trait GpinPin: crate::gpio::Pin {
const NR: usize;
}
macro_rules! impl_gpinpin {
($name:ident, $pin_num:expr, $gpin_num:expr) => {
impl GpinPin for crate::peripherals::$name {
const NR: usize = $gpin_num;
}
};
}
impl_gpinpin!(PIN_20, 20, 0);
impl_gpinpin!(PIN_22, 22, 1);
pub struct Gpin<'d, T: GpinPin> {
gpin: Peri<'d, AnyPin>,
_phantom: PhantomData<T>,
}
impl<'d, T: GpinPin> Gpin<'d, T> {
pub fn new(gpin: Peri<'d, T>) -> Self {
#[cfg(feature = "rp2040")]
gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08));
#[cfg(feature = "_rp235x")]
gpin.gpio().ctrl().write(|w| w.set_funcsel(0x09));
#[cfg(feature = "_rp235x")]
gpin.pad_ctrl().write(|w| {
w.set_iso(false);
});
Gpin {
gpin: gpin.into(),
_phantom: PhantomData,
}
}
}
impl<'d, T: GpinPin> Drop for Gpin<'d, T> {
fn drop(&mut self) {
self.gpin.pad_ctrl().write(|_| {});
self.gpin
.gpio()
.ctrl()
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
}
}
pub trait GpoutPin: crate::gpio::Pin {
fn number(&self) -> usize;
}
macro_rules! impl_gpoutpin {
($name:ident, $gpout_num:expr) => {
impl GpoutPin for crate::peripherals::$name {
fn number(&self) -> usize {
$gpout_num
}
}
};
}
impl_gpoutpin!(PIN_21, 0);
impl_gpoutpin!(PIN_23, 1);
impl_gpoutpin!(PIN_24, 2);
impl_gpoutpin!(PIN_25, 3);
#[repr(u8)]
pub enum GpoutSrc {
PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _,
PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _,
Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _,
Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _,
Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _,
Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
#[cfg(feature = "rp2040")]
Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
}
pub struct Gpout<'d, T: GpoutPin> {
gpout: Peri<'d, T>,
}
impl<'d, T: GpoutPin> Gpout<'d, T> {
pub fn new(gpout: Peri<'d, T>) -> Self {
#[cfg(feature = "rp2040")]
gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08));
#[cfg(feature = "_rp235x")]
gpout.gpio().ctrl().write(|w| w.set_funcsel(0x09));
#[cfg(feature = "_rp235x")]
gpout.pad_ctrl().write(|w| {
w.set_iso(false);
});
Self { gpout }
}
#[cfg(feature = "rp2040")]
pub fn set_div(&self, int: u32, frac: u8) {
let c = pac::CLOCKS;
c.clk_gpout_div(self.gpout.number()).write(|w| {
w.set_int(int);
w.set_frac(frac);
});
}
#[cfg(feature = "_rp235x")]
pub fn set_div(&self, int: u16, frac: u16) {
let c = pac::CLOCKS;
c.clk_gpout_div(self.gpout.number()).write(|w| {
w.set_int(int);
w.set_frac(frac);
});
}
pub fn set_src(&self, src: GpoutSrc) {
let c = pac::CLOCKS;
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _));
});
}
pub fn enable(&self) {
let c = pac::CLOCKS;
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
w.set_enable(true);
});
}
pub fn disable(&self) {
let c = pac::CLOCKS;
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
w.set_enable(false);
});
}
pub fn get_freq(&self) -> u32 {
let c = pac::CLOCKS;
let src = c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc();
let base = match src {
ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(),
ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),
ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),
ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),
_ => unreachable!(),
};
let div = c.clk_gpout_div(self.gpout.number()).read();
let int = if div.int() == 0 { 0xFFFF } else { div.int() } as u64;
let frac = div.frac() as u64;
((base as u64 * 256) / (int * 256 + frac)) as u32
}
}
impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
fn drop(&mut self) {
self.disable();
self.gpout.pad_ctrl().write(|_| {});
self.gpout
.gpio()
.ctrl()
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
}
}
pub struct RoscRng;
impl RoscRng {
pub fn next_u8() -> u8 {
let random_reg = pac::ROSC.randombit();
let mut acc = 0;
for _ in 0..u8::BITS {
acc <<= 1;
acc |= random_reg.read().randombit() as u8;
}
acc
}
pub fn next_u32(&mut self) -> u32 {
rand_core_09::impls::next_u32_via_fill(self)
}
pub fn next_u64(&mut self) -> u64 {
rand_core_09::impls::next_u64_via_fill(self)
}
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
dest.fill_with(Self::next_u8)
}
}
impl rand_core_06::RngCore for RoscRng {
fn next_u32(&mut self) -> u32 {
self.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.fill_bytes(dest);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl rand_core_06::CryptoRng for RoscRng {}
impl rand_core_09::RngCore for RoscRng {
fn next_u32(&mut self) -> u32 {
self.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.fill_bytes(dest);
}
}
impl rand_core_09::CryptoRng for RoscRng {}
#[cfg(all(target_arch = "arm"))]
pub fn dormant_sleep() {
struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);
impl<T: Copy, F: Fn()> Drop for Set<T, F> {
fn drop(&mut self) {
self.0.write_value(self.1);
self.2();
}
}
fn set_with_post_restore<T: Copy, After: Fn(), F: FnOnce(&mut T) -> After>(
reg: Reg<T, RW>,
f: F,
) -> Set<T, impl Fn()> {
reg.modify(|w| {
let old = *w;
let after = f(w);
Set(reg, old, after)
})
}
fn set<T: Copy, F: FnOnce(&mut T)>(reg: Reg<T, RW>, f: F) -> Set<T, impl Fn()> {
set_with_post_restore(reg, |r| {
f(r);
|| ()
})
}
let _stop_adc = set(pac::CLOCKS.clk_adc_ctrl(), |w| w.set_enable(false));
let _stop_usb = set(pac::CLOCKS.clk_usb_ctrl(), |w| w.set_enable(false));
let _stop_peri = set(pac::CLOCKS.clk_peri_ctrl(), |w| w.set_enable(false));
let _configure_rosc = (
set(pac::ROSC.ctrl(), |w| {
w.set_enable(pac::rosc::vals::Enable::ENABLE);
w.set_freq_range(pac::rosc::vals::FreqRange::LOW);
}),
set(pac::ROSC.div(), |w| w.set_div(pac::rosc::vals::Div(0xaa0))),
);
while !pac::ROSC.status().read().stable() {}
let _switch_clk_ref = set(pac::CLOCKS.clk_ref_ctrl(), |w| {
w.set_src(pac::clocks::vals::ClkRefCtrlSrc::ROSC_CLKSRC_PH);
});
let _switch_clk_sys = set(pac::CLOCKS.clk_sys_ctrl(), |w| {
w.set_src(pac::clocks::vals::ClkSysCtrlSrc::CLK_REF);
});
let _stop_pll_sys = set_with_post_restore(pac::PLL_SYS.pwr(), |w| {
let wake = !w.pd() && !w.vcopd();
w.set_pd(true);
w.set_vcopd(true);
move || while wake && !pac::PLL_SYS.cs().read().lock() {}
});
let _stop_pll_usb = set_with_post_restore(pac::PLL_USB.pwr(), |w| {
let wake = !w.pd() && !w.vcopd();
w.set_pd(true);
w.set_vcopd(true);
move || while wake && !pac::PLL_USB.cs().read().lock() {}
});
let _stop_xosc = set_with_post_restore(pac::XOSC.ctrl(), |w| {
let wake = w.enable() == pac::xosc::vals::Enable::ENABLE;
if wake {
w.set_enable(pac::xosc::vals::Enable::DISABLE);
}
move || while wake && !pac::XOSC.status().read().stable() {}
});
let _power_down_xip_cache = set(pac::XIP_CTRL.ctrl(), |w| w.set_power_down(true));
unsafe {
let is_in_flash = {
let pc: usize;
asm!(
"mov {pc}, pc",
pc = out (reg) pc
);
pc < 0x20000000
};
if is_in_flash {
asm!(
"ldr {old_mem}, [{mempowerdown}]",
"str {power_down_mems}, [{mempowerdown}]",
"str {coma}, [{dormant}]",
"str {old_mem}, [{mempowerdown}]",
old_mem = out (reg) _,
mempowerdown = in (reg) pac::SYSCFG.mempowerdown().as_ptr(),
power_down_mems = in (reg) 0b11111111,
dormant = in (reg) pac::ROSC.dormant().as_ptr(),
coma = in (reg) 0x636f6d61,
);
} else {
pac::ROSC.dormant().write_value(rp_pac::rosc::regs::Dormant(0x636f6d61));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "rp2040")]
#[test]
fn test_find_pll_params() {
#[cfg(feature = "rp2040")]
{
let params = find_pll_params(12_000_000, 125_000_000).unwrap();
assert_eq!(params.refdiv, 1);
assert_eq!(params.fbdiv, 125);
let params = find_pll_params(12_000_000, 48_000_000).unwrap();
assert_eq!(params.refdiv, 1);
let ref_freq = 12_000_000 / params.refdiv as u32;
let vco_freq = ref_freq as u64 * params.fbdiv as u64;
let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
assert_eq!(output_freq, 48_000_000);
assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
let params = find_pll_params(12_000_000, 200_000_000).unwrap();
assert_eq!(params.refdiv, 1);
let vco_freq = 12_000_000 as u64 * params.fbdiv as u64;
let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
assert_eq!(output_freq, 200_000_000);
assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
let params = find_pll_params(16_000_000, 125_000_000).unwrap();
let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64;
let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
let params = find_pll_params(15_000_000, 125_000_000).unwrap();
let vco_freq = (15_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64;
let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
let freq_diff = if output_freq > 125_000_000 {
output_freq - 125_000_000
} else {
125_000_000 - output_freq
};
let error_percentage = (freq_diff as f64 / 125_000_000.0) * 100.0;
assert!(
error_percentage < 0.2,
"Output frequency {} is not close enough to target 125 MHz. Error: {:.2}%",
output_freq,
error_percentage
);
assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
}
}
#[cfg(feature = "rp2040")]
#[test]
fn test_pll_config_validation() {
let valid_config = PllConfig {
refdiv: 1,
fbdiv: 125,
post_div1: 6,
post_div2: 2,
};
assert!(valid_config.is_valid(12_000_000));
let mut invalid_config = valid_config;
invalid_config.fbdiv = 15; assert!(!invalid_config.is_valid(12_000_000));
invalid_config.fbdiv = 321; assert!(!invalid_config.is_valid(12_000_000));
invalid_config = valid_config;
invalid_config.post_div1 = 0; assert!(!invalid_config.is_valid(12_000_000));
invalid_config = valid_config;
invalid_config.post_div1 = 8; assert!(!invalid_config.is_valid(12_000_000));
invalid_config = valid_config;
invalid_config.post_div2 = 7;
invalid_config.post_div1 = 3;
assert!(!invalid_config.is_valid(12_000_000));
invalid_config = valid_config;
assert!(!invalid_config.is_valid(4_000_000)); assert!(!invalid_config.is_valid(900_000_000));
invalid_config = valid_config;
invalid_config.fbdiv = 16;
assert!(!invalid_config.is_valid(12_000_000));
invalid_config = valid_config;
invalid_config.fbdiv = 200;
invalid_config.refdiv = 1;
assert!(!invalid_config.is_valid(12_000_000));
invalid_config.fbdiv = 150; assert!(invalid_config.is_valid(12_000_000));
}
#[cfg(feature = "rp2040")]
#[test]
fn test_manual_pll_helper() {
{
let config = ClockConfig::manual_pll(
12_000_000,
PllConfig {
refdiv: 1,
fbdiv: 100,
post_div1: 3,
post_div2: 2,
},
CoreVoltage::V1_15,
);
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1);
assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().fbdiv, 100);
assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div1, 3);
assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div2, 2);
assert_eq!(
config
.xosc
.as_ref()
.unwrap()
.sys_pll
.as_ref()
.unwrap()
.output_frequency(12_000_000),
200_000_000
);
}
}
#[cfg(feature = "rp2040")]
#[test]
fn test_auto_voltage_scaling() {
{
let config = ClockConfig::system_freq(125_000_000).unwrap();
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
let config = ClockConfig::system_freq(150_000_000).unwrap();
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
let config = ClockConfig::system_freq(200_000_000).unwrap();
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
let config = ClockConfig::system_freq(250_000_000).unwrap();
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
let config = ClockConfig::system_freq(100_000_000).unwrap();
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
}
}
}