Refactor clocks (#54)

* Remove unneeded lines

* Reduce macro boilerplate

* Refactor clocks
This commit is contained in:
Hmvp 2021-07-08 12:58:48 +02:00 committed by GitHub
parent d2aa2b238d
commit f310d92b64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 752 additions and 763 deletions

View file

@ -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<S: TryInto<Hertz<u64>>, F: TryInto<Hertz<u64>>>(
src_freq: S,
freq: F,
) -> Result<u32, ()> {
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<Self::Clock>) -> nb::Result<(), ()>;
}
/// Token which can be used to await the glitchless switch
pub struct ChangingClockToken<G: GlitchlessClock> {
clock_nr: u8,
clock: PhantomData<G>,
}
/// 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<Locked, PLL_SYS>;
impl Sealed for PllSys {}
impl ClockSource for PllSys {
fn get_freq(&self) -> Hertz {
self.operating_frequency()
}
}
type PllUsb = PhaseLockedLoop<Locked, PLL_USB>;
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<Stable>;
impl Sealed for Xosc {}
impl ClockSource for Xosc {
fn get_freq(&self) -> Hertz {
self.operating_frequency()
}
}
type Rosc = RingOscillator<Enabled>;
impl Sealed for Rosc {}
// We are assuming the second output is never phase shifted (see 2.17.4)
impl ClockSource for RingOscillator<Enabled> {
fn get_freq(&self) -> Hertz {
self.operating_frequency()
}
}
// GPIN0
type GPin0 = Pin<Gpio20, FunctionClock>;
impl ClockSource for GPin0 {
fn get_freq(&self) -> Hertz {
todo!()
}
}
// GPIN1
type GPin1 = Pin<Gpio22, FunctionClock>;
impl ClockSource for Pin<Gpio22, FunctionClock> {
fn get_freq(&self) -> Hertz {
todo!()
}
}
/// Trait to contrain which ClockSource is valid for which Clock
pub trait ValidSrc<Clock>: 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
}
}

View file

