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 | |
| parent | 10275309021d933d5dfe4c0a96928432e11cd8b4 (diff) | |
Added hash DMA implementation.
| -rw-r--r-- | embassy-stm32/build.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/hash/mod.rs | 141 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/hash.rs | 20 |
3 files changed, 78 insertions, 84 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..1a68dfc9d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -1015,6 +1015,7 @@ fn main() { | |||
| 1015 | (("dac", "CH1"), quote!(crate::dac::DacDma1)), | 1015 | (("dac", "CH1"), quote!(crate::dac::DacDma1)), |
| 1016 | (("dac", "CH2"), quote!(crate::dac::DacDma2)), | 1016 | (("dac", "CH2"), quote!(crate::dac::DacDma2)), |
| 1017 | (("timer", "UP"), quote!(crate::timer::UpDma)), | 1017 | (("timer", "UP"), quote!(crate::timer::UpDma)), |
| 1018 | (("hash", "IN"), quote!(crate::hash::Dma)), | ||
| 1018 | ] | 1019 | ] |
| 1019 | .into(); | 1020 | .into(); |
| 1020 | 1021 | ||
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); | ||
diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 1fd0e87eb..a9f5aa197 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs | |||
| @@ -4,27 +4,30 @@ | |||
| 4 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::Config; | 6 | use embassy_stm32::Config; |
| 7 | use embassy_time::{Duration, Instant}; | 7 | use embassy_time::Instant; |
| 8 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 9 | ||
| 10 | use embassy_stm32::hash::*; | 10 | use embassy_stm32::hash::*; |
| 11 | use sha2::{Digest, Sha256}; | 11 | use sha2::{Digest, Sha256}; |
| 12 | 12 | ||
| 13 | const TEST_STRING_1: &[u8] = b"hello world"; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
| 16 | async fn main(_spawner: Spawner) -> ! { | 14 | async fn main(_spawner: Spawner) -> ! { |
| 17 | let config = Config::default(); | 15 | let config = Config::default(); |
| 18 | let p = embassy_stm32::init(config); | 16 | let p = embassy_stm32::init(config); |
| 19 | 17 | ||
| 18 | let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; | ||
| 19 | let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; | ||
| 20 | |||
| 21 | let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7); | ||
| 22 | |||
| 20 | let hw_start_time = Instant::now(); | 23 | let hw_start_time = Instant::now(); |
| 21 | 24 | ||
| 22 | // Compute a digest in hardware. | 25 | // Compute a digest in hardware. |
| 23 | let mut hw_hasher = Hash::new(p.HASH); | 26 | let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; |
| 24 | let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); | 27 | hw_hasher.update(&mut context, test_1).await; |
| 25 | hw_hasher.update(&mut context, TEST_STRING_1); | 28 | hw_hasher.update(&mut context, test_2).await; |
| 26 | let mut buffer: [u8; 32] = [0; 32]; | 29 | let mut buffer: [u8; 32] = [0; 32]; |
| 27 | let hw_digest = hw_hasher.finish(context, &mut buffer); | 30 | let hw_digest = hw_hasher.finish(context, &mut buffer).await; |
| 28 | 31 | ||
| 29 | let hw_end_time = Instant::now(); | 32 | let hw_end_time = Instant::now(); |
| 30 | let hw_execution_time = hw_end_time - hw_start_time; | 33 | let hw_execution_time = hw_end_time - hw_start_time; |
| @@ -33,7 +36,8 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 33 | 36 | ||
| 34 | // Compute a digest in software. | 37 | // Compute a digest in software. |
| 35 | let mut sw_hasher = Sha256::new(); | 38 | let mut sw_hasher = Sha256::new(); |
| 36 | sw_hasher.update(TEST_STRING_1); | 39 | sw_hasher.update(test_1); |
| 40 | sw_hasher.update(test_2); | ||
| 37 | let sw_digest = sw_hasher.finalize(); | 41 | let sw_digest = sw_hasher.finalize(); |
| 38 | 42 | ||
| 39 | let sw_end_time = Instant::now(); | 43 | let sw_end_time = Instant::now(); |
