From f310d92b640ffa5c92b4f05bdf121725b523762b Mon Sep 17 00:00:00 2001 From: Hmvp Date: Thu, 8 Jul 2021 12:58:48 +0200 Subject: [PATCH] Refactor clocks (#54) * Remove unneeded lines * Reduce macro boilerplate * Refactor clocks --- rp2040-hal/src/clocks/available_clocks.rs | 253 ++++++++++ rp2040-hal/src/clocks/macros.rs | 579 ++++++++++++---------- rp2040-hal/src/clocks/mod.rs | 553 ++------------------- rp2040-hal/src/lib.rs | 1 + rp2040-hal/src/pll.rs | 30 +- rp2040-hal/src/rosc.rs | 99 ++++ 6 files changed, 752 insertions(+), 763 deletions(-) create mode 100644 rp2040-hal/src/clocks/available_clocks.rs create mode 100644 rp2040-hal/src/rosc.rs diff --git a/rp2040-hal/src/clocks/available_clocks.rs b/rp2040-hal/src/clocks/available_clocks.rs new file mode 100644 index 0000000..9f4c61e --- /dev/null +++ b/rp2040-hal/src/clocks/available_clocks.rs @@ -0,0 +1,253 @@ +//! Available clocks + +use super::{ClocksManager, ShareableClocks}; +use crate::{ + gpio::{ + bank0::{Gpio20, Gpio22}, + FunctionClock, Pin, + }, + pll::{Locked, PhaseLockedLoop}, + rosc::{Enabled, RingOscillator}, + typelevel::Sealed, + xosc::{CrystalOscillator, Stable}, +}; +use core::{convert::TryInto, marker::PhantomData}; +use embedded_time::rate::*; +use pac::{PLL_SYS, PLL_USB}; + +fn make_div>, F: TryInto>>( + src_freq: S, + freq: F, +) -> Result { + let src_freq = *src_freq.try_into().map_err(|_| ())?.integer(); + let freq = *freq.try_into().map_err(|_| ())?.integer(); + let div: u64 = (src_freq << 8).wrapping_div(freq); + Ok(div as u32) +} + +/// For clocks with a divider +trait ClockDivision { + /// Set integer divider value. + fn set_div(&mut self, div: u32); + /// Get integer diveder value. + fn get_div(&self) -> u32; +} + +/// Clock with glitchless source +pub trait GlitchlessClock { + /// Self type to hand to ChangingClockToken + type Clock: GlitchlessClock; + + /// Await switching clock sources without glitches. Needs a token that is returned when setting + fn await_select(&self, clock_token: &ChangingClockToken) -> nb::Result<(), ()>; +} + +/// Token which can be used to await the glitchless switch +pub struct ChangingClockToken { + clock_nr: u8, + clock: PhantomData, +} + +/// For clocks that can be disabled +pub trait StoppableClock { + /// Enables the clock. + fn enable(&mut self); + + /// Disables the clock. + fn disable(&mut self); + + /// Kills the clock. + fn kill(&mut self); +} + +/// Trait for things that can be used as clock source +pub trait ClockSource: Sealed { + /// Get the operating frequency for this source + /// + /// Used to determine the divisor + fn get_freq(&self) -> Hertz; +} + +type PllSys = PhaseLockedLoop; +impl Sealed for PllSys {} +impl ClockSource for PllSys { + fn get_freq(&self) -> Hertz { + self.operating_frequency() + } +} + +type PllUsb = PhaseLockedLoop; +impl Sealed for PllUsb {} +impl ClockSource for PllUsb { + fn get_freq(&self) -> Hertz { + self.operating_frequency() + } +} + +impl ClockSource for UsbClock { + fn get_freq(&self) -> Hertz { + self.frequency + } +} + +impl ClockSource for AdcClock { + fn get_freq(&self) -> Hertz { + self.frequency + } +} + +impl ClockSource for RtcClock { + fn get_freq(&self) -> Hertz { + self.frequency + } +} + +impl ClockSource for SystemClock { + fn get_freq(&self) -> Hertz { + self.frequency + } +} + +impl ClockSource for ReferenceClock { + fn get_freq(&self) -> Hertz { + self.frequency + } +} + +type Xosc = CrystalOscillator; +impl Sealed for Xosc {} +impl ClockSource for Xosc { + fn get_freq(&self) -> Hertz { + self.operating_frequency() + } +} + +type Rosc = RingOscillator; +impl Sealed for Rosc {} +// We are assuming the second output is never phase shifted (see 2.17.4) +impl ClockSource for RingOscillator { + fn get_freq(&self) -> Hertz { + self.operating_frequency() + } +} + +// GPIN0 +type GPin0 = Pin; +impl ClockSource for GPin0 { + fn get_freq(&self) -> Hertz { + todo!() + } +} + +// GPIN1 +type GPin1 = Pin; +impl ClockSource for Pin { + fn get_freq(&self) -> Hertz { + todo!() + } +} + +/// Trait to contrain which ClockSource is valid for which Clock +pub trait ValidSrc: Sealed { + /// Which register values are acceptable + type Variant; + + /// Is this a ClockSource for src or aux? + fn is_aux(&self) -> bool; + /// Get register value for this ClockSource + fn variant(&self) -> Self::Variant; +} + +clock!( + /// GPIO Output 0 Clock + struct GpioOutput0Clock { + 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} + } +); +clock!( + /// GPIO Output 1 Clock + struct GpioOutput1Clock { + 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} + } +); +clock!( + /// GPIO Output 2 Clock + struct GpioOutput2Clock { + 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} + } +); +clock!( + /// GPIO Output 3 Clock + struct GpioOutput3Clock { + 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} + } +); +clock!( + /// Reference Clock + struct ReferenceClock { + reg: clk_ref, + src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC}, + auxsrc: {PllUsb:CLKSRC_PLL_USB, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + } +); +clock!( + /// System Clock + struct SystemClock { + 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} + } +); +clock!( + /// Peripheral Clock + struct PeripheralClock { + 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 + } +); +clock!( + /// USB Clock + struct UsbClock { + 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} + } +); +clock!( + /// Adc Clock + struct AdcClock { + 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} + } +); +clock!( + /// RTC Clock + struct RtcClock { + 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 + } +} diff --git a/rp2040-hal/src/clocks/macros.rs b/rp2040-hal/src/clocks/macros.rs index 3ccd2ab..ec22cdc 100644 --- a/rp2040-hal/src/clocks/macros.rs +++ b/rp2040-hal/src/clocks/macros.rs @@ -1,289 +1,366 @@ -macro_rules! int_division { - ($name:ident, $div:ident, $u:ty) => { - impl IntegerDivision for $name { - fn set_int_div(&mut self, div: usize) { - unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { - w.int().bits(div as $u); - w - }); +macro_rules! clock { + { + $(#[$attr:meta])* + struct $name:ident { + reg: $reg:ident, + src: {$($src:ident: $src_variant:ident),*}, + auxsrc: {$($auxsrc:ident: $aux_variant:ident),*} + } + } => { + base_clock!{ + $(#[$attr])* + ($name, $reg, auxsrc={$($auxsrc: $aux_variant),*}) + } + + divisable_clock!($name, $reg); + + $crate::paste::paste!{ + $(impl ValidSrc<$name> for $src { + type Variant = [<$reg:camel SrcType>]; + + fn is_aux(&self) -> bool{ + false + } + fn variant(&self) -> [<$reg:camel SrcType>] { + [<$reg:camel SrcType>]::Src(pac::clocks::[<$reg _ctrl>]::SRC_A::$src_variant) + } + })* + + impl GlitchlessClock for $name { + type Clock = Self; + + fn await_select(&self, clock_token: &ChangingClockToken) -> nb::Result<(),()> { + let shared_dev = unsafe { self.shared_dev.get() }; + + let selected = shared_dev.[<$reg _selected>].read().bits(); + if selected != 1 << clock_token.clock_nr { + return Err(nb::Error::WouldBlock); + } + + Ok(()) + } } - fn get_int_div(&self) -> usize { - unsafe { self.shared_dev.get() }.$div.read().int().bits() as usize + /// Holds register value for ClockSource for this clock + pub enum [<$reg:camel SrcType>] { + /// Its an clock source that is to be used as source + Src(pac::clocks::[<$reg _ctrl>]::SRC_A), + /// Its an clock source that is to be used as aux source + Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A) + } + + impl [<$reg:camel SrcType>] { + fn get_clock_id(&self) -> u8 { + match self { + Self::Src(v) => *v as u8, + Self::Aux(v) => *v as u8, + } + } + + fn unwrap_src(&self) -> pac::clocks::[<$reg _ctrl>]::SRC_A{ + match self { + Self::Src(v) => *v, + Self::Aux(_) => panic!(), + } + } + + fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A { + match self { + Self::Src(_) => panic!(), + Self::Aux(v) => *v + } + } + } + + impl $name { + /// WIP - Helper function to reset source (blocking) + pub fn reset_source_await(&mut self) -> nb::Result<(), ()> { + let shared_dev = unsafe { self.shared_dev.get() }; + + shared_dev.[<$reg _ctrl>].modify(|_,w| { + w.src().variant(self.get_default_clock_source()) + }); + + self.await_select(&ChangingClockToken{clock_nr:0, clock: PhantomData::}) + } + + fn set_src]>>(&mut self, src: &S)-> ChangingClockToken<$name> { + let shared_dev = unsafe { self.shared_dev.get() }; + + shared_dev.[<$reg _ctrl>].modify(|_,w| { + w.src().variant(src.variant().unwrap_src()) + }); + + ChangingClockToken { + clock: PhantomData::<$name>, + clock_nr: src.variant().get_clock_id(), + } + } + + fn set_self_aux_src(&mut self) -> ChangingClockToken<$name> { + unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| { + w.src().variant(self.get_aux_source()) + }); + + ChangingClockToken{ + clock: PhantomData::<$name>, + clock_nr: pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX as u8, + } + } + + /// Configure this clock based on a clock source and desired frequency + pub fn configure_clock]>>(&mut self, src: &S, freq: Hertz) -> bool{ + let src_freq: Hertz = src.get_freq(); + + if freq .gt(& src_freq){ + return false; + } + + // Div register is 24.8) int.frac divider so multiply by 2^8 (left shift by 8) + let div = make_div(src_freq, freq).unwrap(); + + // If increasing divisor, set divisor before source. Otherwise set source + // before divisor. This avoids a momentary overspeed when e.g. switching + // to a faster source and increasing divisor to compensate. + if div > self.get_div() { + self.set_div(div); + } + + // If switching a glitchless slice (ref or sys) to an aux source, switch + // away from aux *first* to avoid passing glitches when changing aux mux. + // Assume (!!!) glitchless source 0 is no faster than the aux source. + nb::block!(self.reset_source_await()).unwrap(); + + + // Set aux mux first, and then glitchless mux if this self has one + let token = if src.is_aux() { + self.set_aux(src); + self.set_self_aux_src() + } else { + self.set_src(src) + }; + + nb::block!(self.await_select(&token)).unwrap(); + + + // Now that the source is configured, we can trust that the user-supplied + // divisor is a safe value. + self.set_div(div); + + // Store the configured frequency + self.frequency = src_freq / div; + + true + } } } }; -} - -macro_rules! frac_division { - ($name:ident, $div:ident, $u:ty) => { - impl FractionDivision for $name { - fn set_frac_div(&mut self, div: usize) { - unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { - w.frac().bits(div as $u); - w - }); - } - - fn get_frac_div(&self) -> usize { - unsafe { self.shared_dev.get() } - .$div - .read() - .frac() - .bits() - .into() - } + { + $( #[$attr:meta])* + struct $name:ident { + reg: $reg:ident, + auxsrc: {$($auxsrc:ident: $variant:ident),*}, + div: false + } + } => { + base_clock!{ + $(#[$attr])* + ($name, $reg, auxsrc={$($auxsrc: $variant),*}) } - }; -} -macro_rules! division { - ($name:ident, $div:ident) => { + // Just to match proper divisable clocks so we don't have to do something special in configure function impl ClockDivision for $name { - fn set_div(&mut self, div: u32) { - unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { - w.bits(div); - w - }); - } + fn set_div(&mut self, _: u32) {} + fn get_div(&self) -> u32 {1} + } - fn get_div(&self) -> u32 { - unsafe { self.shared_dev.get() }.$div.read().bits() + stoppable_clock!($name, $reg); + }; + { + $( #[$attr:meta])* + struct $name:ident { + reg: $reg:ident, + auxsrc: {$($auxsrc:ident: $variant:ident),*} + } + } => { + base_clock!{ + $(#[$attr])* + ($name, $reg, auxsrc={$($auxsrc: $variant),*}) + } + + divisable_clock!($name, $reg); + stoppable_clock!($name, $reg); + }; +} + +macro_rules! divisable_clock { + ($name:ident, $reg:ident) => { + $crate::paste::paste! { + impl ClockDivision for $name { + fn set_div(&mut self, div: u32) { + unsafe { self.shared_dev.get() }.[<$reg _div>].modify(|_, w| unsafe { + w.bits(div); + w + }); + } + fn get_div(&self) -> u32 { + unsafe { self.shared_dev.get() }.[<$reg _div>].read().bits() + } } } }; } -macro_rules! clock_generator { - ($name:ident, $ctrl:ident) => { - impl ClockGenerator for $name { - fn enable(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.enable().set_bit(); - w - }); +macro_rules! stoppable_clock { + ($name:ident, $reg:ident) => { + $crate::paste::paste!{ + /// Holds register value for ClockSource for this clock + pub enum [<$reg:camel SrcType>] { + /// Its an clock source that is to be used as aux source + Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A) } - fn disable(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.enable().clear_bit(); - w - }); + impl [<$reg:camel SrcType>] { + fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A { + match self { + Self::Aux(v) => *v + } + } } - fn kill(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.kill().set_bit(); - w - }); + impl StoppableClock for $name { + fn enable(&mut self) { + unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| { + w.enable().set_bit() + }); + } + + fn disable(&mut self) { + unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| { + w.enable().clear_bit() + }); + } + + fn kill(&mut self) { + unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| { + w.kill().set_bit() + }); + } + } + + impl $name { + /// Configure this clock based on a clock source and desired frequency + pub fn configure_clock]>>(&mut self, src: &S, freq: Hertz) -> bool{ + let src_freq: Hertz = src.get_freq(); + + if freq .gt(& src_freq){ + return false; + } + + // Div register is 24.8) int.frac divider so multiply by 2^8 (left shift by 8) + let div = make_div(src_freq, freq).unwrap(); + + // If increasing divisor, set divisor before source. Otherwise set source + // before divisor. This avoids a momentary overspeed when e.g. switching + // to a faster source and increasing divisor to compensate. + if div > self.get_div() { + self.set_div(div); + } + + // If no glitchless mux, cleanly stop the clock to avoid glitches + // propagating when changing aux mux. Note it would be a really bad idea + // to do this on one of the glitchless clocks (clk_sys, clk_ref). + + // Disable clock. On clk_ref and clk_sys this does nothing, + // all other clocks have the ENABLE bit in the same position. + self.disable(); + if (self.frequency > 0u32.Hz()) { + // Delay for 3 cycles of the target clock, for ENABLE propagation. + // Note XOSC_COUNT is not helpful here because XOSC is not + // necessarily running, nor is timer... so, 3 cycles per loop: + let sys_freq = 125_000_000.Hz(); // TODO + let delay_cyc = sys_freq.div( *self.frequency.integer() ) + 1u32.Hz(); + cortex_m::asm::delay(*delay_cyc.integer()); + } + + // Set aux mux first, and then glitchless mux if this self has one + self.set_aux(src); + + // Enable clock. On clk_ref and clk_sys this does nothing, + // all other clocks have the ENABLE bit in the same posi + self.enable(); + + // Now that the source is configured, we can trust that the user-supplied + // divisor is a safe value. + self.set_div(div); + + // Store the configured frequency + self.frequency = src_freq / div; + true + } } } }; } -macro_rules! xosc_source { - ($name:ident, $ctrl:ident) => { - impl XOSCClockSource for $name { - fn set_xosc_src(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.src().xosc_clksrc(); - w - }); - } - } - }; -} +macro_rules! base_clock { + { + $(#[$attr:meta])* + ($name:ident, $reg:ident, auxsrc={$($auxsrc:ident: $variant:ident),*}) + } => { + $crate::paste::paste!{ -macro_rules! rosc_source { - ($name:ident, $ctrl:ident) => { - impl ROSCClockSource for $name { - fn set_rosc_src(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.src().rosc_clksrc_ph(); - w - }); - } - } - }; -} + $(impl ValidSrc<$name> for $auxsrc { + type Variant = [<$reg:camel SrcType>]; -macro_rules! selfaux_source { - ($name:ident, $ctrl:ident, $self:ident) => { - impl SelfAuxClockSource for $name { - fn set_self_aux_src(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.src().$self(); - w - }); - } - } - }; -} + fn is_aux(&self) -> bool{ + true + } + fn variant(&self) -> [<$reg:camel SrcType>] { + [<$reg:camel SrcType>]::Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant) + } + })* -macro_rules! clockref_source { - ($name:ident, $ctrl:ident) => { - impl ClockREFClockSource for $name { - fn set_clkref_src(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.src().clk_ref(); - w - }); - } - } - }; -} + impl ClocksManager { + #[ doc = "Getter for the" $name ] + pub fn [<$name:snake>](&self) -> $name { -macro_rules! clocksys_auxsource { - ($name:ident, $ctrl:ident) => { - impl ClockSYSClockAuxSource for $name { - fn set_clksys_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clk_sys(); - w - }); - } - } - }; -} + //TODO: Init clock here + $name { + shared_dev: self.shared_clocks, + frequency: 0.Hz(), + } + } -macro_rules! clockusb_auxsource { - ($name:ident, $ctrl:ident) => { - impl ClockUSBClockAuxSource for $name { - fn set_clkusb_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clk_usb(); - w - }); } - } - }; -} + $(#[$attr])* + pub struct $name { + shared_dev: ShareableClocks, + frequency: Hertz, + } -macro_rules! clockadc_auxsource { - ($name:ident, $ctrl:ident) => { - impl ClockADCClockAuxSource for $name { - fn set_clkadc_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clk_adc(); - w - }); - } - } - }; -} + impl $name { + /// Returns the frequency of the configured clock + pub fn freq(&self) -> Hertz { + self.frequency + } -macro_rules! clockrtc_auxsource { - ($name:ident, $ctrl:ident) => { - impl ClockRTCClockAuxSource for $name { - fn set_clkrtc_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clk_rtc(); - w - }); - } - } - }; -} + fn set_aux]>>(&mut self, src: &S) { + let shared_dev = unsafe { self.shared_dev.get() }; -macro_rules! clockref_auxsource { - ($name:ident, $ctrl:ident) => { - impl ClockRefClockAuxSource for $name { - fn set_clkref_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clk_ref(); - w - }); + shared_dev.[<$reg _ctrl>].modify(|_,w| { + w.auxsrc().variant(src.variant().unwrap_aux()) + }); + } } - } - }; -} -macro_rules! xosc_auxsource { - ($name:ident, $ctrl:ident) => { - impl XOSCClockAuxSource for $name { - fn set_xosc_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().xosc_clksrc(); - w - }); - } - } - }; -} + impl Sealed for $name {} -macro_rules! rosc_auxsource { - ($name:ident, $ctrl:ident) => { - impl ROSCClockAuxSource for $name { - fn set_rosc_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().rosc_clksrc(); - w - }); - } - } - }; -} - -macro_rules! rosc_ph_auxsource { - ($name:ident, $ctrl:ident) => { - impl ROSCPHClockAuxSource for $name { - fn set_rosc_ph_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().rosc_clksrc_ph(); - w - }); - } - } - }; -} - -macro_rules! gpin0_auxsource { - ($name:ident, $ctrl:ident) => { - impl Gpin0ClockAuxSource for $name { - fn set_gpin0_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clksrc_gpin0(); - w - }); - } - } - }; -} - -macro_rules! gpin1_auxsource { - ($name:ident, $ctrl:ident) => { - impl Gpin1ClockAuxSource for $name { - fn set_gpin1_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clksrc_gpin1(); - w - }); - } - } - }; -} - -macro_rules! pll_usb_auxsource { - ($name:ident, $ctrl:ident) => { - impl PLLUSBClockAuxSource for $name { - fn set_pll_usb_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clksrc_pll_usb(); - w - }); - } - } - }; -} - -macro_rules! pll_sys_auxsource { - ($name:ident, $ctrl:ident) => { - impl PLLSYSClockAuxSource for $name { - fn set_pll_sys_auxsrc(&mut self) { - unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { - w.auxsrc().clksrc_pll_sys(); - w - }); + impl From<$name> for Hertz + { + fn from(value: $name) -> Hertz { + value.frequency + } } } }; diff --git a/rp2040-hal/src/clocks/mod.rs b/rp2040-hal/src/clocks/mod.rs index e9a80ea..2d98a70 100644 --- a/rp2040-hal/src/clocks/mod.rs +++ b/rp2040-hal/src/clocks/mod.rs @@ -10,34 +10,58 @@ //! // Enable the xosc //! let xosc = setup_xosc_blocking(p.XOSC, XOSC_MHZ.Hz()).ok().unwrap(); //! -//! //! // Configure PLLs //! // REF FBDIV VCO POSTDIV //! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz //! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz -//! let pll_sys = -//! setup_pll_blocking(p.PLL_SYS, 12.MHz().into(), PLL_SYS_125MHZ, &mut clocks, &mut p.RESETS).ok().unwrap(); -//! let pll_usb = -//! setup_pll_blocking(p.PLL_USB, 12.MHz().into(), PLL_USB_48MHZ, &mut clocks, &mut p.RESETS).ok().unwrap(); -//! clocks.init(&xosc, &pll_sys, &pll_usb); +//! let pll_sys = setup_pll_blocking(p.PLL_SYS, 12.MHz().into(), PLL_SYS_125MHZ, &mut clocks, &mut p.RESETS).ok().unwrap(); +//! let pll_usb = setup_pll_blocking(p.PLL_USB, 12.MHz().into(), PLL_USB_48MHZ, &mut clocks, &mut p.RESETS).ok().unwrap(); +//! +//! // Configure clocks +//! // CLK_REF = XOSC (12MHz) / 1 = 12MHz +//! let mut ref_clock = clocks.reference_clock(); +//! ref_clock.configure_clock(&xosc, xosc.get_freq()); +//! +//! // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz +//! let mut sys_clock = clocks.system_clock(); +//! sys_clock.configure_clock(&pll_sys, pll_sys.get_freq()); +//! +//! // CLK USB = PLL USB (48MHz) / 1 = 48MHz +//! let mut usb_clock = clocks.usb_clock(); +//! usb_clock.configure_clock(&pll_usb, pll_usb.get_freq()); +//! +//! // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz +//! let mut adc_clock = clocks.adc_clock(); +//! adc_clock.configure_clock(&pll_usb, pll_usb.get_freq()); +//! +//! // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz +//! let mut rtc_clock = clocks.rtc_clock(); +//! rtc_clock.configure_clock(&pll_usb, 46875u32.Hz()); +//! +//! // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable +//! // Normally choose clk_sys or clk_usb +//! let mut peripheral_clock = clocks.peripheral_clock(); +//! peripheral_clock.configure_clock(&sys_clock, sys_clock.freq()); +//! //! ``` //! //! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details use crate::{ + clocks::available_clocks::ClockSource, pll::{Locked, PhaseLockedLoop}, watchdog::Watchdog, xosc::{CrystalOscillator, Stable}, }; -use core::convert::TryInto; use embedded_time::rate::*; -use pac::{clocks, CLOCKS, PLL_SYS, PLL_USB}; +use pac::{CLOCKS, PLL_SYS, PLL_USB}; #[macro_use] mod macros; +pub mod available_clocks; #[derive(Copy, Clone)] /// Provides refs to the CLOCKS block. -pub struct ShareableClocks { +struct ShareableClocks { _internal: (), } @@ -46,22 +70,13 @@ impl ShareableClocks { ShareableClocks { _internal: () } } - unsafe fn get(&self) -> &clocks::RegisterBlock { + unsafe fn get(&self) -> &pac::clocks::RegisterBlock { &*CLOCKS::ptr() } } const XOSC_MHZ: u32 = 12_000_000_u32; -fn make_div>, F: TryInto>>( - src_freq: S, - freq: F, -) -> Result { - let src_freq = *src_freq.try_into().map_err(|_| ())?.integer(); - let freq = *freq.try_into().map_err(|_| ())?.integer(); - let div: u64 = (src_freq << 8).wrapping_div(freq); - Ok(div as u32) -} /// Abstraction layer providing Clock Management. pub struct ClocksManager { clocks: CLOCKS, @@ -83,516 +98,42 @@ impl ClocksManager { } } - /// Initialize the clocks - pub fn init( + /// Initialize the clocks to a sane default + pub fn init_default( &self, - _: &CrystalOscillator, - _: &PhaseLockedLoop, - _: &PhaseLockedLoop, + xosc: &CrystalOscillator, + pll_sys: &PhaseLockedLoop, + pll_usb: &PhaseLockedLoop, ) { // Configure clocks // CLK_REF = XOSC (12MHz) / 1 = 12MHz - let mut ref_clock = self.ref_clock(); - let div = make_div(12u32.MHz(), 12u32.MHz()).unwrap(); - // If increasing divisor, set divisor before source. - if div > ref_clock.get_div() { - ref_clock.set_div(div); - } - ref_clock.set_xosc_src(); - ref_clock.await_select(2); - ref_clock.set_div(div); + let mut ref_clock = self.reference_clock(); + ref_clock.configure_clock(xosc, xosc.get_freq()); // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz - let mut sys_clock = self.sys_clock(); - let div = make_div(125u32.MHz(), 125u32.MHz()).unwrap(); - // If increasing divisor, set divisor before source. - if div > sys_clock.get_div() { - sys_clock.set_div(div); - } - sys_clock.set_pll_sys_auxsrc(); - sys_clock.set_self_aux_src(); - sys_clock.await_select(1); - sys_clock.set_div(div); + let mut sys_clock = self.system_clock(); + sys_clock.configure_clock(pll_sys, pll_sys.get_freq()); // CLK USB = PLL USB (48MHz) / 1 = 48MHz let mut usb_clock = self.usb_clock(); - let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap(); - // If increasing divisor, set divisor before source. - if div > usb_clock.get_div() { - usb_clock.set_div(div); - } - usb_clock.disable(); - usb_clock.set_pll_usb_auxsrc(); - usb_clock.enable(); - usb_clock.set_div(div); + usb_clock.configure_clock(pll_usb, pll_usb.get_freq()); // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz let mut adc_clock = self.adc_clock(); - let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap(); - // If increasing divisor, set divisor before source. - if div > adc_clock.get_div() { - adc_clock.set_div(div); - } - adc_clock.disable(); - adc_clock.set_pll_usb_auxsrc(); - adc_clock.enable(); - adc_clock.set_div(div); + adc_clock.configure_clock(pll_usb, pll_usb.get_freq()); // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz let mut rtc_clock = self.rtc_clock(); - let div = make_div(48u32.MHz(), 46875u32.Hz()).unwrap(); - // If increasing divisor, set divisor before source. - if div > rtc_clock.get_div() { - rtc_clock.set_div(div); - } - rtc_clock.disable(); - rtc_clock.set_pll_usb_auxsrc(); - rtc_clock.enable(); - rtc_clock.set_div(div); + rtc_clock.configure_clock(pll_usb, 46875u32.Hz()); // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable // Normally choose clk_sys or clk_usb let mut peripheral_clock = self.peripheral_clock(); - peripheral_clock.disable(); - peripheral_clock.set_clksys_auxsrc(); - peripheral_clock.enable(); + peripheral_clock.configure_clock(&sys_clock, sys_clock.freq()); } /// Releases the CLOCKS block pub fn free(self) -> CLOCKS { self.clocks } - - /// Getter for the GPIO Output 0 Clock. - pub fn gpio_output0_clock(&self) -> GpioOutput0Clock { - GpioOutput0Clock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the GPIO Output 1 Clock. - pub fn gpio_output1_clock(&self) -> GpioOutput1Clock { - GpioOutput1Clock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the GPIO Output 2 Clock. - pub fn gpio_output2_clock(&self) -> GpioOutput2Clock { - GpioOutput2Clock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the GPIO Output 3 Clock. - pub fn gpio_output3_clock(&self) -> GpioOutput3Clock { - GpioOutput3Clock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the Reference Clock. - pub fn ref_clock(&self) -> ReferenceClock { - ReferenceClock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the System Clock - pub fn sys_clock(&self) -> SystemClock { - SystemClock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the PeripheralClock - pub fn peripheral_clock(&self) -> PeripheralClock { - PeripheralClock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the Usb Clock - pub fn usb_clock(&self) -> UsbClock { - UsbClock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the Adc Clock - pub fn adc_clock(&self) -> AdcClock { - AdcClock { - shared_dev: self.shared_clocks, - } - } - - /// Getter for the Rtc Clock - pub fn rtc_clock(&self) -> RtcClock { - RtcClock { - shared_dev: self.shared_clocks, - } - } } - -/// For clocks with an integer divider. -pub trait IntegerDivision { - /// Set integer divider value. - fn set_int_div(&mut self, div: usize); - /// Get integer diveder value. - fn get_int_div(&self) -> usize; -} - -/// For clocks with a fraction divider. -pub trait FractionDivision { - /// Set fraction divider value. - fn set_frac_div(&mut self, div: usize); - /// Get fraction divider value. - fn get_frac_div(&self) -> usize; -} - -/// For clocks with a divider -pub trait ClockDivision { - /// Set integer divider value. - fn set_div(&mut self, div: u32); - /// Get integer diveder value. - fn get_div(&self) -> u32; -} - -/// For clocks that can have XOSC as source. -pub trait XOSCClockSource { - /// Set XOSC as a source. - fn set_xosc_src(&mut self); -} -/// For clocks that can have ROSC as source. -pub trait ROSCClockSource { - /// set ROSC as a source. - fn set_rosc_src(&mut self); -} -/// For clocks that can have ... itself (?) as a source (is that the "glitchless mux" ?) -pub trait SelfAuxClockSource { - /// Set ... - fn set_self_aux_src(&mut self); -} -/// For clocks that can have the Reference Clock as source. -pub trait ClockREFClockSource { - /// Set Reference Clock as - fn set_clkref_src(&mut self); -} -/// For clocks that can have the System Clock as an auxilliary source. -pub trait ClockSYSClockAuxSource { - /// Set System Clock as source. - fn set_clksys_auxsrc(&mut self); -} - -/// For clocks that can have the USB Clock as an auxilliary source. -pub trait ClockUSBClockAuxSource { - /// Set USB Clock as source. - fn set_clkusb_auxsrc(&mut self); -} - -/// For clocks that can have the ADC Clock as an auxilliary source. -pub trait ClockADCClockAuxSource { - /// Set ADC Clock as source. - fn set_clkadc_auxsrc(&mut self); -} - -/// For clocks that can have the RTC Clock as an auxilliary source. -pub trait ClockRTCClockAuxSource { - /// Set RTC Clock as source. - fn set_clkrtc_auxsrc(&mut self); -} - -/// For clocks that can have the Reference Clock as an auxilliary source. -pub trait ClockRefClockAuxSource { - /// Set Reference Clock as source. - fn set_clkref_auxsrc(&mut self); -} -/// For clocks that can have XOSC as an auxilliary source. -pub trait XOSCClockAuxSource { - /// Set XOSC as auxilliary source. - fn set_xosc_auxsrc(&mut self); -} -/// For clocks that can have ROSC as an auxilliary source. -pub trait ROSCClockAuxSource { - /// Set ROSC as auxilliary source. - fn set_rosc_auxsrc(&mut self); -} -/// For clocks that can have ROSC_PH as an auxilliary source. -pub trait ROSCPHClockAuxSource { - /// Set ROSC_PH as auxilliary source. - fn set_rosc_ph_auxsrc(&mut self); -} -/// For clocks that can have PLL_USB as an auxilliary source. -pub trait PLLUSBClockAuxSource { - /// Set PLL_USB as auxilliary source. - fn set_pll_usb_auxsrc(&mut self); -} -/// For clocks that can have PLL_SYS as an auxilliary source. -pub trait PLLSYSClockAuxSource { - /// Set PLL_SYS as auxilliary source. - fn set_pll_sys_auxsrc(&mut self); -} -/// For clocks that can have gpin0 as an auxilliary source. -pub trait Gpin0ClockAuxSource { - /// Set clock to be received from gpin0 (auxilliary) - fn set_gpin0_auxsrc(&mut self); -} -/// For clocks that can have gpin1 as an auxilliary source. -pub trait Gpin1ClockAuxSource { - /// Set clock to be received from gpin1 - fn set_gpin1_auxsrc(&mut self); -} - -/// For clocks having a generator. -pub trait ClockGenerator { - /// Enables the clock. - fn enable(&mut self); - - /// Disables the clock. - fn disable(&mut self); - - /// Kills the clock. - fn kill(&mut self); -} - -/// GPIO Output 0 Clock -pub struct GpioOutput0Clock { - shared_dev: ShareableClocks, -} - -clock_generator!(GpioOutput0Clock, clk_gpout0_ctrl); - -// Clock aux sources -pll_sys_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -gpin0_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -gpin1_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -pll_usb_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -rosc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -xosc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -clocksys_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -clockusb_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -clockadc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -clockrtc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); -clockref_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl); - -division!(GpioOutput0Clock, clk_gpout0_div); -int_division!(GpioOutput0Clock, clk_gpout0_div, u32); -frac_division!(GpioOutput0Clock, clk_gpout0_div, u8); - -/// GPIO Output 1 Clock -pub struct GpioOutput1Clock { - shared_dev: ShareableClocks, -} - -clock_generator!(GpioOutput1Clock, clk_gpout1_ctrl); - -// Clock aux sources -pll_sys_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -gpin0_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -gpin1_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -pll_usb_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -rosc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -xosc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -clocksys_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -clockusb_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -clockadc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -clockrtc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); -clockref_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl); - -division!(GpioOutput1Clock, clk_gpout1_div); -int_division!(GpioOutput1Clock, clk_gpout1_div, u32); -frac_division!(GpioOutput1Clock, clk_gpout1_div, u8); - -/// GPIO Output 2 Clock -pub struct GpioOutput2Clock { - shared_dev: ShareableClocks, -} - -clock_generator!(GpioOutput2Clock, clk_gpout2_ctrl); - -// Clock aux sources -pll_sys_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -gpin0_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -gpin1_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -pll_usb_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -rosc_ph_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -xosc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -clocksys_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -clockusb_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -clockadc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -clockrtc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); -clockref_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl); - -division!(GpioOutput2Clock, clk_gpout2_div); -int_division!(GpioOutput2Clock, clk_gpout2_div, u32); -frac_division!(GpioOutput2Clock, clk_gpout2_div, u8); - -/// GPIO Output 3 Clock -pub struct GpioOutput3Clock { - shared_dev: ShareableClocks, -} - -clock_generator!(GpioOutput3Clock, clk_gpout3_ctrl); - -// Clock aux sources -pll_sys_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -gpin0_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -gpin1_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -pll_usb_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -rosc_ph_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -xosc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -clocksys_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -clockusb_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -clockadc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -clockrtc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); -clockref_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl); - -division!(GpioOutput3Clock, clk_gpout3_div); -int_division!(GpioOutput3Clock, clk_gpout3_div, u32); -frac_division!(GpioOutput3Clock, clk_gpout3_div, u8); - -/// Reference Clock -pub struct ReferenceClock { - shared_dev: ShareableClocks, -} - -impl ReferenceClock { - /// WIP - Helper function to reset source (blocking) - pub fn reset_source_await(&mut self) { - let shared_dev = unsafe { self.shared_dev.get() }; - - shared_dev.clk_ref_ctrl.write(|w| { - unsafe { w.src().bits(0) }; - w - }); - - self.await_select(0x0); - } - - /// WIP - Helper function to select new source (blocking) - pub fn await_select(&self, clock: u8) { - let shared_dev = unsafe { self.shared_dev.get() }; - - while (shared_dev.clk_ref_selected.read().bits() & (1 << clock)) == 0 { - cortex_m::asm::nop(); - } - } -} - -rosc_source!(ReferenceClock, clk_ref_ctrl); -selfaux_source!(ReferenceClock, clk_ref_ctrl, clksrc_clk_ref_aux); -xosc_source!(ReferenceClock, clk_ref_ctrl); - -// Clock aux sources -pll_usb_auxsource!(ReferenceClock, clk_ref_ctrl); -gpin0_auxsource!(ReferenceClock, clk_ref_ctrl); -gpin1_auxsource!(ReferenceClock, clk_ref_ctrl); - -division!(ReferenceClock, clk_ref_div); -/// System Clock -pub struct SystemClock { - shared_dev: ShareableClocks, -} -impl SystemClock { - /// WIP - Helper function to reset source (blocking) - pub fn reset_source_await(&mut self) { - let shared_dev = unsafe { self.shared_dev.get() }; - - shared_dev.clk_sys_ctrl.write(|w| { - w.src().clear_bit(); - w - }); - - self.await_select(0x0); - } - - /// WIP - Helper function to select new source (blocking) - pub fn await_select(&self, clock: u8) { - let shared_dev = unsafe { self.shared_dev.get() }; - - while (shared_dev.clk_sys_selected.read().bits() & (1 << clock)) == 0 { - cortex_m::asm::nop(); - } - } -} - -// Clock glitchless sources -clockref_source!(SystemClock, clk_sys_ctrl); -selfaux_source!(SystemClock, clk_sys_ctrl, clksrc_clk_sys_aux); - -// Clock aux sources -pll_sys_auxsource!(SystemClock, clk_sys_ctrl); -pll_usb_auxsource!(SystemClock, clk_sys_ctrl); -rosc_auxsource!(SystemClock, clk_sys_ctrl); -xosc_auxsource!(SystemClock, clk_sys_ctrl); -gpin0_auxsource!(SystemClock, clk_sys_ctrl); -gpin1_auxsource!(SystemClock, clk_sys_ctrl); - -division!(SystemClock, clk_sys_div); -int_division!(SystemClock, clk_sys_div, u32); -frac_division!(SystemClock, clk_sys_div, u8); - -/// Peripheral Clock -pub struct PeripheralClock { - shared_dev: ShareableClocks, -} -clock_generator!(PeripheralClock, clk_peri_ctrl); - -// Clock aux sources -clocksys_auxsource!(PeripheralClock, clk_peri_ctrl); -pll_sys_auxsource!(PeripheralClock, clk_peri_ctrl); -pll_usb_auxsource!(PeripheralClock, clk_peri_ctrl); -rosc_ph_auxsource!(PeripheralClock, clk_peri_ctrl); -xosc_auxsource!(PeripheralClock, clk_peri_ctrl); -gpin0_auxsource!(PeripheralClock, clk_peri_ctrl); -gpin1_auxsource!(PeripheralClock, clk_peri_ctrl); - -/// USB Clock -pub struct UsbClock { - shared_dev: ShareableClocks, -} -clock_generator!(UsbClock, clk_usb_ctrl); - -// Clock aux sources -pll_usb_auxsource!(UsbClock, clk_usb_ctrl); -pll_sys_auxsource!(UsbClock, clk_usb_ctrl); -rosc_ph_auxsource!(UsbClock, clk_usb_ctrl); -xosc_auxsource!(UsbClock, clk_usb_ctrl); -gpin0_auxsource!(UsbClock, clk_usb_ctrl); -gpin1_auxsource!(UsbClock, clk_usb_ctrl); - -division!(UsbClock, clk_usb_div); - -/// Adc Clock -pub struct AdcClock { - shared_dev: ShareableClocks, -} -clock_generator!(AdcClock, clk_adc_ctrl); - -// Clock aux sources -pll_usb_auxsource!(AdcClock, clk_adc_ctrl); -pll_sys_auxsource!(AdcClock, clk_adc_ctrl); -rosc_ph_auxsource!(AdcClock, clk_adc_ctrl); -xosc_auxsource!(AdcClock, clk_adc_ctrl); -gpin0_auxsource!(AdcClock, clk_adc_ctrl); -gpin1_auxsource!(AdcClock, clk_adc_ctrl); - -division!(AdcClock, clk_adc_div); - -/// RTC Clock -pub struct RtcClock { - shared_dev: ShareableClocks, -} -clock_generator!(RtcClock, clk_rtc_ctrl); - -// Clock aux sources -pll_usb_auxsource!(RtcClock, clk_rtc_ctrl); -pll_sys_auxsource!(RtcClock, clk_rtc_ctrl); -rosc_ph_auxsource!(RtcClock, clk_rtc_ctrl); -xosc_auxsource!(RtcClock, clk_rtc_ctrl); -gpin0_auxsource!(RtcClock, clk_rtc_ctrl); -gpin1_auxsource!(RtcClock, clk_rtc_ctrl); - -division!(RtcClock, clk_rtc_div); -int_division!(RtcClock, clk_rtc_div, u32); -frac_division!(RtcClock, clk_rtc_div, u8); diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index b459b6d..9b25d6a 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -23,6 +23,7 @@ pub mod prelude; pub mod pwm; pub mod resets; pub mod rom_data; +pub mod rosc; pub mod rtc; pub mod sio; pub mod spi; diff --git a/rp2040-hal/src/pll.rs b/rp2040-hal/src/pll.rs index c6f0878..c952551 100644 --- a/rp2040-hal/src/pll.rs +++ b/rp2040-hal/src/pll.rs @@ -26,16 +26,20 @@ pub struct Disabled { fbdiv: u16, post_div1: u8, post_div2: u8, + frequency: Hertz, } /// PLL is configured, started and locking into its designated frequency. pub struct Locking { post_div1: u8, post_div2: u8, + frequency: Hertz, } /// PLL is locked : it delivers a steady frequency. -pub struct Locked; +pub struct Locked { + frequency: Hertz, +} impl State for Disabled {} impl State for Locked {} @@ -183,6 +187,8 @@ impl PhaseLockedLoop { let refdiv = config.refdiv; let post_div1 = config.post_div1; let post_div2 = config.post_div2; + let frequency: Hertz = + (ref_freq_hz / refdiv as u32) * fbdiv as u32 / (post_div1 as u32 * post_div2 as u32); Ok(PhaseLockedLoop { state: Disabled { @@ -190,6 +196,7 @@ impl PhaseLockedLoop { fbdiv, post_div1, post_div2, + frequency, }, device: dev, }) @@ -222,10 +229,12 @@ impl PhaseLockedLoop { let post_div1 = self.state.post_div1; let post_div2 = self.state.post_div2; + let frequency = self.state.frequency; self.transition(Locking { post_div1, post_div2, + frequency, }) } } @@ -262,7 +271,16 @@ impl PhaseLockedLoop { w }); - self.transition(Locked) + let frequency = self.state.frequency; + + self.transition(Locked { frequency }) + } +} + +impl PhaseLockedLoop { + /// Get the operating frequency for the PLL + pub fn operating_frequency(&self) -> Hertz { + self.state.frequency } } @@ -278,11 +296,11 @@ where R: Into>, { // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. - let mut sys_clock = clocks.sys_clock(); - sys_clock.reset_source_await(); + let mut sys_clock = clocks.system_clock(); + nb::block!(sys_clock.reset_source_await()).unwrap(); - let mut ref_clock = clocks.ref_clock(); - ref_clock.reset_source_await(); + let mut ref_clock = clocks.reference_clock(); + nb::block!(ref_clock.reset_source_await()).unwrap(); let initialized_pll = PhaseLockedLoop::new(dev, xosc_frequency, config)?.initialize(resets); diff --git a/rp2040-hal/src/rosc.rs b/rp2040-hal/src/rosc.rs new file mode 100644 index 0000000..520d0ea --- /dev/null +++ b/rp2040-hal/src/rosc.rs @@ -0,0 +1,99 @@ +//! Ring Oscillator (ROSC) +// See [Chapter 2 Section 17](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details + +use embedded_time::rate::Extensions; +use embedded_time::rate::Hertz; + +/// State of the Ring Oscillator (typestate trait) +pub trait State {} + +/// ROSC is disabled (typestate) +pub struct Disabled; + +/// ROSC is initialized, ie we've given parameters (typestate) +pub struct Enabled { + freq_hz: Hertz, +} + +/// ROSC is in dormant mode (see Chapter 2, Section 17, §7) +pub struct Dormant; + +impl State for Disabled {} +impl State for Enabled {} +impl State for Dormant {} + +/// A Ring Oscillator. +pub struct RingOscillator { + device: rp2040_pac::ROSC, + state: S, +} + +impl RingOscillator { + /// Transitions the oscillator to another state. + fn transition(self, state: To) -> RingOscillator { + RingOscillator { + device: self.device, + state, + } + } + + /// Releases the underlying device. + pub fn free(self) -> rp2040_pac::ROSC { + self.device + } +} + +impl RingOscillator { + /// Creates a new RingOscillator from the underlying device. + pub fn new(dev: rp2040_pac::ROSC) -> Self { + RingOscillator { + device: dev, + state: Disabled, + } + } + + /// Initializes the ROSC : frequency range is set, startup delay is calculated and set. + pub fn initialize(self) -> RingOscillator { + self.device.ctrl.write(|w| w.enable().enable()); + + self.transition(Enabled { + freq_hz: 6_500_000u32.Hz(), + }) + } +} + +impl RingOscillator { + /// Approx operating frequency of the ROSC in hertz + pub fn operating_frequency(&self) -> Hertz { + self.state.freq_hz + } + + /// Disables the ROSC + pub fn disable(self) -> RingOscillator { + self.device.ctrl.modify(|_r, w| w.enable().disable()); + + self.transition(Disabled) + } + + /// Generate random bit based on the Ring oscillator + /// This is not suited for security purposes + pub fn get_random_bit(&self) -> bool { + self.device.randombit.read().randombit().bit() + } + + /// Put the ROSC in DORMANT state. + /// + /// # Safety + /// This method is marked unsafe because prior to switch the ROSC into DORMANT state, + /// PLLs must be stopped and IRQs have to be properly configured. + /// This method does not do any of that, it merely switches the ROSC to DORMANT state. + /// See Chapter 2, Section 16, §5) for details. + pub unsafe fn dormant(self) -> RingOscillator { + //taken from the C SDK + const ROSC_DORMANT_VALUE: u32 = 0x636f6d61; + + self.device.dormant.write(|w| w.bits(ROSC_DORMANT_VALUE)); + + self.transition(Dormant) + } +}