@ -1,289 +1,366 @@
macro_rules! int_division { macro_rules! clock {
($name:ident, $div:ident, $u:ty) => { {
impl IntegerDivision for $name { $(#[$attr:meta])*
fn set_int_div(&mut self, div: usize) { struct $name:ident {
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { reg: $reg:ident,
w.int().bits(div as $u); src: {$($src:ident: $src_variant:ident),*},
w auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
}); }
} => {
base_clock!{
$(#[$attr])*
($name, $reg, auxsrc={$($auxsrc: $aux_variant),*})
} }
fn get_int_div(&self) -> usize { divisable_clock!($name, $reg);
unsafe { self.shared_dev.get() }.$div.read().int().bits() as usize
$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<Self>) -> 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(())
}
}
/// 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::<Self>})
}
fn set_src<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&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<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&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
}
} }
} }
}; };
} {
$( #[$attr:meta])*
macro_rules! frac_division { struct $name:ident {
($name:ident, $div:ident, $u:ty) => { reg: $reg:ident,
impl FractionDivision for $name { auxsrc: {$($auxsrc:ident: $variant:ident),*},
fn set_frac_div(&mut self, div: usize) { div: false
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { }
w.frac().bits(div as $u); } => {
w base_clock!{
}); $(#[$attr])*
($name, $reg, auxsrc={$($auxsrc: $variant),*})
} }
fn get_frac_div(&self) -> usize { // Just to match proper divisable clocks so we don't have to do something special in configure function
unsafe { self.shared_dev.get() } impl ClockDivision for $name {
.$div fn set_div(&mut self, _: u32) {}
.read() fn get_div(&self) -> u32 {1}
.frac()
.bits()
.into()
} }
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! division { macro_rules! divisable_clock {
($name:ident, $div:ident) => { ($name:ident, $reg:ident) => {
$crate::paste::paste! {
impl ClockDivision for $name { impl ClockDivision for $name {
fn set_div(&mut self, div: u32) { fn set_div(&mut self, div: u32) {
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe { unsafe { self.shared_dev.get() }.[<$reg _div>].modify(|_, w| unsafe {
w.bits(div); w.bits(div);
w w
}); });
} }
fn get_div(&self) -> u32 { fn get_div(&self) -> u32 {
unsafe { self.shared_dev.get() }.$div.read().bits() unsafe { self.shared_dev.get() }.[<$reg _div>].read().bits()
}
} }
} }
}; };
} }
macro_rules! clock_generator { macro_rules! stoppable_clock {
($name:ident, $ctrl:ident) => { ($name:ident, $reg:ident) => {
impl ClockGenerator for $name { $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)
}
impl [<$reg:camel SrcType>] {
fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
match self {
Self::Aux(v) => *v
}
}
}
impl StoppableClock for $name {
fn enable(&mut self) { fn enable(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
w.enable().set_bit(); w.enable().set_bit()
w
}); });
} }
fn disable(&mut self) { fn disable(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
w.enable().clear_bit(); w.enable().clear_bit()
w
}); });
} }
fn kill(&mut self) { fn kill(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
w.kill().set_bit(); w.kill().set_bit()
w
}); });
} }
} }
impl $name {
/// Configure this clock based on a clock source and desired frequency
pub fn configure_clock<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&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 { macro_rules! base_clock {
($name:ident, $ctrl:ident) => { {
impl XOSCClockSource for $name { $(#[$attr:meta])*
fn set_xosc_src(&mut self) { ($name:ident, $reg:ident, auxsrc={$($auxsrc:ident: $variant:ident),*})
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| { } => {
w.src().xosc_clksrc(); $crate::paste::paste!{
w
});
}
}
};
}
macro_rules! rosc_source { $(impl ValidSrc<$name> for $auxsrc {
($name:ident, $ctrl:ident) => { type Variant = [<$reg:camel SrcType>];
impl ROSCClockSource for $name {
fn set_rosc_src(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.src().rosc_clksrc_ph();
w
});
}
}
};
}
macro_rules! selfaux_source { fn is_aux(&self) -> bool{
($name:ident, $ctrl:ident, $self:ident) => { true
impl SelfAuxClockSource for $name {
fn set_self_aux_src(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.src().$self();
w
});
} }
fn variant(&self) -> [<$reg:camel SrcType>] {
[<$reg:camel SrcType>]::Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant)
} }
}; })*
}
macro_rules! clockref_source { impl ClocksManager {
($name:ident, $ctrl:ident) => { #[ doc = "Getter for the" $name ]
impl ClockREFClockSource for $name { pub fn [<$name:snake>](&self) -> $name {
fn set_clkref_src(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.src().clk_ref();
w
});
}
}
};
}
macro_rules! clocksys_auxsource { //TODO: Init clock here
($name:ident, $ctrl:ident) => { $name {
impl ClockSYSClockAuxSource for $name { shared_dev: self.shared_clocks,
fn set_clksys_auxsrc(&mut self) { frequency: 0.Hz(),
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.auxsrc().clk_sys();
w
});
} }
} }
};
}
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 { impl $name {
($name:ident, $ctrl:ident) => { /// Returns the frequency of the configured clock
impl ClockADCClockAuxSource for $name { pub fn freq(&self) -> Hertz {
fn set_clkadc_auxsrc(&mut self) { self.frequency
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.auxsrc().clk_adc();
w
});
} }
}
};
}
macro_rules! clockrtc_auxsource { fn set_aux<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&mut self, src: &S) {
($name:ident, $ctrl:ident) => { let shared_dev = unsafe { self.shared_dev.get() };
impl ClockRTCClockAuxSource for $name {
fn set_clkrtc_auxsrc(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.auxsrc().clk_rtc();
w
});
}
}
};
}
macro_rules! clockref_auxsource { shared_dev.[<$reg _ctrl>].modify(|_,w| {
($name:ident, $ctrl:ident) => { w.auxsrc().variant(src.variant().unwrap_aux())
impl ClockRefClockAuxSource for $name {
fn set_clkref_auxsrc(&mut self) {
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
w.auxsrc().clk_ref();
w
}); });
} }
} }
};
}
macro_rules! xosc_auxsource { impl Sealed for $name {}
($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
});
}
}
};
}
macro_rules! rosc_auxsource { impl From<$name> for Hertz
($name:ident, $ctrl:ident) => { {
impl ROSCClockAuxSource for $name { fn from(value: $name) -> Hertz {
fn set_rosc_auxsrc(&mut self) { value.frequency
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
});
} }
} }
}; };

View file

