Consolidated hash drivers.
This commit is contained in:
		
							parent
							
								
									0c9661a661
								
							
						
					
					
						commit
						eb64d71247
					
				| @ -1,8 +1,545 @@ | ||||
| //! Hash Accelerator (HASH)
 | ||||
| #[cfg_attr(hash_v1, path = "v1v3v4.rs")] | ||||
| #[cfg_attr(hash_v2, path = "v2.rs")] | ||||
| #[cfg_attr(hash_v3, path = "v1v3v4.rs")] | ||||
| #[cfg_attr(hash_v4, path = "v1v3v4.rs")] | ||||
| mod _version; | ||||
| //! Hash generator (HASH)
 | ||||
| use core::cmp::min; | ||||
| #[cfg(hash_v2)] | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| #[cfg(hash_v2)] | ||||
| use core::ptr; | ||||
| #[cfg(hash_v2)] | ||||
| use core::task::Poll; | ||||
| 
 | ||||
| pub use _version::*; | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
| use stm32_metapac::hash::regs::*; | ||||
| 
 | ||||
| use crate::dma::NoDma; | ||||
| #[cfg(hash_v2)] | ||||
| use crate::dma::Transfer; | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::peripherals::HASH; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
| use crate::{interrupt, pac, peripherals, Peripheral}; | ||||
| 
 | ||||
| #[cfg(hash_v1)] | ||||
| const NUM_CONTEXT_REGS: usize = 51; | ||||
| #[cfg(hash_v3)] | ||||
| const NUM_CONTEXT_REGS: usize = 103; | ||||
| #[cfg(any(hash_v2, hash_v4))] | ||||
| const NUM_CONTEXT_REGS: usize = 54; | ||||
| 
 | ||||
| const HASH_BUFFER_LEN: usize = 132; | ||||
| const DIGEST_BLOCK_SIZE: usize = 128; | ||||
| 
 | ||||
| static HASH_WAKER: AtomicWaker = AtomicWaker::new(); | ||||
| 
 | ||||
| /// HASH interrupt handler.
 | ||||
| pub struct InterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
|         let bits = T::regs().sr().read(); | ||||
|         if bits.dinis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dinie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|         if bits.dcis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ///Hash algorithm selection
 | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum Algorithm { | ||||
|     /// SHA-1 Algorithm
 | ||||
|     SHA1 = 0, | ||||
| 
 | ||||
|     #[cfg(any(hash_v1, hash_v2, hash_v4))] | ||||
|     /// MD5 Algorithm
 | ||||
|     MD5 = 1, | ||||
| 
 | ||||
|     /// SHA-224 Algorithm
 | ||||
|     SHA224 = 2, | ||||
| 
 | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA256 = 3, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-384 Algorithm
 | ||||
|     SHA384 = 12, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/224 Algorithm
 | ||||
|     SHA512_224 = 13, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/256 Algorithm
 | ||||
|     SHA512_256 = 14, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA512 = 15, | ||||
| } | ||||
| 
 | ||||
| /// Input data width selection
 | ||||
| #[repr(u8)] | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum DataType { | ||||
|     ///32-bit data, no data is swapped.
 | ||||
|     Width32 = 0, | ||||
|     ///16-bit data, each half-word is swapped.
 | ||||
|     Width16 = 1, | ||||
|     ///8-bit data, all bytes are swapped.
 | ||||
|     Width8 = 2, | ||||
|     ///1-bit data, all bits are swapped.
 | ||||
|     Width1 = 3, | ||||
| } | ||||
| 
 | ||||
| /// Stores the state of the HASH peripheral for suspending/resuming
 | ||||
| /// digest calculation.
 | ||||
| pub struct Context { | ||||
|     first_word_sent: bool, | ||||
|     buffer: [u8; HASH_BUFFER_LEN], | ||||
|     buflen: usize, | ||||
|     algo: Algorithm, | ||||
|     format: DataType, | ||||
|     imr: u32, | ||||
|     str: u32, | ||||
|     cr: u32, | ||||
|     csr: [u32; NUM_CONTEXT_REGS], | ||||
| } | ||||
| 
 | ||||
| /// HASH driver.
 | ||||
| pub struct Hash<'d, T: Instance, D = NoDma> { | ||||
|     _peripheral: PeripheralRef<'d, T>, | ||||
|     #[allow(dead_code)] | ||||
|     dma: PeripheralRef<'d, D>, | ||||
| } | ||||
| 
 | ||||
| impl<'d, T: Instance, D> Hash<'d, T, D> { | ||||
|     /// Instantiates, resets, and enables the HASH peripheral.
 | ||||
|     pub fn new( | ||||
|         peripheral: impl Peripheral<P = T> + 'd, | ||||
|         dma: impl Peripheral<P = D> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||||
|     ) -> Self { | ||||
|         HASH::enable_and_reset(); | ||||
|         into_ref!(peripheral, dma); | ||||
|         let instance = Self { | ||||
|             _peripheral: peripheral, | ||||
|             dma: dma, | ||||
|         }; | ||||
| 
 | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
| 
 | ||||
|         instance | ||||
|     } | ||||
| 
 | ||||
|     /// Starts computation of a new hash and returns the saved peripheral state.
 | ||||
|     pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { | ||||
|         // Define a context for this new computation.
 | ||||
|         let mut ctx = Context { | ||||
|             first_word_sent: false, | ||||
|             buffer: [0; HASH_BUFFER_LEN], | ||||
|             buflen: 0, | ||||
|             algo: algorithm, | ||||
|             format: format, | ||||
|             imr: 0, | ||||
|             str: 0, | ||||
|             cr: 0, | ||||
|             csr: [0; NUM_CONTEXT_REGS], | ||||
|         }; | ||||
| 
 | ||||
|         // Set the data type in the peripheral.
 | ||||
|         T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); | ||||
| 
 | ||||
|         // Select the algorithm.
 | ||||
|         #[cfg(hash_v1)] | ||||
|         if ctx.algo == Algorithm::MD5 { | ||||
|             T::regs().cr().modify(|w| w.set_algo(true)); | ||||
|         } | ||||
| 
 | ||||
|         #[cfg(hash_v2)] | ||||
|         { | ||||
|             // Select the algorithm.
 | ||||
|             let mut algo0 = false; | ||||
|             let mut algo1 = false; | ||||
|             if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo0 = true; | ||||
|             } | ||||
|             if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo1 = true; | ||||
|             } | ||||
|             T::regs().cr().modify(|w| w.set_algo0(algo0)); | ||||
|             T::regs().cr().modify(|w| w.set_algo1(algo1)); | ||||
|         } | ||||
| 
 | ||||
|         #[cfg(any(hash_v3, hash_v4))] | ||||
|         T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); | ||||
| 
 | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
| 
 | ||||
|         // Store and return the state of the peripheral.
 | ||||
|         self.store_context(&mut ctx); | ||||
|         ctx | ||||
|     } | ||||
| 
 | ||||
|     /// Restores the peripheral state using the given context,
 | ||||
|     /// then updates the state with the provided data.
 | ||||
|     /// Peripheral state is saved upon return.
 | ||||
|     pub fn update_blocking(&mut self, ctx: &mut Context, input: &[u8]) { | ||||
|         let mut data_waiting = input.len() + ctx.buflen; | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { | ||||
|             // There isn't enough data to digest a block, so append it to the buffer.
 | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | ||||
|             ctx.buflen += input.len(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         let mut ilen_remaining = input.len(); | ||||
|         let mut input_start = 0; | ||||
| 
 | ||||
|         // Handle first block.
 | ||||
|         if !ctx.first_word_sent { | ||||
|             let empty_len = ctx.buffer.len() - ctx.buflen; | ||||
|             let copy_len = min(empty_len, ilen_remaining); | ||||
|             // Fill the buffer.
 | ||||
|             if copy_len > 0 { | ||||
|                 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); | ||||
|                 ctx.buflen += copy_len; | ||||
|                 ilen_remaining -= copy_len; | ||||
|                 input_start += copy_len; | ||||
|             } | ||||
|             self.accumulate_blocking(ctx.buffer.as_slice()); | ||||
|             data_waiting -= ctx.buflen; | ||||
|             ctx.buflen = 0; | ||||
|             ctx.first_word_sent = true; | ||||
|         } | ||||
| 
 | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE { | ||||
|             // There isn't enough data remaining to process another block, so store it.
 | ||||
|             ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); | ||||
|             ctx.buflen += ilen_remaining; | ||||
|         } else { | ||||
|             // First ingest the data in the buffer.
 | ||||
|             let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | ||||
|             if empty_len > 0 { | ||||
|                 let copy_len = min(empty_len, ilen_remaining); | ||||
|                 ctx.buffer[ctx.buflen..ctx.buflen + copy_len] | ||||
|                     .copy_from_slice(&input[input_start..input_start + copy_len]); | ||||
|                 ctx.buflen += copy_len; | ||||
|                 ilen_remaining -= copy_len; | ||||
|                 input_start += copy_len; | ||||
|             } | ||||
|             self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); | ||||
|             ctx.buflen = 0; | ||||
| 
 | ||||
|             // Move any extra data to the now-empty buffer.
 | ||||
|             let leftovers = ilen_remaining % 64; | ||||
|             if leftovers > 0 { | ||||
|                 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||||
|                 ctx.buflen += leftovers; | ||||
|                 ilen_remaining -= leftovers; | ||||
|             } | ||||
| 
 | ||||
|             // Hash the remaining data.
 | ||||
|             self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]); | ||||
|         } | ||||
| 
 | ||||
|         // Save the peripheral context.
 | ||||
|         self.store_context(ctx); | ||||
|     } | ||||
| 
 | ||||
|     /// Restores the peripheral state using the given context,
 | ||||
|     /// then updates the state with the provided data.
 | ||||
|     /// Peripheral state is saved upon return.
 | ||||
|     #[cfg(hash_v2)] | ||||
|     pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) | ||||
|     where | ||||
|         D: crate::hash::Dma<T>, | ||||
|     { | ||||
|         let data_waiting = input.len() + ctx.buflen; | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE { | ||||
|             // There isn't enough data to digest a block, so append it to the buffer.
 | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | ||||
|             ctx.buflen += input.len(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         // Enable multiple DMA transfers.
 | ||||
|         T::regs().cr().modify(|w| w.set_mdmat(true)); | ||||
| 
 | ||||
|         let mut ilen_remaining = input.len(); | ||||
|         let mut input_start = 0; | ||||
| 
 | ||||
|         // First ingest the data in the buffer.
 | ||||
|         let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | ||||
|         if empty_len > 0 { | ||||
|             let copy_len = min(empty_len, ilen_remaining); | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); | ||||
|             ctx.buflen += copy_len; | ||||
|             ilen_remaining -= copy_len; | ||||
|             input_start += copy_len; | ||||
|         } | ||||
|         self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await; | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         // Move any extra data to the now-empty buffer.
 | ||||
|         let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; | ||||
|         if leftovers > 0 { | ||||
|             assert!(ilen_remaining >= leftovers); | ||||
|             ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||||
|             ctx.buflen += leftovers; | ||||
|             ilen_remaining -= leftovers; | ||||
|         } else { | ||||
|             ctx.buffer | ||||
|                 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); | ||||
|             ctx.buflen += DIGEST_BLOCK_SIZE; | ||||
|             ilen_remaining -= DIGEST_BLOCK_SIZE; | ||||
|         } | ||||
| 
 | ||||
|         // Hash the remaining data.
 | ||||
|         self.accumulate(&input[input_start..input_start + ilen_remaining]).await; | ||||
| 
 | ||||
|         // Save the peripheral context.
 | ||||
|         self.store_context(ctx); | ||||
|     } | ||||
| 
 | ||||
|     /// Computes a digest for the given context.
 | ||||
|     /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
 | ||||
|     /// The largest returned digest size is 128 bytes for SHA-512.
 | ||||
|     /// Panics if the supplied digest buffer is too short.
 | ||||
|     pub fn finish_blocking(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize { | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         // Hash the leftover bytes, if any.
 | ||||
|         self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]); | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         //Start the digest calculation.
 | ||||
|         T::regs().str().write(|w| w.set_dcal(true)); | ||||
| 
 | ||||
|         // Block waiting for digest.
 | ||||
|         while !T::regs().sr().read().dcis() {} | ||||
| 
 | ||||
|         // Return the digest.
 | ||||
|         let digest_words = match ctx.algo { | ||||
|             Algorithm::SHA1 => 5, | ||||
|             #[cfg(any(hash_v1, hash_v2, hash_v4))] | ||||
|             Algorithm::MD5 => 4, | ||||
|             Algorithm::SHA224 => 7, | ||||
|             Algorithm::SHA256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA384 => 12, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_224 => 7, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512 => 16, | ||||
|         }; | ||||
| 
 | ||||
|         let digest_len_bytes = digest_words * 4; | ||||
|         // Panics if the supplied digest buffer is too short.
 | ||||
|         if digest.len() < digest_len_bytes { | ||||
|             panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); | ||||
|         } | ||||
| 
 | ||||
|         let mut i = 0; | ||||
|         while i < digest_words { | ||||
|             let word = T::regs().hr(i).read(); | ||||
|             digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||||
|             i += 1; | ||||
|         } | ||||
|         digest_len_bytes | ||||
|     } | ||||
| 
 | ||||
|     /// Computes a digest for the given context.
 | ||||
|     /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
 | ||||
|     /// The largest returned digest size is 128 bytes for SHA-512.
 | ||||
|     /// Panics if the supplied digest buffer is too short.
 | ||||
|     #[cfg(hash_v2)] | ||||
|     pub async fn finish(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize | ||||
|     where | ||||
|         D: crate::hash::Dma<T>, | ||||
|     { | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         // Must be cleared prior to the last DMA transfer.
 | ||||
|         T::regs().cr().modify(|w| w.set_mdmat(false)); | ||||
| 
 | ||||
|         // Hash the leftover bytes, if any.
 | ||||
|         self.accumulate(&ctx.buffer[0..ctx.buflen]).await; | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         // Wait for completion.
 | ||||
|         poll_fn(|cx| { | ||||
|             // Check if already done.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 return Poll::Ready(()); | ||||
|             } | ||||
|             // Register waker, then enable interrupts.
 | ||||
|             HASH_WAKER.register(cx.waker()); | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(true)); | ||||
|             // Check for completion.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
| 
 | ||||
|         // Return the digest.
 | ||||
|         let digest_words = match ctx.algo { | ||||
|             Algorithm::SHA1 => 5, | ||||
|             #[cfg(any(hash_v1, hash_v2, hash_v4))] | ||||
|             Algorithm::MD5 => 4, | ||||
|             Algorithm::SHA224 => 7, | ||||
|             Algorithm::SHA256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA384 => 12, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_224 => 7, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512 => 16, | ||||
|         }; | ||||
| 
 | ||||
|         let digest_len_bytes = digest_words * 4; | ||||
|         // Panics if the supplied digest buffer is too short.
 | ||||
|         if digest.len() < digest_len_bytes { | ||||
|             panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); | ||||
|         } | ||||
| 
 | ||||
|         let mut i = 0; | ||||
|         while i < digest_words { | ||||
|             let word = T::regs().hr(i).read(); | ||||
|             digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||||
|             i += 1; | ||||
|         } | ||||
|         digest_len_bytes | ||||
|     } | ||||
| 
 | ||||
|     /// Push data into the hash core.
 | ||||
