stm32 CORDIC: ZeroOverhead q1.31 1 arg 1 res mode
This commit is contained in:
parent
1171e11655
commit
cf065d439e
2 changed files with 462 additions and 0 deletions
460
embassy-stm32/src/cordic.rs
Normal file
460
embassy-stm32/src/cordic.rs
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
//! CORDIC co-processor
|
||||||
|
|
||||||
|
use crate::peripherals;
|
||||||
|
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||||
|
|
||||||
|
pub use enums::*;
|
||||||
|
|
||||||
|
mod enums {
|
||||||
|
/// CORDIC function
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Function {
|
||||||
|
Cos = 0,
|
||||||
|
Sin,
|
||||||
|
Phase,
|
||||||
|
Modulus,
|
||||||
|
Arctan,
|
||||||
|
Cosh,
|
||||||
|
Sinh,
|
||||||
|
Arctanh,
|
||||||
|
Ln,
|
||||||
|
Sqrt,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC precision
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Precision {
|
||||||
|
Iters4 = 1,
|
||||||
|
Iters8,
|
||||||
|
Iters12,
|
||||||
|
Iters16,
|
||||||
|
Iters20,
|
||||||
|
Iters24,
|
||||||
|
Iters28,
|
||||||
|
Iters32,
|
||||||
|
Iters36,
|
||||||
|
Iters40,
|
||||||
|
Iters44,
|
||||||
|
Iters48,
|
||||||
|
Iters52,
|
||||||
|
Iters56,
|
||||||
|
Iters60,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC scale
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub enum Scale {
|
||||||
|
#[default]
|
||||||
|
A1_R1 = 0,
|
||||||
|
A1o2_R2,
|
||||||
|
A1o4_R4,
|
||||||
|
A1o8_R8,
|
||||||
|
A1o16_R16,
|
||||||
|
A1o32_R32,
|
||||||
|
A1o64_R64,
|
||||||
|
A1o128_R128,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC argument/result count
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub enum Count {
|
||||||
|
#[default]
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC argument/result data width
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Width {
|
||||||
|
Bits32,
|
||||||
|
Bits16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cordic driver running mode
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Mode {
|
||||||
|
/// After caculation start, a read to RDATA register will block AHB until the caculation finished
|
||||||
|
ZeroOverhead,
|
||||||
|
|
||||||
|
/// Use CORDIC interrupt to trigger a read result value
|
||||||
|
Interrupt,
|
||||||
|
|
||||||
|
/// Use DMA to write/read value
|
||||||
|
Dma,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Low-level CORDIC access.
|
||||||
|
#[cfg(feature = "unstable-pac")]
|
||||||
|
pub mod low_level {
|
||||||
|
pub use super::sealed::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use super::*;
|
||||||
|
use crate::pac::cordic::vals;
|
||||||
|
|
||||||
|
/// Cordic instance
|
||||||
|
pub trait Instance {
|
||||||
|
/// Get access to CORDIC registers
|
||||||
|
fn regs() -> crate::pac::cordic::Cordic;
|
||||||
|
|
||||||
|
/// Set Function value
|
||||||
|
fn set_func(&self, func: Function) {
|
||||||
|
Self::regs()
|
||||||
|
.csr()
|
||||||
|
.modify(|v| v.set_func(vals::Func::from_bits(func as u8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set Precision value
|
||||||
|
fn set_precision(&self, precision: Precision) {
|
||||||
|
Self::regs()
|
||||||
|
.csr()
|
||||||
|
.modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set Scale value
|
||||||
|
fn set_scale(&self, scale: Scale) {
|
||||||
|
Self::regs()
|
||||||
|
.csr()
|
||||||
|
.modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable global interrupt
|
||||||
|
fn enable_irq(&self) {
|
||||||
|
Self::regs().csr().modify(|v| v.set_ien(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable global interrupt
|
||||||
|
fn disable_irq(&self) {
|
||||||
|
Self::regs().csr().modify(|v| v.set_ien(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable Read DMA
|
||||||
|
fn enable_read_dma(&self) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_dmaren(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable Read DMA
|
||||||
|
fn disable_read_dma(&self) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_dmaren(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable Write DMA
|
||||||
|
fn enable_write_dma(&self) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_dmawen(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable Write DMA
|
||||||
|
fn disable_write_dma(&self) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_dmawen(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set NARGS value
|
||||||
|
fn set_argument_count(&self, n: Count) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_nargs(match n {
|
||||||
|
Count::One => vals::Num::NUM1,
|
||||||
|
Count::Two => vals::Num::NUM2,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set NRES value
|
||||||
|
fn set_result_count(&self, n: Count) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_nres(match n {
|
||||||
|
Count::One => vals::Num::NUM1,
|
||||||
|
Count::Two => vals::Num::NUM2,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set ARGSIZE and RESSIZE value
|
||||||
|
fn set_data_width(&self, arg: Width, res: Width) {
|
||||||
|
Self::regs().csr().modify(|v| {
|
||||||
|
v.set_argsize(match arg {
|
||||||
|
Width::Bits32 => vals::Size::BITS32,
|
||||||
|
Width::Bits16 => vals::Size::BITS16,
|
||||||
|
});
|
||||||
|
v.set_ressize(match res {
|
||||||
|
Width::Bits32 => vals::Size::BITS32,
|
||||||
|
Width::Bits16 => vals::Size::BITS16,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read RRDY flag
|
||||||
|
fn ready_to_read(&self) -> bool {
|
||||||
|
Self::regs().csr().read().rrdy()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write value to WDATA
|
||||||
|
fn write_argument(&self, arg: u32) {
|
||||||
|
Self::regs().wdata().write_value(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read value from RDATA
|
||||||
|
fn read_result(&self) -> u32 {
|
||||||
|
Self::regs().rdata().read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC driver
|
||||||
|
pub struct Cordic<'d, T: Instance> {
|
||||||
|
cordic: PeripheralRef<'d, T>,
|
||||||
|
config: Config,
|
||||||
|
//state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CORDIC instance trait
|
||||||
|
pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||||
|
|
||||||
|
/// CORDIC configuration
|
||||||
|
pub struct Config {
|
||||||
|
function: Function,
|
||||||
|
precision: Precision,
|
||||||
|
scale: Scale,
|
||||||
|
mode: Mode,
|
||||||
|
first_result: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORDIC running state
|
||||||
|
//struct State {
|
||||||
|
// input_buf: [u32; 8],
|
||||||
|
// buf_len: usize,
|
||||||
|
//}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Create a config for Cordic driver
|
||||||
|
pub fn new(function: Function, precision: Precision, scale: Option<Scale>, mode: Mode, first_result: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
function,
|
||||||
|
precision,
|
||||||
|
scale: scale.unwrap_or_default(),
|
||||||
|
mode,
|
||||||
|
first_result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_scale(&self) -> bool {
|
||||||
|
let scale_raw = self.scale as u8;
|
||||||
|
|
||||||
|
match self.function {
|
||||||
|
Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw,
|
||||||
|
Function::Arctan => (0..=7).contains(&scale_raw),
|
||||||
|
Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw,
|
||||||
|
Function::Ln => (1..=4).contains(&scale_raw),
|
||||||
|
Function::Sqrt => (0..=2).contains(&scale_raw),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Cordic<'d, T> {
|
||||||
|
/// Create a Cordic driver instance
|
||||||
|
///
|
||||||
|
/// Note:
|
||||||
|
/// If you need a periperhal -> CORDIC -> peripehral mode,
|
||||||
|
/// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config]
|
||||||
|
pub fn new(cordic: impl Peripheral<P = T> + 'd, config: Config) -> Self {
|
||||||
|
T::enable_and_reset();
|
||||||
|
|
||||||
|
into_ref!(cordic);
|
||||||
|
|
||||||
|
if !config.check_scale() {
|
||||||
|
panic!("Scale value is not compatible with Function")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut instance = Self {
|
||||||
|
cordic,
|
||||||
|
config,
|
||||||
|
// state: State {
|
||||||
|
// input_buf: [0u32; 8],
|
||||||
|
// buf_len: 0,
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.reconfigure();
|
||||||
|
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a new config for Cordic driver
|
||||||
|
pub fn set_config(&mut self, config: Config) {
|
||||||
|
self.config = config;
|
||||||
|
self.reconfigure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set extra config for data count and data width.
|
||||||
|
pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) {
|
||||||
|
let peri = &self.cordic;
|
||||||
|
peri.set_argument_count(arg_cnt);
|
||||||
|
peri.set_data_width(arg_width, res_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconfigure(&mut self) {
|
||||||
|
let peri = &self.cordic;
|
||||||
|
let config = &self.config;
|
||||||
|
|
||||||
|
if peri.ready_to_read() {
|
||||||
|
warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST");
|
||||||
|
};
|
||||||
|
|
||||||
|
peri.disable_irq();
|
||||||
|
peri.disable_write_dma();
|
||||||
|
peri.disable_read_dma();
|
||||||
|
|
||||||
|
// clean RRDY flag
|
||||||
|
while peri.ready_to_read() {
|
||||||
|
peri.read_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
peri.set_func(config.function);
|
||||||
|
peri.set_precision(config.precision);
|
||||||
|
peri.set_scale(config.scale);
|
||||||
|
if config.first_result {
|
||||||
|
peri.set_result_count(Count::One)
|
||||||
|
} else {
|
||||||
|
peri.set_result_count(Count::Two)
|
||||||
|
}
|
||||||
|
|
||||||
|
match config.mode {
|
||||||
|
Mode::ZeroOverhead => (),
|
||||||
|
Mode::Interrupt => {
|
||||||
|
peri.enable_irq();
|
||||||
|
}
|
||||||
|
Mode::Dma => {
|
||||||
|
peri.enable_write_dma();
|
||||||
|
peri.enable_read_dma();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//self.state.input_buf.fill(0u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a CORDIC calculation
|
||||||
|
pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize {
|
||||||
|
match self.config.mode {
|
||||||
|
Mode::ZeroOverhead => {
|
||||||
|
if arg2s.is_none() {
|
||||||
|
self.cordic.set_argument_count(Count::One);
|
||||||
|
|
||||||
|
self.cordic.set_result_count(if self.config.first_result {
|
||||||
|
if output.len() < arg1s.len() {
|
||||||
|
panic!("Output buf length is not long enough")
|
||||||
|
}
|
||||||
|
Count::One
|
||||||
|
} else {
|
||||||
|
if output.len() < 2 * arg1s.len() {
|
||||||
|
panic!("Output buf length is not long enough")
|
||||||
|
}
|
||||||
|
Count::Two
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut cnt = 0;
|
||||||
|
|
||||||
|
for &arg in arg1s.iter() {
|
||||||
|
self.cordic.write_argument(f64_to_q1_31(arg));
|
||||||
|
output[cnt] = q1_31_to_f64(self.cordic.read_result());
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::Interrupt => todo!(),
|
||||||
|
Mode::Dma => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Drop for Cordic<'d, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
T::disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_interrupt!(
|
||||||
|
($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => {
|
||||||
|
impl Instance for peripherals::$inst {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sealed::Instance for peripherals::$inst {
|
||||||
|
fn regs() -> crate::pac::cordic::Cordic {
|
||||||
|
crate::pac::$inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
macro_rules! floating_fixed_convert {
|
||||||
|
($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => {
|
||||||
|
/// convert float point to fixed point format
|
||||||
|
pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ {
|
||||||
|
const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) };
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
(-1.0 as $float_ty) <= value,
|
||||||
|
"input value {} should be equal or greater than -1",
|
||||||
|
value
|
||||||
|
);
|
||||||
|
|
||||||
|
let value = if value == 1.0 as $float_ty{
|
||||||
|
(1.0 as $float_ty) - MIN_POSITIVE
|
||||||
|
} else {
|
||||||
|
assert!(
|
||||||
|
value <= (1.0 as $float_ty) - MIN_POSITIVE,
|
||||||
|
"input value {} should be equal or less than 1-2^(-{})",
|
||||||
|
value, $offset
|
||||||
|
);
|
||||||
|
value
|
||||||
|
};
|
||||||
|
|
||||||
|
(value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
/// convert fixed point to float point format
|
||||||
|
pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty {
|
||||||
|
// It's needed to convert from unsigned to signed first, for correct result.
|
||||||
|
-(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
floating_fixed_convert!(
|
||||||
|
f64_to_q1_31,
|
||||||
|
q1_31_to_f64,
|
||||||
|
u32,
|
||||||
|
i32,
|
||||||
|
f64,
|
||||||
|
31,
|
||||||
|
0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31)
|
||||||
|
);
|
||||||
|
|
||||||
|
floating_fixed_convert!(
|
||||||
|
f32_to_q1_15,
|
||||||
|
q1_15_to_f32,
|
||||||
|
u16,
|
||||||
|
i16,
|
||||||
|
f32,
|
||||||
|
15,
|
||||||
|
0x3800_0000u32 // binary form of 1f32^(-15)
|
||||||
|
);
|
|
@ -32,6 +32,8 @@ pub mod timer;
|
||||||
pub mod adc;
|
pub mod adc;
|
||||||
#[cfg(can)]
|
#[cfg(can)]
|
||||||
pub mod can;
|
pub mod can;
|
||||||
|
#[cfg(cordic)]
|
||||||
|
pub mod cordic;
|
||||||
#[cfg(crc)]
|
#[cfg(crc)]
|
||||||
pub mod crc;
|
pub mod crc;
|
||||||
#[cfg(cryp)]
|
#[cfg(cryp)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue