diff options
| author | eZio Pan <[email protected]> | 2024-03-16 21:20:17 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2024-03-23 09:15:25 +0800 |
| commit | c9f759bb21782eb0487c96a59500310d1283694c (patch) | |
| tree | b292c0c1931c0d32064cd203dd657651c6d00de5 | |
| parent | 5d12f594303bdb76bf2356d9fc0661826e2e658e (diff) | |
stm32 CORDIC: ZeroOverhead for q1.31 and q1.15
| -rw-r--r-- | embassy-stm32/src/cordic/enums.rs | 13 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/mod.rs | 440 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/utils.rs | 4 |
3 files changed, 264 insertions, 193 deletions
diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 3e1c47f7f..37c73f549 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs | |||
| @@ -68,16 +68,3 @@ pub enum Width { | |||
| 68 | Bits32, | 68 | Bits32, |
| 69 | Bits16, | 69 | Bits16, |
| 70 | } | 70 | } |
| 71 | |||
| 72 | /// Cordic driver running mode | ||
| 73 | #[derive(Clone, Copy)] | ||
| 74 | pub enum Mode { | ||
| 75 | /// After caculation start, a read to RDATA register will block AHB until the caculation finished | ||
| 76 | ZeroOverhead, | ||
| 77 | |||
| 78 | /// Use CORDIC interrupt to trigger a read result value | ||
| 79 | Interrupt, | ||
| 80 | |||
| 81 | /// Use DMA to write/read value | ||
| 82 | Dma, | ||
| 83 | } | ||
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 997ace113..61277d7e1 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -1,8 +1,9 @@ | |||
| 1 | //! CORDIC co-processor | 1 | //! CORDIC co-processor |
| 2 | 2 | ||
| 3 | use crate::peripherals; | ||
| 4 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 3 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 5 | 4 | ||
| 5 | use crate::peripherals; | ||
| 6 | |||
| 6 | mod enums; | 7 | mod enums; |
| 7 | pub use enums::*; | 8 | pub use enums::*; |
| 8 | 9 | ||
| @@ -10,10 +11,6 @@ pub mod utils; | |||
| 10 | 11 | ||
| 11 | pub(crate) mod sealed; | 12 | pub(crate) mod sealed; |
| 12 | 13 | ||
| 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 | |||
| 17 | /// Low-level CORDIC access. | 14 | /// Low-level CORDIC access. |
| 18 | #[cfg(feature = "unstable-pac")] | 15 | #[cfg(feature = "unstable-pac")] |
| 19 | pub mod low_level { | 16 | pub mod low_level { |
| @@ -31,30 +28,16 @@ pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPer | |||
| 31 | 28 | ||
| 32 | /// CORDIC configuration | 29 | /// CORDIC configuration |
| 33 | pub struct Config { | 30 | pub struct Config { |
| 34 | mode: Mode, | ||
| 35 | function: Function, | 31 | function: Function, |
| 36 | precision: Precision, | 32 | precision: Precision, |
| 37 | scale: Scale, | 33 | scale: Scale, |
| 38 | first_result: bool, | 34 | first_result: bool, |
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | // CORDIC running state | ||
| 42 | struct State { | ||
| 43 | input_buf: [u32; INPUT_BUF_LEN], | ||
| 44 | buf_index: usize, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl Config { | 37 | impl Config { |
| 48 | /// Create a config for Cordic driver | 38 | /// Create a config for Cordic driver |
| 49 | pub fn new( | 39 | pub fn new(function: Function, precision: Option<Precision>, scale: Option<Scale>, first_result: bool) -> Self { |
| 50 | mode: Mode, | ||
| 51 | function: Function, | ||
| 52 | precision: Option<Precision>, | ||
| 53 | scale: Option<Scale>, | ||
| 54 | first_result: bool, | ||
| 55 | ) -> Self { | ||
| 56 | Self { | 40 | Self { |
| 57 | mode, | ||
| 58 | function, | 41 | function, |
| 59 | precision: precision.unwrap_or_default(), | 42 | precision: precision.unwrap_or_default(), |
| 60 | scale: scale.unwrap_or_default(), | 43 | scale: scale.unwrap_or_default(), |
| @@ -133,22 +116,123 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 133 | } else { | 116 | } else { |
| 134 | self.peri.set_result_count(Count::Two) | 117 | self.peri.set_result_count(Count::Two) |
| 135 | } | 118 | } |
| 119 | } | ||
| 120 | |||
| 121 | fn blocking_read_f32(&mut self) -> (f32, Option<f32>) { | ||
| 122 | let reg_value = self.peri.read_result(); | ||
| 123 | |||
| 124 | let res1 = utils::q1_15_to_f32((reg_value & ((1u32 << 16) - 1)) as u16); | ||
| 125 | |||
| 126 | // We don't care about whether the function return 1 or 2 results, | ||
| 127 | // the only thing matter is whether user want 1 or 2 results. | ||
| 128 | let res2 = if !self.config.first_result { | ||
| 129 | Some(utils::q1_15_to_f32((reg_value >> 16) as u16)) | ||
| 130 | } else { | ||
| 131 | None | ||
| 132 | }; | ||
| 133 | |||
| 134 | (res1, res2) | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | ||
| 139 | fn drop(&mut self) { | ||
| 140 | T::disable(); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | // q1.31 related | ||
| 145 | impl<'d, T: Instance> Cordic<'d, T> { | ||
| 146 | /// Run a CORDIC calculation | ||
| 147 | pub fn blocking_calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { | ||
| 148 | if arg1s.is_empty() { | ||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | assert!( | ||
| 153 | match self.config.first_result { | ||
| 154 | true => output.len() >= arg1s.len(), | ||
| 155 | false => output.len() >= 2 * arg1s.len(), | ||
| 156 | }, | ||
| 157 | "Output buf length is not long enough" | ||
| 158 | ); | ||
| 159 | |||
| 160 | self.check_input_f64(arg1s, arg2s); | ||
| 161 | |||
| 162 | self.peri.disable_irq(); | ||
| 163 | self.peri.disable_write_dma(); | ||
| 164 | self.peri.disable_read_dma(); | ||
| 165 | |||
| 166 | self.peri.set_result_count(if self.config.first_result { | ||
| 167 | Count::One | ||
| 168 | } else { | ||
| 169 | Count::Two | ||
| 170 | }); | ||
| 171 | |||
| 172 | self.peri.set_data_width(Width::Bits32, Width::Bits32); | ||
| 173 | |||
| 174 | let mut output_count = 0; | ||
| 175 | |||
| 176 | let mut consumed_input_len = 0; | ||
| 177 | |||
| 178 | // put double input into cordic | ||
| 179 | if arg2s.is_some() && !arg2s.expect("It's infailable").is_empty() { | ||
| 180 | let arg2s = arg2s.expect("It's infailable"); | ||
| 136 | 181 | ||
| 137 | match self.config.mode { | 182 | self.peri.set_argument_count(Count::Two); |
| 138 | Mode::ZeroOverhead => (), | 183 | |
| 139 | Mode::Interrupt => { | 184 | // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. |
| 140 | self.peri.enable_irq(); | 185 | // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. |
| 186 | let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); | ||
| 187 | // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. | ||
| 188 | consumed_input_len = double_input.len() + 1; | ||
| 189 | |||
| 190 | // preload first value from arg1 to cordic | ||
| 191 | self.blocking_write_f64(arg1s[0]); | ||
| 192 | |||
| 193 | for (&arg1, &arg2) in double_input { | ||
| 194 | // Since we manually preload a value before, | ||
| 195 | // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) | ||
| 196 | // and write arg1 (from the actual next pair), then read the result, to "keep preloading" | ||
| 197 | |||
| 198 | self.blocking_write_f64(arg2); | ||
| 199 | self.blocking_write_f64(arg1); | ||
| 200 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 141 | } | 201 | } |
| 142 | Mode::Dma => { | 202 | |
| 143 | self.peri.enable_write_dma(); | 203 | // write last input value from arg2s, then read out the result |
| 144 | self.peri.enable_read_dma(); | 204 | self.blocking_write_f64(arg2s[arg2s.len() - 1]); |
| 205 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 206 | } | ||
| 207 | |||
| 208 | // put single input into cordic | ||
| 209 | let input_left = &arg1s[consumed_input_len..]; | ||
| 210 | |||
| 211 | if !input_left.is_empty() { | ||
| 212 | self.peri.set_argument_count(Count::One); | ||
| 213 | |||
| 214 | // "preload" value to cordic (at this moment, cordic start to calculating) | ||
| 215 | self.blocking_write_f64(input_left[0]); | ||
| 216 | |||
| 217 | for &arg in input_left.iter().skip(1) { | ||
| 218 | // this line write arg for next round caculation to cordic, | ||
| 219 | // and read result from last round | ||
| 220 | self.blocking_write_f64(arg); | ||
| 221 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 145 | } | 222 | } |
| 223 | |||
| 224 | // read the last output | ||
| 225 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 146 | } | 226 | } |
| 227 | |||
| 228 | output_count | ||
| 147 | } | 229 | } |
| 148 | 230 | ||
| 149 | fn blocking_read_f64(&mut self) -> (f64, Option<f64>) { | 231 | fn blocking_read_f64(&mut self) -> (f64, Option<f64>) { |
| 150 | let res1 = utils::q1_31_to_f64(self.peri.read_result()); | 232 | let res1 = utils::q1_31_to_f64(self.peri.read_result()); |
| 151 | 233 | ||
| 234 | // We don't care about whether the function return 1 or 2 results, | ||
| 235 | // the only thing matter is whether user want 1 or 2 results. | ||
| 152 | let res2 = if !self.config.first_result { | 236 | let res2 = if !self.config.first_result { |
| 153 | Some(utils::q1_31_to_f64(self.peri.read_result())) | 237 | Some(utils::q1_31_to_f64(self.peri.read_result())) |
| 154 | } else { | 238 | } else { |
| @@ -174,16 +258,14 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 174 | } | 258 | } |
| 175 | } | 259 | } |
| 176 | 260 | ||
| 177 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | 261 | // q1.15 related |
| 178 | fn drop(&mut self) { | ||
| 179 | T::disable(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | // q1.31 related | ||
| 184 | impl<'d, T: Instance> Cordic<'d, T> { | 262 | impl<'d, T: Instance> Cordic<'d, T> { |
| 185 | /// Run a CORDIC calculation | 263 | /// Run a CORDIC calculation |
| 186 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { | 264 | pub fn blocking_calc_16bit(&mut self, arg1s: &[f32], arg2s: Option<&[f32]>, output: &mut [f32]) -> usize { |
| 265 | if arg1s.is_empty() { | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 187 | assert!( | 269 | assert!( |
| 188 | match self.config.first_result { | 270 | match self.config.first_result { |
| 189 | true => output.len() >= arg1s.len(), | 271 | true => output.len() >= arg1s.len(), |
| @@ -192,180 +274,182 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 192 | "Output buf length is not long enough" | 274 | "Output buf length is not long enough" |
| 193 | ); | 275 | ); |
| 194 | 276 | ||
| 195 | self.check_input_f64(arg1s, arg2s); | 277 | self.check_input_f32(arg1s, arg2s); |
| 196 | |||
| 197 | self.peri.set_result_count(if self.config.first_result { | ||
| 198 | Count::One | ||
| 199 | } else { | ||
| 200 | Count::Two | ||
| 201 | }); | ||
| 202 | |||
| 203 | self.peri.set_data_width(Width::Bits32, Width::Bits32); | ||
| 204 | 278 | ||
| 205 | let mut output_count = 0; | 279 | self.peri.disable_irq(); |
| 280 | self.peri.disable_write_dma(); | ||
| 281 | self.peri.disable_read_dma(); | ||
| 206 | 282 | ||
| 207 | let mut consumed_input_len = 0; | 283 | // In q1.15 mode, 1 write/read to access 2 arguments/results |
| 284 | self.peri.set_argument_count(Count::One); | ||
| 285 | self.peri.set_result_count(Count::One); | ||
| 208 | 286 | ||
| 209 | match self.config.mode { | 287 | self.peri.set_data_width(Width::Bits16, Width::Bits16); |
| 210 | Mode::ZeroOverhead => { | ||
| 211 | // put double input into cordic | ||
| 212 | if arg2s.is_some() && !arg2s.unwrap().is_empty() { | ||
| 213 | let arg2s = arg2s.unwrap(); | ||
| 214 | 288 | ||
| 215 | self.peri.set_argument_count(Count::Two); | 289 | let mut output_count = 0; |
| 216 | 290 | ||
| 217 | // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. | 291 | // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. |
| 218 | // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. | 292 | // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). |
| 219 | let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); | 293 | // If arg2s has some value, and but not as long as arg1s, |
| 220 | // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. | 294 | // we fill the reset of arg2 values with last value from arg2s (as q1.31 version does) |
| 221 | consumed_input_len = double_input.len() + 1; | ||
| 222 | 295 | ||
| 223 | // preload first value from arg1 to cordic | 296 | let arg2_default_value = match arg2s { |
| 224 | self.blocking_write_f64(arg1s[0]); | 297 | Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], |
| 298 | _ => 1.0, | ||
| 299 | }; | ||
| 225 | 300 | ||
| 226 | for (&arg1, &arg2) in double_input { | 301 | let mut args = arg1s.iter().zip( |
| 227 | // Since we manually preload a value before, | 302 | arg2s |
| 228 | // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) | 303 | .unwrap_or(&[]) |
| 229 | // and write arg1 (from the actual next pair), then read the result, to "keep preloading" | 304 | .iter() |
| 305 | .chain(core::iter::repeat(&arg2_default_value)), | ||
| 306 | ); | ||
| 230 | 307 | ||
| 231 | self.blocking_write_f64(arg2); | 308 | let (&arg1, &arg2) = args |
| 232 | self.blocking_write_f64(arg1); | 309 | .next() |
| 233 | self.blocking_read_f64_to_buf(output, &mut output_count); | 310 | .expect("This should be infallible, since arg1s is not empty"); |
| 234 | } | ||
| 235 | 311 | ||
| 236 | // write last input value from arg2s, then read out the result | 312 | // preloading 1 pair of arguments |
| 237 | self.blocking_write_f64(arg2s[arg2s.len() - 1]); | 313 | self.blocking_write_f32(arg1, arg2); |
| 238 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 239 | } | ||
| 240 | 314 | ||
| 241 | // put single input into cordic | 315 | for (&arg1, &arg2) in args { |
| 242 | let input_left = &arg1s[consumed_input_len..]; | 316 | self.blocking_write_f32(arg1, arg2); |
| 317 | self.blocking_read_f32_to_buf(output, &mut output_count); | ||
| 318 | } | ||
| 243 | 319 | ||
| 244 | if !input_left.is_empty() { | 320 | // read last pair of value from cordic |
| 245 | self.peri.set_argument_count(Count::One); | 321 | self.blocking_read_f32_to_buf(output, &mut output_count); |
| 246 | 322 | ||
| 247 | // "preload" value to cordic (at this moment, cordic start to calculating) | 323 | output_count |
| 248 | self.blocking_write_f64(input_left[0]); | 324 | } |
| 249 | 325 | ||
| 250 | for &arg in input_left.iter().skip(1) { | 326 | fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { |
| 251 | // this line write arg for next round caculation to cordic, | 327 | let reg_value: u32 = utils::f32_to_q1_15(arg1) as u32 + ((utils::f32_to_q1_15(arg2) as u32) << 16); |
| 252 | // and read result from last round | 328 | self.peri.write_argument(reg_value); |
| 253 | self.blocking_write_f64(arg); | 329 | } |
| 254 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 255 | } | ||
| 256 | 330 | ||
| 257 | // read the last output | 331 | fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { |
| 258 | self.blocking_read_f64_to_buf(output, &mut output_count); | 332 | let (res1, res2) = self.blocking_read_f32(); |
| 259 | } | 333 | result_buf[*result_index] = res1; |
| 334 | *result_index += 1; | ||
| 260 | 335 | ||
| 261 | output_count | 336 | if let Some(res2) = res2 { |
| 262 | } | 337 | result_buf[*result_index] = res2; |
| 263 | Mode::Interrupt => todo!(), | 338 | *result_index += 1; |
| 264 | Mode::Dma => todo!(), | ||
| 265 | } | 339 | } |
| 266 | } | 340 | } |
| 341 | } | ||
| 267 | 342 | ||
| 268 | fn check_input_f64(&self, arg1s: &[f64], arg2s: Option<&[f64]>) { | 343 | // check input value ARG1, ARG2, SCALE and FUNCTION are compatible with each other |
| 269 | let config = &self.config; | 344 | macro_rules! check_input_value { |
| 270 | 345 | ($func_name:ident, $float_type:ty) => { | |
| 271 | use Function::*; | 346 | impl<'d, T: Instance> Cordic<'d, T> { |
| 272 | 347 | fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) { | |
| 273 | // check SCALE value | 348 | let config = &self.config; |
| 274 | match config.function { | 349 | |
| 275 | Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), | 350 | use Function::*; |
| 276 | Arctan => assert!( | 351 | |
| 277 | (0..=7).contains(&(config.scale as u8)), | 352 | // check SCALE value |
| 278 | "SCALE should be: 0 <= SCALE <= 7" | 353 | match config.function { |
| 279 | ), | 354 | Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), |
| 280 | Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), | 355 | Arctan => assert!( |
| 281 | 356 | (0..=7).contains(&(config.scale as u8)), | |
| 282 | Ln => assert!( | 357 | "SCALE should be: 0 <= SCALE <= 7" |
| 283 | (1..=4).contains(&(config.scale as u8)), | 358 | ), |
| 284 | "SCALE should be: 1 <= SCALE <= 4" | 359 | Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), |
| 285 | ), | ||
| 286 | Sqrt => assert!( | ||
| 287 | (0..=2).contains(&(config.scale as u8)), | ||
| 288 | "SCALE should be: 0 <= SCALE <= 2" | ||
| 289 | ), | ||
| 290 | } | ||
| 291 | |||
| 292 | // check ARG1 value | ||
| 293 | match config.function { | ||
| 294 | Cos | Sin | Phase | Modulus | Arctan => { | ||
| 295 | assert!( | ||
| 296 | arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), | ||
| 297 | "ARG1 should be: -1 <= ARG1 <= 1" | ||
| 298 | ); | ||
| 299 | } | ||
| 300 | 360 | ||
| 301 | Cosh | Sinh => assert!( | 361 | Ln => assert!( |
| 302 | arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), | 362 | (1..=4).contains(&(config.scale as u8)), |
| 303 | "ARG1 should be: -0.559 <= ARG1 <= 0.559" | 363 | "SCALE should be: 1 <= SCALE <= 4" |
| 304 | ), | ||
| 305 | |||
| 306 | Arctanh => assert!( | ||
| 307 | arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), | ||
| 308 | "ARG1 should be: -0.403 <= ARG1 <= 0.403" | ||
| 309 | ), | ||
| 310 | |||
| 311 | Ln => { | ||
| 312 | match config.scale { | ||
| 313 | Scale::A1o2_R2 => assert!( | ||
| 314 | arg1s.iter().all(|v| (0.05354..0.5).contains(v)), | ||
| 315 | "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" | ||
| 316 | ), | 364 | ), |
| 317 | Scale::A1o4_R4 => assert!( | 365 | Sqrt => assert!( |
| 318 | arg1s.iter().all(|v| (0.25..0.75).contains(v)), | 366 | (0..=2).contains(&(config.scale as u8)), |
| 319 | "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" | 367 | "SCALE should be: 0 <= SCALE <= 2" |
| 320 | ), | 368 | ), |
| 321 | Scale::A1o8_R8 => assert!( | 369 | } |
| 322 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), | 370 | |
| 323 | "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" | 371 | // check ARG1 value |
| 372 | match config.function { | ||
| 373 | Cos | Sin | Phase | Modulus | Arctan => { | ||
| 374 | assert!( | ||
| 375 | arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), | ||
| 376 | "ARG1 should be: -1 <= ARG1 <= 1" | ||
| 377 | ); | ||
| 378 | } | ||
| 379 | |||
| 380 | Cosh | Sinh => assert!( | ||
| 381 | arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), | ||
| 382 | "ARG1 should be: -0.559 <= ARG1 <= 0.559" | ||
| 324 | ), | 383 | ), |
| 325 | Scale::A1o16_R16 => assert!( | 384 | |
| 326 | arg1s.iter().all(|v| (0.4375f64..0.584f64).contains(v)), | 385 | Arctanh => assert!( |
| 327 | "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" | 386 | arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), |
| 387 | "ARG1 should be: -0.403 <= ARG1 <= 0.403" | ||
| 328 | ), | 388 | ), |
| 329 | _ => unreachable!(), | ||
| 330 | }; | ||
| 331 | } | ||
| 332 | 389 | ||
| 333 | Function::Sqrt => match config.scale { | 390 | Ln => { |
| 334 | Scale::A1_R1 => assert!( | 391 | match config.scale { |
| 335 | arg1s.iter().all(|v| (0.027..0.75).contains(v)), | 392 | Scale::A1o2_R2 => assert!( |
| 336 | "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" | 393 | arg1s.iter().all(|v| (0.05354..0.5).contains(v)), |
| 337 | ), | 394 | "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" |
| 338 | Scale::A1o2_R2 => assert!( | 395 | ), |
| 339 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), | 396 | Scale::A1o4_R4 => assert!( |
| 340 | "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" | 397 | arg1s.iter().all(|v| (0.25..0.75).contains(v)), |
| 341 | ), | 398 | "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" |
| 342 | Scale::A1o4_R4 => assert!( | 399 | ), |
| 343 | arg1s.iter().all(|v| (0.4375..0.585).contains(v)), | 400 | Scale::A1o8_R8 => assert!( |
| 344 | "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" | 401 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), |
| 345 | ), | 402 | "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" |
| 346 | _ => unreachable!(), | 403 | ), |
| 347 | }, | 404 | Scale::A1o16_R16 => assert!( |
| 348 | } | 405 | arg1s.iter().all(|v| (0.4375..0.584).contains(v)), |
| 406 | "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" | ||
| 407 | ), | ||
| 408 | _ => unreachable!(), | ||
| 409 | }; | ||
| 410 | } | ||
| 411 | |||
| 412 | Function::Sqrt => match config.scale { | ||
| 413 | Scale::A1_R1 => assert!( | ||
| 414 | arg1s.iter().all(|v| (0.027..0.75).contains(v)), | ||
| 415 | "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" | ||
| 416 | ), | ||
| 417 | Scale::A1o2_R2 => assert!( | ||
| 418 | arg1s.iter().all(|v| (0.375..0.875).contains(v)), | ||
| 419 | "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" | ||
| 420 | ), | ||
| 421 | Scale::A1o4_R4 => assert!( | ||
| 422 | arg1s.iter().all(|v| (0.4375..0.585).contains(v)), | ||
| 423 | "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" | ||
| 424 | ), | ||
| 425 | _ => unreachable!(), | ||
| 426 | }, | ||
| 427 | } | ||
| 349 | 428 | ||
| 350 | // check ARG2 value | 429 | // check ARG2 value |
| 351 | if let Some(arg2s) = arg2s { | 430 | if let Some(arg2s) = arg2s { |
| 352 | match config.function { | 431 | match config.function { |
| 353 | Cos | Sin => assert!( | 432 | Cos | Sin => assert!( |
| 354 | arg2s.iter().all(|v| (0.0..=1.0).contains(v)), | 433 | arg2s.iter().all(|v| (0.0..=1.0).contains(v)), |
| 355 | "ARG2 should be: 0 <= ARG2 <= 1" | 434 | "ARG2 should be: 0 <= ARG2 <= 1" |
| 356 | ), | 435 | ), |
| 357 | 436 | ||
| 358 | Phase | Modulus => assert!( | 437 | Phase | Modulus => assert!( |
| 359 | arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), | 438 | arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), |
| 360 | "ARG2 should be: -1 <= ARG2 <= 1" | 439 | "ARG2 should be: -1 <= ARG2 <= 1" |
| 361 | ), | 440 | ), |
| 362 | 441 | ||
| 363 | _ => (), | 442 | _ => (), |
| 443 | } | ||
| 444 | } | ||
| 364 | } | 445 | } |
| 365 | } | 446 | } |
| 366 | } | 447 | }; |
| 367 | } | 448 | } |
| 368 | 449 | ||
| 450 | check_input_value!(check_input_f64, f64); | ||
| 451 | check_input_value!(check_input_f32, f32); | ||
| 452 | |||
| 369 | foreach_interrupt!( | 453 | foreach_interrupt!( |
| 370 | ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { | 454 | ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { |
| 371 | impl Instance for peripherals::$inst { | 455 | impl Instance for peripherals::$inst { |
diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 3f055c34b..2f4b5c5e8 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | macro_rules! floating_fixed_convert { | 3 | macro_rules! floating_fixed_convert { |
| 4 | ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { | 4 | ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { |
| 5 | /// convert float point to fixed point format | 5 | /// convert float point to fixed point format |
| 6 | pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { | 6 | pub(crate) fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { |
| 7 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; | 7 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; |
| 8 | 8 | ||
| 9 | assert!( | 9 | assert!( |
| @@ -31,7 +31,7 @@ macro_rules! floating_fixed_convert { | |||
| 31 | 31 | ||
| 32 | #[inline(always)] | 32 | #[inline(always)] |
| 33 | /// convert fixed point to float point format | 33 | /// convert fixed point to float point format |
| 34 | pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { | 34 | pub(crate) fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { |
| 35 | // It's needed to convert from unsigned to signed first, for correct result. | 35 | // It's needed to convert from unsigned to signed first, for correct result. |
| 36 | -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) | 36 | -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) |
| 37 | } | 37 | } |