|     fn accumulate_blocking(&mut self, input: &[u8]) { | ||||
|         // Set the number of valid bits.
 | ||||
|         let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||||
|         T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||||
| 
 | ||||
|         let mut i = 0; | ||||
|         while i < input.len() { | ||||
|             let mut word: [u8; 4] = [0; 4]; | ||||
|             let copy_idx = min(i + 4, input.len()); | ||||
|             word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | ||||
|             T::regs().din().write_value(u32::from_ne_bytes(word)); | ||||
|             i += 4; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Push data into the hash core.
 | ||||
|     #[cfg(hash_v2)] | ||||
|     async fn accumulate(&mut self, input: &[u8]) | ||||
|     where | ||||
|         D: crate::hash::Dma<T>, | ||||
|     { | ||||
|         // Ignore an input length of 0.
 | ||||
|         if input.len() == 0 { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Set the number of valid bits.
 | ||||
|         let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||||
|         T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||||
| 
 | ||||
|         // Configure DMA to transfer input to hash core.
 | ||||
|         let dma_request = self.dma.request(); | ||||
|         let dst_ptr = T::regs().din().as_ptr(); | ||||
|         let mut num_words = input.len() / 4; | ||||
|         if input.len() % 4 > 0 { | ||||
|             num_words += 1; | ||||
|         } | ||||
|         let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); | ||||
|         let dma_transfer = | ||||
|             unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; | ||||
|         T::regs().cr().modify(|w| w.set_dmae(true)); | ||||
| 
 | ||||
|         // Wait for the transfer to complete.
 | ||||
|         dma_transfer.await; | ||||
|     } | ||||
| 
 | ||||
|     /// Save the peripheral state to a context.
 | ||||
|     fn store_context(&mut self, ctx: &mut Context) { | ||||
|         // Block waiting for data in ready.
 | ||||
|         while !T::regs().sr().read().dinis() {} | ||||
| 
 | ||||
|         // Store peripheral context.
 | ||||
|         ctx.imr = T::regs().imr().read().0; | ||||
|         ctx.str = T::regs().str().read().0; | ||||
|         ctx.cr = T::regs().cr().read().0; | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             ctx.csr[i] = T::regs().csr(i).read(); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Restore the peripheral state from a context.
 | ||||
|     fn load_context(&mut self, ctx: &Context) { | ||||
|         // Restore the peripheral state from the context.
 | ||||
|         T::regs().imr().write_value(Imr { 0: ctx.imr }); | ||||
|         T::regs().str().write_value(Str { 0: ctx.str }); | ||||
|         T::regs().cr().write_value(Cr { 0: ctx.cr }); | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             T::regs().csr(i).write_value(ctx.csr[i]); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) mod sealed { | ||||
|     use super::*; | ||||
| 
 | ||||
|     pub trait Instance { | ||||
|         fn regs() -> pac::hash::Hash; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// HASH instance trait.
 | ||||
| pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | ||||
|     /// Interrupt for this HASH instance.
 | ||||
|     type Interrupt: interrupt::typelevel::Interrupt; | ||||
| } | ||||
| 
 | ||||
| foreach_interrupt!( | ||||
|     ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { | ||||
|         impl Instance for peripherals::$inst { | ||||
|             type Interrupt = crate::interrupt::typelevel::$irq; | ||||
|         } | ||||
| 
 | ||||
|         impl sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::hash::Hash { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| ); | ||||
| 
 | ||||
| dma_trait!(Dma, Instance); | ||||
|  | ||||
| @ -1,399 +0,0 @@ | ||||
| //! Hash generator (HASH)
 | ||||
| use core::cmp::min; | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| use core::task::Poll; | ||||
| 
 | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
| use stm32_metapac::hash::regs::*; | ||||
| 
 | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::peripherals::HASH; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
| use crate::{interrupt, pac, peripherals, Peripheral}; | ||||
| 
 | ||||
| #[cfg(hash_v1)] | ||||
| const NUM_CONTEXT_REGS: usize = 51; | ||||
| #[cfg(hash_v3)] | ||||
| const NUM_CONTEXT_REGS: usize = 103; | ||||
| #[cfg(hash_v4)] | ||||
| const NUM_CONTEXT_REGS: usize = 54; | ||||
| 
 | ||||
| const HASH_BUFFER_LEN: usize = 132; | ||||
| const DIGEST_BLOCK_SIZE: usize = 128; | ||||
| const MAX_DIGEST_SIZE: usize = 128; | ||||
| 
 | ||||
| static HASH_WAKER: AtomicWaker = AtomicWaker::new(); | ||||
| 
 | ||||
| /// HASH interrupt handler.
 | ||||
| pub struct InterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
|         let bits = T::regs().sr().read(); | ||||
|         if bits.dinis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dinie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|         if bits.dcis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ///Hash algorithm selection
 | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum Algorithm { | ||||
|     /// SHA-1 Algorithm
 | ||||
|     SHA1 = 0, | ||||
| 
 | ||||
|     #[cfg(any(hash_v1, hash_v4))] | ||||
|     /// MD5 Algorithm
 | ||||
|     MD5 = 1, | ||||
| 
 | ||||
|     /// SHA-224 Algorithm
 | ||||
|     SHA224 = 2, | ||||
| 
 | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA256 = 3, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-384 Algorithm
 | ||||
|     SHA384 = 12, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/224 Algorithm
 | ||||
|     SHA512_224 = 13, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/256 Algorithm
 | ||||
|     SHA512_256 = 14, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA512 = 15, | ||||
| } | ||||
| 
 | ||||
| /// Input data width selection
 | ||||
| #[repr(u8)] | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum DataType { | ||||
|     ///32-bit data, no data is swapped.
 | ||||
|     Width32 = 0, | ||||
|     ///16-bit data, each half-word is swapped.
 | ||||
|     Width16 = 1, | ||||
|     ///8-bit data, all bytes are swapped.
 | ||||
|     Width8 = 2, | ||||
|     ///1-bit data, all bits are swapped.
 | ||||
|     Width1 = 3, | ||||
| } | ||||
| 
 | ||||
| /// Stores the state of the HASH peripheral for suspending/resuming
 | ||||
| /// digest calculation.
 | ||||
| pub struct Context { | ||||
|     first_word_sent: bool, | ||||
|     buffer: [u8; HASH_BUFFER_LEN], | ||||
|     buflen: usize, | ||||
|     algo: Algorithm, | ||||
|     format: DataType, | ||||
|     imr: u32, | ||||
|     str: u32, | ||||
|     cr: u32, | ||||
|     csr: [u32; NUM_CONTEXT_REGS], | ||||
| } | ||||
| 
 | ||||
| /// HASH driver.
 | ||||
| pub struct Hash<'d, T: Instance> { | ||||
|     _peripheral: PeripheralRef<'d, T>, | ||||
| } | ||||
| 
 | ||||
| impl<'d, T: Instance> Hash<'d, T> { | ||||
|     /// Instantiates, resets, and enables the HASH peripheral.
 | ||||
|     pub fn new( | ||||
|         peripheral: impl Peripheral<P = T> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||||
|     ) -> Self { | ||||
|         HASH::enable_and_reset(); | ||||
|         into_ref!(peripheral); | ||||
|         let instance = Self { | ||||
|             _peripheral: peripheral, | ||||
|         }; | ||||
| 
 | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
| 
 | ||||
|         instance | ||||
|     } | ||||
| 
 | ||||
|     /// Starts computation of a new hash and returns the saved peripheral state.
 | ||||
|     pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { | ||||
|         // Define a context for this new computation.
 | ||||
|         let mut ctx = Context { | ||||
|             first_word_sent: false, | ||||
|             buffer: [0; HASH_BUFFER_LEN], | ||||
|             buflen: 0, | ||||
|             algo: algorithm, | ||||
|             format: format, | ||||
|             imr: 0, | ||||
|             str: 0, | ||||
|             cr: 0, | ||||
|             csr: [0; NUM_CONTEXT_REGS], | ||||
|         }; | ||||
| 
 | ||||
|         // Set the data type in the peripheral.
 | ||||
|         T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); | ||||
| 
 | ||||
|         // Select the algorithm.
 | ||||
|         #[cfg(hash_v1)] | ||||
|         if ctx.algo == Algorithm::MD5 { | ||||
|             T::regs().cr().modify(|w| w.set_algo(true)); | ||||
|         } | ||||
| 
 | ||||
|         #[cfg(hash_v2)] | ||||
|         { | ||||
|             // Select the algorithm.
 | ||||
|             let mut algo0 = false; | ||||
|             let mut algo1 = false; | ||||
|             if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo0 = true; | ||||
|             } | ||||
|             if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo1 = true; | ||||
|             } | ||||
|             T::regs().cr().modify(|w| w.set_algo0(algo0)); | ||||
|             T::regs().cr().modify(|w| w.set_algo1(algo1)); | ||||
|         } | ||||
| 
 | ||||
|         #[cfg(any(hash_v3, hash_v4))] | ||||
|         T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); | ||||
| 
 | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
| 
 | ||||
|         // Store and return the state of the peripheral.
 | ||||
|         self.store_context(&mut ctx).await; | ||||
|         ctx | ||||
|     } | ||||
| 
 | ||||
|     /// Restores the peripheral state using the given context,
 | ||||
|     /// then updates the state with the provided data.
 | ||||
|     /// Peripheral state is saved upon return.
 | ||||
|     pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { | ||||
|         let mut data_waiting = input.len() + ctx.buflen; | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { | ||||
|             // There isn't enough data to digest a block, so append it to the buffer.
 | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | ||||
|             ctx.buflen += input.len(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         let mut ilen_remaining = input.len(); | ||||
|         let mut input_start = 0; | ||||
| 
 | ||||
|         // Handle first block.
 | ||||
|         if !ctx.first_word_sent { | ||||
|             let empty_len = ctx.buffer.len() - ctx.buflen; | ||||
|             let copy_len = min(empty_len, ilen_remaining); | ||||
|             // Fill the buffer.
 | ||||
|             if copy_len > 0 { | ||||
|                 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); | ||||
|                 ctx.buflen += copy_len; | ||||
|                 ilen_remaining -= copy_len; | ||||
|                 input_start += copy_len; | ||||
|             } | ||||
|             self.accumulate(ctx.buffer.as_slice()); | ||||
|             data_waiting -= ctx.buflen; | ||||
|             ctx.buflen = 0; | ||||
|             ctx.first_word_sent = true; | ||||
|         } | ||||
| 
 | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE { | ||||
|             // There isn't enough data remaining to process another block, so store it.
 | ||||
|             ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); | ||||
|             ctx.buflen += ilen_remaining; | ||||
|         } else { | ||||
|             // First ingest the data in the buffer.
 | ||||
|             let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | ||||
|             if empty_len > 0 { | ||||
|                 let copy_len = min(empty_len, ilen_remaining); | ||||
|                 ctx.buffer[ctx.buflen..ctx.buflen + copy_len] | ||||
|                     .copy_from_slice(&input[input_start..input_start + copy_len]); | ||||
|                 ctx.buflen += copy_len; | ||||
|                 ilen_remaining -= copy_len; | ||||
|                 input_start += copy_len; | ||||
|             } | ||||
|             self.accumulate(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); | ||||
|             ctx.buflen = 0; | ||||
| 
 | ||||
|             // Move any extra data to the now-empty buffer.
 | ||||
|             let leftovers = ilen_remaining % 64; | ||||
|             if leftovers > 0 { | ||||
|                 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||||
|                 ctx.buflen += leftovers; | ||||
|                 ilen_remaining -= leftovers; | ||||
|             } | ||||
| 
 | ||||
|             // Hash the remaining data.
 | ||||
|             self.accumulate(&input[input_start..input_start + ilen_remaining]); | ||||
|         } | ||||
| 
 | ||||
|         // Save the peripheral context.
 | ||||
|         self.store_context(ctx).await; | ||||
|     } | ||||
| 
 | ||||
|     /// Computes a digest for the given context. A slice of the provided digest buffer is returned.
 | ||||
|     /// The length of the returned slice is dependent on the digest length of the selected algorithm.
 | ||||
|     pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         // Hash the leftover bytes, if any.
 | ||||
|         self.accumulate(&ctx.buffer[0..ctx.buflen]); | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         //Start the digest calculation.
 | ||||
|         T::regs().str().write(|w| w.set_dcal(true)); | ||||
| 
 | ||||
|         // Wait for completion.
 | ||||
|         poll_fn(|cx| { | ||||
|             // Check if already done.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 return Poll::Ready(()); | ||||
|             } | ||||
|             // Register waker, then enable interrupts.
 | ||||
|             HASH_WAKER.register(cx.waker()); | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(true)); | ||||
|             // Check for completion.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
| 
 | ||||
|         // Return the digest.
 | ||||
|         let digest_words = match ctx.algo { | ||||
|             Algorithm::SHA1 => 5, | ||||
|             #[cfg(any(hash_v1, hash_v4))] | ||||
|             Algorithm::MD5 => 4, | ||||
|             Algorithm::SHA224 => 7, | ||||
|             Algorithm::SHA256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA384 => 12, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_224 => 7, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512 => 16, | ||||
|         }; | ||||
|         let mut i = 0; | ||||
|         while i < digest_words { | ||||
|             let word = T::regs().hr(i).read(); | ||||
|             digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||||
|             i += 1; | ||||
|         } | ||||
|         &digest[0..digest_words * 4] | ||||
|     } | ||||
| 
 | ||||
|     /// Push data into the hash core.
 | ||||
|     fn accumulate(&mut self, input: &[u8]) { | ||||
|         // Set the number of valid bits.
 | ||||
|         let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||||
|         T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||||
| 
 | ||||
|         let mut i = 0; | ||||
|         while i < input.len() { | ||||
|             let mut word: [u8; 4] = [0; 4]; | ||||
|             let copy_idx = min(i + 4, input.len()); | ||||
|             word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | ||||
|             T::regs().din().write_value(u32::from_ne_bytes(word)); | ||||
|             i += 4; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Save the peripheral state to a context.
 | ||||
|     async fn store_context(&mut self, ctx: &mut Context) { | ||||
|         // Wait for interrupt.
 | ||||
|         poll_fn(|cx| { | ||||
|             // Check if already done.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dinis() { | ||||
|                 return Poll::Ready(()); | ||||
|             } | ||||
|             // Register waker, then enable interrupts.
 | ||||
|             HASH_WAKER.register(cx.waker()); | ||||
|             T::regs().imr().modify(|reg| reg.set_dinie(true)); | ||||
|             // Check for completion.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dinis() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
| 
 | ||||
|         ctx.imr = T::regs().imr().read().0; | ||||
|         ctx.str = T::regs().str().read().0; | ||||
|         ctx.cr = T::regs().cr().read().0; | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             ctx.csr[i] = T::regs().csr(i).read(); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Restore the peripheral state from a context.
 | ||||
|     fn load_context(&mut self, ctx: &Context) { | ||||
|         // Restore the peripheral state from the context.
 | ||||
|         T::regs().imr().write_value(Imr { 0: ctx.imr }); | ||||
|         T::regs().str().write_value(Str { 0: ctx.str }); | ||||
|         T::regs().cr().write_value(Cr { 0: ctx.cr }); | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             T::regs().csr(i).write_value(ctx.csr[i]); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) mod sealed { | ||||
|     use super::*; | ||||
| 
 | ||||
|     pub trait Instance { | ||||
|         fn regs() -> pac::hash::Hash; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// HASH instance trait.
 | ||||
| pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | ||||
|     /// Interrupt for this HASH instance.
 | ||||
|     type Interrupt: interrupt::typelevel::Interrupt; | ||||
| } | ||||
| 
 | ||||
| foreach_interrupt!( | ||||
|     ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { | ||||
|         impl Instance for peripherals::$inst { | ||||
|             type Interrupt = crate::interrupt::typelevel::$irq; | ||||
|         } | ||||
| 
 | ||||
|         impl sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::hash::Hash { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| ); | ||||
| 
 | ||||
| dma_trait!(Dma, Instance); | ||||
| @ -1,389 +0,0 @@ | ||||
| //! Hash generator (HASH)
 | ||||
| use core::cmp::min; | ||||
| use core::future::poll_fn; | ||||
| use core::marker::PhantomData; | ||||
| use core::ptr; | ||||
| use core::task::Poll; | ||||
| 
 | ||||
| use embassy_hal_internal::{into_ref, PeripheralRef}; | ||||
| use embassy_sync::waitqueue::AtomicWaker; | ||||
| use stm32_metapac::hash::regs::*; | ||||
| 
 | ||||
| use crate::dma::Transfer; | ||||
| use crate::interrupt::typelevel::Interrupt; | ||||
| use crate::peripherals::HASH; | ||||
| use crate::rcc::sealed::RccPeripheral; | ||||
| use crate::{interrupt, pac, peripherals, Peripheral}; | ||||
| 
 | ||||
| #[cfg(hash_v2)] | ||||
| const NUM_CONTEXT_REGS: usize = 54; | ||||
| #[cfg(hash_v3)] | ||||
| const NUM_CONTEXT_REGS: usize = 103; | ||||
| const DIGEST_BLOCK_SIZE: usize = 64; | ||||
| const MAX_DIGEST_SIZE: usize = 64; | ||||
| 
 | ||||
| static HASH_WAKER: AtomicWaker = AtomicWaker::new(); | ||||
| 
 | ||||
| /// HASH interrupt handler.
 | ||||
| pub struct InterruptHandler<T: Instance> { | ||||
|     _phantom: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||||
|     unsafe fn on_interrupt() { | ||||
|         let bits = T::regs().sr().read(); | ||||
|         if bits.dinis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dinie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|         if bits.dcis() { | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(false)); | ||||
|             HASH_WAKER.wake(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ///Hash algorithm selection
 | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum Algorithm { | ||||
|     /// SHA-1 Algorithm
 | ||||
|     SHA1 = 0, | ||||
| 
 | ||||
|     #[cfg(hash_v2)] | ||||
|     /// MD5 Algorithm
 | ||||
|     MD5 = 1, | ||||
| 
 | ||||
|     /// SHA-224 Algorithm
 | ||||
|     SHA224 = 2, | ||||
| 
 | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA256 = 3, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-384 Algorithm
 | ||||
|     SHA384 = 12, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/224 Algorithm
 | ||||
|     SHA512_224 = 13, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-512/256 Algorithm
 | ||||
|     SHA512_256 = 14, | ||||
| 
 | ||||
|     #[cfg(hash_v3)] | ||||
|     /// SHA-256 Algorithm
 | ||||
|     SHA512 = 15, | ||||
| } | ||||
| 
 | ||||
| /// Input data width selection
 | ||||
| #[repr(u8)] | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum DataType { | ||||
|     ///32-bit data, no data is swapped.
 | ||||
|     Width32 = 0, | ||||
|     ///16-bit data, each half-word is swapped.
 | ||||
|     Width16 = 1, | ||||
|     ///8-bit data, all bytes are swapped.
 | ||||
|     Width8 = 2, | ||||
|     ///1-bit data, all bits are swapped.
 | ||||
|     Width1 = 3, | ||||
| } | ||||
| 
 | ||||
| /// Stores the state of the HASH peripheral for suspending/resuming
 | ||||
| /// digest calculation.
 | ||||
| pub struct Context { | ||||
|     buffer: [u8; DIGEST_BLOCK_SIZE], | ||||
|     buflen: usize, | ||||
|     algo: Algorithm, | ||||
|     format: DataType, | ||||
|     imr: u32, | ||||
|     str: u32, | ||||
|     cr: u32, | ||||
|     csr: [u32; NUM_CONTEXT_REGS], | ||||
| } | ||||
| 
 | ||||
| /// HASH driver.
 | ||||
| pub struct Hash<'d, T: Instance, D: Dma<T>> { | ||||
|     _peripheral: PeripheralRef<'d, T>, | ||||
|     dma: PeripheralRef<'d, D>, | ||||
| } | ||||
| 
 | ||||
| impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> { | ||||
|     /// Instantiates, resets, and enables the HASH peripheral.
 | ||||
|     pub fn new( | ||||
|         peripheral: impl Peripheral<P = T> + 'd, | ||||
|         dma: impl Peripheral<P = D> + 'd, | ||||
|         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||||
|     ) -> Self { | ||||
|         HASH::enable_and_reset(); | ||||
|         into_ref!(peripheral, dma); | ||||
|         let instance = Self { | ||||
|             _peripheral: peripheral, | ||||
|             dma: dma, | ||||
|         }; | ||||
| 
 | ||||
|         T::Interrupt::unpend(); | ||||
|         unsafe { T::Interrupt::enable() }; | ||||
| 
 | ||||
|         instance | ||||
|     } | ||||
| 
 | ||||
|     /// Starts computation of a new hash and returns the saved peripheral state.
 | ||||
|     pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { | ||||
|         // Define a context for this new computation.
 | ||||
|         let mut ctx = Context { | ||||
|             buffer: [0; DIGEST_BLOCK_SIZE], | ||||
|             buflen: 0, | ||||
|             algo: algorithm, | ||||
|             format: format, | ||||
|             imr: 0, | ||||
|             str: 0, | ||||
|             cr: 0, | ||||
|             csr: [0; NUM_CONTEXT_REGS], | ||||
|         }; | ||||
| 
 | ||||
|         // Set the data type in the peripheral.
 | ||||
|         T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); | ||||
| 
 | ||||
|         #[cfg(hash_v2)] | ||||
|         { | ||||
|             // Select the algorithm.
 | ||||
|             let mut algo0 = false; | ||||
|             let mut algo1 = false; | ||||
|             if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo0 = true; | ||||
|             } | ||||
|             if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { | ||||
|                 algo1 = true; | ||||
|             } | ||||
|             T::regs().cr().modify(|w| w.set_algo0(algo0)); | ||||
|             T::regs().cr().modify(|w| w.set_algo1(algo1)); | ||||
|         } | ||||
| 
 | ||||
|         #[cfg(hash_v3)] | ||||
|         T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); | ||||
| 
 | ||||
|         // Enable multiple DMA transfers.
 | ||||
|         T::regs().cr().modify(|w| w.set_mdmat(true)); | ||||
| 
 | ||||
|         // Set init to load the context registers. Necessary before storing context.
 | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
| 
 | ||||
|         // Store and return the state of the peripheral.
 | ||||
|         self.store_context(&mut ctx).await; | ||||
|         ctx | ||||
|     } | ||||
| 
 | ||||
|     /// Restores the peripheral state using the given context,
 | ||||
|     /// then updates the state with the provided data.
 | ||||
|     /// Peripheral state is saved upon return.
 | ||||
|     pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { | ||||
|         let data_waiting = input.len() + ctx.buflen; | ||||
|         if data_waiting < DIGEST_BLOCK_SIZE { | ||||
|             // There isn't enough data to digest a block, so append it to the buffer.
 | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); | ||||
|             ctx.buflen += input.len(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         let mut ilen_remaining = input.len(); | ||||
|         let mut input_start = 0; | ||||
| 
 | ||||
|         // First ingest the data in the buffer.
 | ||||
|         let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; | ||||
|         if empty_len > 0 { | ||||
|             let copy_len = min(empty_len, ilen_remaining); | ||||
|             ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); | ||||
|             ctx.buflen += copy_len; | ||||
|             ilen_remaining -= copy_len; | ||||
|             input_start += copy_len; | ||||
|         } | ||||
|         self.accumulate(&ctx.buffer).await; | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         // Move any extra data to the now-empty buffer.
 | ||||
|         let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; | ||||
|         if leftovers > 0 { | ||||
|             assert!(ilen_remaining >= leftovers); | ||||
|             ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); | ||||
|             ctx.buflen += leftovers; | ||||
|             ilen_remaining -= leftovers; | ||||
|         } else { | ||||
|             ctx.buffer | ||||
|                 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); | ||||
|             ctx.buflen += DIGEST_BLOCK_SIZE; | ||||
|             ilen_remaining -= DIGEST_BLOCK_SIZE; | ||||
|         } | ||||
| 
 | ||||
|         // Hash the remaining data.
 | ||||
|         self.accumulate(&input[input_start..input_start + ilen_remaining]).await; | ||||
| 
 | ||||
|         // Save the peripheral context.
 | ||||
|         self.store_context(ctx).await; | ||||
|     } | ||||
| 
 | ||||
|     /// Computes a digest for the given context. A slice of the provided digest buffer is returned.
 | ||||
|     /// The length of the returned slice is dependent on the digest length of the selected algorithm.
 | ||||
|     pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { | ||||
|         // Restore the peripheral state.
 | ||||
|         self.load_context(&ctx); | ||||
| 
 | ||||
|         // Must be cleared prior to the last DMA transfer.
 | ||||
|         T::regs().cr().modify(|w| w.set_mdmat(false)); | ||||
| 
 | ||||
|         // Hash the leftover bytes, if any.
 | ||||
|         self.accumulate(&ctx.buffer[0..ctx.buflen]).await; | ||||
|         ctx.buflen = 0; | ||||
| 
 | ||||
|         // Wait for completion.
 | ||||
|         poll_fn(|cx| { | ||||
|             // Check if already done.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 return Poll::Ready(()); | ||||
|             } | ||||
|             // Register waker, then enable interrupts.
 | ||||
|             HASH_WAKER.register(cx.waker()); | ||||
|             T::regs().imr().modify(|reg| reg.set_dcie(true)); | ||||
|             // Check for completion.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dcis() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
| 
 | ||||
|         // Return the digest.
 | ||||
|         let digest_words = match ctx.algo { | ||||
|             Algorithm::SHA1 => 5, | ||||
|             #[cfg(hash_v2)] | ||||
|             Algorithm::MD5 => 4, | ||||
|             Algorithm::SHA224 => 7, | ||||
|             Algorithm::SHA256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA384 => 12, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_224 => 7, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512_256 => 8, | ||||
|             #[cfg(hash_v3)] | ||||
|             Algorithm::SHA512 => 16, | ||||
|         }; | ||||
|         let mut i = 0; | ||||
|         while i < digest_words { | ||||
|             let word = T::regs().hr(i).read(); | ||||
|             digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||||
|             i += 1; | ||||
|         } | ||||
|         &digest[0..digest_words * 4] | ||||
|     } | ||||
| 
 | ||||
|     /// Push data into the hash core.
 | ||||
|     async fn accumulate(&mut self, input: &[u8]) { | ||||
|         // Ignore an input length of 0.
 | ||||
|         if input.len() == 0 { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Set the number of valid bits.
 | ||||
|         let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||||
|         T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||||
| 
 | ||||
|         // Configure DMA to transfer input to hash core.
 | ||||
|         let dma_request = self.dma.request(); | ||||
|         let dst_ptr = T::regs().din().as_ptr(); | ||||
|         let mut num_words = input.len() / 4; | ||||
|         if input.len() % 4 > 0 { | ||||
|             num_words += 1; | ||||
|         } | ||||
|         let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); | ||||
|         let dma_transfer = | ||||
|             unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; | ||||
|         T::regs().cr().modify(|w| w.set_dmae(true)); | ||||
| 
 | ||||
|         // Wait for the transfer to complete.
 | ||||
|         dma_transfer.await; | ||||
|     } | ||||
| 
 | ||||
|     /// Save the peripheral state to a context.
 | ||||
|     async fn store_context(&mut self, ctx: &mut Context) { | ||||
|         // Wait for interrupt.
 | ||||
|         poll_fn(|cx| { | ||||
|             // Check if already done.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dinis() { | ||||
|                 return Poll::Ready(()); | ||||
|             } | ||||
|             // Register waker, then enable interrupts.
 | ||||
|             HASH_WAKER.register(cx.waker()); | ||||
|             T::regs().imr().modify(|reg| reg.set_dinie(true)); | ||||
|             // Check for completion.
 | ||||
|             let bits = T::regs().sr().read(); | ||||
|             if bits.dinis() { | ||||
|                 Poll::Ready(()) | ||||
|             } else { | ||||
|                 Poll::Pending | ||||
|             } | ||||
|         }) | ||||
|         .await; | ||||
| 
 | ||||
|         ctx.imr = T::regs().imr().read().0; | ||||
|         ctx.str = T::regs().str().read().0; | ||||
|         ctx.cr = T::regs().cr().read().0; | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             ctx.csr[i] = T::regs().csr(i).read(); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Restore the peripheral state from a context.
 | ||||
|     fn load_context(&mut self, ctx: &Context) { | ||||
|         // Restore the peripheral state from the context.
 | ||||
|         T::regs().imr().write_value(Imr { 0: ctx.imr }); | ||||
|         T::regs().str().write_value(Str { 0: ctx.str }); | ||||
|         T::regs().cr().write_value(Cr { 0: ctx.cr }); | ||||
|         T::regs().cr().modify(|w| w.set_init(true)); | ||||
|         let mut i = 0; | ||||
|         while i < NUM_CONTEXT_REGS { | ||||
|             T::regs().csr(i).write_value(ctx.csr[i]); | ||||
|             i += 1; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) mod sealed { | ||||
|     use super::*; | ||||
| 
 | ||||
|     pub trait Instance { | ||||
|         fn regs() -> pac::hash::Hash; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// HASH instance trait.
 | ||||
| pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | ||||
|     /// Interrupt for this HASH instance.
 | ||||
|     type Interrupt: interrupt::typelevel::Interrupt; | ||||
| } | ||||
| 
 | ||||
| foreach_interrupt!( | ||||
|     ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { | ||||
|         impl Instance for peripherals::$inst { | ||||
|             type Interrupt = crate::interrupt::typelevel::$irq; | ||||
|         } | ||||
| 
 | ||||
|         impl sealed::Instance for peripherals::$inst { | ||||
|             fn regs() -> crate::pac::hash::Hash { | ||||
|                 crate::pac::$inst | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| ); | ||||
| 
 | ||||
| dma_trait!(Dma, Instance); | ||||
| @ -3,7 +3,7 @@ | ||||
| 
 | ||||
| use defmt::info; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::{bind_interrupts, Config, hash, hash::*, peripherals}; | ||||
| use embassy_stm32::{bind_interrupts, hash, hash::*, peripherals, Config}; | ||||
| use embassy_time::Instant; | ||||
| use sha2::{Digest, Sha256}; | ||||
| use {defmt_rtt as _, panic_probe as _}; | ||||
| @ -25,11 +25,11 @@ async fn main(_spawner: Spawner) -> ! { | ||||
|     let hw_start_time = Instant::now(); | ||||
| 
 | ||||
|     // Compute a digest in hardware.
 | ||||
|     let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; | ||||
|     let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); | ||||
|     hw_hasher.update(&mut context, test_1).await; | ||||
|     hw_hasher.update(&mut context, test_2).await; | ||||
|     let mut buffer: [u8; 64] = [0; 64]; | ||||
|     let hw_digest = hw_hasher.finish(context, &mut buffer).await; | ||||
|     let mut hw_digest: [u8; 32] = [0; 32]; | ||||
|     hw_hasher.finish(context, &mut hw_digest).await; | ||||
| 
 | ||||
|     let hw_end_time = Instant::now(); | ||||
|     let hw_execution_time = hw_end_time - hw_start_time; | ||||
| @ -49,7 +49,7 @@ async fn main(_spawner: Spawner) -> ! { | ||||
|     info!("Software Digest: {:?}", sw_digest[..]); | ||||
|     info!("Hardware Execution Time: {:?}", hw_execution_time); | ||||
|     info!("Software Execution Time: {:?}", sw_execution_time); | ||||
|     assert_eq!(*hw_digest, sw_digest[..]); | ||||
|     assert_eq!(hw_digest, sw_digest[..]); | ||||
| 
 | ||||
|     loop {} | ||||
| } | ||||
|  | ||||
| @ -26,7 +26,7 @@ stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash" | ||||
| stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] | ||||
| stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] | ||||
| stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"] | ||||
| stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] | ||||
| stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] | ||||
| stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] | ||||
| stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] | ||||
| stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| mod common; | ||||
| use common::*; | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::dma::NoDma; | ||||
| use embassy_stm32::hash::*; | ||||
| use embassy_stm32::{bind_interrupts, hash, peripherals}; | ||||
| use sha2::{Digest, Sha224, Sha256}; | ||||
| @ -30,27 +31,26 @@ bind_interrupts!(struct Irqs { | ||||
| #[embassy_executor::main] | ||||
| async fn main(_spawner: Spawner) { | ||||
|     let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); | ||||
|     let dma = peri!(p, HASH_DMA); | ||||
|     let mut hw_hasher = Hash::new(p.HASH, dma); | ||||
|     let mut hw_hasher = Hash::new(p.HASH, NoDma, Irqs); | ||||
| 
 | ||||
|     let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; | ||||
|     let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; | ||||
|     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"; | ||||
| 
 | ||||
|     // Start an SHA-256 digest.
 | ||||
|     let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; | ||||
|     hw_hasher.update(&mut sha256context, test_1).await; | ||||
|     let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); | ||||
|     hw_hasher.update_blocking(&mut sha256context, test_1); | ||||
| 
 | ||||
|     // Interrupt the SHA-256 digest to compute an SHA-224 digest.
 | ||||
|     let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8).await; | ||||
|     hw_hasher.update(&mut sha224context, test_3).await; | ||||
|     let mut sha224_digest_buffer: [u8; 64] = [0; 64]; | ||||
|     let sha224_digest = hw_hasher.finish(sha224context, &mut sha224_digest_buffer).await; | ||||
|     let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8); | ||||
|     hw_hasher.update_blocking(&mut sha224context, test_3); | ||||
|     let mut sha224_digest_buffer: [u8; 28] = [0; 28]; | ||||
|     let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer); | ||||
| 
 | ||||
|     // Finish the SHA-256 digest.
 | ||||
|     hw_hasher.update(&mut sha256context, test_2).await; | ||||
|     let mut sha_256_digest_buffer: [u8; 64] = [0; 64]; | ||||
|     let sha256_digest = hw_hasher.finish(sha256context, &mut sha_256_digest_buffer).await; | ||||
|     hw_hasher.update_blocking(&mut sha256context, test_2); | ||||
|     let mut sha256_digest_buffer: [u8; 32] = [0; 32]; | ||||
|     let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer); | ||||
| 
 | ||||
|     // Compute the SHA-256 digest in software.
 | ||||
|     let mut sw_sha256_hasher = Sha256::new(); | ||||
| @ -64,14 +64,14 @@ async fn main(_spawner: Spawner) { | ||||
|     let sw_sha224_digest = sw_sha224_hasher.finalize(); | ||||
| 
 | ||||
|     // Compare the SHA-256 digests.
 | ||||
|     info!("Hardware SHA-256 Digest: {:?}", sha256_digest); | ||||
|     info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer); | ||||
|     info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]); | ||||
|     defmt::assert!(*sha256_digest == sw_sha256_digest[..]); | ||||
|     defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]); | ||||
| 
 | ||||
