diff options
| author | eZio Pan <[email protected]> | 2024-03-16 00:25:38 +0800 |
|---|---|---|
| committer | eZio Pan <[email protected]> | 2024-03-23 09:15:25 +0800 |
| commit | 5d12f594303bdb76bf2356d9fc0661826e2e658e (patch) | |
| tree | 214728fcc25ce3f64949443f8998b7ff91f10d56 | |
| parent | a1ca9088b4e3b4644428eab80e8502a55b2cbe8f (diff) | |
stm32 CORDIC: make use of "preload" feature
| -rw-r--r-- | embassy-stm32/src/cordic/mod.rs | 180 |
1 files changed, 85 insertions, 95 deletions
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index b15521ca6..997ace113 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -22,9 +22,8 @@ pub mod low_level { | |||
| 22 | 22 | ||
| 23 | /// CORDIC driver | 23 | /// CORDIC driver |
| 24 | pub struct Cordic<'d, T: Instance> { | 24 | pub struct Cordic<'d, T: Instance> { |
| 25 | cordic: PeripheralRef<'d, T>, | 25 | peri: PeripheralRef<'d, T>, |
| 26 | config: Config, | 26 | config: Config, |
| 27 | state: State, | ||
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | /// CORDIC instance trait | 29 | /// CORDIC instance trait |
| @@ -83,23 +82,16 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 83 | /// Note: | 82 | /// Note: |
| 84 | /// If you need a periperhal -> CORDIC -> peripehral mode, | 83 | /// If you need a periperhal -> CORDIC -> peripehral mode, |
| 85 | /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] | 84 | /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] |
| 86 | pub fn new(cordic: impl Peripheral<P = T> + 'd, config: Config) -> Self { | 85 | pub fn new(peri: impl Peripheral<P = T> + 'd, config: Config) -> Self { |
| 87 | T::enable_and_reset(); | 86 | T::enable_and_reset(); |
| 88 | 87 | ||
| 89 | into_ref!(cordic); | 88 | into_ref!(peri); |
| 90 | 89 | ||
| 91 | if !config.check_scale() { | 90 | if !config.check_scale() { |
| 92 | panic!("Scale value is not compatible with Function") | 91 | panic!("Scale value is not compatible with Function") |
| 93 | } | 92 | } |
| 94 | 93 | ||
| 95 | let mut instance = Self { | 94 | let mut instance = Self { peri, config }; |
| 96 | cordic, | ||
| 97 | config, | ||
| 98 | state: State { | ||
| 99 | input_buf: [0u32; 8], | ||
| 100 | buf_index: 0, | ||
| 101 | }, | ||
| 102 | }; | ||
| 103 | 95 | ||
| 104 | instance.reconfigure(); | 96 | instance.reconfigure(); |
| 105 | 97 | ||
| @@ -114,51 +106,71 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 114 | 106 | ||
| 115 | /// Set extra config for data count and data width. | 107 | /// Set extra config for data count and data width. |
| 116 | pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { | 108 | pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { |
| 117 | let peri = &self.cordic; | 109 | self.peri.set_argument_count(arg_cnt); |
| 118 | peri.set_argument_count(arg_cnt); | 110 | self.peri.set_data_width(arg_width, res_width); |
| 119 | peri.set_data_width(arg_width, res_width); | ||
| 120 | } | 111 | } |
| 121 | 112 | ||
| 122 | fn reconfigure(&mut self) { | 113 | fn reconfigure(&mut self) { |
| 123 | let peri = &self.cordic; | 114 | if self.peri.ready_to_read() { |
| 124 | let config = &self.config; | ||
| 125 | |||
| 126 | if peri.ready_to_read() { | ||
| 127 | warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); | 115 | warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); |
| 128 | }; | 116 | }; |
| 129 | 117 | ||
| 130 | peri.disable_irq(); | 118 | self.peri.disable_irq(); |
| 131 | peri.disable_write_dma(); | 119 | self.peri.disable_write_dma(); |
| 132 | peri.disable_read_dma(); | 120 | self.peri.disable_read_dma(); |
| 133 | 121 | ||
| 134 | // clean RRDY flag | 122 | // clean RRDY flag |
| 135 | while peri.ready_to_read() { | 123 | while self.peri.ready_to_read() { |
| 136 | peri.read_result(); | 124 | self.peri.read_result(); |
| 137 | } | 125 | } |
| 138 | 126 | ||
| 139 | peri.set_func(config.function); | 127 | self.peri.set_func(self.config.function); |
| 140 | peri.set_precision(config.precision); | 128 | self.peri.set_precision(self.config.precision); |
| 141 | peri.set_scale(config.scale); | 129 | self.peri.set_scale(self.config.scale); |
| 142 | 130 | ||
| 143 | if config.first_result { | 131 | if self.config.first_result { |
| 144 | peri.set_result_count(Count::One) | 132 | self.peri.set_result_count(Count::One) |
| 145 | } else { | 133 | } else { |
| 146 | peri.set_result_count(Count::Two) | 134 | self.peri.set_result_count(Count::Two) |
| 147 | } | 135 | } |
| 148 | 136 | ||
| 149 | match config.mode { | 137 | match self.config.mode { |
| 150 | Mode::ZeroOverhead => (), | 138 | Mode::ZeroOverhead => (), |
| 151 | Mode::Interrupt => { | 139 | Mode::Interrupt => { |
| 152 | peri.enable_irq(); | 140 | self.peri.enable_irq(); |
| 153 | } | 141 | } |
| 154 | Mode::Dma => { | 142 | Mode::Dma => { |
| 155 | peri.enable_write_dma(); | 143 | self.peri.enable_write_dma(); |
| 156 | peri.enable_read_dma(); | 144 | self.peri.enable_read_dma(); |
| 157 | } | 145 | } |
| 158 | } | 146 | } |
| 147 | } | ||
| 148 | |||
| 149 | fn blocking_read_f64(&mut self) -> (f64, Option<f64>) { | ||
| 150 | let res1 = utils::q1_31_to_f64(self.peri.read_result()); | ||
| 151 | |||
| 152 | let res2 = if !self.config.first_result { | ||
| 153 | Some(utils::q1_31_to_f64(self.peri.read_result())) | ||
| 154 | } else { | ||
| 155 | None | ||
| 156 | }; | ||
| 157 | |||
| 158 | (res1, res2) | ||
| 159 | } | ||
| 160 | |||
| 161 | fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { | ||
| 162 | let (res1, res2) = self.blocking_read_f64(); | ||
| 163 | result_buf[*result_index] = res1; | ||
| 164 | *result_index += 1; | ||
| 165 | |||
| 166 | if let Some(res2) = res2 { | ||
| 167 | result_buf[*result_index] = res2; | ||
| 168 | *result_index += 1; | ||
| 169 | } | ||
| 170 | } | ||
| 159 | 171 | ||
| 160 | self.state.input_buf.fill(0u32); | 172 | fn blocking_write_f64(&mut self, arg: f64) { |
| 161 | self.state.buf_index = 0; | 173 | self.peri.write_argument(utils::f64_to_q1_31(arg)); |
| 162 | } | 174 | } |
| 163 | } | 175 | } |
| 164 | 176 | ||
| @@ -172,11 +184,8 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { | |||
| 172 | impl<'d, T: Instance> Cordic<'d, T> { | 184 | impl<'d, T: Instance> Cordic<'d, T> { |
| 173 | /// Run a CORDIC calculation | 185 | /// Run a CORDIC calculation |
| 174 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { | 186 | pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { |
| 175 | let peri = &self.cordic; | ||
| 176 | let config = &self.config; | ||
| 177 | |||
| 178 | assert!( | 187 | assert!( |
| 179 | match config.first_result { | 188 | match self.config.first_result { |
| 180 | true => output.len() >= arg1s.len(), | 189 | true => output.len() >= arg1s.len(), |
| 181 | false => output.len() >= 2 * arg1s.len(), | 190 | false => output.len() >= 2 * arg1s.len(), |
| 182 | }, | 191 | }, |
| @@ -185,87 +194,68 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 185 | 194 | ||
| 186 | self.check_input_f64(arg1s, arg2s); | 195 | self.check_input_f64(arg1s, arg2s); |
| 187 | 196 | ||
| 188 | peri.set_result_count(if config.first_result { Count::One } else { Count::Two }); | 197 | self.peri.set_result_count(if self.config.first_result { |
| 189 | peri.set_data_width(Width::Bits32, Width::Bits32); | 198 | Count::One |
| 199 | } else { | ||
| 200 | Count::Two | ||
| 201 | }); | ||
| 190 | 202 | ||
| 191 | let state = &mut self.state; | 203 | self.peri.set_data_width(Width::Bits32, Width::Bits32); |
| 192 | 204 | ||
| 193 | let mut output_count = 0; | 205 | let mut output_count = 0; |
| 194 | 206 | ||
| 195 | let mut consumed_input_len = 0; | 207 | let mut consumed_input_len = 0; |
| 196 | 208 | ||
| 197 | match config.mode { | 209 | match self.config.mode { |
| 198 | Mode::ZeroOverhead => { | 210 | Mode::ZeroOverhead => { |
| 199 | // put double input into cordic | 211 | // put double input into cordic |
| 200 | if arg2s.is_some() && !arg2s.unwrap().is_empty() { | 212 | if arg2s.is_some() && !arg2s.unwrap().is_empty() { |
| 201 | let arg2s = arg2s.unwrap(); | 213 | let arg2s = arg2s.unwrap(); |
| 202 | 214 | ||
| 203 | peri.set_argument_count(Count::Two); | 215 | self.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]); | ||
| 214 | 216 | ||
| 215 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | 217 | // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. |
| 216 | output_count += 1; | 218 | // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. |
| 219 | let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); | ||
| 220 | // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. | ||
| 221 | consumed_input_len = double_input.len() + 1; | ||
| 217 | 222 | ||
| 218 | if !config.first_result { | 223 | // preload first value from arg1 to cordic |
| 219 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | 224 | self.blocking_write_f64(arg1s[0]); |
| 220 | output_count += 1; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | 225 | ||
| 224 | state.buf_index = 0; | 226 | for (&arg1, &arg2) in double_input { |
| 225 | } | 227 | // Since we manually preload a value before, |
| 228 | // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) | ||
| 229 | // and write arg1 (from the actual next pair), then read the result, to "keep preloading" | ||
| 226 | 230 | ||
| 227 | for &&arg in [arg1, arg2].iter() { | 231 | self.blocking_write_f64(arg2); |
| 228 | state.input_buf[state.buf_index] = utils::f64_to_q1_31(arg); | 232 | self.blocking_write_f64(arg1); |
| 229 | state.buf_index += 1; | 233 | self.blocking_read_f64_to_buf(output, &mut output_count); |
| 230 | } | ||
| 231 | } | 234 | } |
| 232 | 235 | ||
| 233 | // put left paired args into cordic | 236 | // write last input value from arg2s, then read out the result |
| 234 | if state.buf_index > 0 { | 237 | self.blocking_write_f64(arg2s[arg2s.len() - 1]); |
| 235 | for arg in state.input_buf[..state.buf_index].chunks(2) { | 238 | self.blocking_read_f64_to_buf(output, &mut output_count); |
| 236 | peri.write_argument(arg[0]); | ||
| 237 | peri.write_argument(arg[1]); | ||
| 238 | |||
| 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 | } | ||
| 247 | |||
| 248 | state.buf_index = 0; | ||
| 249 | } | ||
| 250 | } | 239 | } |
| 251 | 240 | ||
| 252 | // put single input into cordic | 241 | // put single input into cordic |
| 253 | let input_left = &arg1s[consumed_input_len..]; | 242 | let input_left = &arg1s[consumed_input_len..]; |
| 254 | 243 | ||
| 255 | if !input_left.is_empty() { | 244 | if !input_left.is_empty() { |
| 256 | peri.set_argument_count(Count::One); | 245 | self.peri.set_argument_count(Count::One); |
| 257 | |||
| 258 | for &arg in input_left.iter() { | ||
| 259 | peri.write_argument(utils::f64_to_q1_31(arg)); | ||
| 260 | 246 | ||
| 261 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | 247 | // "preload" value to cordic (at this moment, cordic start to calculating) |
| 262 | output_count += 1; | 248 | self.blocking_write_f64(input_left[0]); |
| 263 | 249 | ||
| 264 | if !config.first_result { | 250 | for &arg in input_left.iter().skip(1) { |
| 265 | output[output_count] = utils::q1_31_to_f64(peri.read_result()); | 251 | // this line write arg for next round caculation to cordic, |
| 266 | output_count += 1; | 252 | // and read result from last round |
| 267 | } | 253 | self.blocking_write_f64(arg); |
| 254 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 268 | } | 255 | } |
| 256 | |||
| 257 | // read the last output | ||
| 258 | self.blocking_read_f64_to_buf(output, &mut output_count); | ||
| 269 | } | 259 | } |
| 270 | 260 | ||
| 271 | output_count | 261 | output_count |
