diff options
| author | eZio Pan <[email protected]> | 2024-03-15 15:12:51 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2024-03-23 09:15:25 +0800 |
| commit | b595d942442a8b267e1311bcadedc8558183aa61 (patch) | |
| tree | 99f599067d49df98e4abab75d4ec708496b2501f | |
| parent | cf065d439efed2141aaf09454beb445e80dc7539 (diff) | |
stm32 CORDIC: split into multiple files
| -rw-r--r-- | embassy-stm32/src/cordic.rs | 460 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/enums.rs | 82 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/mod.rs | 206 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/sealed.rs | 116 | ||||
| -rw-r--r-- | embassy-stm32/src/cordic/utils.rs | 59 |
5 files changed, 463 insertions, 460 deletions
diff --git a/embassy-stm32/src/cordic.rs b/embassy-stm32/src/cordic.rs deleted file mode 100644 index 952ee187a..000000000 --- a/embassy-stm32/src/cordic.rs +++ /dev/null | |||
| @@ -1,460 +0,0 @@ | |||
| 1 | //! CORDIC co-processor | ||
| 2 | |||
| 3 | use crate::peripherals; | ||
| 4 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | ||
| 5 | |||
| 6 | pub use enums::*; | ||
| 7 | |||
| 8 | mod 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")] | ||
| 95 | pub mod low_level { | ||
| 96 | pub use super::sealed::*; | ||
| 97 | } | ||
| 98 | |||
| 99 | pub(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 | ||
| 219 | pub struct Cordic<'d, T: Instance> { | ||
| 220 | cordic: PeripheralRef<'d, T>, | ||
| 221 | config: Config, | ||
| 222 | //state: State, | ||
| 223 | } | ||
| 224 | |||
| 225 | /// CORDIC instance trait | ||
| 226 | pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral {} | ||
| 227 | |||
| 228 | /// CORDIC configuration | ||
| 229 | pub 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 | |||
| 243 | impl 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 | |||
| 268 | impl<'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 | |||
| 388 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | ||
| 389 | fn drop(&mut self) { | ||
| 390 | T::disable(); | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | foreach_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 | |||
| 407 | macro_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 | |||
| 442 | floating_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 | |||
| 452 | floating_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/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs new file mode 100644 index 000000000..4697a1df1 --- /dev/null +++ b/embassy-stm32/src/cordic/enums.rs | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | /// CORDIC function | ||
| 2 | #[allow(missing_docs)] | ||
| 3 | #[derive(Clone, Copy)] | ||
| 4 | pub enum Function { | ||
| 5 | Cos = 0, | ||
| 6 | Sin, | ||
| 7 | Phase, | ||
| 8 | Modulus, | ||
| 9 | Arctan, | ||
| 10 | Cosh, | ||
| 11 | Sinh, | ||
| 12 | Arctanh, | ||
| 13 | Ln, | ||
| 14 | Sqrt, | ||
| 15 | } | ||
| 16 | |||
| 17 | /// CORDIC precision | ||
| 18 | #[allow(missing_docs)] | ||
| 19 | #[derive(Clone, Copy)] | ||
| 20 | pub enum Precision { | ||
| 21 | Iters4 = 1, | ||
| 22 | Iters8, | ||
| 23 | Iters12, | ||
| 24 | Iters16, | ||
| 25 | Iters20, | ||
| 26 | Iters24, | ||
| 27 | Iters28, | ||
| 28 | Iters32, | ||
| 29 | Iters36, | ||
| 30 | Iters40, | ||
| 31 | Iters44, | ||
| 32 | Iters48, | ||
| 33 | Iters52, | ||
| 34 | Iters56, | ||
| 35 | Iters60, | ||
| 36 | } | ||
| 37 | |||
| 38 | /// CORDIC scale | ||
| 39 | #[allow(non_camel_case_types)] | ||
| 40 | #[allow(missing_docs)] | ||
| 41 | #[derive(Clone, Copy, Default)] | ||
| 42 | pub enum Scale { | ||
| 43 | #[default] | ||
| 44 | A1_R1 = 0, | ||
| 45 | A1o2_R2, | ||
| 46 | A1o4_R4, | ||
| 47 | A1o8_R8, | ||
| 48 | A1o16_R16, | ||
| 49 | A1o32_R32, | ||
| 50 | A1o64_R64, | ||
| 51 | A1o128_R128, | ||
| 52 | } | ||
| 53 | |||
| 54 | /// CORDIC argument/result count | ||
| 55 | #[allow(missing_docs)] | ||
| 56 | #[derive(Clone, Copy, Default)] | ||
| 57 | pub enum Count { | ||
| 58 | #[default] | ||
| 59 | One, | ||
| 60 | Two, | ||
| 61 | } | ||
| 62 | |||
| 63 | /// CORDIC argument/result data width | ||
| 64 | #[allow(missing_docs)] | ||
| 65 | #[derive(Clone, Copy)] | ||
| 66 | pub enum Width { | ||
| 67 | Bits32, | ||
| 68 | Bits16, | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Cordic driver running mode | ||
| 72 | #[derive(Clone, Copy)] | ||
| 73 | pub enum Mode { | ||
| 74 | /// After caculation start, a read to RDATA register will block AHB until the caculation finished | ||
| 75 | ZeroOverhead, | ||
| 76 | |||
| 77 | /// Use CORDIC interrupt to trigger a read result value | ||
| 78 | Interrupt, | ||
| 79 | |||
| 80 | /// Use DMA to write/read value | ||
| 81 | Dma, | ||
| 82 | } | ||
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs new file mode 100644 index 000000000..c0a69b757 --- /dev/null +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | //! CORDIC co-processor | ||
| 2 | |||
| 3 | use crate::peripherals; | ||
| 4 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | ||
| 5 | |||
| 6 | mod enums; | ||
| 7 | pub use enums::*; | ||
| 8 | |||
| 9 | pub mod utils; | ||
| 10 | |||
| 11 | pub(crate) mod sealed; | ||
| 12 | |||
| 13 | /// Low-level CORDIC access. | ||
| 14 | #[cfg(feature = "unstable-pac")] | ||
| 15 | pub mod low_level { | ||
| 16 | pub use super::sealed::*; | ||
| 17 | } | ||
| 18 | |||
| 19 | /// CORDIC driver | ||
| 20 | pub struct Cordic<'d, T: Instance> { | ||
| 21 | cordic: PeripheralRef<'d, T>, | ||
| 22 | config: Config, | ||
| 23 | //state: State, | ||
| 24 | } | ||
| 25 | |||
| 26 | /// CORDIC instance trait | ||
| 27 | pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral {} | ||
| 28 | |||
| 29 | /// CORDIC configuration | ||
| 30 | pub struct Config { | ||
| 31 | function: Function, | ||
| 32 | precision: Precision, | ||
| 33 | scale: Scale, | ||
| 34 | mode: Mode, | ||
| 35 | first_result: bool, | ||
| 36 | } | ||
| 37 | |||
| 38 | // CORDIC running state | ||
| 39 | //struct State { | ||
| 40 | // input_buf: [u32; 8], | ||
| 41 | // buf_len: usize, | ||
| 42 | //} | ||
| 43 | |||
| 44 | impl Config { | ||
| 45 | /// Create a config for Cordic driver | ||
| 46 | pub fn new(function: Function, precision: Precision, scale: Option<Scale>, mode: Mode, first_result: bool) -> Self { | ||
| 47 | Self { | ||
| 48 | function, | ||
| 49 | precision, | ||
| 50 | scale: scale.unwrap_or_default(), | ||
| 51 | mode, | ||
| 52 | first_result, | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | fn check_scale(&self) -> bool { | ||
| 57 | let scale_raw = self.scale as u8; | ||
| 58 | |||
| 59 | match self.function { | ||
| 60 | Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw, | ||
| 61 | Function::Arctan => (0..=7).contains(&scale_raw), | ||
| 62 | Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw, | ||
| 63 | Function::Ln => (1..=4).contains(&scale_raw), | ||
| 64 | Function::Sqrt => (0..=2).contains(&scale_raw), | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | impl<'d, T: Instance> Cordic<'d, T> { | ||
| 70 | /// Create a Cordic driver instance | ||
| 71 | /// | ||
| 72 | /// Note: | ||
| 73 | /// If you need a periperhal -> CORDIC -> peripehral mode, | ||
| 74 | /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] | ||
| 75 | pub fn new(cordic: impl Peripheral<P = T> + 'd, config: Config) -> Self { | ||
| 76 | T::enable_and_reset(); | ||
| 77 | |||
| 78 | into_ref!(cordic); | ||
| 79 | |||
| 80 | if !config.check_scale() { | ||
| 81 | panic!("Scale value is not compatible with Function") | ||
| 82 | } | ||
| 83 | |||
| 84 | let mut instance = Self { | ||
| 85 | cordic, | ||
| 86 | config, | ||
| 87 | // state: State { | ||
| 88 | // input_buf: [0u32; 8], | ||
| 89 | // buf_len: 0, | ||
| 90 | // }, | ||
| 91 | }; | ||
| 92 | |||
| 93 | instance.reconfigure(); | ||
| 94 | |||
| 95 | instance | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Set a new config for Cordic driver | ||
| 99 | pub fn set_config(&mut self, config: Config) { | ||
| 100 | self.config = config; | ||
| 101 | self.reconfigure(); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Set extra config for data count and data width. | ||
| 105 | pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { | ||
| 106 | let peri = &self.cordic; | ||
| 107 | peri.set_argument_count(arg_cnt); | ||
| 108 | peri.set_data_width(arg_width, res_width); | ||
| 109 | } | ||
| 110 | |||
| 111 | fn reconfigure(&mut self) { | ||
| 112 | let peri = &self.cordic; | ||
| 113 | let config = &self.config; | ||
| 114 | |||
| 115 | if peri.ready_to_read() { | ||
| 116 | warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); | ||
| 117 | }; | ||
| 118 | |||
| 119 | peri.disable_irq(); | ||
| 120 | peri.disable_write_dma(); | ||
| 121 | peri.disable_read_dma(); | ||
| 122 | |||
| 123 | // clean RRDY flag | ||
| 124 | while peri.ready_to_read() { | ||
| 125 | peri.read_result(); | ||
| 126 | } | ||
| 127 | |||
| 128 | peri.set_func(config.function); | ||
| 129 | peri.set_precision(config.precision); | ||
| 130 | peri.set_scale(config.scale); | ||
| 131 | if config.first_result { | ||
| 132 | peri.set_result_count(Count::One) | ||
| 133 | } else { | ||
| 134 | peri.set_result_count(Count::Two) | ||
| 135 | } | ||
| 136 | |||
| 137 | match config.mode { | ||
| 138 | Mode::ZeroOverhead => (), | ||
| 139 | Mode::Interrupt => { | ||
| 140 | peri.enable_irq(); | ||
| 141 | } | ||
| 142 | Mode::Dma => { | ||
| 143 | peri.enable_write_dma(); | ||
| 144 | peri.enable_read_dma(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | //self.state.input_buf.fill(0u32); | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Run a CORDIC calculation | ||
| 152 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { | ||
| 153 | match self.config.mode { | ||
| 154 | Mode::ZeroOverhead => { | ||
| 155 | if arg2s.is_none() { | ||
| 156 | self.cordic.set_argument_count(Count::One); | ||
| 157 | |||
| 158 | self.cordic.set_result_count(if self.config.first_result { | ||
| 159 | if output.len() < arg1s.len() { | ||
| 160 | panic!("Output buf length is not long enough") | ||
| 161 | } | ||
| 162 | Count::One | ||
| 163 | } else { | ||
| 164 | if output.len() < 2 * arg1s.len() { | ||
| 165 | panic!("Output buf length is not long enough") | ||
| 166 | } | ||
| 167 | Count::Two | ||
| 168 | }); | ||
| 169 | |||
| 170 | let mut cnt = 0; | ||
| 171 | |||
| 172 | for &arg in arg1s.iter() { | ||
| 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 | } | ||
| 177 | |||
| 178 | cnt | ||
| 179 | } else { | ||
| 180 | todo!() | ||
| 181 | } | ||
| 182 | } | ||
| 183 | Mode::Interrupt => todo!(), | ||
| 184 | Mode::Dma => todo!(), | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | impl<'d, T: Instance> Drop for Cordic<'d, T> { | ||
| 190 | fn drop(&mut self) { | ||
| 191 | T::disable(); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | foreach_interrupt!( | ||
| 196 | ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { | ||
| 197 | impl Instance for peripherals::$inst { | ||
| 198 | } | ||
| 199 | |||
| 200 | impl sealed::Instance for peripherals::$inst { | ||
| 201 | fn regs() -> crate::pac::cordic::Cordic { | ||
| 202 | crate::pac::$inst | ||
| 203 | } | ||
| 204 | } | ||
| 205 | }; | ||
| 206 | ); | ||
diff --git a/embassy-stm32/src/cordic/sealed.rs b/embassy-stm32/src/cordic/sealed.rs new file mode 100644 index 000000000..0f00e380c --- /dev/null +++ b/embassy-stm32/src/cordic/sealed.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | use super::*; | ||
| 2 | use crate::pac::cordic::vals; | ||
| 3 | |||
| 4 | /// Cordic instance | ||
| 5 | pub trait Instance { | ||
| 6 | /// Get access to CORDIC registers | ||
| 7 | fn regs() -> crate::pac::cordic::Cordic; | ||
| 8 | |||
| 9 | /// Set Function value | ||
| 10 | fn set_func(&self, func: Function) { | ||
| 11 | Self::regs() | ||
| 12 | .csr() | ||
| 13 | .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); | ||
| 14 | } | ||
| 15 | |||
| 16 | /// Set Precision value | ||
| 17 | fn set_precision(&self, precision: Precision) { | ||
| 18 | Self::regs() | ||
| 19 | .csr() | ||
| 20 | .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Set Scale value | ||
| 24 | fn set_scale(&self, scale: Scale) { | ||
| 25 | Self::regs() | ||
| 26 | .csr() | ||
| 27 | .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Enable global interrupt | ||
| 31 | fn enable_irq(&self) { | ||
| 32 | Self::regs().csr().modify(|v| v.set_ien(true)) | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Disable global interrupt | ||
| 36 | fn disable_irq(&self) { | ||
| 37 | Self::regs().csr().modify(|v| v.set_ien(false)) | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Enable Read DMA | ||
| 41 | fn enable_read_dma(&self) { | ||
| 42 | Self::regs().csr().modify(|v| { | ||
| 43 | v.set_dmaren(true); | ||
| 44 | }) | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Disable Read DMA | ||
| 48 | fn disable_read_dma(&self) { | ||
| 49 | Self::regs().csr().modify(|v| { | ||
| 50 | v.set_dmaren(false); | ||
| 51 | }) | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Enable Write DMA | ||
| 55 | fn enable_write_dma(&self) { | ||
| 56 | Self::regs().csr().modify(|v| { | ||
| 57 | v.set_dmawen(true); | ||
| 58 | }) | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Disable Write DMA | ||
| 62 | fn disable_write_dma(&self) { | ||
| 63 | Self::regs().csr().modify(|v| { | ||
| 64 | v.set_dmawen(false); | ||
| 65 | }) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Set NARGS value | ||
| 69 | fn set_argument_count(&self, n: Count) { | ||
| 70 | Self::regs().csr().modify(|v| { | ||
| 71 | v.set_nargs(match n { | ||
| 72 | Count::One => vals::Num::NUM1, | ||
| 73 | Count::Two => vals::Num::NUM2, | ||
| 74 | }) | ||
| 75 | }) | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Set NRES value | ||
| 79 | fn set_result_count(&self, n: Count) { | ||
| 80 | Self::regs().csr().modify(|v| { | ||
| 81 | v.set_nres(match n { | ||
| 82 | Count::One => vals::Num::NUM1, | ||
| 83 | Count::Two => vals::Num::NUM2, | ||
| 84 | }); | ||
| 85 | }) | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Set ARGSIZE and RESSIZE value | ||
| 89 | fn set_data_width(&self, arg: Width, res: Width) { | ||
| 90 | Self::regs().csr().modify(|v| { | ||
| 91 | v.set_argsize(match arg { | ||
| 92 | Width::Bits32 => vals::Size::BITS32, | ||
| 93 | Width::Bits16 => vals::Size::BITS16, | ||
| 94 | }); | ||
| 95 | v.set_ressize(match res { | ||
| 96 | Width::Bits32 => vals::Size::BITS32, | ||
| 97 | Width::Bits16 => vals::Size::BITS16, | ||
| 98 | }) | ||
| 99 | }) | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Read RRDY flag | ||
| 103 | fn ready_to_read(&self) -> bool { | ||
| 104 | Self::regs().csr().read().rrdy() | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Write value to WDATA | ||
| 108 | fn write_argument(&self, arg: u32) { | ||
| 109 | Self::regs().wdata().write_value(arg) | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Read value from RDATA | ||
| 113 | fn read_result(&self) -> u32 { | ||
| 114 | Self::regs().rdata().read() | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs new file mode 100644 index 000000000..3f055c34b --- /dev/null +++ b/embassy-stm32/src/cordic/utils.rs | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | //! Common match utils | ||
| 2 | |||
| 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) => { | ||
| 5 | /// convert float point to fixed point format | ||
| 6 | pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { | ||
| 7 | const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; | ||
| 8 | |||
| 9 | assert!( | ||
| 10 | (-1.0 as $float_ty) <= value, | ||
| 11 | "input value {} should be equal or greater than -1", | ||
| 12 | value | ||
| 13 | ); | ||
| 14 | |||
| 15 | |||
| 16 | let value = if value == 1.0 as $float_ty{ | ||
| 17 | // make a exception for user specifing exact 1.0 float point, | ||
| 18 | // convert 1.0 to max representable value of q1.x format | ||
| 19 | (1.0 as $float_ty) - MIN_POSITIVE | ||
| 20 | } 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 | ||
| 27 | }; | ||
| 28 | |||
| 29 | (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ | ||
| 30 | } | ||
| 31 | |||
| 32 | #[inline(always)] | ||
| 33 | /// convert fixed point to float point format | ||
| 34 | 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 | -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | floating_fixed_convert!( | ||
| 42 | f64_to_q1_31, | ||
| 43 | q1_31_to_f64, | ||
| 44 | u32, | ||
| 45 | i32, | ||
| 46 | f64, | ||
| 47 | 31, | ||
| 48 | 0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31) | ||
| 49 | ); | ||
| 50 | |||
| 51 | floating_fixed_convert!( | ||
| 52 | f32_to_q1_15, | ||
| 53 | q1_15_to_f32, | ||
| 54 | u16, | ||
| 55 | i16, | ||
| 56 | f32, | ||
| 57 | 15, | ||
| 58 | 0x3800_0000u32 // binary form of 1f32^(-15) | ||
| 59 | ); | ||