@ -10,34 +10,58 @@
//! // Enable the xosc //! // Enable the xosc
//! let xosc = setup_xosc_blocking(p.XOSC, XOSC_MHZ.Hz()).ok().unwrap(); //! let xosc = setup_xosc_blocking(p.XOSC, XOSC_MHZ.Hz()).ok().unwrap();
//! //!
//!
//! // Configure PLLs //! // Configure PLLs
//! // REF FBDIV VCO POSTDIV //! // REF FBDIV VCO POSTDIV
//! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz //! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
//! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz //! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
//! let pll_sys = //! let pll_sys = setup_pll_blocking(p.PLL_SYS, 12.MHz().into(), PLL_SYS_125MHZ, &mut clocks, &mut p.RESETS).ok().unwrap();
//! 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();
//! let pll_usb = //!
//! setup_pll_blocking(p.PLL_USB, 12.MHz().into(), PLL_USB_48MHZ, &mut clocks, &mut p.RESETS).ok().unwrap(); //! // Configure clocks
//! clocks.init(&xosc, &pll_sys, &pll_usb); //! // 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 //! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
use crate::{ use crate::{
clocks::available_clocks::ClockSource,
pll::{Locked, PhaseLockedLoop}, pll::{Locked, PhaseLockedLoop},
watchdog::Watchdog, watchdog::Watchdog,
xosc::{CrystalOscillator, Stable}, xosc::{CrystalOscillator, Stable},
}; };
use core::convert::TryInto;
use embedded_time::rate::*; use embedded_time::rate::*;
use pac::{clocks, CLOCKS, PLL_SYS, PLL_USB}; use pac::{CLOCKS, PLL_SYS, PLL_USB};
#[macro_use] #[macro_use]
mod macros; mod macros;
pub mod available_clocks;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
/// Provides refs to the CLOCKS block. /// Provides refs to the CLOCKS block.
pub struct ShareableClocks { struct ShareableClocks {
_internal: (), _internal: (),
} }
@ -46,22 +70,13 @@ impl ShareableClocks {
ShareableClocks { _internal: () } ShareableClocks { _internal: () }
} }
unsafe fn get(&self) -> &clocks::RegisterBlock { unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
&*CLOCKS::ptr() &*CLOCKS::ptr()
} }
} }
const XOSC_MHZ: u32 = 12_000_000_u32; const XOSC_MHZ: u32 = 12_000_000_u32;
fn make_div<S: TryInto<Hertz<u64>>, F: TryInto<Hertz<u64>>>(
src_freq: S,
freq: F,
) -> Result<u32, ()> {
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. /// Abstraction layer providing Clock Management.
pub struct ClocksManager { pub struct ClocksManager {
clocks: CLOCKS, clocks: CLOCKS,
@ -83,516 +98,42 @@ impl ClocksManager {
} }
} }
/// Initialize the clocks /// Initialize the clocks to a sane default
pub fn init( pub fn init_default(
&self, &self,
_: &CrystalOscillator<Stable>, xosc: &CrystalOscillator<Stable>,
_: &PhaseLockedLoop<Locked, PLL_SYS>, pll_sys: &PhaseLockedLoop<Locked, PLL_SYS>,
_: &PhaseLockedLoop<Locked, PLL_USB>, pll_usb: &PhaseLockedLoop<Locked, PLL_USB>,
) { ) {
// Configure clocks // Configure clocks
// CLK_REF = XOSC (12MHz) / 1 = 12MHz // CLK_REF = XOSC (12MHz) / 1 = 12MHz
let mut ref_clock = self.ref_clock(); let mut ref_clock = self.reference_clock();
let div = make_div(12u32.MHz(), 12u32.MHz()).unwrap(); ref_clock.configure_clock(xosc, xosc.get_freq());
// 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);
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
let mut sys_clock = self.sys_clock(); let mut sys_clock = self.system_clock();
let div = make_div(125u32.MHz(), 125u32.MHz()).unwrap(); sys_clock.configure_clock(pll_sys, pll_sys.get_freq());
// 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);
// CLK USB = PLL USB (48MHz) / 1 = 48MHz // CLK USB = PLL USB (48MHz) / 1 = 48MHz
let mut usb_clock = self.usb_clock(); let mut usb_clock = self.usb_clock();
let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap(); usb_clock.configure_clock(pll_usb, pll_usb.get_freq());
// 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);
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
let mut adc_clock = self.adc_clock(); let mut adc_clock = self.adc_clock();
let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap(); adc_clock.configure_clock(pll_usb, pll_usb.get_freq());
// 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);
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
let mut rtc_clock = self.rtc_clock(); let mut rtc_clock = self.rtc_clock();
let div = make_div(48u32.MHz(), 46875u32.Hz()).unwrap(); rtc_clock.configure_clock(pll_usb, 46875u32.Hz());
// 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);
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
// Normally choose clk_sys or clk_usb // Normally choose clk_sys or clk_usb
let mut peripheral_clock = self.peripheral_clock(); let mut peripheral_clock = self.peripheral_clock();
peripheral_clock.disable(); peripheral_clock.configure_clock(&sys_clock, sys_clock.freq());
peripheral_clock.set_clksys_auxsrc();
peripheral_clock.enable();
} }
/// Releases the CLOCKS block /// Releases the CLOCKS block
pub fn free(self) -> CLOCKS { pub fn free(self) -> CLOCKS {
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);

View file

@ -23,6 +23,7 @@ pub mod prelude;
pub mod pwm; pub mod pwm;
pub mod resets; pub mod resets;
pub mod rom_data; pub mod rom_data;
pub mod rosc;
pub mod rtc; pub mod rtc;
pub mod sio; pub mod sio;
pub mod spi; pub mod spi;

View file

@ -26,16 +26,20 @@ pub struct Disabled {
fbdiv: u16, fbdiv: u16,
post_div1: u8, post_div1: u8,
post_div2: u8, post_div2: u8,
frequency: Hertz,
} }
/// PLL is configured, started and locking into its designated frequency. /// PLL is configured, started and locking into its designated frequency.
pub struct Locking { pub struct Locking {
post_div1: u8, post_div1: u8,
post_div2: u8, post_div2: u8,
frequency: Hertz,
} }
/// PLL is locked : it delivers a steady frequency. /// PLL is locked : it delivers a steady frequency.
pub struct Locked; pub struct Locked {
frequency: Hertz,
}
impl State for Disabled {} impl State for Disabled {}
impl State for Locked {} impl State for Locked {}
@ -183,6 +187,8 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
let refdiv = config.refdiv; let refdiv = config.refdiv;
let post_div1 = config.post_div1; let post_div1 = config.post_div1;
let post_div2 = config.post_div2; 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 { Ok(PhaseLockedLoop {
state: Disabled { state: Disabled {
@ -190,6 +196,7 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
fbdiv, fbdiv,
post_div1, post_div1,
post_div2, post_div2,
frequency,
}, },
device: dev, device: dev,
}) })
@ -222,10 +229,12 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
let post_div1 = self.state.post_div1; let post_div1 = self.state.post_div1;
let post_div2 = self.state.post_div2; let post_div2 = self.state.post_div2;
let frequency = self.state.frequency;
self.transition(Locking { self.transition(Locking {
post_div1, post_div1,
post_div2, post_div2,
frequency,
}) })
} }
} }
@ -262,7 +271,16 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locking, D> {
w w
}); });
self.transition(Locked) let frequency = self.state.frequency;
self.transition(Locked { frequency })
}
}
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locked, D> {
/// Get the operating frequency for the PLL
pub fn operating_frequency(&self) -> Hertz {
self.state.frequency
} }
} }
@ -278,11 +296,11 @@ where
R: Into<Hertz<u64>>, R: Into<Hertz<u64>>,
{ {
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources. // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
let mut sys_clock = clocks.sys_clock(); let mut sys_clock = clocks.system_clock();
sys_clock.reset_source_await(); nb::block!(sys_clock.reset_source_await()).unwrap();
let mut ref_clock = clocks.ref_clock(); let mut ref_clock = clocks.reference_clock();
ref_clock.reset_source_await(); nb::block!(ref_clock.reset_source_await()).unwrap();
let initialized_pll = PhaseLockedLoop::new(dev, xosc_frequency, config)?.initialize(resets); let initialized_pll = PhaseLockedLoop::new(dev, xosc_frequency, config)?.initialize(resets);

99
rp2040-hal/src/rosc.rs Normal file
View file

@ -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<S: State> {
device: rp2040_pac::ROSC,
state: S,
}
impl<S: State> RingOscillator<S> {
/// Transitions the oscillator to another state.
fn transition<To: State>(self, state: To) -> RingOscillator<To> {
RingOscillator {
device: self.device,
state,
}
}
/// Releases the underlying device.
pub fn free(self) -> rp2040_pac::ROSC {
self.device
}
}
impl RingOscillator<Disabled> {
/// 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<Enabled> {
self.device.ctrl.write(|w| w.enable().enable());
self.transition(Enabled {
freq_hz: 6_500_000u32.Hz(),
})
}
}
impl RingOscillator<Enabled> {
/// 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<Disabled> {
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<Dormant> {
//taken from the C SDK
const ROSC_DORMANT_VALUE: u32 = 0x636f6d61;
self.device.dormant.write(|w| w.bits(ROSC_DORMANT_VALUE));
self.transition(Dormant)
}
}