diff options
| author | eZio Pan <[email protected]> | 2024-03-15 19:34:55 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2024-03-23 09:15:25 +0800 |
| commit | a1ca9088b4e3b4644428eab80e8502a55b2cbe8f (patch) | |
| tree | 64d5863ad5c7c084501f1237fe06773839da5ae5 | |
| parent | b595d942442a8b267e1311bcadedc8558183aa61 (diff) | |
stm32 CORDIC: ZeroOverhead q1.31 mode
| -rw-r--r-- | embassy-stm32/src/cordic/enums.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/mod.rs | 262 |
2 files changed, 227 insertions, 42 deletions
diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 4697a1df1..3e1c47f7f 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs | |||
| @@ -16,14 +16,15 @@ pub enum Function { | |||
| 16 | 16 | ||
| 17 | /// CORDIC precision | 17 | /// CORDIC precision |
| 18 | #[allow(missing_docs)] | 18 | #[allow(missing_docs)] |
| 19 | #[derive(Clone, Copy)] | 19 | #[derive(Clone, Copy, Default)] |
| 20 | pub enum Precision { | 20 | pub enum Precision { |
| 21 | Iters4 = 1, | 21 | Iters4 = 1, |
| 22 | Iters8, | 22 | Iters8, |
| 23 | Iters12, | 23 | Iters12, |
| 24 | Iters16, | 24 | Iters16, |
| 25 | Iters20, | 25 | Iters20, |
| 26 | Iters24, | 26 | #[default] |
| 27 | Iters24, // this value is recomended by Reference Manual | ||
| 27 | Iters28, | 28 | Iters28, |
| 28 | Iters32, | 29 | Iters32, |
| 29 | Iters36, | 30 | Iters36, |
| @@ -38,7 +39,7 @@ pub enum Precision { | |||
| 38 | /// CORDIC scale | 39 | /// CORDIC scale |
| 39 | #[allow(non_camel_case_types)] | 40 | #[allow(non_camel_case_types)] |
| 40 | #[allow(missing_docs)] | 41 | #[allow(missing_docs)] |
| 41 | #[derive(Clone, Copy, Default)] | 42 | #[derive(Clone, Copy, Default, PartialEq)] |
| 42 | pub enum Scale { | 43 | pub enum Scale { |
| 43 | #[default] | 44 | #[default] |
| 44 | A1_R1 = 0, | 45 | A1_R1 = 0, |
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index c0a69b757..b15521ca6 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -10,6 +10,10 @@ pub mod utils; | |||
| 10 | 10 | ||
| 11 | pub(crate) mod sealed; | 11 | pub(crate) mod sealed; |
| 12 | 12 | ||
| 13 | // length of pre-allocated [u32] memory for CORDIC input, | ||
| 14 | // length should be multiple of 2 | ||
| 15 | const INPUT_BUF_LEN: usize = 8; | ||
| 16 | |||
| 13 | /// Low-level CORDIC access. | 17 | /// Low-level CORDIC access. |
| 14 | #[cfg(feature = "unstable-pac")] | 18 | #[cfg(feature = "unstable-pac")] |
| 15 | pub mod low_level { | 19 | pub mod low_level { |
| @@ -20,7 +24,7 @@ pub mod low_level { | |||
| 20 | pub struct Cordic<'d, T: Instance> { | 24 | pub struct Cordic<'d, T: Instance> { |
| 21 | cordic: PeripheralRef<'d, T>, | 25 | cordic: PeripheralRef<'d, T>, |
| 22 | config: Config, | 26 | config: Config, |
| 23 | //state: State, | 27 | state: State, |
| 24 | } | 28 | } |
| 25 | 29 | ||
| 26 | /// CORDIC instance trait | 30 | /// CORDIC instance trait |
| @@ -28,27 +32,33 @@ pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPer | |||
| 28 | 32 | ||
| 29 | /// CORDIC configuration | 33 | /// CORDIC configuration |
| 30 | pub struct Config { | 34 | pub struct Config { |
| 35 | mode: Mode, | ||
| 31 | function: Function, | 36 | function: Function, |
| 32 | precision: Precision, | 37 | precision: Precision, |
| 33 | scale: Scale, | 38 | scale: Scale, |
| 34 | mode: Mode, | ||
| 35 | first_result: bool, | 39 | first_result: bool, |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | // CORDIC running state | 42 | // CORDIC running state |
| 39 | //struct State { | 43 | struct State { |
| 40 | // input_buf: [u32; 8], | 44 | input_buf: [u32; INPUT_BUF_LEN], |
| 41 | // buf_len: usize, | 45 | buf_index: usize, |
| 42 | //} | 46 | } |
| 43 | 47 | ||
| 44 | impl Config { | 48 | impl Config { |
| 45 | /// Create a config for Cordic driver | 49 | /// Create a config for Cordic driver |
| 46 | pub fn new(function: Function, precision: Precision, scale: Option<Scale>, mode: Mode, first_result: bool) -> Self { | 50 | pub fn new( |
| 51 | mode: Mode, | ||
| 52 | function: Function, | ||
| 53 | precision: Option<Precision>, | ||
| 54 | scale: Option<Scale>, | ||
| 55 | first_result: bool, | ||
| 56 | ) -> Self { | ||
| 47 | Self { | 57 | Self { |
| 58 | mode, | ||
| 48 | function, | 59 | function, |
| 49 | precision, | 60 | precision: precision.unwrap_or_default(), |
| 50 | scale: scale.unwrap_or_default(), | 61 | scale: scale.unwrap_or_default(), |
| 51 | mode, | ||
| 52 | first_result, | 62 | first_result, |
| 53 | } | 63 | } |
| 54 | } | 64 | } |
| @@ -66,6 +76,7 @@ impl Config { | |||
| 66 | } | 76 | } |
| 67 | } | 77 | } |
| 68 | 78 | ||
| 79 | // common method | ||
| 69 | impl<'d, T: Instance> Cordic<'d, T> { | 80 | impl<'d, T: Instance> Cordic<'d, T> { |
| 70 | /// Create a Cordic driver instance | 81 | /// Create a Cordic driver instance |
| 71 | /// | 82 | /// |
| @@ -84,10 +95,10 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 84 | let mut instance = Self { | 95 | let mut instance = Self { |
| 85 | cordic, | 96 | cordic, |
| 86 | config, | 97 | config, |
| 87 | // state: State { | 98 | state: State { |
| 88 | // input_buf: [0u32; 8], | 99 | input_buf: [0u32; 8], |
| 89 | // buf_len: 0, | 100 | buf_index: 0, |
| 90 | // }, | 101 | }, |
| 91 | }; | 102 | }; |
| 92 | 103 | ||
| 93 | instance.reconfigure(); | 104 | instance.reconfigure(); |
| @@ -128,6 +139,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 128 | peri.set_func(config.function); | 139 | peri.set_func(config.function); |
| 129 | peri.set_precision(config.precision); | 140 | peri.set_precision(config.precision); |
| 130 | peri.set_scale(config.scale); | 141 | peri.set_scale(config.scale); |
| 142 | |||
| 131 | if config.first_result { | 143 | if config.first_result { |
| 132 | peri.set_result_count(Count::One) | 144 | peri.set_result_count(Count::One) |
| 133 | } else { | 145 | } else { |
| @@ -145,55 +157,227 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 145 | } | 157 | } |
| 146 | } | 158 | } |
| 147 | 159 | ||
| 148 | //self.state.input_buf.fill(0u32); | 160 | self.state.input_buf.fill(0u32); |
| 161 | self.state.buf_index = 0; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | ||
| 166 | fn drop(&mut self) { | ||
| 167 | T::disable(); | ||
| 149 | } | 168 | } |
| 169 | } | ||
| 150 | 170 | ||
| 171 | // q1.31 related | ||
| 172 | impl<'d, T: Instance> Cordic<'d, T> { | ||
| 151 | /// Run a CORDIC calculation | 173 | /// Run a CORDIC calculation |
| 152 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { | 174 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { |
| 153 | match self.config.mode { | 175 | let peri = &self.cordic; |
| 176 | let config = &self.config; | ||
| 177 | |||
| 178 | assert!( | ||
| 179 | match config.first_result { | ||
| 180 | true => output.len() >= arg1s.len(), | ||
| 181 | false => output.len() >= 2 * arg1s.len(), | ||
| 182 | }, | ||
| 183 | "Output buf length is not long enough" | ||
| 184 | ); | ||
| 185 | |||
| 186 | self.check_input_f64(arg1s, arg2s); | ||
| 187 | |||
| 188 | peri.set_result_count(if config.first_result { Count::One } else { Count::Two }); | ||
| 189 | peri.set_data_width(Width::Bits32, Width::Bits32); | ||
| 190 | |||
| 191 | let state = &mut self.state; | ||
| 192 | |||
| 193 | let mut output_count = 0; | ||
| 194 | |||
| 195 | let mut consumed_input_len = 0; | ||
| 196 | |||
| 197 | match config.mode { | ||
| 154 | Mode::ZeroOverhead => { | 198 | Mode::ZeroOverhead => { |
| 155 | if arg2s.is_none() { | 199 | // put double input into cordic |
| 156 | self.cordic.set_argument_count(Count::One); | 200 | if arg2s.is_some() && !arg2s.unwrap().is_empty() { |
| 201 | let arg2s = arg2s.unwrap(); | ||
| 202 | |||
| 203 | peri.set_argument_count(Count::Two); | ||
| 204 | |||
| 205 | let double_value = arg1s.iter().zip(arg2s); | ||
| 206 | consumed_input_len = double_value.len(); | ||
| 207 | |||
| 208 | for (arg1, arg2) in double_value { | ||
| 209 | // if input_buf is full, send values to cordic | ||
| 210 | if state.buf_index == INPUT_BUF_LEN - 1 { | ||
| 211 | for arg in state.input_buf.chunks(2) { | ||
| 212 | peri.write_argument(arg[0]); | ||
| 213 | peri.write_argument(arg[1]); | ||
| 157 | 214 | ||
| 158 | self.cordic.set_result_count(if self.config.first_result { | 215 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); |
| 159 | if output.len() < arg1s.len() { | 216 | output_count += 1; |
| 160 | panic!("Output buf length is not long enough") | 217 | |
| 218 | if !config.first_result { | ||
| 219 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | ||
| 220 | output_count += 1; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | state.buf_index = 0; | ||
| 161 | } | 225 | } |
| 162 | Count::One | 226 | |
| 163 | } else { | 227 | for &&arg in [arg1, arg2].iter() { |
| 164 | if output.len() < 2 * arg1s.len() { | 228 | state.input_buf[state.buf_index] = utils::f64_to_q1_31(arg); |
| 165 | panic!("Output buf length is not long enough") | 229 | state.buf_index += 1; |
| 166 | } | 230 | } |
| 167 | Count::Two | 231 | } |
| 168 | }); | 232 | |
| 233 | // put left paired args into cordic | ||
| 234 | if state.buf_index > 0 { | ||
| 235 | for arg in state.input_buf[..state.buf_index].chunks(2) { | ||
| 236 | peri.write_argument(arg[0]); | ||
| 237 | peri.write_argument(arg[1]); | ||
| 169 | 238 | ||
| 170 | let mut cnt = 0; | 239 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); |
| 240 | output_count += 1; | ||
| 241 | |||
| 242 | if !config.first_result { | ||
| 243 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | ||
| 244 | output_count += 1; | ||
| 245 | } | ||
| 246 | } | ||
| 171 | 247 | ||
| 172 | for &arg in arg1s.iter() { | 248 | state.buf_index = 0; |
| 173 | self.cordic.write_argument(utils::f64_to_q1_31(arg)); | ||
| 174 | output[cnt] = utils::q1_31_to_f64(self.cordic.read_result()); | ||
| 175 | cnt += 1; | ||
| 176 | } | 249 | } |
| 250 | } | ||
| 251 | |||
| 252 | // put single input into cordic | ||
| 253 | let input_left = &arg1s[consumed_input_len..]; | ||
| 254 | |||
| 255 | if !input_left.is_empty() { | ||
| 256 | peri.set_argument_count(Count::One); | ||
| 257 | |||
| 258 | for &arg in input_left.iter() { | ||
| 259 | peri.write_argument(utils::f64_to_q1_31(arg)); | ||
| 177 | 260 | ||
| 178 | cnt | 261 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); |
| 179 | } else { | 262 | output_count += 1; |
| 180 | todo!() | 263 | |
| 264 | if !config.first_result { | ||
| 265 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | ||
| 266 | output_count += 1; | ||
| 267 | } | ||
| 268 | } | ||
| 181 | } | 269 | } |
| 270 | |||
| 271 | output_count | ||
| 182 | } | 272 | } |
| 183 | Mode::Interrupt => todo!(), | 273 | Mode::Interrupt => todo!(), |
| 184 | Mode::Dma => todo!(), | 274 | Mode::Dma => todo!(), |
| 185 | } | 275 | } |
| 186 | } | 276 | } |
| 187 | } | ||
| 188 | 277 | ||
| 189 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | 278 | fn check_input_f64(&self, arg1s: &[f64], arg2s: Option<&[f64]>) { |
| 190 | fn drop(&mut self) { | 279 | let config = &self.config; |
| 191 | T::disable(); | 280 | |
| 281 | use Function::*; | ||
| 282 | |||
| 283 | // check SCALE value | ||
| 284 | match config.function { | ||
| 285 | Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), | ||
| 286 | Arctan => assert!( | ||
| 287 | (0..=7).contains(&(config.scale as u8)), | ||
| 288 | "SCALE should be: 0 <= SCALE <= 7" | ||
| 289 | ), | ||
| 290 | Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), | ||
| 291 | |||
| 292 | Ln => assert!( | ||
| 293 | (1..=4).contains(&(config.scale as u8)), | ||
| 294 | "SCALE should be: 1 <= SCALE <= 4" | ||
| 295 | ), | ||
| 296 | Sqrt => assert!( | ||
| 297 | (0..=2).contains(&(config.scale as u8)), | ||
| 298 | "SCALE should be: 0 <= SCALE <= 2" | ||
| 299 | ), | ||
| 300 | } | ||
| 301 | |||
| 302 | // check ARG1 value | ||
| 303 | match config.function { | ||
| 304 | Cos | Sin | Phase | Modulus | Arctan => { | ||
| 305 | assert!( | ||
| 306 | arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), | ||
| 307 | "ARG1 should be: -1 <= ARG1 <= 1" | ||
| 308 | ); | ||
| 309 | } | ||
| 310 | |||
| 311 | Cosh | Sinh => assert!( | ||
| 312 | arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), | ||
| 313 | "ARG1 should be: -0.559 <= ARG1 <= 0.559" | ||
| 314 | ), | ||
| 315 | |||
| 316 | Arctanh => assert!( | ||
| 317 | arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), | ||
| 318 | "ARG1 should be: -0.403 <= ARG1 <= 0.403" | ||
| 319 | ), | ||
| 320 | |||
| 321 | Ln => { | ||
| 322 | match config.scale { | ||
| 323 | Scale::A1o2_R2 => assert!( | ||
| 324 | arg1s.iter().all(|v| (0.05354..0.5).contains(v)), | ||
| 325 | "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" | ||
| 326 | ), | ||
| 327 | Scale::A1o4_R4 => assert!( | ||
| 328 | arg1s.iter().all(|v| (0.25..0.75).contains(v)), | ||
| 329 | "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" | ||
| 330 | ), | ||
| 331 | Scale::A1o8_R8 => assert!( | ||
| 332 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), | ||
| 333 | "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" | ||
| 334 | ), | ||
| 335 | Scale::A1o16_R16 => assert!( | ||
| 336 | arg1s.iter().all(|v| (0.4375f64..0.584f64).contains(v)), | ||
| 337 | "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" | ||
| 338 | ), | ||
| 339 | _ => unreachable!(), | ||
| 340 | }; | ||
| 341 | } | ||
| 342 | |||
| 343 | Function::Sqrt => match config.scale { | ||
| 344 | Scale::A1_R1 => assert!( | ||
| 345 | arg1s.iter().all(|v| (0.027..0.75).contains(v)), | ||
| 346 | "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" | ||
| 347 | ), | ||
| 348 | Scale::A1o2_R2 => assert!( | ||
| 349 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), | ||
| 350 | "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" | ||
| 351 | ), | ||
| 352 | Scale::A1o4_R4 => assert!( | ||
| 353 | arg1s.iter().all(|v| (0.4375..0.585).contains(v)), | ||
| 354 | "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" | ||
| 355 | ), | ||
| 356 | _ => unreachable!(), | ||
| 357 | }, | ||
| 358 | } | ||
| 359 | |||
| 360 | // check ARG2 value | ||
| 361 | if let Some(arg2s) = arg2s { | ||
| 362 | match config.function { | ||
| 363 | Cos | Sin => assert!( | ||
| 364 | arg2s.iter().all(|v| (0.0..=1.0).contains(v)), | ||
| 365 | "ARG2 should be: 0 <= ARG2 <= 1" | ||
| 366 | ), | ||
| 367 | |||
| 368 | Phase | Modulus => assert!( | ||
| 369 | arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), | ||
| 370 | "ARG2 should be: -1 <= ARG2 <= 1" | ||
| 371 | ), | ||
| 372 | |||
| 373 | _ => (), | ||
| 374 | } | ||
| 375 | } | ||
| 192 | } | 376 | } |
| 193 | } | 377 | } |
| 194 | 378 | ||
| 195 | foreach_interrupt!( | 379 | foreach_interrupt!( |
| 196 | ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { | 380 | ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { |
| 197 | impl Instance for peripherals::$inst { | 381 | impl Instance for peripherals::$inst { |
| 198 | } | 382 | } |
| 199 | 383 | ||
