mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-24 21:21:31 +11:00
Refactor clocks (#54)
* Remove unneeded lines * Reduce macro boilerplate * Refactor clocks
This commit is contained in:
parent
d2aa2b238d
commit
f310d92b64
253
rp2040-hal/src/clocks/available_clocks.rs
Normal file
253
rp2040-hal/src/clocks/available_clocks.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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])*
|
||||||
|
struct $name:ident {
|
||||||
|
reg: $reg:ident,
|
||||||
|
auxsrc: {$($auxsrc:ident: $variant:ident),*},
|
||||||
|
div: false
|
||||||
|
}
|
||||||
|
} => {
|
||||||
|
base_clock!{
|
||||||
|
$(#[$attr])*
|
||||||
|
($name, $reg, auxsrc={$($auxsrc: $variant),*})
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! frac_division {
|
// Just to match proper divisable clocks so we don't have to do something special in configure function
|
||||||
($name:ident, $div:ident, $u:ty) => {
|
impl ClockDivision for $name {
|
||||||
impl FractionDivision for $name {
|
fn set_div(&mut self, _: u32) {}
|
||||||
fn set_frac_div(&mut self, div: usize) {
|
fn get_div(&self) -> u32 {1}
|
||||||
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe {
|
|
||||||
w.frac().bits(div as $u);
|
|
||||||
w
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_frac_div(&self) -> usize {
|
stoppable_clock!($name, $reg);
|
||||||
unsafe { self.shared_dev.get() }
|
};
|
||||||
.$div
|
{
|
||||||
.read()
|
$( #[$attr:meta])*
|
||||||
.frac()
|
struct $name:ident {
|
||||||
.bits()
|
reg: $reg:ident,
|
||||||
.into()
|
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
|
|
||||||
});
|
$(impl ValidSrc<$name> for $auxsrc {
|
||||||
|
type Variant = [<$reg:camel SrcType>];
|
||||||
|
|
||||||
|
fn is_aux(&self) -> bool{
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
fn variant(&self) -> [<$reg:camel SrcType>] {
|
||||||
|
[<$reg:camel SrcType>]::Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant)
|
||||||
|
}
|
||||||
|
})*
|
||||||
|
|
||||||
|
impl ClocksManager {
|
||||||
|
#[ doc = "Getter for the" $name ]
|
||||||
|
pub fn [<$name:snake>](&self) -> $name {
|
||||||
|
|
||||||
|
//TODO: Init clock here
|
||||||
|
$name {
|
||||||
|
shared_dev: self.shared_clocks,
|
||||||
|
frequency: 0.Hz(),
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
$(#[$attr])*
|
||||||
};
|
pub struct $name {
|
||||||
|
shared_dev: ShareableClocks,
|
||||||
|
frequency: Hertz,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! selfaux_source {
|
impl $name {
|
||||||
($name:ident, $ctrl:ident, $self:ident) => {
|
/// Returns the frequency of the configured clock
|
||||||
impl SelfAuxClockSource for $name {
|
pub fn freq(&self) -> Hertz {
|
||||||
fn set_self_aux_src(&mut self) {
|
self.frequency
|
||||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
|
||||||
w.src().$self();
|
|
||||||
w
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! clockref_source {
|
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 ClockREFClockSource for $name {
|
|
||||||
fn set_clkref_src(&mut self) {
|
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
w.auxsrc().variant(src.variant().unwrap_aux())
|
||||||
w.src().clk_ref();
|
|
||||||
w
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! clocksys_auxsource {
|
impl Sealed for $name {}
|
||||||
($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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! clockusb_auxsource {
|
impl From<$name> for Hertz
|
||||||
($name:ident, $ctrl:ident) => {
|
{
|
||||||
impl ClockUSBClockAuxSource for $name {
|
fn from(value: $name) -> Hertz {
|
||||||
fn set_clkusb_auxsrc(&mut self) {
|
value.frequency
|
||||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
}
|
||||||
w.auxsrc().clk_usb();
|
|
||||||
w
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
99
rp2040-hal/src/rosc.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue