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