diff options
| author | eZio Pan <[email protected]> | 2024-03-21 13:25:40 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2024-03-23 09:15:25 +0800 |
| commit | c42d9f9eaae546faae46c4d1121f1fbc393c2073 (patch) | |
| tree | b766631c0fe8b33c0318ce50d5bad212c1d1f977 | |
| parent | 641da3602e1d7565d08180e0f5608f1ab81c309a (diff) | |
stm32 CORDIC: bug fix
| -rw-r--r-- | embassy-stm32/src/cordic/errors.rs | 76 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/mod.rs | 53 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/utils.rs | 43 |
3 files changed, 109 insertions, 63 deletions
diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs index d0b2dc618..2c0aca4a2 100644 --- a/embassy-stm32/src/cordic/errors.rs +++ b/embassy-stm32/src/cordic/errors.rs | |||
| @@ -9,6 +9,26 @@ pub enum CordicError { | |||
| 9 | ArgError(ArgError), | 9 | ArgError(ArgError), |
| 10 | /// Output buffer length error | 10 | /// Output buffer length error |
| 11 | OutputLengthNotEnough, | 11 | OutputLengthNotEnough, |
| 12 | /// Input value is out of range for Q1.x format | ||
| 13 | NumberOutOfRange(NumberOutOfRange), | ||
| 14 | } | ||
| 15 | |||
| 16 | impl From<ConfigError> for CordicError { | ||
| 17 | fn from(value: ConfigError) -> Self { | ||
| 18 | Self::ConfigError(value) | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | impl From<ArgError> for CordicError { | ||
| 23 | fn from(value: ArgError) -> Self { | ||
| 24 | Self::ArgError(value) | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | impl From<NumberOutOfRange> for CordicError { | ||
| 29 | fn from(value: NumberOutOfRange) -> Self { | ||
| 30 | Self::NumberOutOfRange(value) | ||
| 31 | } | ||
| 12 | } | 32 | } |
| 13 | 33 | ||
| 14 | #[cfg(feature = "defmt")] | 34 | #[cfg(feature = "defmt")] |
| @@ -19,6 +39,7 @@ impl defmt::Format for CordicError { | |||
| 19 | match self { | 39 | match self { |
| 20 | ConfigError(e) => defmt::write!(fmt, "{}", e), | 40 | ConfigError(e) => defmt::write!(fmt, "{}", e), |
| 21 | ArgError(e) => defmt::write!(fmt, "{}", e), | 41 | ArgError(e) => defmt::write!(fmt, "{}", e), |
| 42 | NumberOutOfRange(e) => defmt::write!(fmt, "{}", e), | ||
| 22 | OutputLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), | 43 | OutputLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), |
| 23 | } | 44 | } |
| 24 | } | 45 | } |
| @@ -68,28 +89,51 @@ impl defmt::Format for ArgError { | |||
| 68 | defmt::write!(fmt, " when SCALE is {},", scale); | 89 | defmt::write!(fmt, " when SCALE is {},", scale); |
| 69 | } | 90 | } |
| 70 | 91 | ||
| 71 | let arg_string = match self.arg_type { | 92 | defmt::write!(fmt, " {} should be", self.arg_type); |
| 72 | ArgType::Arg1 => "ARG1", | ||
| 73 | ArgType::Arg2 => "ARG2", | ||
| 74 | }; | ||
| 75 | |||
| 76 | defmt::write!(fmt, " {} should be", arg_string); | ||
| 77 | |||
| 78 | let inclusive_string = if self.inclusive_upper_bound { "=" } else { "" }; | ||
| 79 | 93 | ||
| 80 | defmt::write!( | 94 | if self.inclusive_upper_bound { |
| 81 | fmt, | 95 | defmt::write!( |
| 82 | " {} <= {} <{} {}", | 96 | fmt, |
| 83 | self.arg_range[0], | 97 | " {} <= {} <= {}", |
| 84 | arg_string, | 98 | self.arg_range[0], |
| 85 | inclusive_string, | 99 | self.arg_type, |
| 86 | self.arg_range[1] | 100 | self.arg_range[1] |
| 87 | ) | 101 | ) |
| 102 | } else { | ||
| 103 | defmt::write!( | ||
| 104 | fmt, | ||
| 105 | " {} <= {} < {}", | ||
| 106 | self.arg_range[0], | ||
| 107 | self.arg_type, | ||
| 108 | self.arg_range[1] | ||
| 109 | ) | ||
| 110 | }; | ||
| 88 | } | 111 | } |
| 89 | } | 112 | } |
| 90 | 113 | ||
| 91 | #[derive(Debug)] | 114 | #[derive(Debug)] |
| 115 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 92 | pub(super) enum ArgType { | 116 | pub(super) enum ArgType { |
| 93 | Arg1, | 117 | Arg1, |
| 94 | Arg2, | 118 | Arg2, |
| 95 | } | 119 | } |
| 120 | |||
| 121 | /// Input value is out of range for Q1.x format | ||
| 122 | #[allow(missing_docs)] | ||
| 123 | #[derive(Debug)] | ||
| 124 | pub enum NumberOutOfRange { | ||
| 125 | BelowLowerBound, | ||
| 126 | AboveUpperBound, | ||
| 127 | } | ||
| 128 | |||
| 129 | #[cfg(feature = "defmt")] | ||
| 130 | impl defmt::Format for NumberOutOfRange { | ||
| 131 | fn format(&self, fmt: defmt::Formatter) { | ||
| 132 | use NumberOutOfRange::*; | ||
| 133 | |||
| 134 | match self { | ||
| 135 | BelowLowerBound => defmt::write!(fmt, "input value should be equal or greater than -1"), | ||
| 136 | AboveUpperBound => defmt::write!(fmt, "input value should be equal or less than 1"), | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 5ac9addd8..b0db3f060 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -56,7 +56,7 @@ impl Config { | |||
| 56 | Ok(config) | 56 | Ok(config) |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | fn check_scale(&self) -> Result<(), CordicError> { | 59 | fn check_scale(&self) -> Result<(), ConfigError> { |
| 60 | use Function::*; | 60 | use Function::*; |
| 61 | 61 | ||
| 62 | let scale_raw = self.scale as u8; | 62 | let scale_raw = self.scale as u8; |
| @@ -76,10 +76,10 @@ impl Config { | |||
| 76 | }; | 76 | }; |
| 77 | 77 | ||
| 78 | if let Some(range) = err_range { | 78 | if let Some(range) = err_range { |
| 79 | Err(CordicError::ConfigError(ConfigError { | 79 | Err(ConfigError { |
| 80 | func: self.function, | 80 | func: self.function, |
| 81 | scale_range: range, | 81 | scale_range: range, |
| 82 | })) | 82 | }) |
| 83 | } else { | 83 | } else { |
| 84 | Ok(()) | 84 | Ok(()) |
| 85 | } | 85 | } |
| @@ -226,20 +226,20 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 226 | consumed_input_len = double_input.len() + 1; | 226 | consumed_input_len = double_input.len() + 1; |
| 227 | 227 | ||
| 228 | // preload first value from arg1 to cordic | 228 | // preload first value from arg1 to cordic |
| 229 | self.blocking_write_f64(arg1s[0]); | 229 | self.blocking_write_f64(arg1s[0])?; |
| 230 | 230 | ||
| 231 | for (&arg1, &arg2) in double_input { | 231 | for (&arg1, &arg2) in double_input { |
| 232 | // Since we manually preload a value before, | 232 | // Since we manually preload a value before, |
| 233 | // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) | 233 | // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) |
| 234 | // and write arg1 (from the actual next pair), then read the result, to "keep preloading" | 234 | // and write arg1 (from the actual next pair), then read the result, to "keep preloading" |
| 235 | 235 | ||
| 236 | self.blocking_write_f64(arg2); | 236 | self.blocking_write_f64(arg2)?; |
| 237 | self.blocking_write_f64(arg1); | 237 | self.blocking_write_f64(arg1)?; |
| 238 | self.blocking_read_f64_to_buf(output, &mut output_count); | 238 | self.blocking_read_f64_to_buf(output, &mut output_count); |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | // write last input value from arg2s, then read out the result | 241 | // write last input value from arg2s, then read out the result |
| 242 | self.blocking_write_f64(arg2s[arg2s.len() - 1]); | 242 | self.blocking_write_f64(arg2s[arg2s.len() - 1])?; |
| 243 | self.blocking_read_f64_to_buf(output, &mut output_count); | 243 | self.blocking_read_f64_to_buf(output, &mut output_count); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| @@ -253,12 +253,12 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 253 | self.peri.set_argument_count(AccessCount::One); | 253 | self.peri.set_argument_count(AccessCount::One); |
| 254 | 254 | ||
| 255 | // "preload" value to cordic (at this moment, cordic start to calculating) | 255 | // "preload" value to cordic (at this moment, cordic start to calculating) |
| 256 | self.blocking_write_f64(input_left[0]); | 256 | self.blocking_write_f64(input_left[0])?; |
| 257 | 257 | ||
| 258 | for &arg in input_left.iter().skip(1) { | 258 | for &arg in input_left.iter().skip(1) { |
| 259 | // this line write arg for next round caculation to cordic, | 259 | // this line write arg for next round caculation to cordic, |
| 260 | // and read result from last round | 260 | // and read result from last round |
| 261 | self.blocking_write_f64(arg); | 261 | self.blocking_write_f64(arg)?; |
| 262 | self.blocking_read_f64_to_buf(output, &mut output_count); | 262 | self.blocking_read_f64_to_buf(output, &mut output_count); |
| 263 | } | 263 | } |
| 264 | 264 | ||
| @@ -281,8 +281,9 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 281 | } | 281 | } |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | fn blocking_write_f64(&mut self, arg: f64) { | 284 | fn blocking_write_f64(&mut self, arg: f64) -> Result<(), NumberOutOfRange> { |
| 285 | self.peri.write_argument(utils::f64_to_q1_31(arg)); | 285 | self.peri.write_argument(utils::f64_to_q1_31(arg)?); |
| 286 | Ok(()) | ||
| 286 | } | 287 | } |
| 287 | 288 | ||
| 288 | /// Run a async CORDIC calculation in q.1.31 format | 289 | /// Run a async CORDIC calculation in q.1.31 format |
| @@ -339,7 +340,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 339 | 340 | ||
| 340 | for (&arg1, &arg2) in double_input { | 341 | for (&arg1, &arg2) in double_input { |
| 341 | for &arg in [arg1, arg2].iter() { | 342 | for &arg in [arg1, arg2].iter() { |
| 342 | input_buf[input_buf_len] = utils::f64_to_q1_31(arg); | 343 | input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; |
| 343 | input_buf_len += 1; | 344 | input_buf_len += 1; |
| 344 | } | 345 | } |
| 345 | 346 | ||
| @@ -383,7 +384,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 383 | self.peri.set_argument_count(AccessCount::One); | 384 | self.peri.set_argument_count(AccessCount::One); |
| 384 | 385 | ||
| 385 | for &arg in input_remain { | 386 | for &arg in input_remain { |
| 386 | input_buf[input_buf_len] = utils::f64_to_q1_31(arg); | 387 | input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; |
| 387 | input_buf_len += 1; | 388 | input_buf_len += 1; |
| 388 | 389 | ||
| 389 | if input_buf_len == INPUT_BUF_MAX_LEN { | 390 | if input_buf_len == INPUT_BUF_MAX_LEN { |
| @@ -509,10 +510,10 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 509 | let (&arg1, &arg2) = args.next().unwrap(); | 510 | let (&arg1, &arg2) = args.next().unwrap(); |
| 510 | 511 | ||
| 511 | // preloading 1 pair of arguments | 512 | // preloading 1 pair of arguments |
| 512 | self.blocking_write_f32(arg1, arg2); | 513 | self.blocking_write_f32(arg1, arg2)?; |
| 513 | 514 | ||
| 514 | for (&arg1, &arg2) in args { | 515 | for (&arg1, &arg2) in args { |
| 515 | self.blocking_write_f32(arg1, arg2); | 516 | self.blocking_write_f32(arg1, arg2)?; |
| 516 | self.blocking_read_f32_to_buf(output, &mut output_count); | 517 | self.blocking_read_f32_to_buf(output, &mut output_count); |
| 517 | } | 518 | } |
| 518 | 519 | ||
| @@ -522,15 +523,13 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 522 | Ok(output_count) | 523 | Ok(output_count) |
| 523 | } | 524 | } |
| 524 | 525 | ||
| 525 | fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { | 526 | fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) -> Result<(), NumberOutOfRange> { |
| 526 | let reg_value: u32 = utils::f32_args_to_u32(arg1, arg2); | 527 | self.peri.write_argument(utils::f32_args_to_u32(arg1, arg2)?); |
| 527 | self.peri.write_argument(reg_value); | 528 | Ok(()) |
| 528 | } | 529 | } |
| 529 | 530 | ||
| 530 | fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { | 531 | fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { |
| 531 | let reg_value = self.peri.read_result(); | 532 | let (res1, res2) = utils::u32_to_f32_res(self.peri.read_result()); |
| 532 | |||
| 533 | let (res1, res2) = utils::u32_to_f32_res(reg_value); | ||
| 534 | 533 | ||
| 535 | result_buf[*result_index] = res1; | 534 | result_buf[*result_index] = res1; |
| 536 | *result_index += 1; | 535 | *result_index += 1; |
| @@ -597,7 +596,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 597 | ); | 596 | ); |
| 598 | 597 | ||
| 599 | for (&arg1, &arg2) in args { | 598 | for (&arg1, &arg2) in args { |
| 600 | input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2); | 599 | input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2)?; |
| 601 | input_buf_len += 1; | 600 | input_buf_len += 1; |
| 602 | 601 | ||
| 603 | if input_buf_len == INPUT_BUF_MAX_LEN { | 602 | if input_buf_len == INPUT_BUF_MAX_LEN { |
| @@ -655,7 +654,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 655 | macro_rules! check_input_value { | 654 | macro_rules! check_input_value { |
| 656 | ($func_name:ident, $float_type:ty) => { | 655 | ($func_name:ident, $float_type:ty) => { |
| 657 | impl<'d, T: Instance> Cordic<'d, T> { | 656 | impl<'d, T: Instance> Cordic<'d, T> { |
| 658 | fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), CordicError> { | 657 | fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), ArgError> { |
| 659 | let config = &self.config; | 658 | let config = &self.config; |
| 660 | 659 | ||
| 661 | use Function::*; | 660 | use Function::*; |
| @@ -741,13 +740,13 @@ macro_rules! check_input_value { | |||
| 741 | }; | 740 | }; |
| 742 | 741 | ||
| 743 | if let Some(err) = err_info { | 742 | if let Some(err) = err_info { |
| 744 | return Err(CordicError::ArgError(ArgError { | 743 | return Err(ArgError { |
| 745 | func: config.function, | 744 | func: config.function, |
| 746 | scale: err.scale, | 745 | scale: err.scale, |
| 747 | arg_range: err.range, | 746 | arg_range: err.range, |
| 748 | inclusive_upper_bound: err.inclusive_upper_bound, | 747 | inclusive_upper_bound: err.inclusive_upper_bound, |
| 749 | arg_type: ArgType::Arg1, | 748 | arg_type: ArgType::Arg1, |
| 750 | })); | 749 | }); |
| 751 | } | 750 | } |
| 752 | 751 | ||
| 753 | // check ARG2 value | 752 | // check ARG2 value |
| @@ -769,13 +768,13 @@ macro_rules! check_input_value { | |||
| 769 | }; | 768 | }; |
| 770 | 769 | ||
| 771 | if let Some(err) = err_info { | 770 | if let Some(err) = err_info { |
| 772 | return Err(CordicError::ArgError(ArgError { | 771 | return Err(ArgError { |
| 773 | func: config.function, | 772 | func: config.function, |
| 774 | scale: None, | 773 | scale: None, |
| 775 | arg_range: err.range, | 774 | arg_range: err.range, |
| 776 | inclusive_upper_bound: true, | 775 | inclusive_upper_bound: true, |
| 777 | arg_type: ArgType::Arg2, | 776 | arg_type: ArgType::Arg2, |
| 778 | })); | 777 | }); |
| 779 | } | 778 | } |
| 780 | } | 779 | } |
| 781 | 780 | ||
diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 79bef6b97..3c3ed224f 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs | |||
| @@ -1,39 +1,42 @@ | |||
| 1 | //! Common match utils | 1 | //! Common match utils |
| 2 | use super::errors::NumberOutOfRange; | ||
| 2 | 3 | ||
| 3 | macro_rules! floating_fixed_convert { | 4 | 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) => { | 5 | ($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 | 6 | /// convert float point to fixed point format |
| 6 | pub(crate) fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { | 7 | pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { |
| 7 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; | 8 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; |
| 8 | 9 | ||
| 9 | assert!( | 10 | if value < -1.0 { |
| 10 | (-1.0 as $float_ty) <= value, | 11 | return Err(NumberOutOfRange::BelowLowerBound) |
| 11 | "input value {} should be equal or greater than -1", | 12 | } |
| 12 | value | 13 | |
| 13 | ); | 14 | if value > 1.0 { |
| 15 | return Err(NumberOutOfRange::AboveUpperBound) | ||
| 16 | } | ||
| 14 | 17 | ||
| 15 | 18 | ||
| 16 | let value = if value == 1.0 as $float_ty{ | 19 | let value = if 1.0 - MIN_POSITIVE < value && value <= 1.0 { |
| 17 | // make a exception for user specifing exact 1.0 float point, | 20 | // make a exception for value between (1.0^{-x} , 1.0] float point, |
| 18 | // convert 1.0 to max representable value of q1.x format | 21 | // convert it to max representable value of q1.x format |
| 19 | (1.0 as $float_ty) - MIN_POSITIVE | 22 | (1.0 as $float_ty) - MIN_POSITIVE |
| 20 | } else { | 23 | } else { |
| 21 | assert!( | ||
| 22 | value <= (1.0 as $float_ty) - MIN_POSITIVE, | ||
| 23 | "input value {} should be equal or less than 1-2^(-{})", | ||
| 24 | value, $offset | ||
| 25 | ); | ||
| 26 | value | 24 | value |
| 27 | }; | 25 | }; |
| 28 | 26 | ||
| 29 | (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ | 27 | // It's necessary to cast the float value to signed integer, before convert it to a unsigned value. |
| 28 | // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as unsgined value. | ||
| 29 | // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast | ||
| 30 | Ok((value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $signed_bin_typ as $unsigned_bin_typ) | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | #[inline(always)] | 33 | #[inline(always)] |
| 33 | /// convert fixed point to float point format | 34 | /// convert fixed point to float point format |
| 34 | pub(crate) fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { | 35 | pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { |
| 35 | // It's needed to convert from unsigned to signed first, for correct result. | 36 | // It's necessary to cast the unsigned integer to signed integer, before convert it to a float value. |
| 36 | -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) | 37 | // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as signed value. |
| 38 | // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast | ||
| 39 | (value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) | ||
| 37 | } | 40 | } |
| 38 | }; | 41 | }; |
| 39 | } | 42 | } |
| @@ -59,8 +62,8 @@ floating_fixed_convert!( | |||
| 59 | ); | 62 | ); |
| 60 | 63 | ||
| 61 | #[inline(always)] | 64 | #[inline(always)] |
| 62 | pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> u32 { | 65 | pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> Result<u32, NumberOutOfRange> { |
| 63 | f32_to_q1_15(arg1) as u32 + ((f32_to_q1_15(arg2) as u32) << 16) | 66 | Ok(f32_to_q1_15(arg1)? as u32 + ((f32_to_q1_15(arg2)? as u32) << 16)) |
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | #[inline(always)] | 69 | #[inline(always)] |
