aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-02-13 01:36:11 +0100
committerGitHub <[email protected]>2024-02-13 01:36:11 +0100
commit8c82d1bcbcb8b04f18e9fd2a2d388a316bc50b49 (patch)
tree3ef1dcdc7f98476e3febbd1515b1cc7221d1c9b7
parente8c998aad882d766988ac2c0cb0c357c600b28c1 (diff)
parent7bf044278e8c85b5ea6dbe3de6b3cdea884c995a (diff)
Merge pull request #2528 from caleb-garrett/hash
STM32 Hash Accelerator
-rw-r--r--embassy-stm32/build.rs1
-rw-r--r--embassy-stm32/src/hash/mod.rs545
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--examples/stm32f7/.cargo/config.toml2
-rw-r--r--examples/stm32f7/Cargo.toml5
-rw-r--r--examples/stm32f7/src/bin/eth.rs2
-rw-r--r--examples/stm32f7/src/bin/hash.rs56
-rw-r--r--tests/stm32/Cargo.toml21
-rw-r--r--tests/stm32/src/bin/hash.rs78
9 files changed, 701 insertions, 11 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 3885c5d18..35023bf1f 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1056,6 +1056,7 @@ fn main() {
1056 (("dac", "CH1"), quote!(crate::dac::DacDma1)), 1056 (("dac", "CH1"), quote!(crate::dac::DacDma1)),
1057 (("dac", "CH2"), quote!(crate::dac::DacDma2)), 1057 (("dac", "CH2"), quote!(crate::dac::DacDma2)),
1058 (("timer", "UP"), quote!(crate::timer::UpDma)), 1058 (("timer", "UP"), quote!(crate::timer::UpDma)),
1059 (("hash", "IN"), quote!(crate::hash::Dma)),
1059 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), 1060 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
1060 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), 1061 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
1061 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), 1062 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs
new file mode 100644
index 000000000..f0c2c839a
--- /dev/null
+++ b/embassy-stm32/src/hash/mod.rs
@@ -0,0 +1,545 @@
1//! Hash generator (HASH)
2use core::cmp::min;
3#[cfg(hash_v2)]
4use core::future::poll_fn;
5use core::marker::PhantomData;
6#[cfg(hash_v2)]
7use core::ptr;
8#[cfg(hash_v2)]
9use core::task::Poll;
10
11use embassy_hal_internal::{into_ref, PeripheralRef};
12use embassy_sync::waitqueue::AtomicWaker;
13use stm32_metapac::hash::regs::*;
14
15use crate::dma::NoDma;
16#[cfg(hash_v2)]
17use crate::dma::Transfer;
18use crate::interrupt::typelevel::Interrupt;
19use crate::peripherals::HASH;
20use crate::rcc::sealed::RccPeripheral;
21use crate::{interrupt, pac, peripherals, Peripheral};
22
23#[cfg(hash_v1)]
24const NUM_CONTEXT_REGS: usize = 51;
25#[cfg(hash_v3)]
26const NUM_CONTEXT_REGS: usize = 103;
27#[cfg(any(hash_v2, hash_v4))]
28const NUM_CONTEXT_REGS: usize = 54;
29
30const HASH_BUFFER_LEN: usize = 132;
31const DIGEST_BLOCK_SIZE: usize = 128;
32
33static HASH_WAKER: AtomicWaker = AtomicWaker::new();
34
35/// HASH interrupt handler.
36pub struct InterruptHandler<T: Instance> {
37 _phantom: PhantomData<T>,
38}
39
40impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
41 unsafe fn on_interrupt() {
42 let bits = T::regs().sr().read();
43 if bits.dinis() {
44 T::regs().imr().modify(|reg| reg.set_dinie(false));
45 HASH_WAKER.wake();
46 }
47 if bits.dcis() {
48 T::regs().imr().modify(|reg| reg.set_dcie(false));
49 HASH_WAKER.wake();
50 }
51 }
52}
53
54///Hash algorithm selection
55#[derive(Clone, Copy, PartialEq)]
56pub enum Algorithm {
57 /// SHA-1 Algorithm
58 SHA1 = 0,
59
60 #[cfg(any(hash_v1, hash_v2, hash_v4))]
61 /// MD5 Algorithm
62 MD5 = 1,
63
64 /// SHA-224 Algorithm
65 SHA224 = 2,
66
67 /// SHA-256 Algorithm
68 SHA256 = 3,
69
70 #[cfg(hash_v3)]
71 /// SHA-384 Algorithm
72 SHA384 = 12,
73
74 #[cfg(hash_v3)]
75 /// SHA-512/224 Algorithm
76 SHA512_224 = 13,
77
78 #[cfg(hash_v3)]
79 /// SHA-512/256 Algorithm
80 SHA512_256 = 14,
81
82 #[cfg(hash_v3)]
83 /// SHA-256 Algorithm
84 SHA512 = 15,
85}
86
87/// Input data width selection
88#[repr(u8)]
89#[derive(Clone, Copy)]
90pub enum DataType {
91 ///32-bit data, no data is swapped.
92 Width32 = 0,
93 ///16-bit data, each half-word is swapped.
94 Width16 = 1,
95 ///8-bit data, all bytes are swapped.
96 Width8 = 2,
97 ///1-bit data, all bits are swapped.
98 Width1 = 3,
99}
100
101/// Stores the state of the HASH peripheral for suspending/resuming
102/// digest calculation.
103pub struct Context {
104 first_word_sent: bool,
105 buffer: [u8; HASH_BUFFER_LEN],
106 buflen: usize,
107 algo: Algorithm,
108 format: DataType,
109 imr: u32,
110 str: u32,
111 cr: u32,
112 csr: [u32; NUM_CONTEXT_REGS],
113}
114
115/// HASH driver.
116pub struct Hash<'d, T: Instance, D = NoDma> {
117 _peripheral: PeripheralRef<'d, T>,
118 #[allow(dead_code)]
119 dma: PeripheralRef<'d, D>,
120}
121
122impl<'d, T: Instance, D> Hash<'d, T, D> {
123 /// Instantiates, resets, and enables the HASH peripheral.
124 pub fn new(
125 peripheral: impl Peripheral<P = T> + 'd,
126 dma: impl Peripheral<P = D> + 'd,
127 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
128 ) -> Self {
129 HASH::enable_and_reset();
130 into_ref!(peripheral, dma);
131 let instance = Self {
132 _peripheral: peripheral,
133 dma: dma,
134 };
135
136 T::Interrupt::unpend();
137 unsafe { T::Interrupt::enable() };
138
139 instance
140 }
141
142 /// Starts computation of a new hash and returns the saved peripheral state.
143 pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
144 // Define a context for this new computation.
145 let mut ctx = Context {
146 first_word_sent: false,
147 buffer: [0; HASH_BUFFER_LEN],
148 buflen: 0,
149 algo: algorithm,
150 format: format,
151 imr: 0,
152 str: 0,
153 cr: 0,
154 csr: [0; NUM_CONTEXT_REGS],
155 };
156
157 // Set the data type in the peripheral.
158 T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
159
160 // Select the algorithm.
161 #[cfg(hash_v1)]
162 if ctx.algo == Algorithm::MD5 {
163 T::regs().cr().modify(|w| w.set_algo(true));
164 }
165
166 #[cfg(hash_v2)]
167 {
168 // Select the algorithm.
169 let mut algo0 = false;
170 let mut algo1 = false;
171 if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
172 algo0 = true;
173 }
174 if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
175 algo1 = true;
176 }
177 T::regs().cr().modify(|w| w.set_algo0(algo0));
178 T::regs().cr().modify(|w| w.set_algo1(algo1));
179 }
180
181 #[cfg(any(hash_v3, hash_v4))]
182 T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));
183
184 T::regs().cr().modify(|w| w.set_init(true));
185
186 // Store and return the state of the peripheral.
187 self.store_context(&mut ctx);
188 ctx
189 }
190
191 /// Restores the peripheral state using the given context,
192 /// then updates the state with the provided data.
193 /// Peripheral state is saved upon return.
194 pub fn update_blocking(&mut self, ctx: &mut Context, input: &[u8]) {
195 let mut data_waiting = input.len() + ctx.buflen;
196 if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
197 // There isn't enough data to digest a block, so append it to the buffer.
198 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
199 ctx.buflen += input.len();
200 return;
201 }
202
203 // Restore the peripheral state.
204 self.load_context(&ctx);
205
206 let mut ilen_remaining = input.len();
207 let mut input_start = 0;
208
209 // Handle first block.
210 if !ctx.first_word_sent {
211 let empty_len = ctx.buffer.len() - ctx.buflen;
212 let copy_len = min(empty_len, ilen_remaining);
213 // Fill the buffer.
214 if copy_len > 0 {
215 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);
216 ctx.buflen += copy_len;
217 ilen_remaining -= copy_len;
218 input_start += copy_len;
219 }
220 self.accumulate_blocking(ctx.buffer.as_slice());
221 data_waiting -= ctx.buflen;
222 ctx.buflen = 0;
223 ctx.first_word_sent = true;
224 }
225
226 if data_waiting < DIGEST_BLOCK_SIZE {
227 // There isn't enough data remaining to process another block, so store it.
228 ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
229 ctx.buflen += ilen_remaining;
230 } else {
231 // First ingest the data in the buffer.
232 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
233 if empty_len > 0 {
234 let copy_len = min(empty_len, ilen_remaining);
235 ctx.buffer[ctx.buflen..ctx.buflen + copy_len]
236 .copy_from_slice(&input[input_start..input_start + copy_len]);
237 ctx.buflen += copy_len;
238 ilen_remaining -= copy_len;
239 input_start += copy_len;
240 }
241 self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]);
242 ctx.buflen = 0;
243
244 // Move any extra data to the now-empty buffer.
245 let leftovers = ilen_remaining % 64;
246 if leftovers > 0 {
247 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
248 ctx.buflen += leftovers;
249 ilen_remaining -= leftovers;
250 }
251
252 // Hash the remaining data.
253 self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]);
254 }
255
256 // Save the peripheral context.
257 self.store_context(ctx);
258 }
259
260 /// Restores the peripheral state using the given context,
261 /// then updates the state with the provided data.
262 /// Peripheral state is saved upon return.
263 #[cfg(hash_v2)]
264 pub async fn update(&mut self, ctx: &mut Context, input: &[u8])
265 where
266 D: crate::hash::Dma<T>,
267 {
268 let data_waiting = input.len() + ctx.buflen;
269 if data_waiting < DIGEST_BLOCK_SIZE {
270 // There isn't enough data to digest a block, so append it to the buffer.
271 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
272 ctx.buflen += input.len();
273 return;
274 }
275
276 // Restore the peripheral state.
277 self.load_context(&ctx);
278
279 // Enable multiple DMA transfers.
280 T::regs().cr().modify(|w| w.set_mdmat(true));
281
282 let mut ilen_remaining = input.len();
283 let mut input_start = 0;
284
285 // First ingest the data in the buffer.
286 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
287 if empty_len > 0 {
288 let copy_len = min(empty_len, ilen_remaining);
289 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
290 ctx.buflen += copy_len;
291 ilen_remaining -= copy_len;
292 input_start += copy_len;
293 }
294 self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await;
295 ctx.buflen = 0;
296
297 // Move any extra data to the now-empty buffer.
298 let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
299 if leftovers > 0 {
300 assert!(ilen_remaining >= leftovers);
301 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
302 ctx.buflen += leftovers;
303 ilen_remaining -= leftovers;
304 } else {
305 ctx.buffer
306 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
307 ctx.buflen += DIGEST_BLOCK_SIZE;
308 ilen_remaining -= DIGEST_BLOCK_SIZE;
309 }
310
311 // Hash the remaining data.
312 self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
313
314 // Save the peripheral context.
315 self.store_context(ctx);
316 }
317
318 /// Computes a digest for the given context.
319 /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
320 /// The largest returned digest size is 128 bytes for SHA-512.
321 /// Panics if the supplied digest buffer is too short.
322 pub fn finish_blocking(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize {
323 // Restore the peripheral state.
324 self.load_context(&ctx);
325
326 // Hash the leftover bytes, if any.
327 self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]);
328 ctx.buflen = 0;
329
330 //Start the digest calculation.
331 T::regs().str().write(|w| w.set_dcal(true));
332
333 // Block waiting for digest.
334 while !T::regs().sr().read().dcis() {}
335
336 // Return the digest.
337 let digest_words = match ctx.algo {
338 Algorithm::SHA1 => 5,
339 #[cfg(any(hash_v1, hash_v2, hash_v4))]
340 Algorithm::MD5 => 4,
341 Algorithm::SHA224 => 7,
342 Algorithm::SHA256 => 8,
343 #[cfg(hash_v3)]
344 Algorithm::SHA384 => 12,
345 #[cfg(hash_v3)]
346 Algorithm::SHA512_224 => 7,
347 #[cfg(hash_v3)]
348 Algorithm::SHA512_256 => 8,
349 #[cfg(hash_v3)]
350 Algorithm::SHA512 => 16,
351 };
352
353 let digest_len_bytes = digest_words * 4;
354 // Panics if the supplied digest buffer is too short.
355 if digest.len() < digest_len_bytes {
356 panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
357 }
358
359 let mut i = 0;
360 while i < digest_words {
361 let word = T::regs().hr(i).read();
362 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
363 i += 1;
364 }
365 digest_len_bytes
366 }
367
368 /// Computes a digest for the given context.
369 /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
370 /// The largest returned digest size is 128 bytes for SHA-512.
371 /// Panics if the supplied digest buffer is too short.
372 #[cfg(hash_v2)]
373 pub async fn finish(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize
374 where
375 D: crate::hash::Dma<T>,
376 {
377 // Restore the peripheral state.
378 self.load_context(&ctx);
379
380 // Must be cleared prior to the last DMA transfer.
381 T::regs().cr().modify(|w| w.set_mdmat(false));
382
383 // Hash the leftover bytes, if any.
384 self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
385 ctx.buflen = 0;
386
387 // Wait for completion.
388 poll_fn(|cx| {
389 // Check if already done.
390 let bits = T::regs().sr().read();
391 if bits.dcis() {
392 return Poll::Ready(());
393 }
394 // Register waker, then enable interrupts.
395 HASH_WAKER.register(cx.waker());
396 T::regs().imr().modify(|reg| reg.set_dcie(true));
397 // Check for completion.
398 let bits = T::regs().sr().read();
399 if bits.dcis() {
400 Poll::Ready(())
401 } else {
402 Poll::Pending
403 }
404 })
405 .await;
406
407 // Return the digest.
408 let digest_words = match ctx.algo {
409 Algorithm::SHA1 => 5,
410 #[cfg(any(hash_v1, hash_v2, hash_v4))]
411 Algorithm::MD5 => 4,
412 Algorithm::SHA224 => 7,
413 Algorithm::SHA256 => 8,
414 #[cfg(hash_v3)]
415 Algorithm::SHA384 => 12,
416 #[cfg(hash_v3)]
417 Algorithm::SHA512_224 => 7,
418 #[cfg(hash_v3)]
419 Algorithm::SHA512_256 => 8,
420 #[cfg(hash_v3)]
421 Algorithm::SHA512 => 16,
422 };
423
424 let digest_len_bytes = digest_words * 4;
425 // Panics if the supplied digest buffer is too short.
426 if digest.len() < digest_len_bytes {
427 panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
428 }
429
430 let mut i = 0;
431 while i < digest_words {
432 let word = T::regs().hr(i).read();
433 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
434 i += 1;
435 }
436 digest_len_bytes
437 }
438
439 /// Push data into the hash core.
440 fn accumulate_blocking(&mut self, input: &[u8]) {
441 // Set the number of valid bits.
442 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
443 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
444
445 let mut i = 0;
446 while i < input.len() {
447 let mut word: [u8; 4] = [0; 4];
448 let copy_idx = min(i + 4, input.len());
449 word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]);
450 T::regs().din().write_value(u32::from_ne_bytes(word));
451 i += 4;
452 }
453 }
454
455 /// Push data into the hash core.
456 #[cfg(hash_v2)]
457 async fn accumulate(&mut self, input: &[u8])
458 where
459 D: crate::hash::Dma<T>,
460 {
461 // Ignore an input length of 0.
462 if input.len() == 0 {
463 return;
464 }
465
466 // Set the number of valid bits.
467 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
468 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
469
470 // Configure DMA to transfer input to hash core.
471 let dma_request = self.dma.request();
472 let dst_ptr = T::regs().din().as_ptr();
473 let mut num_words = input.len() / 4;
474 if input.len() % 4 > 0 {
475 num_words += 1;
476 }
477 let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
478 let dma_transfer =
479 unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
480 T::regs().cr().modify(|w| w.set_dmae(true));
481
482 // Wait for the transfer to complete.
483 dma_transfer.await;
484 }
485
486 /// Save the peripheral state to a context.
487 fn store_context(&mut self, ctx: &mut Context) {
488 // Block waiting for data in ready.
489 while !T::regs().sr().read().dinis() {}
490
491 // Store peripheral context.
492 ctx.imr = T::regs().imr().read().0;
493 ctx.str = T::regs().str().read().0;
494 ctx.cr = T::regs().cr().read().0;
495 let mut i = 0;
496 while i < NUM_CONTEXT_REGS {
497 ctx.csr[i] = T::regs().csr(i).read();
498 i += 1;
499 }
500 }
501
502 /// Restore the peripheral state from a context.
503 fn load_context(&mut self, ctx: &Context) {
504 // Restore the peripheral state from the context.
505 T::regs().imr().write_value(Imr { 0: ctx.imr });
506 T::regs().str().write_value(Str { 0: ctx.str });
507 T::regs().cr().write_value(Cr { 0: ctx.cr });
508 T::regs().cr().modify(|w| w.set_init(true));
509 let mut i = 0;
510 while i < NUM_CONTEXT_REGS {
511 T::regs().csr(i).write_value(ctx.csr[i]);
512 i += 1;
513 }
514 }
515}
516
517pub(crate) mod sealed {
518 use super::*;
519
520 pub trait Instance {
521 fn regs() -> pac::hash::Hash;
522 }
523}
524
525/// HASH instance trait.
526pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
527 /// Interrupt for this HASH instance.
528 type Interrupt: interrupt::typelevel::Interrupt;
529}
530
531foreach_interrupt!(
532 ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
533 impl Instance for peripherals::$inst {
534 type Interrupt = crate::interrupt::typelevel::$irq;
535 }
536
537 impl sealed::Instance for peripherals::$inst {
538 fn regs() -> crate::pac::hash::Hash {
539 crate::pac::$inst
540 }
541 }
542 };
543);
544
545dma_trait!(Dma, Instance);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index a465fccd8..cd1ede0fa 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -45,6 +45,8 @@ pub mod exti;
45pub mod flash; 45pub mod flash;
46#[cfg(fmc)] 46#[cfg(fmc)]
47pub mod fmc; 47pub mod fmc;
48#[cfg(hash)]
49pub mod hash;
48#[cfg(hrtim)] 50#[cfg(hrtim)]
49pub mod hrtim; 51pub mod hrtim;
50#[cfg(i2c)] 52#[cfg(i2c)]
diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml
index 9088eea6e..086da2d78 100644
--- a/examples/stm32f7/.cargo/config.toml
+++ b/examples/stm32f7/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32F767ZITx" 3runner = "probe-rs run --chip STM32F777ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 941ba38cd..a612c2554 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f767zi to your chip name, if necessary. 8# Change stm32f777zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
@@ -28,6 +28,7 @@ rand_core = "0.6.3"
28critical-section = "1.1" 28critical-section = "1.1"
29embedded-storage = "0.3.1" 29embedded-storage = "0.3.1"
30static_cell = "2" 30static_cell = "2"
31sha2 = { version = "0.10.8", default-features = false }
31 32
32[profile.release] 33[profile.release]
33debug = 2 34debug = 2
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index 5bff48197..9a608e909 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
19 19
20bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
21 ETH => eth::InterruptHandler; 21 ETH => eth::InterruptHandler;
22 RNG => rng::InterruptHandler<peripherals::RNG>; 22 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
23}); 23});
24 24
25type Device = Ethernet<'static, ETH, GenericSMI>; 25type Device = Ethernet<'static, ETH, GenericSMI>;
diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs
new file mode 100644
index 000000000..96e50f84b
--- /dev/null
+++ b/examples/stm32f7/src/bin/hash.rs
@@ -0,0 +1,56 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_stm32::hash::*;
7use embassy_stm32::{bind_interrupts, hash, peripherals, Config};
8use embassy_time::Instant;
9use sha2::{Digest, Sha256};
10use {defmt_rtt as _, panic_probe as _};
11
12bind_interrupts!(struct Irqs {
13 HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
14});
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) -> ! {
18 let config = Config::default();
19 let p = embassy_stm32::init(config);
20
21 let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
22 let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
23
24 let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs);
25
26 let hw_start_time = Instant::now();
27
28 // Compute a digest in hardware.
29 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8);
30 hw_hasher.update(&mut context, test_1).await;
31 hw_hasher.update(&mut context, test_2).await;
32 let mut hw_digest: [u8; 32] = [0; 32];
33 hw_hasher.finish(context, &mut hw_digest).await;
34
35 let hw_end_time = Instant::now();
36 let hw_execution_time = hw_end_time - hw_start_time;
37
38 let sw_start_time = Instant::now();
39
40 // Compute a digest in software.
41 let mut sw_hasher = Sha256::new();
42 sw_hasher.update(test_1);
43 sw_hasher.update(test_2);
44 let sw_digest = sw_hasher.finalize();
45
46 let sw_end_time = Instant::now();
47 let sw_execution_time = sw_end_time - sw_start_time;
48
49 info!("Hardware Digest: {:?}", hw_digest);
50 info!("Software Digest: {:?}", sw_digest[..]);
51 info!("Hardware Execution Time: {:?}", hw_execution_time);
52 info!("Software Execution Time: {:?}", sw_execution_time);
53 assert_eq!(hw_digest, sw_digest[..]);
54
55 loop {}
56}
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index cb1bd9a50..fc4420687 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -15,22 +15,23 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma"
15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] 15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] 16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] 17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"]
18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] 18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"]
19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"] 19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash"]
20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"] 20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash"]
21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] 21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] 22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] 23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
24stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] 24stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
25stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"] 25stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"]
26stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] 26stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
27stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"] 27stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
28stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"] 28stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"]
29stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] 29stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
30stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] 30stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
31stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"] 31stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
32stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] 32stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
33 33
34hash = []
34eth = ["embassy-executor/task-arena-size-16384"] 35eth = ["embassy-executor/task-arena-size-16384"]
35rng = [] 36rng = []
36sdmmc = [] 37sdmmc = []
@@ -74,6 +75,7 @@ static_cell = "2"
74portable-atomic = { version = "1.5", features = [] } 75portable-atomic = { version = "1.5", features = [] }
75 76
76chrono = { version = "^0.4", default-features = false, optional = true} 77chrono = { version = "^0.4", default-features = false, optional = true}
78sha2 = { version = "0.10.8", default-features = false }
77 79
78# BEGIN TESTS 80# BEGIN TESTS
79# Generated by gen_test.py. DO NOT EDIT. 81# Generated by gen_test.py. DO NOT EDIT.
@@ -108,6 +110,11 @@ path = "src/bin/gpio.rs"
108required-features = [] 110required-features = []
109 111
110[[bin]] 112[[bin]]
113name = "hash"
114path = "src/bin/hash.rs"
115required-features = [ "hash",]
116
117[[bin]]
111name = "rng" 118name = "rng"
112path = "src/bin/rng.rs" 119path = "src/bin/rng.rs"
113required-features = [ "rng",] 120required-features = [ "rng",]
diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs
new file mode 100644
index 000000000..cfcf3d976
--- /dev/null
+++ b/tests/stm32/src/bin/hash.rs
@@ -0,0 +1,78 @@
1// required-features: hash
2#![no_std]
3#![no_main]
4
5#[path = "../common.rs"]
6mod common;
7use common::*;
8use embassy_executor::Spawner;
9use embassy_stm32::dma::NoDma;
10use embassy_stm32::hash::*;
11use embassy_stm32::{bind_interrupts, hash, peripherals};
12use sha2::{Digest, Sha224, Sha256};
13use {defmt_rtt as _, panic_probe as _};
14
15#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))]
16bind_interrupts!(struct Irqs {
17 HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
18});
19
20#[cfg(any(
21 feature = "stm32wba52cg",
22 feature = "stm32l552ze",
23 feature = "stm32h563zi",
24 feature = "stm32u5a5zj",
25 feature = "stm32u585ai"
26))]
27bind_interrupts!(struct Irqs {
28 HASH => hash::InterruptHandler<peripherals::HASH>;
29});
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
34 let mut hw_hasher = Hash::new(p.HASH, NoDma, Irqs);
35
36 let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
37 let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
38 let test_3: &[u8] = b"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk";
39
40 // Start an SHA-256 digest.
41 let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8);
42 hw_hasher.update_blocking(&mut sha256context, test_1);
43
44 // Interrupt the SHA-256 digest to compute an SHA-224 digest.
45 let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8);
46 hw_hasher.update_blocking(&mut sha224context, test_3);
47 let mut sha224_digest_buffer: [u8; 28] = [0; 28];
48 let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer);
49
50 // Finish the SHA-256 digest.
51 hw_hasher.update_blocking(&mut sha256context, test_2);
52 let mut sha256_digest_buffer: [u8; 32] = [0; 32];
53 let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer);
54
55 // Compute the SHA-256 digest in software.
56 let mut sw_sha256_hasher = Sha256::new();
57 sw_sha256_hasher.update(test_1);
58 sw_sha256_hasher.update(test_2);
59 let sw_sha256_digest = sw_sha256_hasher.finalize();
60
61 //Compute the SHA-224 digest in software.
62 let mut sw_sha224_hasher = Sha224::new();
63 sw_sha224_hasher.update(test_3);
64 let sw_sha224_digest = sw_sha224_hasher.finalize();
65
66 // Compare the SHA-256 digests.
67 info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer);
68 info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]);
69 defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]);
70
71 // Compare the SHA-224 digests.
72 info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer);
73 info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]);
74 defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]);
75
76 info!("Test OK");
77 cortex_m::asm::bkpt();
78}