mirror of
https://github.com/laanwj/k210-sdk-stuff.git
synced 2024-11-22 01:16:20 +04:00
rust: Factor PLL computation out from sysctl
If you need to have mystery meat algorithms, at least abstract them.
This commit is contained in:
parent
d7eb8dd0e8
commit
9bdf7ea2df
@ -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};
|
||||
|
@ -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 {})",
|
||||
|
@ -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();
|
||||
|
169
rust/k210-shared/src/soc/sysctl/pll_compute.rs
Normal file
169
rust/k210-shared/src/soc/sysctl/pll_compute.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user