diff options
| author | Caleb Garrett <[email protected]> | 2024-02-03 16:10:00 -0500 |
|---|---|---|
| committer | Caleb Garrett <[email protected]> | 2024-02-03 16:10:00 -0500 |
| commit | 72bbfec39d3f826c1a8dd485af2da4bcbdd32e35 (patch) | |
| tree | 4a3ea34a01d552e4a897b4cda7ea0d4844eb4914 /embassy-stm32/src/hash | |
| parent | 10275309021d933d5dfe4c0a96928432e11cd8b4 (diff) | |
Added hash DMA implementation.
Diffstat (limited to 'embassy-stm32/src/hash')
| -rw-r--r-- | embassy-stm32/src/hash/mod.rs | 141 |
1 files changed, 65 insertions, 76 deletions
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 4e37e60e1..ac4854f80 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs | |||
| @@ -2,11 +2,13 @@ | |||
| 2 | use core::cmp::min; | 2 | use core::cmp::min; |
| 3 | use core::future::poll_fn; | 3 | use core::future::poll_fn; |
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | 7 | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 10 | ||
| 11 | use crate::dma::Transfer; | ||
| 10 | use crate::peripherals::HASH; | 12 | use crate::peripherals::HASH; |
| 11 | use stm32_metapac::hash::regs::*; | 13 | use stm32_metapac::hash::regs::*; |
| 12 | 14 | ||
| @@ -18,7 +20,6 @@ use crate::{interrupt, pac, peripherals, Peripheral}; | |||
| 18 | const NUM_CONTEXT_REGS: usize = 51; | 20 | const NUM_CONTEXT_REGS: usize = 51; |
| 19 | #[cfg(hash_v2)] | 21 | #[cfg(hash_v2)] |
| 20 | const NUM_CONTEXT_REGS: usize = 54; | 22 | const NUM_CONTEXT_REGS: usize = 54; |
| 21 | const HASH_BUFFER_LEN: usize = 68; | ||
| 22 | const DIGEST_BLOCK_SIZE: usize = 64; | 23 | const DIGEST_BLOCK_SIZE: usize = 64; |
| 23 | 24 | ||
| 24 | static HASH_WAKER: AtomicWaker = AtomicWaker::new(); | 25 | static HASH_WAKER: AtomicWaker = AtomicWaker::new(); |
| @@ -74,8 +75,7 @@ pub enum DataType { | |||
| 74 | /// Stores the state of the HASH peripheral for suspending/resuming | 75 | /// Stores the state of the HASH peripheral for suspending/resuming |
| 75 | /// digest calculation. | 76 | /// digest calculation. |
| 76 | pub struct Context { | 77 | pub struct Context { |
| 77 | first_word_sent: bool, | 78 | buffer: [u8; DIGEST_BLOCK_SIZE], |
| 78 | buffer: [u8; HASH_BUFFER_LEN], | ||
| 79 | buflen: usize, | 79 | buflen: usize, |
| 80 | algo: Algorithm, | 80 | algo: Algorithm, |
| 81 | format: DataType, | 81 | format: DataType, |
| @@ -86,17 +86,19 @@ pub struct Context { | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | /// HASH driver. | 88 | /// HASH driver. |
| 89 | pub struct Hash<'d, T: Instance> { | 89 | pub struct Hash<'d, T: Instance, D: Dma<T>> { |
| 90 | _peripheral: PeripheralRef<'d, T>, | 90 | _peripheral: PeripheralRef<'d, T>, |
| 91 | dma: PeripheralRef<'d, D>, | ||
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | impl<'d, T: Instance> Hash<'d, T> { | 94 | impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> { |
| 94 | /// Instantiates, resets, and enables the HASH peripheral. | 95 | /// Instantiates, resets, and enables the HASH peripheral. |
| 95 | pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self { | 96 | pub fn new(peripheral: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = D> + 'd) -> Self { |
| 96 | HASH::enable_and_reset(); | 97 | HASH::enable_and_reset(); |
| 97 | into_ref!(peripheral); | 98 | into_ref!(peripheral, dma); |
| 98 | let instance = Self { | 99 | let instance = Self { |
| 99 | _peripheral: peripheral, | 100 | _peripheral: peripheral, |
| 101 | dma: dma, | ||
| 100 | }; | 102 | }; |
| 101 | 103 | ||
| 102 | T::Interrupt::unpend(); | 104 | T::Interrupt::unpend(); |
| @@ -109,8 +111,7 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 109 | pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { | 111 | pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { |
| 110 | // Define a context for this new computation. | 112 | // Define a context for this new computation. |
| 111 | let mut ctx = Context { | 113 | let mut ctx = Context { |
| 112 | first_word_sent: false, | 114 | buffer: [0; DIGEST_BLOCK_SIZE], |
| 113 | buffer: [0; 68], | ||
| 114 | buflen: 0, | 115 | buflen: 0, |
| 115 | algo: algorithm, | 116 | algo: algorithm, |
| 116 | format: format, | 117 | format: format, |
| @@ -134,6 +135,11 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 134 | } | 135 | } |
| 135 | T::regs().cr().modify(|w| w.set_algo0(algo0)); | 136 | T::regs().cr().modify(|w| w.set_algo0(algo0)); |
| 136 | T::regs().cr().modify(|w| w.set_algo1(algo1)); | 137 | T::regs().cr().modify(|w| w.set_algo1(algo1)); |
| 138 | |||
| 139 | // Enable multiple DMA transfers. | ||
| 140 | T::regs().cr().modify(|w| w.set_mdmat(true)); | ||
| 141 | |||
| 142 | // Set init to load the context registers. Necessary before storing context. | ||
| 137 | T::regs().cr().modify(|w| w.set_init(true)); | 143 | T::regs().cr().modify(|w| w.set_init(true)); |
| 138 | 144 | ||
| 139 | // Store and return the state of the peripheral. | 145 | // Store and return the state of the peripheral. |
| @@ -145,8 +151,8 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 145 | /// then updates the state with the provided data. | 151 | /// then updates the state with the provided data. |
| 146 | /// Peripheral state is saved upon return. | 152 | /// Peripheral state is saved upon return. |
| 147 | pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { | 153 | pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { |
| 148 | let mut data_waiting = input.len() + ctx.buflen; | 154 | let data_waiting = input.len() + ctx.buflen; |
| 149 | if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { | 155 | if data_waiting < DIGEST_BLOCK_SIZE { |
| 150 | // There isn't enough data to digest a block, so append it to the buffer. | 156 | // There isn't enough data to digest a block, so append it to the buffer. |
| 151 | ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | 157 | ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); |
| 152 | ctx.buflen += input.len(); | 158 | ctx.buflen += input.len(); |
| @@ -159,65 +165,35 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 159 | let mut ilen_remaining = input.len(); | 165 | let mut ilen_remaining = input.len(); |
| 160 | let mut input_start = 0; | 166 | let mut input_start = 0; |
| 161 | 167 | ||
| 162 | // Handle first block. | 168 | // First ingest the data in the buffer. |
| 163 | if !ctx.first_word_sent { | 169 | let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; |
| 164 | let empty_len = ctx.buffer.len() - ctx.buflen; | 170 | if empty_len > 0 { |
| 165 | let copy_len = min(empty_len, ilen_remaining); | 171 | let copy_len = min(empty_len, ilen_remaining); |
| 166 | // Fill the buffer. | 172 | ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); |
| 167 | if copy_len > 0 { | 173 | ctx.buflen += copy_len; |
| 168 | ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); | 174 | ilen_remaining -= copy_len; |
| 169 | ctx.buflen += copy_len; | 175 | input_start += copy_len; |
| 170 | ilen_remaining -= copy_len; | ||
| 171 | input_start += copy_len; | ||
| 172 | } | ||
| 173 | assert_eq!(ctx.buflen, HASH_BUFFER_LEN); | ||
| 174 | self.accumulate(ctx.buffer.as_slice()); | ||
| 175 | data_waiting -= ctx.buflen; | ||
| 176 | ctx.buflen = 0; | ||
| 177 | ctx.first_word_sent = true; | ||
| 178 | } | 176 | } |
| 177 | self.accumulate(&ctx.buffer).await; | ||
| 178 | ctx.buflen = 0; | ||
| 179 | 179 | ||
| 180 | if data_waiting < 64 { | 180 | // Move any extra data to the now-empty buffer. |
| 181 | // There isn't enough data remaining to process another block, so store it. | 181 | let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; |
| 182 | assert_eq!(ctx.buflen, 0); | 182 | if leftovers > 0 { |
| 183 | ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); | 183 | assert!(ilen_remaining >= leftovers); |
| 184 | ctx.buflen += ilen_remaining; | 184 | ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); |
| 185 | ctx.buflen += leftovers; | ||
| 186 | ilen_remaining -= leftovers; | ||
| 185 | } else { | 187 | } else { |
| 186 | let mut total_data_sent = 0; | 188 | ctx.buffer |
| 187 | 189 | .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); | |
| 188 | // First ingest the data in the buffer. | 190 | ctx.buflen += DIGEST_BLOCK_SIZE; |
| 189 | let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | 191 | ilen_remaining -= DIGEST_BLOCK_SIZE; |
| 190 | if empty_len > 0 { | ||
| 191 | let copy_len = min(empty_len, ilen_remaining); | ||
| 192 | ctx.buffer[ctx.buflen..ctx.buflen + copy_len] | ||
| 193 | .copy_from_slice(&input[input_start..input_start + copy_len]); | ||
| 194 | ctx.buflen += copy_len; | ||
| 195 | ilen_remaining -= copy_len; | ||
| 196 | input_start += copy_len; | ||
| 197 | } | ||
| 198 | assert_eq!(ctx.buflen % 64, 0); | ||
| 199 | self.accumulate(&ctx.buffer[0..64]); | ||
| 200 | total_data_sent += ctx.buflen; | ||
| 201 | ctx.buflen = 0; | ||
| 202 | |||
| 203 | // Move any extra data to the now-empty buffer. | ||
| 204 | let leftovers = ilen_remaining % 64; | ||
| 205 | if leftovers > 0 { | ||
| 206 | assert!(ilen_remaining >= leftovers); | ||
| 207 | ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||
| 208 | ctx.buflen += leftovers; | ||
| 209 | ilen_remaining -= leftovers; | ||
| 210 | } | ||
| 211 | assert_eq!(ilen_remaining % 64, 0); | ||
| 212 | |||
| 213 | // Hash the remaining data. | ||
| 214 | self.accumulate(&input[input_start..input_start + ilen_remaining]); | ||
| 215 | |||
| 216 | total_data_sent += ilen_remaining; | ||
| 217 | assert_eq!(total_data_sent % 64, 0); | ||
| 218 | assert!(total_data_sent >= 64); | ||
| 219 | } | 192 | } |
| 220 | 193 | ||
| 194 | // Hash the remaining data. | ||
| 195 | self.accumulate(&input[input_start..input_start + ilen_remaining]).await; | ||
| 196 | |||
| 221 | // Save the peripheral context. | 197 | // Save the peripheral context. |
| 222 | self.store_context(ctx).await; | 198 | self.store_context(ctx).await; |
| 223 | } | 199 | } |
| @@ -228,13 +204,13 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 228 | // Restore the peripheral state. | 204 | // Restore the peripheral state. |
| 229 | self.load_context(&ctx); | 205 | self.load_context(&ctx); |
| 230 | 206 | ||
| 207 | // Must be cleared prior to the last DMA transfer. | ||
| 208 | T::regs().cr().modify(|w| w.set_mdmat(false)); | ||
| 209 | |||
| 231 | // Hash the leftover bytes, if any. | 210 | // Hash the leftover bytes, if any. |
| 232 | self.accumulate(&ctx.buffer[0..ctx.buflen]); | 211 | self.accumulate(&ctx.buffer[0..ctx.buflen]).await; |
| 233 | ctx.buflen = 0; | 212 | ctx.buflen = 0; |
| 234 | 213 | ||
| 235 | //Start the digest calculation. | ||
| 236 | T::regs().str().write(|w| w.set_dcal(true)); | ||
| 237 | |||
| 238 | // Wait for completion. | 214 | // Wait for completion. |
| 239 | poll_fn(|cx| { | 215 | poll_fn(|cx| { |
| 240 | // Check if already done. | 216 | // Check if already done. |
| @@ -272,19 +248,30 @@ impl<'d, T: Instance> Hash<'d, T> { | |||
| 272 | } | 248 | } |
| 273 | 249 | ||
| 274 | /// Push data into the hash core. | 250 | /// Push data into the hash core. |
| 275 | fn accumulate(&mut self, input: &[u8]) { | 251 | async fn accumulate(&mut self, input: &[u8]) { |
| 252 | // Ignore an input length of 0. | ||
| 253 | if input.len() == 0 { | ||
| 254 | return; | ||
| 255 | } | ||
| 256 | |||
| 276 | // Set the number of valid bits. | 257 | // Set the number of valid bits. |
| 277 | let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | 258 | let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; |
| 278 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | 259 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); |
| 279 | 260 | ||
| 280 | let mut i = 0; | 261 | // Configure DMA to transfer input to hash core. |
| 281 | while i < input.len() { | 262 | let dma_request = self.dma.request(); |
| 282 | let mut word: [u8; 4] = [0; 4]; | 263 | let dst_ptr = T::regs().din().as_ptr(); |
| 283 | let copy_idx = min(i + 4, input.len()); | 264 | let mut num_words = input.len() / 4; |
| 284 | word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | 265 | if input.len() % 4 > 0 { |
| 285 | T::regs().din().write_value(u32::from_ne_bytes(word)); | 266 | num_words += 1; |
| 286 | i += 4; | ||
| 287 | } | 267 | } |
| 268 | let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); | ||
| 269 | let dma_transfer = | ||
| 270 | unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; | ||
| 271 | T::regs().cr().modify(|w| w.set_dmae(true)); | ||
| 272 | |||
| 273 | // Wait for the transfer to complete. | ||
| 274 | dma_transfer.await; | ||
| 288 | } | 275 | } |
| 289 | 276 | ||
| 290 | /// Save the peripheral state to a context. | 277 | /// Save the peripheral state to a context. |
| @@ -361,3 +348,5 @@ foreach_interrupt!( | |||
| 361 | } | 348 | } |
| 362 | }; | 349 | }; |
| 363 | ); | 350 | ); |
| 351 | |||
| 352 | dma_trait!(Dma, Instance); | ||
