Added hash DMA implementation.

This commit is contained in:
Caleb Garrett 2024-02-03 16:10:00 -05:00
parent 1027530902
commit 72bbfec39d
3 changed files with 79 additions and 85 deletions

View file

@ -1015,6 +1015,7 @@ fn main() {
(("dac", "CH1"), quote!(crate::dac::DacDma1)), (("dac", "CH1"), quote!(crate::dac::DacDma1)),
(("dac", "CH2"), quote!(crate::dac::DacDma2)), (("dac", "CH2"), quote!(crate::dac::DacDma2)),
(("timer", "UP"), quote!(crate::timer::UpDma)), (("timer", "UP"), quote!(crate::timer::UpDma)),
(("hash", "IN"), quote!(crate::hash::Dma)),
] ]
.into(); .into();

View file

@ -2,11 +2,13 @@
use core::cmp::min; use core::cmp::min;
use core::future::poll_fn; use core::future::poll_fn;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ptr;
use core::task::Poll; use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use crate::dma::Transfer;
use crate::peripherals::HASH; use crate::peripherals::HASH;
use stm32_metapac::hash::regs::*; use stm32_metapac::hash::regs::*;
@ -18,7 +20,6 @@ use crate::{interrupt, pac, peripherals, Peripheral};
const NUM_CONTEXT_REGS: usize = 51; const NUM_CONTEXT_REGS: usize = 51;
#[cfg(hash_v2)] #[cfg(hash_v2)]
const NUM_CONTEXT_REGS: usize = 54; const NUM_CONTEXT_REGS: usize = 54;
const HASH_BUFFER_LEN: usize = 68;
const DIGEST_BLOCK_SIZE: usize = 64; const DIGEST_BLOCK_SIZE: usize = 64;
static HASH_WAKER: AtomicWaker = AtomicWaker::new(); static HASH_WAKER: AtomicWaker = AtomicWaker::new();
@ -74,8 +75,7 @@ pub enum DataType {
/// Stores the state of the HASH peripheral for suspending/resuming /// Stores the state of the HASH peripheral for suspending/resuming
/// digest calculation. /// digest calculation.
pub struct Context { pub struct Context {
first_word_sent: bool, buffer: [u8; DIGEST_BLOCK_SIZE],
buffer: [u8; HASH_BUFFER_LEN],
buflen: usize, buflen: usize,
algo: Algorithm, algo: Algorithm,
format: DataType, format: DataType,
@ -86,17 +86,19 @@ pub struct Context {
} }
/// HASH driver. /// HASH driver.
pub struct Hash<'d, T: Instance> { pub struct Hash<'d, T: Instance, D: Dma<T>> {
_peripheral: PeripheralRef<'d, T>, _peripheral: PeripheralRef<'d, T>,
dma: PeripheralRef<'d, D>,
} }
impl<'d, T: Instance> Hash<'d, T> { impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> {
/// Instantiates, resets, and enables the HASH peripheral. /// Instantiates, resets, and enables the HASH peripheral.
pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self { pub fn new(peripheral: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = D> + 'd) -> Self {
HASH::enable_and_reset(); HASH::enable_and_reset();
into_ref!(peripheral); into_ref!(peripheral, dma);
let instance = Self { let instance = Self {
_peripheral: peripheral, _peripheral: peripheral,
dma: dma,
}; };
T::Interrupt::unpend(); T::Interrupt::unpend();
@ -109,8 +111,7 @@ impl<'d, T: Instance> Hash<'d, T> {
pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
// Define a context for this new computation. // Define a context for this new computation.
let mut ctx = Context { let mut ctx = Context {
first_word_sent: false, buffer: [0; DIGEST_BLOCK_SIZE],
buffer: [0; 68],
buflen: 0, buflen: 0,
algo: algorithm, algo: algorithm,
format: format, format: format,
@ -134,6 +135,11 @@ impl<'d, T: Instance> Hash<'d, T> {
} }
T::regs().cr().modify(|w| w.set_algo0(algo0)); T::regs().cr().modify(|w| w.set_algo0(algo0));
T::regs().cr().modify(|w| w.set_algo1(algo1)); T::regs().cr().modify(|w| w.set_algo1(algo1));
// 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)); T::regs().cr().modify(|w| w.set_init(true));
// Store and return the state of the peripheral. // Store and return the state of the peripheral.
@ -145,8 +151,8 @@ impl<'d, T: Instance> Hash<'d, T> {
/// then updates the state with the provided data. /// then updates the state with the provided data.
/// Peripheral state is saved upon return. /// Peripheral state is saved upon return.
pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) {
let mut data_waiting = input.len() + ctx.buflen; let data_waiting = input.len() + ctx.buflen;
if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { if data_waiting < DIGEST_BLOCK_SIZE {
// There isn't enough data to digest a block, so append it to the buffer. // 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.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
ctx.buflen += input.len(); ctx.buflen += input.len();
@ -159,65 +165,35 @@ impl<'d, T: Instance> Hash<'d, T> {
let mut ilen_remaining = input.len(); let mut ilen_remaining = input.len();
let mut input_start = 0; let mut input_start = 0;
// Handle first block. // First ingest the data in the buffer.
if !ctx.first_word_sent { let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
let empty_len = ctx.buffer.len() - ctx.buflen; if empty_len > 0 {
let copy_len = min(empty_len, ilen_remaining); let copy_len = min(empty_len, ilen_remaining);
// Fill the buffer. ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
if copy_len > 0 { ctx.buflen += copy_len;
ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); ilen_remaining -= copy_len;
ctx.buflen += copy_len; input_start += copy_len;
ilen_remaining -= copy_len;
input_start += copy_len;
}
assert_eq!(ctx.buflen, HASH_BUFFER_LEN);
self.accumulate(ctx.buffer.as_slice());
data_waiting -= ctx.buflen;
ctx.buflen = 0;
ctx.first_word_sent = true;
} }
self.accumulate(&ctx.buffer).await;
ctx.buflen = 0;
if data_waiting < 64 { // Move any extra data to the now-empty buffer.
// There isn't enough data remaining to process another block, so store it. let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
assert_eq!(ctx.buflen, 0); if leftovers > 0 {
ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); assert!(ilen_remaining >= leftovers);
ctx.buflen += ilen_remaining; ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
ctx.buflen += leftovers;
ilen_remaining -= leftovers;
} else { } else {
let mut total_data_sent = 0; ctx.buffer
.copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
// First ingest the data in the buffer. ctx.buflen += DIGEST_BLOCK_SIZE;
let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; ilen_remaining -= DIGEST_BLOCK_SIZE;
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;
}
assert_eq!(ctx.buflen % 64, 0);
self.accumulate(&ctx.buffer[0..64]);
total_data_sent += ctx.buflen;
ctx.buflen = 0;
// Move any extra data to the now-empty buffer.
let leftovers = ilen_remaining % 64;
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;
}
assert_eq!(ilen_remaining % 64, 0);
// Hash the remaining data.
self.accumulate(&input[input_start..input_start + ilen_remaining]);
total_data_sent += ilen_remaining;
assert_eq!(total_data_sent % 64, 0);
assert!(total_data_sent >= 64);
} }
// Hash the remaining data.
self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
// Save the peripheral context. // Save the peripheral context.
self.store_context(ctx).await; self.store_context(ctx).await;
} }
@ -228,12 +204,12 @@ impl<'d, T: Instance> Hash<'d, T> {
// Restore the peripheral state. // Restore the peripheral state.
self.load_context(&ctx); self.load_context(&ctx);
// Hash the leftover bytes, if any. // Must be cleared prior to the last DMA transfer.
self.accumulate(&ctx.buffer[0..ctx.buflen]); T::regs().cr().modify(|w| w.set_mdmat(false));
ctx.buflen = 0;
//Start the digest calculation. // Hash the leftover bytes, if any.
T::regs().str().write(|w| w.set_dcal(true)); self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
ctx.buflen = 0;
// Wait for completion. // Wait for completion.
poll_fn(|cx| { poll_fn(|cx| {
@ -272,19 +248,30 @@ impl<'d, T: Instance> Hash<'d, T> {
} }
/// Push data into the hash core. /// Push data into the hash core.
fn accumulate(&mut self, input: &[u8]) { async fn accumulate(&mut self, input: &[u8]) {
// Ignore an input length of 0.
if input.len() == 0 {
return;
}
// Set the number of valid bits. // Set the number of valid bits.
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
let mut i = 0; // Configure DMA to transfer input to hash core.
while i < input.len() { let dma_request = self.dma.request();
let mut word: [u8; 4] = [0; 4]; let dst_ptr = T::regs().din().as_ptr();
let copy_idx = min(i + 4, input.len()); let mut num_words = input.len() / 4;
word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); if input.len() % 4 > 0 {
T::regs().din().write_value(u32::from_ne_bytes(word)); num_words += 1;
i += 4;
} }
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. /// Save the peripheral state to a context.
@ -361,3 +348,5 @@ foreach_interrupt!(
} }
}; };
); );
dma_trait!(Dma, Instance);

View file

@ -4,27 +4,30 @@
use defmt::info; use defmt::info;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::Config; use embassy_stm32::Config;
use embassy_time::{Duration, Instant}; use embassy_time::Instant;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
use embassy_stm32::hash::*; use embassy_stm32::hash::*;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
const TEST_STRING_1: &[u8] = b"hello world";
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let config = Config::default(); let config = Config::default();
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7);
let hw_start_time = Instant::now(); let hw_start_time = Instant::now();
// Compute a digest in hardware. // Compute a digest in hardware.
let mut hw_hasher = Hash::new(p.HASH); 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_STRING_1); hw_hasher.update(&mut context, test_2).await;
let mut buffer: [u8; 32] = [0; 32]; let mut buffer: [u8; 32] = [0; 32];
let hw_digest = hw_hasher.finish(context, &mut buffer); let hw_digest = hw_hasher.finish(context, &mut buffer).await;
let hw_end_time = Instant::now(); let hw_end_time = Instant::now();
let hw_execution_time = hw_end_time - hw_start_time; let hw_execution_time = hw_end_time - hw_start_time;
@ -33,7 +36,8 @@ async fn main(_spawner: Spawner) -> ! {
// Compute a digest in software. // Compute a digest in software.
let mut sw_hasher = Sha256::new(); let mut sw_hasher = Sha256::new();
sw_hasher.update(TEST_STRING_1); sw_hasher.update(test_1);
sw_hasher.update(test_2);
let sw_digest = sw_hasher.finalize(); let sw_digest = sw_hasher.finalize();
let sw_end_time = Instant::now(); let sw_end_time = Instant::now();