|     // Compare the SHA-224 digests.
 | ||||
|     info!("Hardware SHA-256 Digest: {:?}", sha224_digest); | ||||
|     info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer); | ||||
|     info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]); | ||||
|     defmt::assert!(*sha224_digest == sw_sha224_digest[..]); | ||||
|     defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]); | ||||
| 
 | ||||
|     info!("Test OK"); | ||||
|     cortex_m::asm::bkpt(); | ||||
|  | ||||
| @ -128,7 +128,6 @@ define_peris!( | ||||
| ); | ||||
| #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))] | ||||
| define_peris!( | ||||
|     HASH_DMA = DMA1_CH0, | ||||
|     UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, | ||||
|     SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, | ||||
|     ADC = ADC1, DAC = DAC1, DAC_PIN = PA4, | ||||
| @ -142,21 +141,18 @@ define_peris!( | ||||
| ); | ||||
| #[cfg(feature = "stm32u585ai")] | ||||
| define_peris!( | ||||
|     HASH_DMA = GPDMA1_CH0, | ||||
|     UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, | ||||
|     SPI = SPI1, SPI_SCK = PE13, SPI_MOSI = PE15, SPI_MISO = PE14, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, | ||||
|     @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;}, | ||||
| ); | ||||
| #[cfg(feature = "stm32u5a5zj")] | ||||
| define_peris!( | ||||
|     HASH_DMA = GPDMA1_CH0, | ||||
|     UART = LPUART1, UART_TX = PG7, UART_RX = PG8, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, | ||||
|     SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, | ||||
|     @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, | ||||
| ); | ||||
| #[cfg(feature = "stm32h563zi")] | ||||
| define_peris!( | ||||
|     HASH_DMA = GPDMA1_CH0, | ||||
|     UART = LPUART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, | ||||
|     SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, | ||||
|     @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, | ||||
| @ -175,7 +171,6 @@ define_peris!( | ||||
| ); | ||||
| #[cfg(feature = "stm32l4a6zg")] | ||||
| define_peris!( | ||||
|     HASH_DMA = DMA2_CH7, | ||||
|     UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, | ||||
|     SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, | ||||
|     @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;}, | ||||
| @ -201,7 +196,6 @@ define_peris!( | ||||
| ); | ||||
| #[cfg(feature = "stm32l552ze")] | ||||
| define_peris!( | ||||
|     HASH_DMA = DMA1_CH1, | ||||
|     UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, | ||||
|     SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, | ||||
|     @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;}, | ||||
| @ -232,7 +226,6 @@ define_peris!( | ||||
| ); | ||||
| #[cfg(feature = "stm32wba52cg")] | ||||
| define_peris!( | ||||
|     HASH_DMA = GPDMA1_CH0, | ||||
|     UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, | ||||
|     SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, | ||||
|     @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user