use crate::time::Hertz;
use e310x::{Aonclk as AONCLK, Prci as PRCI, CLINT};
use riscv::interrupt;
use riscv::register::mcycle;
const PLLREF_MIN: u32 = 6_000_000;
const PLLREF_MAX: u32 = 48_000_000;
const REFR_MIN: u32 = 6_000_000;
const REFR_MAX: u32 = 12_000_000;
const VCO_MIN: u32 = 384_000_000;
const VCO_MAX: u32 = 768_000_000;
const PLLOUT_MIN: u32 = 48_000_000;
const PLLOUT_MAX: u32 = 384_000_000;
const DIVOUT_MIN: u32 = 375_000;
const DIVOUT_MAX: u32 = 384_000_000;
pub trait PrciExt {
fn constrain(self) -> CoreClk;
}
pub trait AonExt {
fn constrain(self) -> AonClk;
}
impl PrciExt for PRCI {
fn constrain(self) -> CoreClk {
CoreClk {
hfxosc: None,
coreclk: Hertz(13_800_000), }
}
}
impl AonExt for AONCLK {
fn constrain(self) -> AonClk {
AonClk { lfaltclk: None }
}
}
pub struct CoreClk {
hfxosc: Option<Hertz>,
coreclk: Hertz,
}
impl CoreClk {
pub fn use_external<F: Into<Hertz>>(mut self, freq: F) -> Self {
let hz: Hertz = freq.into();
assert!(hz.0 < 20_000_000);
self.hfxosc = Some(hz);
self
}
pub fn coreclk<F: Into<Hertz>>(mut self, freq: F) -> Self {
self.coreclk = freq.into();
self
}
pub(crate) fn freeze(self) -> Hertz {
let prci = unsafe { PRCI::steal() };
let hfrosc_freq = self.configure_hfrosc();
prci.pllcfg()
.modify(|_, w| w.sel().bit(false).bypass().bit(true));
if let Some(freq) = self.hfxosc {
self.configure_with_external(freq)
} else {
self.configure_with_internal(hfrosc_freq)
}
}
fn configure_with_external(self, source_freq: Hertz) -> Hertz {
let prci = unsafe { PRCI::steal() };
prci.hfxosccfg().write(|w| w.enable().bit(true));
while !prci.hfxosccfg().read().ready().bit_is_set() {}
prci.pllcfg().modify(|_, w| w.refsel().bit(true));
let freq;
if source_freq.0 == self.coreclk.0 {
freq = source_freq;
prci.pllcfg().modify(|_, w| w.bypass().bit(true));
prci.plloutdiv().write(|w| w.divby1().bit(true));
} else {
freq = self.configure_pll(source_freq, self.coreclk);
}
prci.pllcfg().modify(|_, w| w.sel().bit(true));
prci.hfrosccfg().write(|w| w.enable().bit(false));
freq
}
fn configure_with_internal(self, hfrosc_freq: Hertz) -> Hertz {
let prci = unsafe { PRCI::steal() };
let freq;
if hfrosc_freq.0 == self.coreclk.0 {
freq = hfrosc_freq;
prci.pllcfg()
.modify(|_, w| w.sel().bit(false).bypass().bit(true));
prci.pllcfg().modify(|_, w| w.bypass().bit(true));
} else {
freq = self.configure_pll(hfrosc_freq, self.coreclk);
prci.pllcfg().modify(|_, w| w.sel().bit(true));
}
prci.hfxosccfg().write(|w| w.enable().bit(false));
freq
}
fn configure_hfrosc(&self) -> Hertz {
let prci = unsafe { PRCI::steal() };
prci.hfrosccfg()
.write(|w| unsafe { w.div().bits(4).trim().bits(16).enable().bit(true) });
while !prci.hfrosccfg().read().ready().bit_is_set() {}
Hertz(13_800_000)
}
fn configure_pll(&self, pllref_freq: Hertz, divout_freq: Hertz) -> Hertz {
let pllref_freq = pllref_freq.0;
assert!((PLLREF_MIN..=PLLREF_MAX).contains(&pllref_freq));
let divout_freq = divout_freq.0;
assert!((DIVOUT_MIN..=DIVOUT_MAX).contains(&divout_freq));
let divider_div;
let divider_bypass;
let d = PLLOUT_MAX / divout_freq;
if d > 1 {
divider_bypass = false;
if d > 128 {
divider_div = (128 / 2) - 1;
} else {
divider_div = (d / 2) - 1;
}
} else {
divider_div = 0;
divider_bypass = true;
}
let d = if divider_bypass {
1
} else {
2 * (divider_div + 1)
};
let pllout_freq = divout_freq * d;
assert!((PLLOUT_MIN..=PLLOUT_MAX).contains(&pllout_freq));
let r = match pllref_freq {
24_000_000..=48_000_000 => 4,
18_000_000..=23_999_999 => 3,
12_000_000..=17_999_999 => 2,
6_000_000..=11_999_999 => 1,
_ => unreachable!(),
};
let refr_freq = pllref_freq / r;
assert!((REFR_MIN..=REFR_MAX).contains(&refr_freq));
let q = match pllout_freq {
192_000_000..=384_000_000 => 2,
96_000_000..=191_999_999 => 4,
48_000_000..=95_999_999 => 8,
_ => unreachable!(),
};
let target_vco_freq: u32 = pllout_freq * q;
assert!((VCO_MIN..=VCO_MAX).contains(&target_vco_freq));
let f = target_vco_freq / refr_freq;
assert!(f <= 128);
let f_lo = (f / 2) * 2; let vco_lo = refr_freq * f_lo;
let f_hi = f_lo + 2;
let vco_hi = refr_freq * f_hi;
let (f, vco_freq) = if (f_hi <= 128 && vco_hi <= VCO_MAX)
&& (target_vco_freq as i32 - vco_hi as i32).abs()
< (target_vco_freq as i32 - vco_lo as i32).abs()
{
(f_hi, vco_hi)
} else {
(f_lo, vco_lo)
};
assert!((VCO_MIN..=VCO_MAX).contains(&vco_freq));
let pllout_freq = vco_freq / q;
assert!((PLLOUT_MIN..=PLLOUT_MAX).contains(&pllout_freq));
let divout_freq = pllout_freq / d;
assert!((DIVOUT_MIN..=DIVOUT_MAX).contains(&divout_freq));
let r: u8 = (r - 1) as u8;
let f: u8 = (f / 2 - 1) as u8;
let q: u8 = match q {
2 => 0b01,
4 => 0b10,
8 => 0b11,
_ => unreachable!(),
};
let prci = unsafe { PRCI::steal() };
prci.pllcfg().modify(|_, w| unsafe {
w.pllr()
.bits(r)
.pllf()
.bits(f)
.pllq()
.bits(q)
.bypass()
.bit(false)
});
prci.plloutdiv()
.write(|w| unsafe { w.div().bits(divider_div as u8).divby1().bit(divider_bypass) });
let mtime = CLINT::mtimer().mtime;
let time = mtime.read() + 4;
while mtime.read() < time {}
while !prci.pllcfg().read().lock().bit_is_set() {}
Hertz(divout_freq)
}
}
pub struct AonClk {
lfaltclk: Option<Hertz>,
}
impl AonClk {
pub fn use_external<F: Into<Hertz>>(mut self, freq: F) -> Self {
let hz: Hertz = freq.into();
assert!(hz.0 < 500_000);
self.lfaltclk = Some(hz);
self
}
pub(crate) fn freeze(self) -> Hertz {
let aonclk = unsafe { AONCLK::steal() };
if let Some(freq) = self.lfaltclk {
aonclk.lfrosccfg().write(|w| w.enable().bit(false));
freq
} else {
let trim = 16;
let div = 4;
aonclk.lfrosccfg().write(|w| unsafe {
w.trim().bits(trim);
w.div().bits(div);
w.enable().bit(true)
});
while !aonclk.lfrosccfg().read().ready().bit_is_set() {}
Hertz(32_768) }
}
}
#[derive(Clone, Copy)]
pub struct Clocks {
coreclk: Hertz,
lfclk: Hertz,
}
impl Clocks {
pub fn freeze(coreclk: CoreClk, aonclk: AonClk) -> Self {
let coreclk = coreclk.freeze();
let lfclk = aonclk.freeze();
Clocks { coreclk, lfclk }
}
pub fn coreclk(&self) -> Hertz {
self.coreclk
}
pub fn tlclk(&self) -> Hertz {
self.coreclk
}
pub fn lfclk(&self) -> Hertz {
self.lfclk
}
fn _measure_coreclk(&self, min_ticks: u64) -> Hertz {
let mtime = CLINT::mtimer().mtime;
interrupt::free(|| {
while mtime.read() == mtime.read() {}
let start_cycle = mcycle::read64();
let start_time = mtime.read();
while start_time + min_ticks > mtime.read() {}
let end_cycle = mcycle::read64();
let end_time = mtime.read();
let delta_cycle: u64 = end_cycle - start_cycle;
let delta_time: u64 = end_time - start_time;
let res = (delta_cycle / delta_time) * 32768
+ ((delta_cycle % delta_time) * 32768) / delta_time;
Hertz(res as u32)
})
}
pub fn measure_coreclk(&self) -> Hertz {
self._measure_coreclk(1);
self._measure_coreclk(10)
}
}