Merge pull request #2528 from caleb-garrett/hash

STM32 Hash Accelerator
This commit is contained in:
Dario Nieuwenhuis 2024-02-13 01:36:11 +01:00 committed by GitHub
commit 8c82d1bcbc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 701 additions and 11 deletions

View file

@ -1056,6 +1056,7 @@ fn main() {
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
(("timer", "UP"), quote!(crate::timer::UpDma)),
(("hash", "IN"), quote!(crate::hash::Dma)),
(("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),

View file

@ -0,0 +1,545 @@
//! 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;
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);

View file

@ -45,6 +45,8 @@ pub mod exti;
pub mod flash;
#[cfg(fmc)]
pub mod fmc;
#[cfg(hash)]
pub mod hash;
#[cfg(hrtim)]
pub mod hrtim;
#[cfg(i2c)]

View file

@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32F767ZITx"
runner = "probe-rs run --chip STM32F777ZITx"
[build]
target = "thumbv7em-none-eabihf"

View file

@ -5,8 +5,8 @@ version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
# Change stm32f767zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
# Change stm32f777zi to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-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"
critical-section = "1.1"
embedded-storage = "0.3.1"
static_cell = "2"
sha2 = { version = "0.10.8", default-features = false }
[profile.release]
debug = 2

View file

@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
ETH => eth::InterruptHandler;
RNG => rng::InterruptHandler<peripherals::RNG>;
HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
});
type Device = Ethernet<'static, ETH, GenericSMI>;

View file

@ -0,0 +1,56 @@
#![no_std]
#![no_main]
use defmt::info;
use embassy_executor::Spawner;
use embassy_stm32::hash::*;
use embassy_stm32::{bind_interrupts, hash, peripherals, Config};
use embassy_time::Instant;
use sha2::{Digest, Sha256};
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let config = Config::default();
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, Irqs);
let hw_start_time = Instant::now();
// Compute a digest in hardware.
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 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;
let sw_start_time = Instant::now();
// Compute a digest in software.
let mut sw_hasher = Sha256::new();
sw_hasher.update(test_1);
sw_hasher.update(test_2);
let sw_digest = sw_hasher.finalize();
let sw_end_time = Instant::now();
let sw_execution_time = sw_end_time - sw_start_time;
info!("Hardware Digest: {:?}", hw_digest);
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[..]);
loop {}
}

View file

@ -15,22 +15,23 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma"
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"]
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"]
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"]
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash"]
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash"]
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"]
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"]
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"]
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"]
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
stm32u585ai = ["embassy-stm32/stm32u585ai", "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"]
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
hash = []
eth = ["embassy-executor/task-arena-size-16384"]
rng = []
sdmmc = []
@ -74,6 +75,7 @@ static_cell = "2"
portable-atomic = { version = "1.5", features = [] }
chrono = { version = "^0.4", default-features = false, optional = true}
sha2 = { version = "0.10.8", default-features = false }
# BEGIN TESTS
# Generated by gen_test.py. DO NOT EDIT.
@ -107,6 +109,11 @@ name = "gpio"
path = "src/bin/gpio.rs"
required-features = []
[[bin]]
name = "hash"
path = "src/bin/hash.rs"
required-features = [ "hash",]
[[bin]]
name = "rng"
path = "src/bin/rng.rs"

View file

@ -0,0 +1,78 @@
// required-features: hash
#![no_std]
#![no_main]
#[path = "../common.rs"]
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};
use {defmt_rtt as _, panic_probe as _};
#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))]
bind_interrupts!(struct Irqs {
HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
});
#[cfg(any(
feature = "stm32wba52cg",
feature = "stm32l552ze",
feature = "stm32h563zi",
feature = "stm32u5a5zj",
feature = "stm32u585ai"
))]
bind_interrupts!(struct Irqs {
HASH => hash::InterruptHandler<peripherals::HASH>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
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);
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);
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_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();
sw_sha256_hasher.update(test_1);
sw_sha256_hasher.update(test_2);
let sw_sha256_digest = sw_sha256_hasher.finalize();
//Compute the SHA-224 digest in software.
let mut sw_sha224_hasher = Sha224::new();
sw_sha224_hasher.update(test_3);
let sw_sha224_digest = sw_sha224_hasher.finalize();
// Compare the SHA-256 digests.
info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer);
info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]);
defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]);
// Compare the SHA-224 digests.
info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer);
info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]);
defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]);
info!("Test OK");
cortex_m::asm::bkpt();
}