use core::{convert::Infallible, marker::PhantomData};
use fugit::{HertzU32, RateExtU32};
use crate::{
pac::{self, CLOCKS, PLL_SYS, PLL_USB, RESETS, XOSC},
pll::{
common_configs::{PLL_SYS_125MHZ, PLL_USB_48MHZ},
setup_pll_blocking, Error as PllError, Locked, PhaseLockedLoop,
},
typelevel::Sealed,
watchdog::Watchdog,
xosc::{setup_xosc_blocking, CrystalOscillator, Error as XoscError, Stable},
};
#[macro_use]
mod macros;
mod clock_sources;
use clock_sources::PllSys;
use self::clock_sources::{GpIn0, GpIn1, PllUsb, Rosc, Xosc};
bitfield::bitfield! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Default)]
pub struct ClockGate(u64);
pub sys_clock, set_sys_clock: 0;
pub adc_adc, set_adc_adc: 1;
pub sys_adc, set_sys_adc: 2;
pub sys_busctrl, set_sys_busctrl: 3;
pub sys_busfabric, set_sys_busfabric: 4;
pub sys_dma, set_sys_dma: 5;
pub sys_i2c0, set_sys_i2c0: 6;
pub sys_i2c1, set_sys_i2c1: 7;
pub sys_io, set_sys_io: 8;
pub sys_jtag, set_sys_jtag: 9;
pub sys_vreg_and_chip_reset, set_sys_vreg_and_chip_reset: 10;
pub sys_pads, set_sys_pads: 11;
pub sys_pio0, set_sys_pio0: 12;
pub sys_pio1, set_sys_pio1: 13;
pub sys_pll_sys, set_sys_pll_sys: 14;
pub sys_pll_usb, set_sys_pll_usb: 15;
pub sys_psm, set_sys_psm: 16;
pub sys_pwm, set_sys_pwm: 17;
pub sys_resets, set_sys_resets: 18;
pub sys_rom, set_sys_rom: 19;
pub sys_rosc, set_sys_rosc: 20;
pub rtc_rtc, set_rtc_rtc: 21;
pub sys_rtc, set_sys_rtc: 22;
pub sys_sio, set_sys_sio: 23;
pub peri_spi0, set_peri_spi0: 24;
pub sys_spi0, set_sys_spi0: 25;
pub peri_spi1, set_peri_spi1: 26;
pub sys_spi1, set_sys_spi1: 27;
pub sys_sram0, set_sys_sram0: 28;
pub sys_sram1, set_sys_sram1: 29;
pub sys_sram2, set_sys_sram2: 30;
pub sys_sram3, set_sys_sram3: 31;
pub sys_sram4, set_sys_sram4: 32;
pub sys_sram5, set_sys_sram5: 33;
pub sys_syscfg, set_sys_syscfg: 34;
pub sys_sysinfo, set_sys_sysinfo: 35;
pub sys_tbman, set_sys_tbman: 36;
pub sys_timer, set_sys_timer: 37;
pub peri_uart0, set_peri_uart0: 38;
pub sys_uart0, set_sys_uart0: 39;
pub peri_uart1, set_peri_uart1: 40;
pub sys_uart1, set_sys_uart1: 41;
pub sys_usbctrl, set_sys_usbctrl: 42;
pub usb_usbctrl, set_usb_usbctrl: 43;
pub sys_watchdog, set_sys_watchdog: 44;
pub sys_xip, set_sys_xip: 45;
pub sys_xosc, set_sys_xosc: 46;
}
impl core::fmt::Debug for ClockGate {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ClockGate")
.field("sys_clock", &self.sys_clock())
.field("adc_adc", &self.adc_adc())
.field("sys_adc", &self.sys_adc())
.field("sys_busctrl", &self.sys_busctrl())
.field("sys_busfabric", &self.sys_busfabric())
.field("sys_dma", &self.sys_dma())
.field("sys_i2c0", &self.sys_i2c0())
.field("sys_i2c1", &self.sys_i2c1())
.field("sys_io", &self.sys_io())
.field("sys_jtag", &self.sys_jtag())
.field("sys_vreg_and_chip_reset", &self.sys_vreg_and_chip_reset())
.field("sys_pads", &self.sys_pads())
.field("sys_pio0", &self.sys_pio0())
.field("sys_pio1", &self.sys_pio1())
.field("sys_pll_sys", &self.sys_pll_sys())
.field("sys_pll_usb", &self.sys_pll_usb())
.field("sys_psm", &self.sys_psm())
.field("sys_pwm", &self.sys_pwm())
.field("sys_resets", &self.sys_resets())
.field("sys_rom", &self.sys_rom())
.field("sys_rosc", &self.sys_rosc())
.field("rtc_rtc", &self.rtc_rtc())
.field("sys_rtc", &self.sys_rtc())
.field("sys_sio", &self.sys_sio())
.field("peri_spi0", &self.peri_spi0())
.field("sys_spi0", &self.sys_spi0())
.field("peri_spi1", &self.peri_spi1())
.field("sys_spi1", &self.sys_spi1())
.field("sys_sram0", &self.sys_sram0())
.field("sys_sram1", &self.sys_sram1())
.field("sys_sram2", &self.sys_sram2())
.field("sys_sram3", &self.sys_sram3())
.field("sys_sram4", &self.sys_sram4())
.field("sys_syscfg", &self.sys_syscfg())
.field("sys_sysinfo", &self.sys_sysinfo())
.field("sys_tbman", &self.sys_tbman())
.field("sys_timer", &self.sys_timer())
.field("peri_uart0", &self.peri_uart0())
.field("sys_uart0", &self.sys_uart0())
.field("peri_uart1", &self.peri_uart1())
.field("sys_uart1", &self.sys_uart1())
.field("sys_usbctrl", &self.sys_usbctrl())
.field("usb_usbctrl", &self.usb_usbctrl())
.field("sys_watchdog", &self.sys_watchdog())
.field("sys_xip", &self.sys_xip())
.field("sys_xosc", &self.sys_xosc())
.finish()
}
}
#[derive(Copy, Clone)]
struct ShareableClocks {
_internal: (),
}
impl ShareableClocks {
fn new(_clocks: &mut CLOCKS) -> Self {
ShareableClocks { _internal: () }
}
unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
&*CLOCKS::ptr()
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClockError {
CantIncreaseFreq,
FrequencyTooHigh,
FrequencyTooLow,
}
pub trait Clock: Sealed + Sized {
type Variant;
fn freq(&self) -> HertzU32;
fn configure_clock<S: ValidSrc<Self>>(
&mut self,
src: &S,
freq: HertzU32,
) -> Result<(), ClockError>;
}
trait ClockDivision {
fn set_div(&mut self, div: u32);
fn get_div(&self) -> u32;
}
trait GlitchlessClock {
type Clock: Clock;
fn await_select(
&self,
clock_token: &ChangingClockToken<Self::Clock>,
) -> nb::Result<(), Infallible>;
}
pub struct ChangingClockToken<G: Clock> {
clock_nr: u8,
clock: PhantomData<G>,
}
pub trait StoppableClock: Sealed {
fn enable(&mut self);
fn disable(&mut self);
fn kill(&mut self);
}
pub trait ClockSource: Sealed {
fn get_freq(&self) -> HertzU32;
}
pub trait ValidSrc<C: Clock>: Sealed + ClockSource {
fn is_aux(&self) -> bool;
fn variant(&self) -> C::Variant;
}
clocks! {
struct GpioOutput0Clock {
init_freq: 0,
reg: clk_gpout0,
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
}
struct GpioOutput1Clock {
init_freq: 0,
reg: clk_gpout1,
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
}
struct GpioOutput2Clock {
init_freq: 0,
reg: clk_gpout2,
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
}
struct GpioOutput3Clock {
init_freq: 0,
reg: clk_gpout3,
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
}
struct ReferenceClock {
init_freq: 12_000_000, reg: clk_ref,
src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC},
auxsrc: {PllUsb:CLKSRC_PLL_USB, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
}
struct SystemClock {
init_freq: 12_000_000, reg: clk_sys,
src: {ReferenceClock: CLK_REF},
auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
}
struct PeripheralClock {
init_freq: 12_000_000, reg: clk_peri,
auxsrc: {SystemClock: CLK_SYS, PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1 },
div: false
}
struct UsbClock {
init_freq: 0,
reg: clk_usb,
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
}
struct AdcClock {
init_freq: 0,
reg: clk_adc,
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
}
struct RtcClock {
init_freq: 0,
reg: clk_rtc,
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
}
}
impl SystemClock {
fn get_default_clock_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
pac::clocks::clk_sys_ctrl::SRC_A::CLK_REF
}
fn get_aux_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
pac::clocks::clk_sys_ctrl::SRC_A::CLKSRC_CLK_SYS_AUX
}
}
impl ReferenceClock {
fn get_default_clock_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
pac::clocks::clk_ref_ctrl::SRC_A::ROSC_CLKSRC_PH
}
fn get_aux_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX
}
}
impl ClocksManager {
pub fn init_default(
&mut self,
xosc: &CrystalOscillator<Stable>,
pll_sys: &PhaseLockedLoop<Locked, PLL_SYS>,
pll_usb: &PhaseLockedLoop<Locked, PLL_USB>,
) -> Result<(), ClockError> {
self.reference_clock
.configure_clock(xosc, xosc.get_freq())?;
self.system_clock
.configure_clock(pll_sys, pll_sys.get_freq())?;
self.usb_clock
.configure_clock(pll_usb, pll_usb.get_freq())?;
self.adc_clock
.configure_clock(pll_usb, pll_usb.get_freq())?;
self.rtc_clock.configure_clock(pll_usb, 46875u32.Hz())?;
self.peripheral_clock
.configure_clock(&self.system_clock, self.system_clock.freq())
}
pub fn configure_sleep_enable(&mut self, clock_gate: ClockGate) {
self.clocks
.sleep_en0()
.write(|w| unsafe { w.bits(clock_gate.0 as u32) });
self.clocks
.sleep_en1()
.write(|w| unsafe { w.bits((clock_gate.0 >> 32) as u32) });
}
pub fn sleep_enable(&self) -> ClockGate {
ClockGate(
(u64::from(self.clocks.sleep_en1().read().bits()) << 32)
| u64::from(self.clocks.sleep_en0().read().bits()),
)
}
pub fn wake_enable(&self) -> ClockGate {
ClockGate(
(u64::from(self.clocks.wake_en1().read().bits()) << 32)
| u64::from(self.clocks.wake_en0().read().bits()),
)
}
pub fn free(self) -> CLOCKS {
self.clocks
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitError {
XoscErr(XoscError),
PllError(PllError),
ClockError(ClockError),
}
pub fn init_clocks_and_plls(
xosc_crystal_freq: u32,
xosc_dev: XOSC,
clocks_dev: CLOCKS,
pll_sys_dev: PLL_SYS,
pll_usb_dev: PLL_USB,
resets: &mut RESETS,
watchdog: &mut Watchdog,
) -> Result<ClocksManager, InitError> {
let xosc = setup_xosc_blocking(xosc_dev, xosc_crystal_freq.Hz()).map_err(InitError::XoscErr)?;
watchdog.enable_tick_generation((xosc_crystal_freq / 1_000_000) as u8);
let mut clocks = ClocksManager::new(clocks_dev);
let pll_sys = setup_pll_blocking(
pll_sys_dev,
xosc.operating_frequency(),
PLL_SYS_125MHZ,
&mut clocks,
resets,
)
.map_err(InitError::PllError)?;
let pll_usb = setup_pll_blocking(
pll_usb_dev,
xosc.operating_frequency(),
PLL_USB_48MHZ,
&mut clocks,
resets,
)
.map_err(InitError::PllError)?;
clocks
.init_default(&xosc, &pll_sys, &pll_usb)
.map_err(InitError::ClockError)?;
Ok(clocks)
}
fn fractional_div(numerator: u32, denominator: u32) -> Option<u32> {
if denominator.eq(&numerator) {
return Some(1 << 8);
}
let div_int = numerator / denominator;
if div_int >= 1 << 24 {
return None;
}
let div_rem = numerator - (div_int * denominator);
let div_frac = if div_rem < 1 << 24 {
(div_rem << 8) / denominator
} else {
(div_rem) / (denominator >> 8)
};
Some((div_int << 8) + div_frac)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fractional_div() {
assert_eq!(fractional_div(1, 1), Some(1 << 8));
assert_eq!(fractional_div(125_000_000, 48_000_000), Some(666));
assert_eq!(fractional_div(48_000_000, 46875), Some(1024 << 8));
assert_eq!(
fractional_div(
125_000_000,
fractional_div(125_000_000, 48_000_000).unwrap()
),
Some(48_048_048)
);
assert_eq!(
fractional_div(48_000_000, fractional_div(48_000_000, 46875).unwrap()),
Some(46875)
);
assert_eq!(fractional_div(1, 2), Some(128));
assert_eq!(fractional_div(1, 256), Some(1));
assert_eq!(fractional_div(1, 257), Some(0));
assert_eq!(fractional_div((1 << 24) - 1, 1), Some(((1 << 24) - 1) << 8));
assert_eq!(fractional_div(1 << 24, 1), None);
assert_eq!(fractional_div(1 << 24, 2), Some(1 << (23 + 8)));
assert_eq!(fractional_div(1 << 24, (1 << 24) + 1), Some(1 << 8));
assert_eq!(fractional_div(u32::MAX, u32::MAX), Some(1 << 8));
}
}