71df28e269
This has the potential to not be random, but it should not be an issue if default clock settings are used.
236 lines
7.0 KiB
Rust
236 lines
7.0 KiB
Rust
use pac::clocks::vals::*;
|
|
|
|
use crate::{pac, reset};
|
|
|
|
const XOSC_MHZ: u32 = 12;
|
|
|
|
/// safety: must be called exactly once at bootup
|
|
pub(crate) unsafe fn init() {
|
|
// Reset everything except:
|
|
// - QSPI (we're using it to run this code!)
|
|
// - PLLs (it may be suicide if that's what's clocking us)
|
|
// - USB, SYSCFG (breaks usb-to-swd on core1)
|
|
let mut peris = reset::ALL_PERIPHERALS;
|
|
peris.set_io_qspi(false);
|
|
peris.set_pads_qspi(false);
|
|
peris.set_pll_sys(false);
|
|
peris.set_pll_usb(false);
|
|
peris.set_usbctrl(false);
|
|
peris.set_syscfg(false);
|
|
reset::reset(peris);
|
|
|
|
// Remove reset from peripherals which are clocked only by clk_sys and
|
|
// clk_ref. Other peripherals stay in reset until we've configured clocks.
|
|
let mut peris = reset::ALL_PERIPHERALS;
|
|
peris.set_adc(false);
|
|
peris.set_rtc(false);
|
|
peris.set_spi0(false);
|
|
peris.set_spi1(false);
|
|
peris.set_uart0(false);
|
|
peris.set_uart1(false);
|
|
peris.set_usbctrl(false);
|
|
reset::unreset_wait(peris);
|
|
|
|
// Start tick in watchdog
|
|
// xosc 12 mhz
|
|
pac::WATCHDOG.tick().write(|w| {
|
|
w.set_cycles(XOSC_MHZ as u16);
|
|
w.set_enable(true);
|
|
});
|
|
|
|
// Disable resus that may be enabled from previous software
|
|
let c = pac::CLOCKS;
|
|
c.clk_sys_resus_ctrl()
|
|
.write_value(pac::clocks::regs::ClkSysResusCtrl(0));
|
|
|
|
// start XOSC
|
|
start_xosc();
|
|
|
|
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
|
|
c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
|
|
while c.clk_sys_selected().read() != 1 {}
|
|
c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));
|
|
while c.clk_ref_selected().read() != 1 {}
|
|
|
|
// Configure PLLs
|
|
// REF FBDIV VCO POSTDIV
|
|
// PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
|
|
// PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
|
|
configure_pll(pac::PLL_SYS, 1, 1500_000_000, 6, 2);
|
|
configure_pll(pac::PLL_USB, 1, 480_000_000, 5, 2);
|
|
|
|
// CLK_REF = XOSC (12MHz) / 1 = 12MHz2Mhz
|
|
c.clk_ref_ctrl().write(|w| {
|
|
w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC);
|
|
});
|
|
while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {}
|
|
c.clk_ref_div().write(|w| w.set_int(1));
|
|
|
|
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
|
c.clk_sys_ctrl().write(|w| {
|
|
w.set_src(ClkSysCtrlSrc::CLK_REF);
|
|
});
|
|
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {}
|
|
c.clk_sys_div().write(|w| w.set_int(1));
|
|
c.clk_sys_ctrl().write(|w| {
|
|
w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS);
|
|
w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX);
|
|
});
|
|
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {}
|
|
|
|
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
|
c.clk_usb_div().write(|w| w.set_int(1));
|
|
c.clk_usb_ctrl().write(|w| {
|
|
w.set_enable(true);
|
|
w.set_auxsrc(ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB);
|
|
});
|
|
|
|
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
|
c.clk_adc_div().write(|w| w.set_int(1));
|
|
c.clk_adc_ctrl().write(|w| {
|
|
w.set_enable(true);
|
|
w.set_auxsrc(ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB);
|
|
});
|
|
|
|
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
|
c.clk_rtc_ctrl().modify(|w| {
|
|
w.set_enable(false);
|
|
});
|
|
c.clk_rtc_div().write(|w| w.set_int(1024));
|
|
c.clk_rtc_ctrl().write(|w| {
|
|
w.set_enable(true);
|
|
w.set_auxsrc(ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB);
|
|
});
|
|
|
|
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
|
|
// Normally choose clk_sys or clk_usb
|
|
c.clk_peri_ctrl().write(|w| {
|
|
w.set_enable(true);
|
|
w.set_auxsrc(ClkPeriCtrlAuxsrc::CLK_SYS);
|
|
});
|
|
|
|
// Peripheral clocks should now all be running
|
|
let peris = reset::ALL_PERIPHERALS;
|
|
reset::unreset_wait(peris);
|
|
}
|
|
|
|
pub(crate) fn _clk_sys_freq() -> u32 {
|
|
125_000_000
|
|
}
|
|
|
|
pub(crate) fn clk_peri_freq() -> u32 {
|
|
125_000_000
|
|
}
|
|
|
|
pub(crate) fn clk_rtc_freq() -> u32 {
|
|
46875
|
|
}
|
|
|
|
unsafe fn start_xosc() {
|
|
const XOSC_MHZ: u32 = 12;
|
|
pac::XOSC
|
|
.ctrl()
|
|
.write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ));
|
|
|
|
let startup_delay = (((XOSC_MHZ * 1_000_000) / 1000) + 128) / 256;
|
|
pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
|
|
pac::XOSC.ctrl().write(|w| {
|
|
w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
|
|
w.set_enable(pac::xosc::vals::Enable::ENABLE);
|
|
});
|
|
while !pac::XOSC.status().read().stable() {}
|
|
}
|
|
|
|
unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: u8, post_div2: u8) {
|
|
let ref_freq = XOSC_MHZ * 1_000_000 / refdiv;
|
|
|
|
let fbdiv = vco_freq / ref_freq;
|
|
assert!(fbdiv >= 16 && fbdiv <= 320);
|
|
assert!(post_div1 >= 1 && post_div1 <= 7);
|
|
assert!(post_div2 >= 1 && post_div2 <= 7);
|
|
assert!(post_div2 <= post_div1);
|
|
assert!(ref_freq <= (vco_freq / 16));
|
|
|
|
// do not disrupt PLL that is already correctly configured and operating
|
|
let cs = p.cs().read();
|
|
let prim = p.prim().read();
|
|
if cs.lock()
|
|
&& cs.refdiv() == refdiv as _
|
|
&& p.fbdiv_int().read().fbdiv_int() == fbdiv as _
|
|
&& prim.postdiv1() == post_div1
|
|
&& prim.postdiv2() == post_div2
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reset it
|
|
let mut peris = reset::Peripherals(0);
|
|
match p {
|
|
pac::PLL_SYS => peris.set_pll_sys(true),
|
|
pac::PLL_USB => peris.set_pll_usb(true),
|
|
_ => unreachable!(),
|
|
}
|
|
reset::reset(peris);
|
|
reset::unreset_wait(peris);
|
|
|
|
// Load VCO-related dividers before starting VCO
|
|
p.cs().write(|w| w.set_refdiv(refdiv as _));
|
|
p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _));
|
|
|
|
// Turn on PLL
|
|
p.pwr().modify(|w| {
|
|
w.set_pd(false);
|
|
w.set_vcopd(false);
|
|
w.set_postdivpd(true);
|
|
});
|
|
|
|
// Wait for PLL to lock
|
|
while !p.cs().read().lock() {}
|
|
|
|
// Wait for PLL to lock
|
|
p.prim().write(|w| {
|
|
w.set_postdiv1(post_div1);
|
|
w.set_postdiv2(post_div2);
|
|
});
|
|
|
|
// Turn on post divider
|
|
p.pwr().modify(|w| w.set_postdivpd(false));
|
|
}
|
|
|
|
/// Random number generator based on the ROSC RANDOMBIT register.
|
|
///
|
|
/// This will not produce random values if the ROSC is stopped or run at some
|
|
/// harmonic of the bus frequency. With default clock settings these are not
|
|
/// issues.
|
|
pub struct RoscRng;
|
|
|
|
impl RoscRng {
|
|
fn next_u8() -> u8 {
|
|
let random_reg = pac::ROSC.randombit();
|
|
let mut acc = 0;
|
|
for _ in 0..u8::BITS {
|
|
acc <<= 1;
|
|
acc |= unsafe { random_reg.read().randombit() as u8 };
|
|
}
|
|
acc
|
|
}
|
|
}
|
|
|
|
impl rand_core::RngCore for RoscRng {
|
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
|
Ok(self.fill_bytes(dest))
|
|
}
|
|
|
|
fn next_u32(&mut self) -> u32 {
|
|
rand_core::impls::next_u32_via_fill(self)
|
|
}
|
|
|
|
fn next_u64(&mut self) -> u64 {
|
|
rand_core::impls::next_u64_via_fill(self)
|
|
}
|
|
|
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
|
dest.fill_with(Self::next_u8)
|
|
}
|
|
}
|