aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src
diff options
context:
space:
mode:
authoreZio Pan <[email protected]>2024-03-12 00:54:26 +0800
committereZio Pan <[email protected]>2024-03-23 09:15:25 +0800
commitcf065d439efed2141aaf09454beb445e80dc7539 (patch)
tree92483c34c3c035d1b73328311a1c631023cfd5bf /embassy-stm32/src
parent1171e116553f6ac43e12505b387b6eabe3037bf9 (diff)
stm32 CORDIC: ZeroOverhead q1.31 1 arg 1 res mode
Diffstat (limited to 'embassy-stm32/src')
-rw-r--r--embassy-stm32/src/cordic.rs460
-rw-r--r--embassy-stm32/src/lib.rs2
2 files changed, 462 insertions, 0 deletions
diff --git a/embassy-stm32/src/cordic.rs b/embassy-stm32/src/cordic.rs
new file mode 100644
index 000000000..952ee187a
--- /dev/null
+++ b/embassy-stm32/src/cordic.rs
@@ -0,0 +1,460 @@
1//! CORDIC co-processor
2
3use crate::peripherals;
4use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
5
6pub use enums::*;
7
8mod enums {
9 /// CORDIC function
10 #[allow(missing_docs)]
11 #[derive(Clone, Copy)]
12 pub enum Function {
13 Cos = 0,
14 Sin,
15 Phase,
16 Modulus,
17 Arctan,
18 Cosh,
19 Sinh,
20 Arctanh,
21 Ln,
22 Sqrt,
23 }
24
25 /// CORDIC precision
26 #[allow(missing_docs)]
27 #[derive(Clone, Copy)]
28 pub enum Precision {
29 Iters4 = 1,
30 Iters8,
31 Iters12,
32 Iters16,
33 Iters20,
34 Iters24,
35 Iters28,
36 Iters32,
37 Iters36,
38 Iters40,
39 Iters44,
40 Iters48,
41 Iters52,
42 Iters56,
43 Iters60,
44 }
45
46 /// CORDIC scale
47 #[allow(non_camel_case_types)]
48 #[allow(missing_docs)]
49 #[derive(Clone, Copy, Default)]
50 pub enum Scale {
51 #[default]
52 A1_R1 = 0,
53 A1o2_R2,
54 A1o4_R4,
55 A1o8_R8,
56 A1o16_R16,
57 A1o32_R32,
58 A1o64_R64,
59 A1o128_R128,
60 }
61
62 /// CORDIC argument/result count
63 #[allow(missing_docs)]
64 #[derive(Clone, Copy, Default)]
65 pub enum Count {
66 #[default]
67 One,
68 Two,
69 }
70
71 /// CORDIC argument/result data width
72 #[allow(missing_docs)]
73 #[derive(Clone, Copy)]
74 pub enum Width {
75 Bits32,
76 Bits16,
77 }
78
79 /// Cordic driver running mode
80 #[derive(Clone, Copy)]
81 pub enum Mode {
82 /// After caculation start, a read to RDATA register will block AHB until the caculation finished
83 ZeroOverhead,
84
85 /// Use CORDIC interrupt to trigger a read result value
86 Interrupt,
87
88 /// Use DMA to write/read value
89 Dma,
90 }
91}
92
93/// Low-level CORDIC access.
94#[cfg(feature = "unstable-pac")]
95pub mod low_level {
96 pub use super::sealed::*;
97}
98
99pub(crate) mod sealed {
100 use super::*;
101 use crate::pac::cordic::vals;
102
103 /// Cordic instance
104 pub trait Instance {
105 /// Get access to CORDIC registers
106 fn regs() -> crate::pac::cordic::Cordic;
107
108 /// Set Function value
109 fn set_func(&self, func: Function) {
110 Self::regs()
111 .csr()
112 .modify(|v| v.set_func(vals::Func::from_bits(func as u8)));
113 }
114
115 /// Set Precision value
116 fn set_precision(&self, precision: Precision) {
117 Self::regs()
118 .csr()
119 .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8)))
120 }
121
122 /// Set Scale value
123 fn set_scale(&self, scale: Scale) {
124 Self::regs()
125 .csr()
126 .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8)))
127 }
128
129 /// Enable global interrupt
130 fn enable_irq(&self) {
131 Self::regs().csr().modify(|v| v.set_ien(true))
132 }
133
134 /// Disable global interrupt
135 fn disable_irq(&self) {
136 Self::regs().csr().modify(|v| v.set_ien(false))
137 }
138
139 /// Enable Read DMA
140 fn enable_read_dma(&self) {
141 Self::regs().csr().modify(|v| {
142 v.set_dmaren(true);
143 })
144 }
145
146 /// Disable Read DMA
147 fn disable_read_dma(&self) {
148 Self::regs().csr().modify(|v| {
149 v.set_dmaren(false);
150 })
151 }
152
153 /// Enable Write DMA
154 fn enable_write_dma(&self) {
155 Self::regs().csr().modify(|v| {
156 v.set_dmawen(true);
157 })
158 }
159
160 /// Disable Write DMA
161 fn disable_write_dma(&self) {
162 Self::regs().csr().modify(|v| {
163 v.set_dmawen(false);
164 })
165 }
166
167 /// Set NARGS value
168 fn set_argument_count(&self, n: Count) {
169 Self::regs().csr().modify(|v| {
170 v.set_nargs(match n {
171 Count::One => vals::Num::NUM1,
172 Count::Two => vals::Num::NUM2,
173 })
174 })
175 }
176
177 /// Set NRES value
178 fn set_result_count(&self, n: Count) {
179 Self::regs().csr().modify(|v| {
180 v.set_nres(match n {
181 Count::One => vals::Num::NUM1,
182 Count::Two => vals::Num::NUM2,
183 });
184 })
185 }
186
187 /// Set ARGSIZE and RESSIZE value
188 fn set_data_width(&self, arg: Width, res: Width) {
189 Self::regs().csr().modify(|v| {
190 v.set_argsize(match arg {
191 Width::Bits32 => vals::Size::BITS32,
192 Width::Bits16 => vals::Size::BITS16,
193 });
194 v.set_ressize(match res {
195 Width::Bits32 => vals::Size::BITS32,
196 Width::Bits16 => vals::Size::BITS16,
197 })
198 })
199 }
200
201 /// Read RRDY flag
202 fn ready_to_read(&self) -> bool {
203 Self::regs().csr().read().rrdy()
204 }
205
206 /// Write value to WDATA
207 fn write_argument(&self, arg: u32) {
208 Self::regs().wdata().write_value(arg)
209 }
210
211 /// Read value from RDATA
212 fn read_result(&self) -> u32 {
213 Self::regs().rdata().read()
214 }
215 }
216}
217
218/// CORDIC driver
219pub struct Cordic<'d, T: Instance> {
220 cordic: PeripheralRef<'d, T>,
221 config: Config,
222 //state: State,
223}
224
225/// CORDIC instance trait
226pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral {}
227
228/// CORDIC configuration
229pub struct Config {
230 function: Function,
231 precision: Precision,
232 scale: Scale,
233 mode: Mode,
234 first_result: bool,
235}
236
237// CORDIC running state
238//struct State {
239// input_buf: [u32; 8],
240// buf_len: usize,
241//}
242
243impl Config {
244 /// Create a config for Cordic driver
245 pub fn new(function: Function, precision: Precision, scale: Option<Scale>, mode: Mode, first_result: bool) -> Self {
246 Self {
247 function,
248 precision,
249 scale: scale.unwrap_or_default(),
250 mode,
251 first_result,
252 }
253 }
254
255 fn check_scale(&self) -> bool {
256 let scale_raw = self.scale as u8;
257
258 match self.function {
259 Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw,
260 Function::Arctan => (0..=7).contains(&scale_raw),
261 Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw,
262 Function::Ln => (1..=4).contains(&scale_raw),
263 Function::Sqrt => (0..=2).contains(&scale_raw),
264 }
265 }
266}
267
268impl<'d, T: Instance> Cordic<'d, T> {
269 /// Create a Cordic driver instance
270 ///
271 /// Note:
272 /// If you need a periperhal -> CORDIC -> peripehral mode,
273 /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config]
274 pub fn new(cordic: impl Peripheral<P = T> + 'd, config: Config) -> Self {
275 T::enable_and_reset();
276
277 into_ref!(cordic);
278
279 if !config.check_scale() {
280 panic!("Scale value is not compatible with Function")
281 }
282
283 let mut instance = Self {
284 cordic,
285 config,
286 // state: State {
287 // input_buf: [0u32; 8],
288 // buf_len: 0,
289 // },
290 };
291
292 instance.reconfigure();
293
294 instance
295 }
296
297 /// Set a new config for Cordic driver
298 pub fn set_config(&mut self, config: Config) {
299 self.config = config;
300 self.reconfigure();
301 }
302
303 /// Set extra config for data count and data width.
304 pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) {
305 let peri = &self.cordic;
306 peri.set_argument_count(arg_cnt);
307 peri.set_data_width(arg_width, res_width);
308 }
309
310 fn reconfigure(&mut self) {
311 let peri = &self.cordic;
312 let config = &self.config;
313
314 if peri.ready_to_read() {
315 warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST");
316 };
317
318 peri.disable_irq();
319 peri.disable_write_dma();
320 peri.disable_read_dma();
321
322 // clean RRDY flag
323 while peri.ready_to_read() {
324 peri.read_result();
325 }
326
327 peri.set_func(config.function);
328 peri.set_precision(config.precision);
329 peri.set_scale(config.scale);
330 if config.first_result {
331 peri.set_result_count(Count::One)
332 } else {
333 peri.set_result_count(Count::Two)
334 }
335
336 match config.mode {
337 Mode::ZeroOverhead => (),
338 Mode::Interrupt => {
339 peri.enable_irq();
340 }
341 Mode::Dma => {
342 peri.enable_write_dma();
343 peri.enable_read_dma();
344 }
345 }
346
347 //self.state.input_buf.fill(0u32);
348 }
349
350 /// Run a CORDIC calculation
351 pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize {
352 match self.config.mode {
353 Mode::ZeroOverhead => {
354 if arg2s.is_none() {
355 self.cordic.set_argument_count(Count::One);
356
357 self.cordic.set_result_count(if self.config.first_result {
358 if output.len() < arg1s.len() {
359 panic!("Output buf length is not long enough")
360 }
361 Count::One
362 } else {
363 if output.len() < 2 * arg1s.len() {
364 panic!("Output buf length is not long enough")
365 }
366 Count::Two
367 });
368
369 let mut cnt = 0;
370
371 for &arg in arg1s.iter() {
372 self.cordic.write_argument(f64_to_q1_31(arg));
373 output[cnt] = q1_31_to_f64(self.cordic.read_result());
374 cnt += 1;
375 }
376
377 cnt
378 } else {
379 todo!()
380 }
381 }
382 Mode::Interrupt => todo!(),
383 Mode::Dma => todo!(),
384 }
385 }
386}
387
388impl<'d, T: Instance> Drop for Cordic<'d, T> {
389 fn drop(&mut self) {
390 T::disable();
391 }
392}
393
394foreach_interrupt!(
395 ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => {
396 impl Instance for peripherals::$inst {
397 }
398
399 impl sealed::Instance for peripherals::$inst {
400 fn regs() -> crate::pac::cordic::Cordic {
401 crate::pac::$inst
402 }
403 }
404 };
405);
406
407macro_rules! floating_fixed_convert {
408 ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => {
409 /// convert float point to fixed point format
410 pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ {
411 const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) };
412
413 assert!(
414 (-1.0 as $float_ty) <= value,
415 "input value {} should be equal or greater than -1",
416 value
417 );
418
419 let value = if value == 1.0 as $float_ty{
420 (1.0 as $float_ty) - MIN_POSITIVE
421 } else {
422 assert!(
423 value <= (1.0 as $float_ty) - MIN_POSITIVE,
424 "input value {} should be equal or less than 1-2^(-{})",
425 value, $offset
426 );
427 value
428 };
429
430 (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ
431 }
432
433 #[inline(always)]
434 /// convert fixed point to float point format
435 pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty {
436 // It's needed to convert from unsigned to signed first, for correct result.
437 -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty)
438 }
439 };
440}
441
442floating_fixed_convert!(
443 f64_to_q1_31,
444 q1_31_to_f64,
445 u32,
446 i32,
447 f64,
448 31,
449 0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31)
450);
451
452floating_fixed_convert!(
453 f32_to_q1_15,
454 q1_15_to_f32,
455 u16,
456 i16,
457 f32,
458 15,
459 0x3800_0000u32 // binary form of 1f32^(-15)
460);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 6a3d1c463..ae2e95435 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -32,6 +32,8 @@ pub mod timer;
32pub mod adc; 32pub mod adc;
33#[cfg(can)] 33#[cfg(can)]
34pub mod can; 34pub mod can;
35#[cfg(cordic)]
36pub mod cordic;
35#[cfg(crc)] 37#[cfg(crc)]
36pub mod crc; 38pub mod crc;
37#[cfg(cryp)] 39#[cfg(cryp)]