rust: Factor PLL computation out from sysctl

If you need to have mystery meat algorithms, at least abstract
them.
This commit is contained in:
Wladimir J. van der Laan 2019-08-21 08:44:12 +00:00
parent d7eb8dd0e8
commit 9bdf7ea2df
6 changed files with 181 additions and 167 deletions

View File

@ -10,6 +10,7 @@ use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use riscv_rt::entry;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::sysctl;
use core::ptr;
use riscv::register::{mie,mstatus,mhartid,mvendorid,marchid,mimpid,mcause};
use core::sync::atomic::{AtomicBool,Ordering};

View File

@ -73,6 +73,12 @@ fn main() -> ! {
clocks.cpu().0,
)
.unwrap();
writeln!(
stdout,
" KPU {}",
sysctl::clock_get_freq(sysctl::clock::AI),
)
.unwrap();
writeln!(
stdout,
" APB0 {} (HAL assumes {})",

View File

@ -2,11 +2,12 @@
use k210_hal::pac;
use core::convert::TryInto;
use libm::F64Ext;
use crate::soc::utils::set_bit;
use crate::soc::sleep::usleep;
mod pll_compute;
const SYSCTRL_CLOCK_FREQ_IN0: u32 = 26000000;
#[derive(Copy, Clone, PartialEq, Eq)]
@ -704,22 +705,6 @@ fn pll_clear_slip(pll: pll) -> bool {
pll_is_lock(pll)
}
/* constants for PLL frequency computation */
const VCO_MIN: f64 = 3.5e+08;
const VCO_MAX: f64 = 1.75e+09;
const REF_MIN: f64 = 1.36719e+07;
const REF_MAX: f64 = 1.75e+09;
const NR_MIN: i32 = 1;
const NR_MAX: i32 = 16;
const NF_MIN: i32 = 1;
const NF_MAX: i32 = 64;
const NO_MIN: i32 = 1;
const NO_MAX: i32 = 16;
const NB_MIN: i32 = 1;
const NB_MAX: i32 = 64;
const MAX_VCO: bool = true;
const REF_RNG: bool = true;
fn pll_source_set_freq(pll: pll, source: clock_source, freq: u32) -> Result<u32,()> {
use pll::*;
/* PLL0 and 1 can only source from IN0 */
@ -730,156 +715,7 @@ fn pll_source_set_freq(pll: pll, source: clock_source, freq: u32) -> Result<u32,
if freq_in == 0 {
return Err(());
}
/*
* Calculate PLL registers' value by finding closest matching parameters
* NOTE: this uses floating point math ... this is horrible :-(
* TODO: implement this without fp ops
*/
/* Parameters to be exported from the loop */
struct Params {
nrx: i32,
no: i32,
nb: i32,
nfx: i64,
fvco: f64,
};
let fin: f64 = freq_in.into();
let fout: f64 = freq.into();
let val: f64 = fout / fin;
let terr: f64 = 0.5 / ((NF_MAX / 2) as f64);
// NOTE: removed the handling that the Kendryte SDK has for terr<=0.0, as this is impossible
// given that NF_MAX is a positive integer constant
let mut merr: f64 = terr;
let mut found: Option<Params> = None;
for nfi in (val as i32)..NF_MAX {
let nr: i32 = ((nfi as f64) / val) as i32;
if nr == 0 {
continue;
}
if REF_RNG && (nr < NR_MIN) {
continue;
}
if fin / (nr as f64) > REF_MAX {
continue;
}
let mut nrx: i32 = nr;
let mut nf: i32 = nfi;
let mut nfx: i64 = nfi.into();
let nval: f64 = (nfx as f64) / (nr as f64);
if nf == 0 {
nf = 1;
}
let err: f64 = 1.0 - nval / val;
if (err.abs() < merr * (1.0 + 1e-6)) || (err.abs() < 1e-16) {
let mut not: i32 = (VCO_MAX / fout).floor() as i32;
let mut no: i32 = if not > NO_MAX { NO_MAX } else { not };
while no > NO_MIN {
if (REF_RNG) && ((nr / no) < NR_MIN) {
no -= 1;
continue;
}
if (nr % no) == 0 {
break;
}
no -= 1;
}
if (nr % no) != 0 {
continue;
}
let mut nor: i32 = (if not > NO_MAX { NO_MAX } else { not } ) / no;
let mut nore: i32 = NF_MAX / nf;
if nor > nore {
nor = nore;
}
let noe: i32 = (VCO_MIN / fout).ceil() as i32;
if !MAX_VCO {
nore = (noe - 1) / no + 1;
nor = nore;
not = 0; /* force next if to fail */
}
if (((no * nor) < (not >> 1)) || ((no * nor) < noe)) && ((no * nor) < (NF_MAX / nf)) {
no = NF_MAX / nf;
if no > NO_MAX {
no = NO_MAX;
}
if no > not {
no = not;
}
nfx *= no as i64;
nf *= no;
if (no > 1) && !found.is_none() {
continue;
}
/* wait for larger nf in later iterations */
} else {
nrx /= no;
nfx *= nor as i64;
nf *= nor;
no *= nor;
if no > NO_MAX {
continue;
}
if (nor > 1) && !found.is_none() {
continue;
}
/* wait for larger nf in later iterations */
}
let mut nb: i32 = nfx as i32;
if nb < NB_MIN {
nb = NB_MIN;
}
if nb > NB_MAX {
continue;
}
let fvco: f64 = fin / (nrx as f64) * (nfx as f64);
if fvco < VCO_MIN {
continue;
}
if fvco > VCO_MAX {
continue;
}
if nf < NF_MIN {
continue;
}
if REF_RNG && (fin / (nrx as f64) < REF_MIN) {
continue;
}
if REF_RNG && (nrx > NR_MAX) {
continue;
}
if let Some(found) = &found {
// check that this reduces error compared to minimum error value, or is an improvement
// in x_no
if !((err.abs() < merr * (1.0 - 1e-6))
|| (MAX_VCO && (no > found.no))
) {
continue;
}
if nrx > found.nrx {
continue;
}
}
found = Some(Params {
nrx,
no,
nb,
nfx,
fvco,
});
merr = err.abs();
}
}
if let Some(found) = found {
if merr >= terr * (1.0 - 1e-6) {
return Err(());
}
if let Some(found) = pll_compute::compute_params(freq_in, freq) {
let ptr = pac::SYSCTL::ptr();
unsafe {
let clkr: u8 = (found.nrx - 1).try_into().unwrap();

View File

@ -0,0 +1,169 @@
use libm::F64Ext;
/** Parameters for PLL */
pub struct Params {
pub nrx: i32,
pub no: i32,
pub nb: i32,
pub nfx: i64,
}
/* constants for PLL frequency computation */
const VCO_MIN: f64 = 3.5e+08;
const VCO_MAX: f64 = 1.75e+09;
const REF_MIN: f64 = 1.36719e+07;
const REF_MAX: f64 = 1.75e+09;
const NR_MIN: i32 = 1;
const NR_MAX: i32 = 16;
const NF_MIN: i32 = 1;
const NF_MAX: i32 = 64;
const NO_MIN: i32 = 1;
const NO_MAX: i32 = 16;
const NB_MIN: i32 = 1;
const NB_MAX: i32 = 64;
const MAX_VCO: bool = true;
const REF_RNG: bool = true;
/*
* Calculate PLL registers' value by finding closest matching parameters
* NOTE: this uses floating point math ... this is horrible for something so critical :-(
* TODO: implement this without fp ops
*/
pub fn compute_params(freq_in: u32, freq_out: u32) -> Option<Params> {
/* Parameters to be exported from the loop */
let fin: f64 = freq_in.into();
let fout: f64 = freq_out.into();
let val: f64 = fout / fin;
let terr: f64 = 0.5 / ((NF_MAX / 2) as f64);
// NOTE: removed the handling that the Kendryte SDK has for terr<=0.0, as this is impossible
// given that NF_MAX is a positive integer constant
let mut merr: f64 = terr;
let mut found: Option<Params> = None;
for nfi in (val as i32)..NF_MAX {
let nr: i32 = ((nfi as f64) / val) as i32;
if nr == 0 {
continue;
}
if REF_RNG && (nr < NR_MIN) {
continue;
}
if fin / (nr as f64) > REF_MAX {
continue;
}
let mut nrx: i32 = nr;
let mut nf: i32 = nfi;
let mut nfx: i64 = nfi.into();
let nval: f64 = (nfx as f64) / (nr as f64);
if nf == 0 {
nf = 1;
}
let err: f64 = 1.0 - nval / val;
if (err.abs() < merr * (1.0 + 1e-6)) || (err.abs() < 1e-16) {
let mut not: i32 = (VCO_MAX / fout).floor() as i32;
let mut no: i32 = if not > NO_MAX { NO_MAX } else { not };
while no > NO_MIN {
if (REF_RNG) && ((nr / no) < NR_MIN) {
no -= 1;
continue;
}
if (nr % no) == 0 {
break;
}
no -= 1;
}
if (nr % no) != 0 {
continue;
}
let mut nor: i32 = (if not > NO_MAX { NO_MAX } else { not } ) / no;
let mut nore: i32 = NF_MAX / nf;
if nor > nore {
nor = nore;
}
let noe: i32 = (VCO_MIN / fout).ceil() as i32;
if !MAX_VCO {
nore = (noe - 1) / no + 1;
nor = nore;
not = 0; /* force next if to fail */
}
if (((no * nor) < (not >> 1)) || ((no * nor) < noe)) && ((no * nor) < (NF_MAX / nf)) {
no = NF_MAX / nf;
if no > NO_MAX {
no = NO_MAX;
}
if no > not {
no = not;
}
nfx *= no as i64;
nf *= no;
if (no > 1) && !found.is_none() {
continue;
}
/* wait for larger nf in later iterations */
} else {
nrx /= no;
nfx *= nor as i64;
nf *= nor;
no *= nor;
if no > NO_MAX {
continue;
}
if (nor > 1) && !found.is_none() {
continue;
}
/* wait for larger nf in later iterations */
}
let mut nb: i32 = nfx as i32;
if nb < NB_MIN {
nb = NB_MIN;
}
if nb > NB_MAX {
continue;
}
let fvco: f64 = fin / (nrx as f64) * (nfx as f64);
if fvco < VCO_MIN {
continue;
}
if fvco > VCO_MAX {
continue;
}
if nf < NF_MIN {
continue;
}
if REF_RNG && (fin / (nrx as f64) < REF_MIN) {
continue;
}
if REF_RNG && (nrx > NR_MAX) {
continue;
}
if let Some(found) = &found {
// check that this reduces error compared to minimum error value, or is an improvement
// in x_no
if !((err.abs() < merr * (1.0 - 1e-6))
|| (MAX_VCO && (no > found.no))
) {
continue;
}
if nrx > found.nrx {
continue;
}
}
found = Some(Params {
nrx,
no,
nb,
nfx,
});
merr = err.abs();
}
}
if merr >= terr * (1.0 - 1e-6) {
None
} else {
found
}
}

View File

@ -12,6 +12,7 @@ use k210_shared::board::def::io;
use k210_shared::board::sdcard;
use k210_shared::soc::dmac::{dma_channel, DMACExt};
use k210_shared::soc::fpioa;
use k210_shared::soc::sysctl;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::spi::SPIExt;
use riscv_rt::entry;

View File

@ -11,6 +11,7 @@ use k210_hal::prelude::*;
use k210_hal::stdout::Stdout;
use riscv_rt::entry;
use k210_shared::soc::sleep::usleep;
use k210_shared::soc::sysctl;
use secp256k1::{Secp256k1, Message, SecretKey, PublicKey};
use core::slice;