From 1f57692d042de781f884d50f0cbd9eea0c71626d Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 8 Jan 2024 00:20:40 +0100 Subject: [PATCH 01/68] Add function to create logger from class --- embassy-usb-logger/src/lib.rs | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 45d780bf8..422cefb59 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -105,6 +105,34 @@ impl UsbLogger { join(run_fut, join(log_fut, discard_fut)).await; } } + + // Creates the futures needed for the logger from a given class + pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D> ) + where + D: Driver<'d>, + { + const MAX_PACKET_SIZE: u8 = 64; + let (mut sender, mut receiver) = class.split(); + + loop { + let log_fut = async { + let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + sender.wait_connection().await; + loop { + let len = self.buffer.read(&mut rx[..]).await; + let _ = sender.write_packet(&rx[..len]).await; + } + }; + let discard_fut = async { + let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + receiver.wait_connection().await; + loop { + let _ = receiver.read_packet(&mut discard_buf).await; + } + }; + join(log_fut, discard_fut).await; + } + } } impl log::Log for UsbLogger { @@ -153,3 +181,29 @@ macro_rules! run { let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; }; } + +/// Initialize the USB serial logger from a serial class and return the future to run it. +/// +/// Arguments specify the buffer size, log level and the serial class, respectively. +/// +/// # Usage +/// +/// ``` +/// embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, class); +/// ``` +/// +/// # Safety +/// +/// This macro should only be invoked only once since it is setting the global logging state of the application. +#[macro_export] +macro_rules! with_class { + ( $x:expr, $l:expr, $p:ident ) => { + { + static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + unsafe { + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); + } + LOGGER.create_future_from_class($p) + } + }; +} From 6f505feeb1640c3d76c47aa21160a5a802fb6b93 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 8 Jan 2024 00:21:02 +0100 Subject: [PATCH 02/68] Add example --- examples/rp/src/bin/usb_serial_with_logger.rs | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 examples/rp/src/bin/usb_serial_with_logger.rs diff --git a/examples/rp/src/bin/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs new file mode 100644 index 000000000..4ba4fc25c --- /dev/null +++ b/examples/rp/src/bin/usb_serial_with_logger.rs @@ -0,0 +1,117 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip as well as how to create multiple usb classes for one device +//! +//! This creates a USB serial port that echos. It will also print out logging information on a separate serial device + +#![no_std] +#![no_main] + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + let mut logger_state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Create a class for the logger + let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64); + + // Creates the logger and returns the logger future + // Note: You'll need to use log::info! afterwards instead of info! for this to work (this also applies to all the other log::* macros) + let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + log::info!("Connected"); + let _ = echo(&mut class).await; + log::info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(echo_fut, log_fut)).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From f0c750422970db71304101fca821a0c254571604 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 8 Jan 2024 00:21:22 +0100 Subject: [PATCH 03/68] Fix log messages not always showing up straight away --- embassy-usb-logger/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 422cefb59..5142d9073 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -93,6 +93,9 @@ impl UsbLogger { loop { let len = self.buffer.read(&mut rx[..]).await; let _ = sender.write_packet(&rx[..len]).await; + if len as u8 == MAX_PACKET_SIZE { + let _ = sender.write_packet(&[]).await; + } } }; let discard_fut = async { @@ -121,6 +124,9 @@ impl UsbLogger { loop { let len = self.buffer.read(&mut rx[..]).await; let _ = sender.write_packet(&rx[..len]).await; + if len as u8 == MAX_PACKET_SIZE { + let _ = sender.write_packet(&[]).await; + } } }; let discard_fut = async { From 03bf72f690f341b9ca7dbcd329df7571fe47d014 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 8 Jan 2024 00:24:15 +0100 Subject: [PATCH 04/68] Better explanation --- embassy-usb-logger/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 5142d9073..f197c6874 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -109,7 +109,8 @@ impl UsbLogger { } } - // Creates the futures needed for the logger from a given class + /// Creates the futures needed for the logger from a given class + /// This can be used in cases where the usb device is already in use for another connection pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D> ) where D: Driver<'d>, From 2c6b475f4e663402fd87977d77dcfc4ccb70babb Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 8 Jan 2024 00:39:32 +0100 Subject: [PATCH 05/68] Fix formatting --- embassy-usb-logger/src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index f197c6874..a057fcf32 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -111,7 +111,7 @@ impl UsbLogger { /// Creates the futures needed for the logger from a given class /// This can be used in cases where the usb device is already in use for another connection - pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D> ) + pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>) where D: Driver<'d>, { @@ -121,7 +121,7 @@ impl UsbLogger { loop { let log_fut = async { let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - sender.wait_connection().await; + sender.wait_connection().await; loop { let len = self.buffer.read(&mut rx[..]).await; let _ = sender.write_packet(&rx[..len]).await; @@ -204,13 +204,11 @@ macro_rules! run { /// This macro should only be invoked only once since it is setting the global logging state of the application. #[macro_export] macro_rules! with_class { - ( $x:expr, $l:expr, $p:ident ) => { - { - static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); - unsafe { - let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); - } - LOGGER.create_future_from_class($p) + ( $x:expr, $l:expr, $p:ident ) => {{ + static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + unsafe { + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); } - }; + LOGGER.create_future_from_class($p) + }}; } From 6e9ddd46267fd0fce2333af4f15bfd86f6f17f4d Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:21:36 -0500 Subject: [PATCH 06/68] Added hash module with blocking implementation. Included SHA256 example. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 260 +++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + examples/stm32f7/Cargo.toml | 5 +- examples/stm32f7/src/bin/hash.rs | 49 ++++++ 5 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 embassy-stm32/src/hash/mod.rs create mode 100644 examples/stm32f7/src/bin/hash.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 70d4daf09..d8a4c65fa 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs new file mode 100644 index 000000000..e3d2d7b16 --- /dev/null +++ b/embassy-stm32/src/hash/mod.rs @@ -0,0 +1,260 @@ +//! Hash generator (HASH) +use core::cmp::min; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use stm32_metapac::hash::regs::*; + +use crate::pac::HASH as PAC_HASH; +use crate::peripherals::HASH; +use crate::rcc::sealed::RccPeripheral; +use crate::Peripheral; + +const NUM_CONTEXT_REGS: usize = 54; +const HASH_BUFFER_LEN: usize = 68; +const DIGEST_BLOCK_SIZE: usize = 64; + +///Hash algorithm selection +#[derive(PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + /// MD5 Algorithm + MD5 = 1, + /// SHA-224 Algorithm + SHA224 = 2, + /// SHA-256 Algorithm + SHA256 = 3, +} + +/// 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> { + _peripheral: PeripheralRef<'d, HASH>, +} + +impl<'d> Hash<'d> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + HASH::enable_and_reset(); + into_ref!(peripheral); + let instance = Self { + _peripheral: peripheral, + }; + 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; 68], + buflen: 0, + algo: algorithm, + format: format, + imr: 0, + str: 0, + cr: 0, + csr: [0; NUM_CONTEXT_REGS], + }; + + // Set the data type in the peripheral. + PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8)); + + // 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; + } + PAC_HASH.cr().modify(|w| w.set_algo0(algo0)); + PAC_HASH.cr().modify(|w| w.set_algo1(algo1)); + PAC_HASH.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. + pub 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; + } + 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; + } + + if data_waiting < 64 { + // There isn't enough data remaining to process another block, so store it. + assert_eq!(ctx.buflen, 0); + ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); + ctx.buflen += ilen_remaining; + } else { + let mut total_data_sent = 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; + } + 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); + } + + // Save the peripheral context. + self.store_context(ctx); + } + + /// 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 fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'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. + PAC_HASH.str().write(|w| w.set_dcal(true)); + + //Wait for completion. + while !PAC_HASH.sr().read().dcis() {} + + //Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + }; + let mut i = 0; + while i < digest_words { + let word = PAC_HASH.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] + } + + fn accumulate(&mut self, input: &[u8]) { + //Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + PAC_HASH.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]); + PAC_HASH.din().write_value(u32::from_ne_bytes(word)); + i += 4; + } + } + + /// Save the peripheral state to a context. + fn store_context(&mut self, ctx: &mut Context) { + while !PAC_HASH.sr().read().dinis() {} + ctx.imr = PAC_HASH.imr().read().0; + ctx.str = PAC_HASH.str().read().0; + ctx.cr = PAC_HASH.cr().read().0; + let mut i = 0; + while i < NUM_CONTEXT_REGS { + ctx.csr[i] = PAC_HASH.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. + PAC_HASH.imr().write_value(Imr { 0: ctx.imr }); + PAC_HASH.str().write_value(Str { 0: ctx.str }); + PAC_HASH.cr().write_value(Cr { 0: ctx.cr }); + PAC_HASH.cr().modify(|w| w.set_init(true)); + let mut i = 0; + while i < NUM_CONTEXT_REGS { + PAC_HASH.csr(i).write_value(ctx.csr[i]); + i += 1; + } + } +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a465fccd8..cd1ede0fa 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -45,6 +45,8 @@ pub mod exti; pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hash)] +pub mod hash; #[cfg(hrtim)] pub mod hrtim; #[cfg(i2c)] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 941ba38cd..a612c2554 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -5,8 +5,8 @@ version = "0.1.0" 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 diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs new file mode 100644 index 000000000..1fd0e87eb --- /dev/null +++ b/examples/stm32f7/src/bin/hash.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_time::{Duration, Instant}; +use {defmt_rtt as _, panic_probe as _}; + +use embassy_stm32::hash::*; +use sha2::{Digest, Sha256}; + +const TEST_STRING_1: &[u8] = b"hello world"; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let config = Config::default(); + let p = embassy_stm32::init(config); + + let hw_start_time = Instant::now(); + + // Compute a digest in hardware. + let mut hw_hasher = Hash::new(p.HASH); + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); + hw_hasher.update(&mut context, TEST_STRING_1); + let mut buffer: [u8; 32] = [0; 32]; + let hw_digest = hw_hasher.finish(context, &mut buffer); + + 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_STRING_1); + 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 {} +} From 1dbfa5ab72e3596932ccb6bd258fac70d2efa563 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:28:12 -0500 Subject: [PATCH 07/68] Added hash v1/v2 configs. --- embassy-stm32/src/hash/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index e3d2d7b16..622777b02 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -9,7 +9,11 @@ use crate::peripherals::HASH; use crate::rcc::sealed::RccPeripheral; use crate::Peripheral; +#[cfg(hash_v1)] +const NUM_CONTEXT_REGS: usize = 51; +#[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; + const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; @@ -20,8 +24,10 @@ pub enum Algorithm { SHA1 = 0, /// MD5 Algorithm MD5 = 1, + #[cfg(hash_v2)] /// SHA-224 Algorithm SHA224 = 2, + #[cfg(hash_v2)] /// SHA-256 Algorithm SHA256 = 3, } From 10275309021d933d5dfe4c0a96928432e11cd8b4 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:27:25 -0500 Subject: [PATCH 08/68] Added hash interrupts for async. --- embassy-stm32/src/hash/mod.rs | 171 ++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 622777b02..4e37e60e1 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,22 +1,47 @@ //! 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 crate::peripherals::HASH; use stm32_metapac::hash::regs::*; -use crate::pac::HASH as PAC_HASH; -use crate::peripherals::HASH; +use crate::interrupt::typelevel::Interrupt; use crate::rcc::sealed::RccPeripheral; -use crate::Peripheral; +use crate::{interrupt, pac, peripherals, Peripheral}; #[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; #[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; - const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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(PartialEq)] pub enum Algorithm { @@ -61,23 +86,27 @@ pub struct Context { } /// HASH driver. -pub struct Hash<'d> { - _peripheral: PeripheralRef<'d, HASH>, +pub struct Hash<'d, T: Instance> { + _peripheral: PeripheralRef<'d, T>, } -impl<'d> Hash<'d> { +impl<'d, T: Instance> Hash<'d, T> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new(peripheral: impl Peripheral

+ '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 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. let mut ctx = Context { first_word_sent: false, @@ -92,7 +121,7 @@ impl<'d> Hash<'d> { }; // Set the data type in the peripheral. - PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8)); + T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); // Select the algorithm. let mut algo0 = false; @@ -103,18 +132,19 @@ impl<'d> Hash<'d> { if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { algo1 = true; } - PAC_HASH.cr().modify(|w| w.set_algo0(algo0)); - PAC_HASH.cr().modify(|w| w.set_algo1(algo1)); - PAC_HASH.cr().modify(|w| w.set_init(true)); + 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_init(true)); // Store and return the state of the peripheral. - self.store_context(&mut ctx); + self.store_context(&mut ctx).await; ctx } /// Restores the peripheral state using the given context, /// then updates the state with the provided data. - pub fn update(&mut self, ctx: &mut Context, input: &[u8]) { + /// 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. @@ -123,7 +153,7 @@ impl<'d> Hash<'d> { return; } - //Restore the peripheral state. + // Restore the peripheral state. self.load_context(&ctx); let mut ilen_remaining = input.len(); @@ -154,6 +184,7 @@ impl<'d> Hash<'d> { ctx.buflen += ilen_remaining; } else { let mut total_data_sent = 0; + // First ingest the data in the buffer. let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; if empty_len > 0 { @@ -188,25 +219,43 @@ impl<'d> Hash<'d> { } // Save the peripheral context. - self.store_context(ctx); + 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 fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { + pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'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. - PAC_HASH.str().write(|w| w.set_dcal(true)); + T::regs().str().write(|w| w.set_dcal(true)); - //Wait for completion. - while !PAC_HASH.sr().read().dcis() {} + // 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_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dcis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - //Return the digest. + // Return the digest. let digest_words = match ctx.algo { Algorithm::SHA1 => 5, Algorithm::MD5 => 4, @@ -215,37 +264,57 @@ impl<'d> Hash<'d> { }; let mut i = 0; while i < digest_words { - let word = PAC_HASH.hr(i).read(); + 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. + // Set the number of valid bits. let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; - PAC_HASH.str().modify(|w| w.set_nblw(num_valid_bits)); + 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]); - PAC_HASH.din().write_value(u32::from_ne_bytes(word)); + T::regs().din().write_value(u32::from_ne_bytes(word)); i += 4; } } /// Save the peripheral state to a context. - fn store_context(&mut self, ctx: &mut Context) { - while !PAC_HASH.sr().read().dinis() {} - ctx.imr = PAC_HASH.imr().read().0; - ctx.str = PAC_HASH.str().read().0; - ctx.cr = PAC_HASH.cr().read().0; + 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] = PAC_HASH.csr(i).read(); + ctx.csr[i] = T::regs().csr(i).read(); i += 1; } } @@ -253,14 +322,42 @@ impl<'d> Hash<'d> { /// Restore the peripheral state from a context. fn load_context(&mut self, ctx: &Context) { // Restore the peripheral state from the context. - PAC_HASH.imr().write_value(Imr { 0: ctx.imr }); - PAC_HASH.str().write_value(Str { 0: ctx.str }); - PAC_HASH.cr().write_value(Cr { 0: ctx.cr }); - PAC_HASH.cr().modify(|w| w.set_init(true)); + 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 { - PAC_HASH.csr(i).write_value(ctx.csr[i]); + 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

+ 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 + } + } + }; +); From 72bbfec39d3f826c1a8dd485af2da4bcbdd32e35 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:10:00 -0500 Subject: [PATCH 09/68] Added hash DMA implementation. --- embassy-stm32/build.rs | 1 + embassy-stm32/src/hash/mod.rs | 143 ++++++++++++++----------------- examples/stm32f7/src/bin/hash.rs | 20 +++-- 3 files changed, 79 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..1a68dfc9d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1015,6 +1015,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)), ] .into(); 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 @@ 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 crate::dma::Transfer; use crate::peripherals::HASH; use stm32_metapac::hash::regs::*; @@ -18,7 +20,6 @@ use crate::{interrupt, pac, peripherals, Peripheral}; const NUM_CONTEXT_REGS: usize = 51; #[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; -const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; static HASH_WAKER: AtomicWaker = AtomicWaker::new(); @@ -74,8 +75,7 @@ pub enum DataType { /// Stores the state of the HASH peripheral for suspending/resuming /// digest calculation. pub struct Context { - first_word_sent: bool, - buffer: [u8; HASH_BUFFER_LEN], + buffer: [u8; DIGEST_BLOCK_SIZE], buflen: usize, algo: Algorithm, format: DataType, @@ -86,17 +86,19 @@ pub struct Context { } /// HASH driver. -pub struct Hash<'d, T: Instance> { +pub struct Hash<'d, T: Instance, D: Dma> { _peripheral: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, D>, } -impl<'d, T: Instance> Hash<'d, T> { +impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { HASH::enable_and_reset(); - into_ref!(peripheral); + into_ref!(peripheral, dma); let instance = Self { _peripheral: peripheral, + dma: dma, }; 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 { // Define a context for this new computation. let mut ctx = Context { - first_word_sent: false, - buffer: [0; 68], + buffer: [0; DIGEST_BLOCK_SIZE], buflen: 0, algo: algorithm, 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_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)); // 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. /// 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) { + 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(); @@ -159,65 +165,35 @@ impl<'d, T: Instance> Hash<'d, T> { 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; + // 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); - // 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; - } - 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; + 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; - if data_waiting < 64 { - // There isn't enough data remaining to process another block, so store it. - assert_eq!(ctx.buflen, 0); - ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); - ctx.buflen += ilen_remaining; + // 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 { - let mut total_data_sent = 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; - } - 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); + 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; } @@ -228,12 +204,12 @@ impl<'d, T: Instance> Hash<'d, T> { // Restore the peripheral state. self.load_context(&ctx); - // Hash the leftover bytes, if any. - self.accumulate(&ctx.buffer[0..ctx.buflen]); - ctx.buflen = 0; + // Must be cleared prior to the last DMA transfer. + T::regs().cr().modify(|w| w.set_mdmat(false)); - //Start the digest calculation. - T::regs().str().write(|w| w.set_dcal(true)); + // Hash the leftover bytes, if any. + self.accumulate(&ctx.buffer[0..ctx.buflen]).await; + ctx.buflen = 0; // Wait for completion. poll_fn(|cx| { @@ -272,19 +248,30 @@ impl<'d, T: Instance> Hash<'d, T> { } /// 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. 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; + // 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. @@ -361,3 +348,5 @@ foreach_interrupt!( } }; ); + +dma_trait!(Dma, Instance); diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 1fd0e87eb..a9f5aa197 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -4,27 +4,30 @@ use defmt::info; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_time::{Duration, Instant}; +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::hash::*; use sha2::{Digest, Sha256}; -const TEST_STRING_1: &[u8] = b"hello world"; - #[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); + let hw_start_time = Instant::now(); // Compute a digest in hardware. - let mut hw_hasher = Hash::new(p.HASH); - let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); - hw_hasher.update(&mut context, TEST_STRING_1); + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; + hw_hasher.update(&mut context, test_1).await; + hw_hasher.update(&mut context, test_2).await; 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_execution_time = hw_end_time - hw_start_time; @@ -33,7 +36,8 @@ async fn main(_spawner: Spawner) -> ! { // Compute a digest in software. 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_end_time = Instant::now(); From 66f44b95d70547be8e32daac1ab611eec5fbe28a Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:16:33 -0500 Subject: [PATCH 10/68] Addressed hash CI build issues. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 357 +--------------------------- embassy-stm32/src/hash/v1.rs | 334 +++++++++++++++++++++++++++ embassy-stm32/src/hash/v2v3.rs | 385 +++++++++++++++++++++++++++++++ examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/hash.rs | 6 +- 6 files changed, 731 insertions(+), 357 deletions(-) create mode 100644 embassy-stm32/src/hash/v1.rs create mode 100644 embassy-stm32/src/hash/v2v3.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d8a4c65fa..00d8a5f63 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index ac4854f80..6b23f3b55 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,352 +1,7 @@ -//! Hash generator (HASH) -use core::cmp::min; -use core::future::poll_fn; -use core::marker::PhantomData; -use core::ptr; -use core::task::Poll; +//! Hash Accelerator (HASH) +#[cfg_attr(hash_v1, path = "v1.rs")] +#[cfg_attr(hash_v2, path = "v2v3.rs")] +#[cfg_attr(hash_v3, path = "v2v3.rs")] +mod _version; -use embassy_hal_internal::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; - -use crate::dma::Transfer; -use crate::peripherals::HASH; -use stm32_metapac::hash::regs::*; - -use crate::interrupt::typelevel::Interrupt; -use crate::rcc::sealed::RccPeripheral; -use crate::{interrupt, pac, peripherals, Peripheral}; - -#[cfg(hash_v1)] -const NUM_CONTEXT_REGS: usize = 51; -#[cfg(hash_v2)] -const NUM_CONTEXT_REGS: usize = 54; -const DIGEST_BLOCK_SIZE: usize = 64; - -static HASH_WAKER: AtomicWaker = AtomicWaker::new(); - -/// HASH interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - 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(PartialEq)] -pub enum Algorithm { - /// SHA-1 Algorithm - SHA1 = 0, - /// MD5 Algorithm - MD5 = 1, - #[cfg(hash_v2)] - /// SHA-224 Algorithm - SHA224 = 2, - #[cfg(hash_v2)] - /// SHA-256 Algorithm - SHA256 = 3, -} - -/// 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> { - _peripheral: PeripheralRef<'d, T>, - dma: PeripheralRef<'d, D>, -} - -impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { - /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ '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)); - - // 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)); - - // 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; 32]) -> &'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_dinie(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, - Algorithm::MD5 => 4, - Algorithm::SHA224 => 7, - Algorithm::SHA256 => 8, - }; - 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

+ 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); +pub use _version::*; diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1.rs new file mode 100644 index 000000000..50f9adc83 --- /dev/null +++ b/embassy-stm32/src/hash/v1.rs @@ -0,0 +1,334 @@ +//! 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}; + +const NUM_CONTEXT_REGS: usize = 51; +const HASH_BUFFER_LEN: usize = 68; +const DIGEST_BLOCK_SIZE: usize = 64; +const MAX_DIGEST_SIZE: usize = 20; + +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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(PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + /// MD5 Algorithm + MD5 = 1, +} + +/// 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

+ '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. + if ctx.algo == Algorithm::MD5 { + T::regs().cr().modify(|w| w.set_algo(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..64]); + 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_dinie(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, + Algorithm::MD5 => 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[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

+ 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); diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2v3.rs new file mode 100644 index 000000000..058864568 --- /dev/null +++ b/embassy-stm32/src/hash/v2v3.rs @@ -0,0 +1,385 @@ +//! 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 { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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> { + _peripheral: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, D>, +} + +impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ '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_dinie(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

+ 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); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5bff48197..9a608e909 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; - RNG => rng::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index a9f5aa197..cf52cea5c 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,12 +3,12 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::hash::*; use embassy_stm32::Config; use embassy_time::Instant; -use {defmt_rtt as _, panic_probe as _}; -use embassy_stm32::hash::*; use sha2::{Digest, Sha256}; +use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! { let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; hw_hasher.update(&mut context, test_1).await; hw_hasher.update(&mut context, test_2).await; - let mut buffer: [u8; 32] = [0; 32]; + let mut buffer: [u8; 64] = [0; 64]; let hw_digest = hw_hasher.finish(context, &mut buffer).await; let hw_end_time = Instant::now(); From a260c0a701b0385691b57a22a19d86d7ce4788b8 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:21:16 -0500 Subject: [PATCH 11/68] Format hash example. --- examples/stm32f7/src/bin/hash.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index cf52cea5c..4bd9b4e2e 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::hash::*; use embassy_stm32::Config; use embassy_time::Instant; - use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; From 079bb7b4901b3fa6787c2ca5d8c022de3533ad8c Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:36:02 -0500 Subject: [PATCH 12/68] Added STM32 hash test. --- tests/stm32/Cargo.toml | 23 ++++++++----- tests/stm32/src/bin/hash.rs | 66 +++++++++++++++++++++++++++++++++++++ tests/stm32/src/common.rs | 7 ++++ 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tests/stm32/src/bin/hash.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index cb1bd9a50..d02f1a253 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -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"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] +stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] +stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] 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" diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs new file mode 100644 index 000000000..60a60b0f1 --- /dev/null +++ b/tests/stm32/src/bin/hash.rs @@ -0,0 +1,66 @@ +// required-features: hash +#![no_std] +#![no_main] + +#[path = "../common.rs"] +mod common; +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::hash::*; +use embassy_stm32::{bind_interrupts, peripherals, hash}; +use sha2::{Digest, Sha224, Sha256}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + HASH_RNG => hash::InterruptHandler; +}); + +#[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 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; + + // 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; + + // 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; + + // 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); + info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]); + defmt::assert!(*sha256_digest == sw_sha256_digest[..]); + + // Compare the SHA-224 digests. + info!("Hardware SHA-256 Digest: {:?}", sha224_digest); + info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]); + defmt::assert!(*sha224_digest == sw_sha224_digest[..]); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index fefe72c86..7e7915b2e 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -128,6 +128,7 @@ 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, @@ -141,18 +142,21 @@ 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;}, ); #[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;}, ); #[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;}, @@ -171,6 +175,7 @@ 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;}, @@ -196,6 +201,7 @@ define_peris!( ); #[cfg(feature = "stm32l552ze")] define_peris!( + HASH_DMA = DMA1_CH0, 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;}, @@ -226,6 +232,7 @@ 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;}, From 09973ad4827222251a46cb7b1f48841245b54709 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:44:50 -0500 Subject: [PATCH 13/68] Corrected hash CI build issues. --- tests/stm32/src/bin/hash.rs | 4 ++-- tests/stm32/src/common.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 60a60b0f1..05b61a10c 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -7,7 +7,7 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_stm32::hash::*; -use embassy_stm32::{bind_interrupts, peripherals, hash}; +use embassy_stm32::{bind_interrupts, hash, peripherals}; use sha2::{Digest, Sha224, Sha256}; use {defmt_rtt as _, panic_probe as _}; @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { 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; - + // Compute the SHA-256 digest in software. let mut sw_sha256_hasher = Sha256::new(); sw_sha256_hasher.update(test_1); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 7e7915b2e..14d5b6d7b 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -201,7 +201,7 @@ define_peris!( ); #[cfg(feature = "stm32l552ze")] define_peris!( - HASH_DMA = DMA1_CH0, + 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;}, From 0b70d67bf645708c6681220c4f1c2e3ffb69dc35 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:54:17 -0500 Subject: [PATCH 14/68] Separated hash interrupt bindings. --- tests/stm32/src/bin/hash.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 05b61a10c..53dd0551f 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -11,10 +11,26 @@ 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; }); +#[cfg(any( + feature = "stm32wba52cg", + feature = "stm32l552ze", + feature = "stm32h563zi", + feature = "stm32u5a5zj", + feature = "stm32u585ai" +))] +bind_interrupts!(struct Irqs { + HASH => hash::InterruptHandler; + }); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); From b7db75adff16eb0a4670e926dc664549433654fd Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:44:52 -0500 Subject: [PATCH 15/68] Updated stm32-metapac. --- embassy-stm32/Cargo.toml | 4 ++-- tests/stm32/src/bin/hash.rs | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 63bc32197..ef6063656 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25", default-features = false, features = ["metadata"]} [features] diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 53dd0551f..2867115dc 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -11,11 +11,7 @@ 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" -))] +#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))] bind_interrupts!(struct Irqs { HASH_RNG => hash::InterruptHandler; }); @@ -29,7 +25,7 @@ bind_interrupts!(struct Irqs { ))] bind_interrupts!(struct Irqs { HASH => hash::InterruptHandler; - }); +}); #[embassy_executor::main] async fn main(_spawner: Spawner) { From bfa67c29932ba9b326da0c661b1b03dcee2ef3fe Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Tue, 6 Feb 2024 18:37:48 -0500 Subject: [PATCH 16/68] Fix digest interrupt enable. --- embassy-stm32/src/hash/v1.rs | 2 +- embassy-stm32/src/hash/v2v3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1.rs index 50f9adc83..36beb7c3e 100644 --- a/embassy-stm32/src/hash/v1.rs +++ b/embassy-stm32/src/hash/v1.rs @@ -215,7 +215,7 @@ impl<'d, T: Instance> Hash<'d, T> { } // Register waker, then enable interrupts. HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); + T::regs().imr().modify(|reg| reg.set_dcie(true)); // Check for completion. let bits = T::regs().sr().read(); if bits.dcis() { diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2v3.rs index 058864568..ba1e05f0c 100644 --- a/embassy-stm32/src/hash/v2v3.rs +++ b/embassy-stm32/src/hash/v2v3.rs @@ -244,7 +244,7 @@ impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { } // Register waker, then enable interrupts. HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); + T::regs().imr().modify(|reg| reg.set_dcie(true)); // Check for completion. let bits = T::regs().sr().read(); if bits.dcis() { From 2e8b7d259057a0cf345396b250cacfc04a0e64a0 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Wed, 7 Feb 2024 16:40:24 +0100 Subject: [PATCH 17/68] feat(boot): introduce non-erase flash write method --- embassy-boot/src/firmware_updater/blocking.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index f1368540d..514070639 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -194,6 +194,41 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> Ok(()) } + /// Write data directly to a flash page without erasing it first. + /// + /// This function writes the provided data to the specified offset in the flash memory, + /// without performing an erase operation beforehand. It is crucial that the area being + /// written to is either already erased. + /// This method is intended to be used in conjunction with the `prepare_update` method. + /// + /// The buffer must follow the alignment requirements of the target flash and be a multiple of + /// the page size. This is essential to ensure data integrity and prevent corruption. + /// + /// # Safety + /// + /// This function requires careful management of the memory being written to. Writing to a + /// non-erased page or not adhering to alignment and size requirements may result in a panic. + /// + /// Ensure that the data being written is compatible with the current contents of the flash + /// memory, as no erase operation will be performed to reset the page content to a default state. + /// + /// # Parameters + /// + /// - `offset`: The offset within the DFU partition where the data will be written. Must be + /// aligned according to the flash's requirements and within the writable memory range. + /// - `data`: A reference to the slice of bytes to be written. The length of the data must not + /// exceed the partition size and must follow the flash's alignment requirements. + /// + /// # Returns + /// + /// A result indicating the success or failure of the write operation. On success, returns `Ok(())`. + /// On failure, returns an `Err` with a `FirmwareUpdaterError` detailing the cause of the failure. + pub fn write_firmware_without_erase(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + self.dfu.write(offset as u32, data)?; + + Ok(()) + } + /// Prepare for an incoming DFU update by erasing the entire DFU area and /// returning its `Partition`. /// From c95bf6895adfcd33b5238c02620e83c6713205ce Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Wed, 7 Feb 2024 16:41:58 +0100 Subject: [PATCH 18/68] feat(usb-dfu): change usb dfu chunks write mechanism --- embassy-usb-dfu/src/dfu.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index e99aa70c3..5f2c98684 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -60,6 +60,21 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha } Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => { if req.value == 0 { + match self.updater.prepare_update() { + Ok(_) => { + self.status = Status::Ok; + } + Err(e) => { + self.state = State::Error; + match e { + embassy_boot::FirmwareUpdaterError::Flash(e) => match e { + NorFlashErrorKind::NotAligned => self.status = Status::ErrErase, + _ => self.status = Status::ErrUnknown, + }, + _ => self.status = Status::ErrUnknown, + } + } + } self.state = State::Download; self.offset = 0; } @@ -93,7 +108,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha self.state = State::Error; return Some(OutResponse::Rejected); } - match self.updater.write_firmware(self.offset, buf.as_ref()) { + match self.updater.write_firmware_without_erase(self.offset, buf.as_ref()) { Ok(_) => { self.status = Status::Ok; self.state = State::DlSync; From f6645750c95ac008f74b980b553117e7a390a833 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:24:27 -0500 Subject: [PATCH 19/68] Removed hash DMA from unsupported configs. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 7 +- embassy-stm32/src/hash/{v1.rs => v1v3v4.rs} | 77 +++++++++++++++++++-- embassy-stm32/src/hash/{v2v3.rs => v2.rs} | 6 +- examples/stm32f7/.cargo/config.toml | 2 +- examples/stm32f7/src/bin/hash.rs | 9 ++- 6 files changed, 89 insertions(+), 16 deletions(-) rename embassy-stm32/src/hash/{v1.rs => v1v3v4.rs} (84%) rename embassy-stm32/src/hash/{v2v3.rs => v2.rs} (98%) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ef6063656..87815c63a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-36a3262735a169e31b702bcb0ac6c0067c3f078e" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-36a3262735a169e31b702bcb0ac6c0067c3f078e", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 6b23f3b55..64c1a0a8c 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,7 +1,8 @@ //! Hash Accelerator (HASH) -#[cfg_attr(hash_v1, path = "v1.rs")] -#[cfg_attr(hash_v2, path = "v2v3.rs")] -#[cfg_attr(hash_v3, path = "v2v3.rs")] +#[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; pub use _version::*; diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1v3v4.rs similarity index 84% rename from embassy-stm32/src/hash/v1.rs rename to embassy-stm32/src/hash/v1v3v4.rs index 36beb7c3e..771144b11 100644 --- a/embassy-stm32/src/hash/v1.rs +++ b/embassy-stm32/src/hash/v1v3v4.rs @@ -13,10 +13,16 @@ use crate::peripherals::HASH; use crate::rcc::sealed::RccPeripheral; use crate::{interrupt, pac, peripherals, Peripheral}; +#[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; -const HASH_BUFFER_LEN: usize = 68; -const DIGEST_BLOCK_SIZE: usize = 64; -const MAX_DIGEST_SIZE: usize = 20; +#[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(); @@ -40,12 +46,36 @@ impl interrupt::typelevel::Handler for InterruptHandl } ///Hash algorithm selection -#[derive(PartialEq)] +#[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 @@ -83,7 +113,10 @@ pub struct Hash<'d, T: Instance> { impl<'d, T: Instance> Hash<'d, T> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new( + peripheral: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { HASH::enable_and_reset(); into_ref!(peripheral); let instance = Self { @@ -115,10 +148,31 @@ impl<'d, T: Instance> Hash<'d, T> { 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 @@ -174,7 +228,7 @@ impl<'d, T: Instance> Hash<'d, T> { ilen_remaining -= copy_len; input_start += copy_len; } - self.accumulate(&ctx.buffer[0..64]); + self.accumulate(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); ctx.buflen = 0; // Move any extra data to the now-empty buffer. @@ -229,7 +283,18 @@ impl<'d, T: Instance> Hash<'d, T> { // 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 { diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2.rs similarity index 98% rename from embassy-stm32/src/hash/v2v3.rs rename to embassy-stm32/src/hash/v2.rs index ba1e05f0c..b8104c825 100644 --- a/embassy-stm32/src/hash/v2v3.rs +++ b/embassy-stm32/src/hash/v2.rs @@ -111,7 +111,11 @@ pub struct Hash<'d, T: Instance, D: Dma> { impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + pub fn new( + peripheral: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { HASH::enable_and_reset(); into_ref!(peripheral, dma); let instance = Self { diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml index 9088eea6e..086da2d78 100644 --- a/examples/stm32f7/.cargo/config.toml +++ b/examples/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [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" diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 4bd9b4e2e..7d96bd49c 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,12 +3,15 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::hash::*; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, Config, hash, hash::*, peripherals}; use embassy_time::Instant; use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + HASH_RNG => hash::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let config = Config::default(); @@ -17,7 +20,7 @@ async fn main(_spawner: Spawner) -> ! { 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 mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs); let hw_start_time = Instant::now(); From d77b6a60d21568b8470957441b07a4974751c288 Mon Sep 17 00:00:00 2001 From: Han Cen Date: Sat, 10 Feb 2024 20:44:14 +0800 Subject: [PATCH 20/68] Fix unaligned buffer in async updater --- embassy-boot/src/firmware_updater/asynch.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 668f16f16..a7c360a35 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -276,16 +276,25 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { self.state.read(0, &mut self.aligned).await?; - if self.aligned.iter().any(|&b| b != magic) { + if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) { // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; + if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE { + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; + } else { + self.aligned.rotate_left(STATE::WRITE_SIZE); + } - if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + if self.aligned[..STATE::WRITE_SIZE] + .iter() + .any(|&b| b != STATE_ERASE_VALUE) + { // The current progress validity marker is invalid } else { // Invalidate progress self.aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; + self.state + .write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE]) + .await?; } // Clear magic and progress @@ -293,7 +302,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { // Set magic self.aligned.fill(magic); - self.state.write(0, &self.aligned).await?; + self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?; } Ok(()) } From c873dcbb20f06e00659ab1c984ce7a753aaea7dc Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sat, 10 Feb 2024 16:55:32 -0500 Subject: [PATCH 21/68] Add explicit reset time to ws2812 write fn. --- examples/rp/src/bin/pio_ws2812.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 9a97cb8a7..e9a3d0e41 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -107,6 +107,8 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { // DMA transfer self.sm.tx().dma_push(self.dma.reborrow(), &words).await; + + Timer::after_micros(55).await; } } From b4dc406e199a7e4aafcdd601aaef999c6b7ba590 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sat, 10 Feb 2024 17:00:10 -0500 Subject: [PATCH 22/68] Switch to ticker --- examples/rp/src/bin/pio_ws2812.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index e9a3d0e41..ac145933c 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -12,7 +12,7 @@ use embassy_rp::pio::{ Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; -use embassy_time::Timer; +use embassy_time::{Duration, Ticker, Timer}; use fixed::types::U24F8; use fixed_macro::fixed; use smart_leds::RGB8; @@ -145,6 +145,7 @@ async fn main(_spawner: Spawner) { let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); // Loop forever making RGB values and pushing them out to the WS2812. + let mut ticker = Ticker::every(Duration::from_millis(10)); loop { for j in 0..(256 * 5) { debug!("New Colors:"); @@ -154,7 +155,7 @@ async fn main(_spawner: Spawner) { } ws2812.write(&data).await; - Timer::after_millis(10).await; + ticker.next().await; } } } From eb64d71247dd7c217c7ead98635610fdd8a104e3 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:32:29 -0500 Subject: [PATCH 23/68] Consolidated hash drivers. --- embassy-stm32/src/hash/mod.rs | 551 ++++++++++++++++++++++++++++++- embassy-stm32/src/hash/v1v3v4.rs | 399 ---------------------- embassy-stm32/src/hash/v2.rs | 389 ---------------------- examples/stm32f7/src/bin/hash.rs | 10 +- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/hash.rs | 30 +- tests/stm32/src/common.rs | 7 - 7 files changed, 565 insertions(+), 823 deletions(-) delete mode 100644 embassy-stm32/src/hash/v1v3v4.rs delete mode 100644 embassy-stm32/src/hash/v2.rs diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 64c1a0a8c..f0c2c839a 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -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 { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + 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

+ 'd, + dma: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + '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, + { + 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, + { + // 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, + { + // 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

+ 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); diff --git a/embassy-stm32/src/hash/v1v3v4.rs b/embassy-stm32/src/hash/v1v3v4.rs deleted file mode 100644 index 771144b11..000000000 --- a/embassy-stm32/src/hash/v1v3v4.rs +++ /dev/null @@ -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 { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - 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

+ 'd, - _irq: impl interrupt::typelevel::Binding> + '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

+ 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); diff --git a/embassy-stm32/src/hash/v2.rs b/embassy-stm32/src/hash/v2.rs deleted file mode 100644 index b8104c825..000000000 --- a/embassy-stm32/src/hash/v2.rs +++ /dev/null @@ -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 { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - 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> { - _peripheral: PeripheralRef<'d, T>, - dma: PeripheralRef<'d, D>, -} - -impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { - /// Instantiates, resets, and enables the HASH peripheral. - pub fn new( - peripheral: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + '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

+ 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); diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 7d96bd49c..31f8d32a7 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -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 {} } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d02f1a253..fc4420687 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -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"] diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 2867115dc..cfcf3d976 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -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(); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 14d5b6d7b..fefe72c86 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -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;}, ); #[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;}, ); #[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;}, @@ -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;}, @@ -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;}, @@ -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;}, From 7bf044278e8c85b5ea6dbe3de6b3cdea884c995a Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:47:38 -0500 Subject: [PATCH 24/68] fmt --- examples/stm32f7/src/bin/hash.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 31f8d32a7..96e50f84b 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,7 +3,8 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::{bind_interrupts, hash, hash::*, peripherals, Config}; +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 _}; From 8f7d80f9f71e55ffe97b72dbb361409aee003ea4 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Sun, 11 Feb 2024 19:37:48 +0100 Subject: [PATCH 25/68] Revert "feat(boot): introduce non-erase flash write method " This reverts commit 2e8b7d259057a0cf345396b250cacfc04a0e64a0. --- embassy-boot/src/firmware_updater/blocking.rs | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 3e83366af..4044871f0 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -225,41 +225,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> Ok(()) } - /// Write data directly to a flash page without erasing it first. - /// - /// This function writes the provided data to the specified offset in the flash memory, - /// without performing an erase operation beforehand. It is crucial that the area being - /// written to is either already erased. - /// This method is intended to be used in conjunction with the `prepare_update` method. - /// - /// The buffer must follow the alignment requirements of the target flash and be a multiple of - /// the page size. This is essential to ensure data integrity and prevent corruption. - /// - /// # Safety - /// - /// This function requires careful management of the memory being written to. Writing to a - /// non-erased page or not adhering to alignment and size requirements may result in a panic. - /// - /// Ensure that the data being written is compatible with the current contents of the flash - /// memory, as no erase operation will be performed to reset the page content to a default state. - /// - /// # Parameters - /// - /// - `offset`: The offset within the DFU partition where the data will be written. Must be - /// aligned according to the flash's requirements and within the writable memory range. - /// - `data`: A reference to the slice of bytes to be written. The length of the data must not - /// exceed the partition size and must follow the flash's alignment requirements. - /// - /// # Returns - /// - /// A result indicating the success or failure of the write operation. On success, returns `Ok(())`. - /// On failure, returns an `Err` with a `FirmwareUpdaterError` detailing the cause of the failure. - pub fn write_firmware_without_erase(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { - self.dfu.write(offset as u32, data)?; - - Ok(()) - } - /// Prepare for an incoming DFU update by erasing the entire DFU area and /// returning its `Partition`. /// From 72ab04c45335ceb725076a38a252749e781a8cf6 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Sun, 11 Feb 2024 19:38:01 +0100 Subject: [PATCH 26/68] Revert "feat(usb-dfu): change usb dfu chunks write mechanism " This reverts commit c95bf6895adfcd33b5238c02620e83c6713205ce. --- embassy-usb-dfu/src/dfu.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index 5f2c98684..e99aa70c3 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -60,21 +60,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha } Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => { if req.value == 0 { - match self.updater.prepare_update() { - Ok(_) => { - self.status = Status::Ok; - } - Err(e) => { - self.state = State::Error; - match e { - embassy_boot::FirmwareUpdaterError::Flash(e) => match e { - NorFlashErrorKind::NotAligned => self.status = Status::ErrErase, - _ => self.status = Status::ErrUnknown, - }, - _ => self.status = Status::ErrUnknown, - } - } - } self.state = State::Download; self.offset = 0; } @@ -108,7 +93,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha self.state = State::Error; return Some(OutResponse::Rejected); } - match self.updater.write_firmware_without_erase(self.offset, buf.as_ref()) { + match self.updater.write_firmware(self.offset, buf.as_ref()) { Ok(_) => { self.status = Status::Ok; self.state = State::DlSync; From eb3bd39b068ae34892520ec38f111704ea357355 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Sun, 11 Feb 2024 20:02:28 +0100 Subject: [PATCH 27/68] feat(boot): enhance firmware write functionality --- embassy-boot/src/firmware_updater/asynch.rs | 71 ++++++++++++++++--- embassy-boot/src/firmware_updater/blocking.rs | 70 +++++++++++++++--- 2 files changed, 124 insertions(+), 17 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 668f16f16..99a3aa246 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -172,21 +172,69 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { self.state.mark_booted().await } - /// Write data to a flash page. + /// Writes firmware data to the device. /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// This function writes the given data to the firmware area starting at the specified offset. + /// It handles sector erasures and data writes while verifying the device is in a proper state + /// for firmware updates. The function ensures that only unerased sectors are erased before + /// writing and efficiently handles the writing process across sector boundaries and in + /// various configurations (data size, page size, etc.). /// - /// # Safety + /// # Arguments /// - /// Failing to meet alignment and size requirements may result in a panic. + /// * `offset` - The starting offset within the firmware area where data writing should begin. + /// * `data` - A slice of bytes representing the firmware data to be written. It must be a + /// multiple of NorFlash WRITE_SIZE. + /// + /// # Returns + /// + /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. + /// + /// # Errors + /// + /// This function will return an error if: + /// + /// - The device is not in a proper state to receive firmware updates (e.g., not booted). + /// - There is a failure erasing a sector before writing. + /// - There is a failure writing data to the device. pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= DFU::ERASE_SIZE); - + // Make sure we are running a booted firmware to avoid reverting to a bad state. self.state.verify_booted().await?; - self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; + // Initialize variables to keep track of the remaining data and the current offset. + let mut remaining_data = data; + let mut offset = offset; - self.dfu.write(offset as u32, data).await?; + // Continue writing as long as there is data left to write. + while !remaining_data.is_empty() { + // Compute the current sector and its boundaries. + let current_sector = offset / DFU::ERASE_SIZE; + let sector_start = current_sector * DFU::ERASE_SIZE; + let sector_end = sector_start + DFU::ERASE_SIZE; + // Determine if the current sector needs to be erased before writing. + let need_erase = self + .state + .last_erased_dfu_page_index + .map_or(true, |last_erased_sector| current_sector != last_erased_sector); + + // If the sector needs to be erased, erase it and update the last erased sector index. + if need_erase { + self.dfu.erase(sector_start as u32, sector_end as u32).await?; + self.state.last_erased_dfu_page_index = Some(current_sector); + } + + // Calculate the size of the data chunk that can be written in the current iteration. + let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); + // Split the data to get the current chunk to be written and the remaining data. + let (data_chunk, rest) = remaining_data.split_at(write_size); + + // Write the current data chunk. + self.dfu.write(offset as u32, data_chunk).await?; + + // Update the offset and remaining data for the next iteration. + remaining_data = rest; + offset += write_size; + } Ok(()) } @@ -210,6 +258,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { pub struct FirmwareState<'d, STATE> { state: STATE, aligned: &'d mut [u8], + last_erased_dfu_page_index: Option, } impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { @@ -231,7 +280,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { /// and follow the alignment rules for the flash being read from and written to. pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE)); - Self { state, aligned } + Self { + state, + aligned, + last_erased_dfu_page_index: None, + } } // Make sure we are running a booted firmware to avoid reverting to a bad state. diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 4044871f0..45ae966f3 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -207,20 +207,69 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> self.state.mark_booted() } - /// Write data to a flash page. + /// Writes firmware data to the device. /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// This function writes the given data to the firmware area starting at the specified offset. + /// It handles sector erasures and data writes while verifying the device is in a proper state + /// for firmware updates. The function ensures that only unerased sectors are erased before + /// writing and efficiently handles the writing process across sector boundaries and in + /// various configurations (data size, page size, etc.). /// - /// # Safety + /// # Arguments /// - /// Failing to meet alignment and size requirements may result in a panic. + /// * `offset` - The starting offset within the firmware area where data writing should begin. + /// * `data` - A slice of bytes representing the firmware data to be written. It must be a + /// multiple of NorFlash WRITE_SIZE. + /// + /// # Returns + /// + /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation. + /// + /// # Errors + /// + /// This function will return an error if: + /// + /// - The device is not in a proper state to receive firmware updates (e.g., not booted). + /// - There is a failure erasing a sector before writing. + /// - There is a failure writing data to the device. pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= DFU::ERASE_SIZE); + // Make sure we are running a booted firmware to avoid reverting to a bad state. self.state.verify_booted()?; - self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; + // Initialize variables to keep track of the remaining data and the current offset. + let mut remaining_data = data; + let mut offset = offset; - self.dfu.write(offset as u32, data)?; + // Continue writing as long as there is data left to write. + while !remaining_data.is_empty() { + // Compute the current sector and its boundaries. + let current_sector = offset / DFU::ERASE_SIZE; + let sector_start = current_sector * DFU::ERASE_SIZE; + let sector_end = sector_start + DFU::ERASE_SIZE; + // Determine if the current sector needs to be erased before writing. + let need_erase = self + .state + .last_erased_dfu_page_index + .map_or(true, |last_erased_sector| current_sector != last_erased_sector); + + // If the sector needs to be erased, erase it and update the last erased sector index. + if need_erase { + self.dfu.erase(sector_start as u32, sector_end as u32)?; + self.state.last_erased_dfu_page_index = Some(current_sector); + } + + // Calculate the size of the data chunk that can be written in the current iteration. + let write_size = core::cmp::min(remaining_data.len(), sector_end - offset); + // Split the data to get the current chunk to be written and the remaining data. + let (data_chunk, rest) = remaining_data.split_at(write_size); + + // Write the current data chunk. + self.dfu.write(offset as u32, data_chunk)?; + + // Update the offset and remaining data for the next iteration. + remaining_data = rest; + offset += write_size; + } Ok(()) } @@ -244,6 +293,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> pub struct BlockingFirmwareState<'d, STATE> { state: STATE, aligned: &'d mut [u8], + last_erased_dfu_page_index: Option, } impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { @@ -265,7 +315,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { /// and written to. pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { assert_eq!(aligned.len(), STATE::WRITE_SIZE); - Self { state, aligned } + Self { + state, + aligned, + last_erased_dfu_page_index: None, + } } // Make sure we are running a booted firmware to avoid reverting to a bad state. From 333b2afe6d5319317b2679e634c3cf242bab0e73 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Sun, 11 Feb 2024 20:17:15 +0100 Subject: [PATCH 28/68] test(boot): add various write firmware test configurations --- embassy-boot/src/firmware_updater/asynch.rs | 72 +++++++++++++++++ embassy-boot/src/firmware_updater/blocking.rs | 78 +++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 99a3aa246..d31eff005 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -382,4 +382,76 @@ mod tests { assert_eq!(Sha1::digest(update).as_slice(), hash); } + + #[test] + fn can_verify_sha1_page_bigger_than_chunk() { + let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); + let state = Partition::new(&flash, 0, 4096); + let dfu = Partition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(1024) { + block_on(updater.write_firmware(offset, chunk)).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } + + #[test] + fn can_verify_sha1_page_smaller_than_chunk() { + let flash = Mutex::::new(MemFlash::<131072, 1024, 8>::default()); + let state = Partition::new(&flash, 0, 4096); + let dfu = Partition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(2048) { + block_on(updater.write_firmware(offset, chunk)).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } + + #[test] + fn can_verify_sha1_cross_page_boundary() { + let flash = Mutex::::new(MemFlash::<131072, 1024, 8>::default()); + let state = Partition::new(&flash, 0, 4096); + let dfu = Partition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(896) { + block_on(updater.write_firmware(offset, chunk)).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } } diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 45ae966f3..5b8076f81 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -422,4 +422,82 @@ mod tests { assert_eq!(Sha1::digest(update).as_slice(), hash); } + + #[test] + fn can_verify_sha1_page_bigger_than_chunk() { + let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); + let state = BlockingPartition::new(&flash, 0, 4096); + let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(1024) { + updater.write_firmware(offset, chunk).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + updater + .hash::(update.len() as u32, &mut chunk_buf, &mut hash) + .unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } + + #[test] + fn can_verify_sha1_page_smaller_than_chunk() { + let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); + let state = BlockingPartition::new(&flash, 0, 4096); + let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(2048) { + updater.write_firmware(offset, chunk).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + updater + .hash::(update.len() as u32, &mut chunk_buf, &mut hash) + .unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } + + #[test] + fn can_verify_sha1_cross_page_boundary() { + let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); + let state = BlockingPartition::new(&flash, 0, 4096); + let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + let mut offset = 0; + for chunk in to_write.chunks(896) { + updater.write_firmware(offset, chunk).unwrap(); + offset += chunk.len(); + } + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + updater + .hash::(update.len() as u32, &mut chunk_buf, &mut hash) + .unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } } From 0dc5e6d3e4646fd8f67840f32a756d55ac36569a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Feb 2024 02:17:33 +0100 Subject: [PATCH 29/68] stm32/rcc: port F3 RCC to new API See #2515 --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/f3.rs | 611 ++++++++++--------------- embassy-stm32/src/rcc/mco.rs | 24 +- examples/stm32f3/src/bin/hello.rs | 5 +- examples/stm32f3/src/bin/usb_serial.rs | 21 +- examples/stm32f334/src/bin/adc.rs | 24 +- examples/stm32f334/src/bin/hello.rs | 5 +- examples/stm32f334/src/bin/opamp.rs | 24 +- examples/stm32f334/src/bin/pwm.rs | 27 +- tests/stm32/src/common.rs | 18 + 10 files changed, 351 insertions(+), 412 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3f5f12f06..24af17327 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8ae5bb5fe696a7e61fb41b8b797372aed8103a82" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8ae5bb5fe696a7e61fb41b8b797372aed8103a82", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 25866e446..0a5e67b4a 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -1,208 +1,230 @@ -#[cfg(rcc_f3)] -use crate::pac::adccommon::vals::Ckmode; use crate::pac::flash::vals::Latency; -pub use crate::pac::rcc::vals::Adcpres; -use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre}; +pub use crate::pac::rcc::vals::{ + Adcpres as AdcPllPrescaler, Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Prediv as PllPreDiv, + Sw as Sysclk, +}; +use crate::pac::rcc::vals::{Pllsrc, Usbpre}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(8_000_000); -#[cfg(rcc_f3)] -impl From for Ckmode { - fn from(value: AdcClockSource) -> Self { - match value { - AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1, - AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2, - AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4, - _ => unreachable!(), - } - } +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum PllSource { + HSE, + HSI, +} + +#[derive(Clone, Copy)] +pub struct Pll { + pub src: PllSource, + + /// PLL pre-divider. + /// + /// On some F3 chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. + pub prediv: PllPreDiv, + + /// PLL multiplication factor. + pub mul: PllMul, } #[derive(Clone, Copy)] pub enum AdcClockSource { - Pll(Adcpres), - BusDiv1, - BusDiv2, - BusDiv4, + Pll(AdcPllPrescaler), + Hclk(AdcHclkPrescaler), } -impl AdcClockSource { - pub fn bus_div(&self) -> u32 { - match self { - Self::BusDiv1 => 1, - Self::BusDiv2 => 2, - Self::BusDiv4 => 4, - _ => unreachable!(), - } - } +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AdcHclkPrescaler { + Div1, + Div2, + Div4, } -#[derive(Default)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum HrtimClockSource { - #[default] BusClk, PllClk, } /// Clocks configutation #[non_exhaustive] -#[derive(Default)] pub struct Config { - /// Frequency of HSE oscillator - /// 4MHz to 32MHz - pub hse: Option, - /// Bypass HSE for an external clock - pub bypass_hse: bool, - /// Frequency of the System Clock - pub sysclk: Option, - /// Frequency of AHB bus - pub hclk: Option, - /// Frequency of APB1 bus - /// - Max frequency 36MHz - pub pclk1: Option, - /// Frequency of APB2 bus - /// - Max frequency with HSE is 72MHz - /// - Max frequency without HSE is 64MHz - pub pclk2: Option, - /// USB clock setup - /// It is valid only when, - /// - HSE is enabled, - /// - The System clock frequency is either 48MHz or 72MHz - /// - APB1 clock has a minimum frequency of 10MHz - pub pll48: bool, - #[cfg(rcc_f3)] - /// ADC clock setup - /// - For AHB, a psc of 4 or less must be used - pub adc: Option, - #[cfg(rcc_f3)] - /// ADC clock setup - /// - For AHB, a psc of 4 or less must be used - pub adc34: Option, + pub hsi: bool, + pub hse: Option, + pub sys: Sysclk, + + pub pll: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + + #[cfg(not(rcc_f37))] + pub adc: AdcClockSource, + #[cfg(all(not(rcc_f37), adc3_common))] + pub adc34: AdcClockSource, #[cfg(stm32f334)] pub hrtim: HrtimClockSource, + pub ls: super::LsConfig, } -// Information required to setup the PLL clock -#[derive(Clone, Copy)] -struct PllConfig { - pll_src: Pllsrc, - pll_mul: Pllmul, - pll_div: Option, +impl Default for Config { + fn default() -> Self { + Self { + hsi: true, + hse: None, + sys: Sysclk::HSI, + pll: None, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + ls: Default::default(), + + #[cfg(not(rcc_f37))] + adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), + #[cfg(all(not(rcc_f37), adc3_common))] + adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), + #[cfg(stm32f334)] + hrtim: HrtimClockSource::BusClk, + } + } } /// Initialize and Set the clock frequencies pub(crate) unsafe fn init(config: Config) { - // Calculate the real System clock, and PLL configuration if applicable - let (sysclk, pll_config) = get_sysclk(&config); - assert!(sysclk.0 <= 72_000_000); - - // Calculate real AHB clock - let hclk = config.hclk.map(|h| h).unwrap_or(sysclk); - let hpre = match sysclk.0 / hclk.0 { - 0 => unreachable!(), - 1 => Hpre::DIV1, - 2 => Hpre::DIV2, - 3..=5 => Hpre::DIV4, - 6..=11 => Hpre::DIV8, - 12..=39 => Hpre::DIV16, - 40..=95 => Hpre::DIV64, - 96..=191 => Hpre::DIV128, - 192..=383 => Hpre::DIV256, - _ => Hpre::DIV512, + // Configure HSI + let hsi = match config.hsi { + false => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + true => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + Some(HSI_FREQ) + } }; - let hclk = sysclk / hpre; - assert!(hclk <= Hertz(72_000_000)); - // Calculate real APB1 clock - let pclk1 = config.pclk1.unwrap_or(hclk); - let ppre1 = match hclk / pclk1 { - 0 => unreachable!(), - 1 => Ppre::DIV1, - 2 => Ppre::DIV2, - 3..=5 => Ppre::DIV4, - 6..=11 => Ppre::DIV8, - _ => Ppre::DIV16, - }; - let timer_mul1 = if ppre1 == Ppre::DIV1 { 1u32 } else { 2 }; - let pclk1 = hclk / ppre1; - assert!(pclk1 <= Hertz(36_000_000)); + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } - // Calculate real APB2 clock - let pclk2 = config.pclk2.unwrap_or(hclk); - let ppre2 = match hclk / pclk2 { - 0 => unreachable!(), - 1 => Ppre::DIV1, - 2 => Ppre::DIV2, - 3..=5 => Ppre::DIV4, - 6..=11 => Ppre::DIV8, - _ => Ppre::DIV16, + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } }; - let timer_mul2 = if ppre2 == Ppre::DIV1 { 1u32 } else { 2 }; - let pclk2 = hclk / ppre2; - assert!(pclk2 <= Hertz(72_000_000)); + + // Enable PLL + // RM0316: "Reserved, must be kept at reset value." + let pll = config.pll.map(|pll| { + let (src_val, src_freq) = match pll.src { + #[cfg(rcc_f3v3)] + PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)), + #[cfg(not(rcc_f3v3))] + PllSource::HSI => { + if pll.prediv != PllPreDiv::DIV2 { + panic!("if PLL source is HSI, PLL prediv must be 2."); + } + (Pllsrc::HSI_DIV2, unwrap!(hsi)) + } + PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), + }; + let in_freq = src_freq / pll.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let out_freq = in_freq * pll.mul; + assert!(max::PLL_OUT.contains(&out_freq)); + + RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); + RCC.cfgr().modify(|w| { + w.set_pllmul(pll.mul); + w.set_pllsrc(src_val); + }); + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + + out_freq + }); + + let usb = match pll { + Some(Hertz(72_000_000)) => { + RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1_5)); + Some(Hertz(48_000_000)) + } + Some(Hertz(48_000_000)) => { + RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1)); + Some(Hertz(48_000_000)) + } + _ => None, + }; + + // Configure sysclk + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_P => unwrap!(pll), + _ => unreachable!(), + }; + + let hclk = sys / config.ahb_pre; + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); + + assert!(max::HCLK.contains(&hclk)); + assert!(max::PCLK1.contains(&pclk1)); + assert!(max::PCLK2.contains(&pclk2)); // Set latency based on HCLK frquency - // RM0316: "The prefetch buffer must be kept on when using a prescaler - // different from 1 on the AHB clock.", "Half-cycle access cannot be - // used when there is a prescaler different from 1 on the AHB clock" + let latency = match hclk.0 { + ..=24_000_000 => Latency::WS0, + ..=48_000_000 => Latency::WS1, + _ => Latency::WS2, + }; FLASH.acr().modify(|w| { - w.set_latency(if hclk <= Hertz(24_000_000) { - Latency::WS0 - } else if hclk <= Hertz(48_000_000) { - Latency::WS1 - } else { - Latency::WS2 - }); - if hpre != Hpre::DIV1 { + w.set_latency(latency); + // RM0316: "The prefetch buffer must be kept on when using a prescaler + // different from 1 on the AHB clock.", "Half-cycle access cannot be + // used when there is a prescaler different from 1 on the AHB clock" + if config.ahb_pre != AHBPrescaler::DIV1 { w.set_hlfcya(false); w.set_prftbe(true); } }); - // Enable HSE - // RM0316: "Bits 31:26 Reserved, must be kept at reset value." - if config.hse.is_some() { - RCC.cr().modify(|w| { - w.set_hsebyp(config.bypass_hse); - // We turn on clock security to switch to HSI when HSE fails - w.set_csson(true); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - } - - // Enable PLL - // RM0316: "Reserved, must be kept at reset value." - if let Some(ref pll_config) = pll_config { - RCC.cfgr().modify(|w| { - w.set_pllmul(pll_config.pll_mul); - w.set_pllsrc(pll_config.pll_src); - }); - if let Some(pll_div) = pll_config.pll_div { - RCC.cfgr2().modify(|w| w.set_prediv(pll_div)); - } - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - } - - // CFGR has been written before (PLL) don't overwrite these settings - if config.pll48 { - let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config); - RCC.cfgr().modify(|w| { - w.set_usbpre(usb_pre); - }); - } - // Set prescalers // CFGR has been written before (PLL, PLL48) don't overwrite these settings RCC.cfgr().modify(|w| { - w.set_ppre2(ppre2); - w.set_ppre1(ppre1); - w.set_hpre(hpre); + w.set_ppre2(config.apb1_pre); + w.set_ppre1(config.apb2_pre); + w.set_hpre(config.ahb_pre); }); // Wait for the new prescalers to kick in @@ -211,53 +233,60 @@ pub(crate) unsafe fn init(config: Config) { cortex_m::asm::delay(16); // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings - RCC.cfgr().modify(|w| { - w.set_sw(match (pll_config, config.hse) { - (Some(_), _) => Sw::PLL1_P, - (None, Some(_)) => Sw::HSE, - (None, None) => Sw::HSI, - }) - }); + RCC.cfgr().modify(|w| w.set_sw(config.sys)); - #[cfg(rcc_f3)] - let adc = config.adc.map(|adc| match adc { + let rtc = config.ls.init(); + + #[cfg(not(rcc_f37))] + use crate::pac::adccommon::vals::Ckmode; + + #[cfg(not(rcc_f37))] + let adc = match config.adc { AdcClockSource::Pll(adcpres) => { - RCC.cfgr2().modify(|w| { - // Make sure that we're using the PLL - pll_config.unwrap(); - w.set_adc12pres(adcpres); + RCC.cfgr2().modify(|w| w.set_adc12pres(adcpres)); + crate::pac::ADC_COMMON + .ccr() + .modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS)); - sysclk / adcpres - }) + unwrap!(pll) / adcpres } - _ => crate::pac::ADC_COMMON.ccr().modify(|w| { - assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1)); + AdcClockSource::Hclk(adcpres) => { + assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); - w.set_ckmode(adc.into()); + let (div, ckmode) = match adcpres { + AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1), + AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2), + AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4), + }; + crate::pac::ADC_COMMON.ccr().modify(|w| w.set_ckmode(ckmode)); - sysclk / adc.bus_div() - }), - }); + hclk / div + } + }; - #[cfg(all(rcc_f3, adc3_common))] - let adc34 = config.adc34.map(|adc| match adc { + #[cfg(all(not(rcc_f37), adc3_common))] + let adc34 = match config.adc34 { AdcClockSource::Pll(adcpres) => { - RCC.cfgr2().modify(|w| { - // Make sure that we're using the PLL - pll_config.unwrap(); - w.set_adc34pres(adcpres); + RCC.cfgr2().modify(|w| w.set_adc34pres(adcpres)); + crate::pac::ADC3_COMMON + .ccr() + .modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS)); - sysclk / adcpres - }) + unwrap!(pll) / adcpres } - _ => crate::pac::ADC_COMMON.ccr().modify(|w| { - assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1)); + AdcClockSource::Hclk(adcpres) => { + assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); - w.set_ckmode(adc.into()); + let (div, ckmode) = match adcpres { + AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1), + AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2), + AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4), + }; + crate::pac::ADC3_COMMON.ccr().modify(|w| w.set_ckmode(ckmode)); - sysclk / adc.bus_div() - }), - }); + hclk / div + } + }; #[cfg(stm32f334)] let hrtim = match config.hrtim { @@ -267,195 +296,49 @@ pub(crate) unsafe fn init(config: Config) { use crate::pac::rcc::vals::Timsw; // Make sure that we're using the PLL - pll_config.unwrap(); - assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk)); + let pll = unwrap!(pll); + assert!((pclk2 == pll) || (pclk2 * 2u32 == pll)); RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P)); - Some(sysclk * 2u32) + Some(pll * 2u32) } }; - let rtc = config.ls.init(); - set_clocks!( - hsi: None, - lse: None, - pll1_p: None, - sys: Some(sysclk), + hsi: hsi, + hse: hse, + pll1_p: pll, + sys: Some(sys), pclk1: Some(pclk1), pclk2: Some(pclk2), - pclk1_tim: Some(pclk1 * timer_mul1), - pclk2_tim: Some(pclk2 * timer_mul2), + pclk1_tim: Some(pclk1_tim), + pclk2_tim: Some(pclk2_tim), hclk1: Some(hclk), - #[cfg(rcc_f3)] - adc: adc, - #[cfg(all(rcc_f3, adc3_common))] - adc34: adc34, - #[cfg(all(rcc_f3, not(adc3_common)))] - adc34: None, + #[cfg(not(rcc_f37))] + adc: Some(adc), + #[cfg(all(not(rcc_f37), adc3_common))] + adc34: Some(adc34), #[cfg(stm32f334)] hrtim: hrtim, rtc: rtc, + usb: usb, + lse: None, ); } -#[inline] -fn get_sysclk(config: &Config) -> (Hertz, Option) { - match (config.sysclk, config.hse) { - (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None), - (Some(sysclk), None) if sysclk == HSI_FREQ => (HSI_FREQ, None), - // If the user selected System clock is different from HSI or HSE - // we will have to setup PLL clock source - (Some(sysclk), _) => { - let (sysclk, pll_config) = calc_pll(config, sysclk); - (sysclk, Some(pll_config)) - } - (None, Some(hse)) => (hse, None), - (None, None) => (HSI_FREQ, None), - } -} +mod max { + use core::ops::RangeInclusive; -#[inline] -fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { - // Calculates the Multiplier and the Divisor to arrive at - // the required System clock from PLL source frequency - let get_mul_div = |sysclk, pllsrcclk| { - let bus_div = gcd(sysclk, pllsrcclk); - let mut multiplier = sysclk / bus_div; - let mut divisor = pllsrcclk / bus_div; - // Minimum PLL multiplier is two - if multiplier == 1 { - multiplier *= 2; - divisor *= 2; - } - assert!(multiplier <= 16); - assert!(divisor <= 16); - (multiplier, divisor) - }; - // Based on the source of Pll, we calculate the actual system clock - // frequency, PLL's source identifier, multiplier and divisor - let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse { - Some(Hertz(hse)) => { - let (multiplier, divisor) = get_mul_div(sysclk, hse); - ( - Hertz((hse / divisor) * multiplier), - Pllsrc::HSE_DIV_PREDIV, - into_pll_mul(multiplier), - Some(into_pre_div(divisor)), - ) - } - None => { - cfg_if::cfg_if! { - // For some chips PREDIV is always two, and cannot be changed - if #[cfg(any(flashsize_d, flashsize_e))] { - let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0); - ( - Hertz((HSI_FREQ.0 / divisor) * multiplier), - Pllsrc::HSI_DIV_PREDIV, - into_pll_mul(multiplier), - Some(into_pre_div(divisor)), - ) - } else { - let pllsrcclk = HSI_FREQ.0 / 2; - let multiplier = sysclk / pllsrcclk; - assert!(multiplier <= 16); - ( - Hertz(pllsrcclk * multiplier), - Pllsrc::HSI_DIV2, - into_pll_mul(multiplier), - None, - ) - } - } - } - }; - ( - act_sysclk, - PllConfig { - pll_src, - pll_mul, - pll_div, - }, - ) -} + use crate::time::Hertz; -#[inline] -#[allow(unused_variables)] -fn get_usb_pre(config: &Config, sysclk: Hertz, pclk1: Hertz, pll_config: &Option) -> Usbpre { - cfg_if::cfg_if! { - // Some chips do not have USB - if #[cfg(any(stm32f301, stm32f318, stm32f334))] { - panic!("USB clock not supported by the chip"); - } else { - let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= Hertz(10_000_000)); - match (usb_ok, sysclk) { - (true, Hertz(72_000_000)) => Usbpre::DIV1_5, - (true, Hertz(48_000_000)) => Usbpre::DIV1, - _ => panic!( - "USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz" - ), - } - } - } -} + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(32_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(32_000_000); -// This function assumes cases when multiplier is one and it -// being greater than 16 is made impossible -#[inline] -fn into_pll_mul(multiplier: u32) -> Pllmul { - match multiplier { - 2 => Pllmul::MUL2, - 3 => Pllmul::MUL3, - 4 => Pllmul::MUL4, - 5 => Pllmul::MUL5, - 6 => Pllmul::MUL6, - 7 => Pllmul::MUL7, - 8 => Pllmul::MUL8, - 9 => Pllmul::MUL9, - 10 => Pllmul::MUL10, - 11 => Pllmul::MUL11, - 12 => Pllmul::MUL12, - 13 => Pllmul::MUL13, - 14 => Pllmul::MUL14, - 15 => Pllmul::MUL15, - 16 => Pllmul::MUL16, - _ => unreachable!(), - } -} + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(72_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(36_000_000); + pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(72_000_000); -// This function assumes the incoming divisor cannot be greater -// than 16 -#[inline] -fn into_pre_div(divisor: u32) -> Prediv { - match divisor { - 1 => Prediv::DIV1, - 2 => Prediv::DIV2, - 3 => Prediv::DIV3, - 4 => Prediv::DIV4, - 5 => Prediv::DIV5, - 6 => Prediv::DIV6, - 7 => Prediv::DIV7, - 8 => Prediv::DIV8, - 9 => Prediv::DIV9, - 10 => Prediv::DIV10, - 11 => Prediv::DIV11, - 12 => Prediv::DIV12, - 13 => Prediv::DIV13, - 14 => Prediv::DIV14, - 15 => Prediv::DIV15, - 16 => Prediv::DIV16, - _ => unreachable!(), - } -} - -// Determine GCD using Euclidean algorithm -#[inline] -fn gcd(mut a: u32, mut b: u32) -> u32 { - while b != 0 { - let r = a % b; - a = b; - b = r; - } - a + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(24_000_000); + pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(72_000_000); } diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index eaaf8071c..db0df9fac 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -4,7 +4,7 @@ use embassy_hal_internal::into_ref; use crate::gpio::sealed::AFType; use crate::gpio::Speed; -#[cfg(not(stm32f1))] +#[cfg(not(any(stm32f1, rcc_f3v1, rcc_f37)))] pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; #[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))] pub use crate::pac::rcc::vals::Mcosel as McoSource; @@ -13,10 +13,16 @@ pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; use crate::pac::RCC; use crate::{peripherals, Peripheral}; +#[cfg(any(stm32f1, rcc_f3v1, rcc_f37))] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum McoPrescaler { + DIV1, +} + pub(crate) mod sealed { pub trait McoInstance { type Source; - unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: super::McoPrescaler); + unsafe fn apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler); } } @@ -29,7 +35,7 @@ macro_rules! impl_peri { impl sealed::McoInstance for peripherals::$peri { type Source = $source; - unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: McoPrescaler) { + unsafe fn apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { #[cfg(not(any(stm32u5, stm32wba)))] let r = RCC.cfgr(); #[cfg(any(stm32u5, stm32wba))] @@ -37,8 +43,8 @@ macro_rules! impl_peri { r.modify(|w| { w.$set_source(source); - #[cfg(not(stm32f1))] - w.$set_prescaler(prescaler); + #[cfg(not(any(stm32f1, rcc_f3v1, rcc_f37)))] + w.$set_prescaler(_prescaler); }); } } @@ -68,16 +74,12 @@ impl<'d, T: McoInstance> Mco<'d, T> { _peri: impl Peripheral

+ 'd, pin: impl Peripheral

> + 'd, source: T::Source, - #[cfg(not(stm32f1))] prescaler: McoPrescaler, + prescaler: McoPrescaler, ) -> Self { into_ref!(pin); critical_section::with(|_| unsafe { - T::apply_clock_settings( - source, - #[cfg(not(stm32f1))] - prescaler, - ); + T::apply_clock_settings(source, prescaler); pin.set_as_af(pin.af_num(), AFType::OutputPushPull); pin.set_speed(Speed::VeryHigh); }); diff --git a/examples/stm32f3/src/bin/hello.rs b/examples/stm32f3/src/bin/hello.rs index fd54da53d..3c295612c 100644 --- a/examples/stm32f3/src/bin/hello.rs +++ b/examples/stm32f3/src/bin/hello.rs @@ -3,16 +3,13 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - let mut config = Config::default(); - config.rcc.hse = Some(Hertz(8_000_000)); - config.rcc.sysclk = Some(Hertz(16_000_000)); + let config = Config::default(); let _p = embassy_stm32::init(config); loop { diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index cf9ecedfa..ee1c43afd 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -21,11 +21,22 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.hse = Some(mhz(8)); - config.rcc.sysclk = Some(mhz(48)); - config.rcc.pclk1 = Some(mhz(24)); - config.rcc.pclk2 = Some(mhz(24)); - config.rcc.pll48 = true; + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: mhz(8), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs index 063ee9dac..a9fb7f1a6 100644 --- a/examples/stm32f334/src/bin/adc.rs +++ b/examples/stm32f334/src/bin/adc.rs @@ -5,7 +5,6 @@ use defmt::info; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::peripherals::ADC1; -use embassy_stm32::rcc::{AdcClockSource, Adcpres}; use embassy_stm32::time::mhz; use embassy_stm32::{adc, bind_interrupts, Config}; use embassy_time::{Delay, Timer}; @@ -18,12 +17,23 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sysclk = Some(mhz(64)); - config.rcc.hclk = Some(mhz(64)); - config.rcc.pclk1 = Some(mhz(32)); - config.rcc.pclk2 = Some(mhz(64)); - config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1)); - + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: mhz(8), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1); + } let mut p = embassy_stm32::init(config); info!("create adc..."); diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs index fd54da53d..3c295612c 100644 --- a/examples/stm32f334/src/bin/hello.rs +++ b/examples/stm32f334/src/bin/hello.rs @@ -3,16 +3,13 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - let mut config = Config::default(); - config.rcc.hse = Some(Hertz(8_000_000)); - config.rcc.sysclk = Some(Hertz(16_000_000)); + let config = Config::default(); let _p = embassy_stm32::init(config); loop { diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index 850a0e335..6f25191be 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::opamp::{OpAmp, OpAmpGain}; use embassy_stm32::peripherals::ADC2; -use embassy_stm32::rcc::{AdcClockSource, Adcpres}; use embassy_stm32::time::mhz; use embassy_stm32::{adc, bind_interrupts, Config}; use embassy_time::{Delay, Timer}; @@ -19,12 +18,23 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sysclk = Some(mhz(64)); - config.rcc.hclk = Some(mhz(64)); - config.rcc.pclk1 = Some(mhz(32)); - config.rcc.pclk2 = Some(mhz(64)); - config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1)); - + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: mhz(8), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1); + } let mut p = embassy_stm32::init(config); info!("create adc..."); diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index c149cad92..7fc1ea926 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::hrtim::*; -use embassy_stm32::rcc::HrtimClockSource; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::Config; use embassy_time::Timer; @@ -12,14 +11,26 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let mut config: Config = Default::default(); - config.rcc.sysclk = Some(mhz(64)); - config.rcc.hclk = Some(mhz(64)); - config.rcc.pclk1 = Some(mhz(32)); - config.rcc.pclk2 = Some(mhz(64)); - config.rcc.hrtim = HrtimClockSource::PllClk; - + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: mhz(8), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.hrtim = HrtimClockSource::PllClk; + } let p = embassy_stm32::init(config); + info!("Hello World!"); let ch1 = PwmPin::new_cha(p.PA8); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index fefe72c86..36fe8a235 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -276,6 +276,24 @@ pub fn config() -> Config { config.rcc.apb2_pre = APBPrescaler::DIV2; } + #[cfg(feature = "stm32f303ze")] + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + } + #[cfg(feature = "stm32f429zi")] { use embassy_stm32::rcc::*; From 16ed0b1e37a6106596efb2f3fa26344d550fc1ff Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Mon, 12 Feb 2024 19:01:22 +0100 Subject: [PATCH 30/68] Move usb clas loop to private function Move const to the outside of the logger --- embassy-usb-logger/src/lib.rs | 75 +++++++++++++++-------------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index a057fcf32..da5ff0f36 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -6,7 +6,7 @@ use core::fmt::Write as _; use embassy_futures::join::join; use embassy_sync::pipe::Pipe; -use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::Driver; use embassy_usb::{Builder, Config}; use log::{Metadata, Record}; @@ -37,6 +37,9 @@ impl<'d> LoggerState<'d> { } } +/// The packet size used in the usb logger, to be used with `create_future_from_class` +pub const MAX_PACKET_SIZE: u8 = 64; + /// The logger handle, which contains a pipe with configurable size for buffering log messages. pub struct UsbLogger { buffer: Pipe, @@ -54,7 +57,6 @@ impl UsbLogger { D: Driver<'d>, Self: 'd, { - const MAX_PACKET_SIZE: u8 = 64; let mut config = Config::new(0xc0de, 0xcafe); config.manufacturer = Some("Embassy"); config.product = Some("USB-serial logger"); @@ -87,57 +89,46 @@ impl UsbLogger { let mut device = builder.build(); loop { let run_fut = device.run(); - let log_fut = async { - let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - sender.wait_connection().await; - loop { - let len = self.buffer.read(&mut rx[..]).await; - let _ = sender.write_packet(&rx[..len]).await; - if len as u8 == MAX_PACKET_SIZE { - let _ = sender.write_packet(&[]).await; - } - } - }; - let discard_fut = async { - let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - receiver.wait_connection().await; - loop { - let _ = receiver.read_packet(&mut discard_buf).await; - } - }; - join(run_fut, join(log_fut, discard_fut)).await; + let class_fut = self.run_logger_class(&mut sender, &mut receiver); + join(run_fut, class_fut).await; } } + async fn run_logger_class<'d, D>(&self, sender: &mut Sender<'d, D>, receiver: &mut Receiver<'d, D>) + where + D: Driver<'d>, + { + let log_fut = async { + let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + sender.wait_connection().await; + loop { + let len = self.buffer.read(&mut rx[..]).await; + let _ = sender.write_packet(&rx[..len]).await; + if len as u8 == MAX_PACKET_SIZE { + let _ = sender.write_packet(&[]).await; + } + } + }; + let discard_fut = async { + let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + receiver.wait_connection().await; + loop { + let _ = receiver.read_packet(&mut discard_buf).await; + } + }; + + join(log_fut, discard_fut).await; + } + /// Creates the futures needed for the logger from a given class /// This can be used in cases where the usb device is already in use for another connection pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>) where D: Driver<'d>, { - const MAX_PACKET_SIZE: u8 = 64; let (mut sender, mut receiver) = class.split(); - loop { - let log_fut = async { - let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - sender.wait_connection().await; - loop { - let len = self.buffer.read(&mut rx[..]).await; - let _ = sender.write_packet(&rx[..len]).await; - if len as u8 == MAX_PACKET_SIZE { - let _ = sender.write_packet(&[]).await; - } - } - }; - let discard_fut = async { - let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - receiver.wait_connection().await; - loop { - let _ = receiver.read_packet(&mut discard_buf).await; - } - }; - join(log_fut, discard_fut).await; + self.run_logger_class(&mut sender, &mut receiver).await; } } } From affaf2be1f7e351312e3e34e32c6707b43a23843 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Feb 2024 20:50:06 +0100 Subject: [PATCH 31/68] net: enable dhcpv4-hostname feature in docs. --- embassy-net/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 44bd2e8f3..be9f1d784 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -16,11 +16,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] +features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] +features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"] [features] default = [] From 937a9e7955210b5e5467ba4e049d4fa86ec0c42b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Feb 2024 20:58:04 +0100 Subject: [PATCH 32/68] stm32/rcc: use h7 sdlevel enum from pac. --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/rcc/h.rs | 27 ++++++--------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 24af17327..cc76408f3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8ae5bb5fe696a7e61fb41b8b797372aed8103a82" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a3ad0b738292ae40af201d79b28db60fe876e11" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8ae5bb5fe696a7e61fb41b8b797372aed8103a82", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a3ad0b738292ae40af201d79b28db60fe876e11", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 474c44115..c2a71eaf1 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -170,22 +170,7 @@ pub enum SupplyConfig { /// This is only used in certain power supply configurations: /// SMPSLDO, SMPSExternalLDO, SMPSExternalLDOBypass. #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] -#[derive(PartialEq)] -pub enum SMPSSupplyVoltage { - V1_8, - V2_5, -} - -#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] -impl SMPSSupplyVoltage { - /// Convert SMPSSupplyVoltage to u8 representation. - fn to_u8(&self) -> u8 { - match self { - SMPSSupplyVoltage::V1_8 => 0b01, - SMPSSupplyVoltage::V2_5 => 0b10, - } - } -} +pub use pac::pwr::vals::Sdlevel as SMPSSupplyVoltage; /// Configuration of the core clocks #[non_exhaustive] @@ -279,7 +264,7 @@ pub(crate) unsafe fn init(config: Config) { match config.supply_config { SupplyConfig::Default => { PWR.cr3().modify(|w| { - w.set_sdlevel(0b00); + w.set_sdlevel(SMPSSupplyVoltage::RESET); w.set_sdexthp(false); w.set_sden(true); w.set_ldoen(true); @@ -301,11 +286,11 @@ pub(crate) unsafe fn init(config: Config) { w.set_bypass(false); }); } - SupplyConfig::SMPSLDO(ref smps_supply_voltage) - | SupplyConfig::SMPSExternalLDO(ref smps_supply_voltage) - | SupplyConfig::SMPSExternalLDOBypass(ref smps_supply_voltage) => { + SupplyConfig::SMPSLDO(smps_supply_voltage) + | SupplyConfig::SMPSExternalLDO(smps_supply_voltage) + | SupplyConfig::SMPSExternalLDOBypass(smps_supply_voltage) => { PWR.cr3().modify(|w| { - w.set_sdlevel(smps_supply_voltage.to_u8()); + w.set_sdlevel(smps_supply_voltage); w.set_sdexthp(matches!( config.supply_config, SupplyConfig::SMPSExternalLDO(_) | SupplyConfig::SMPSExternalLDOBypass(_) From 6e24f0562d4dc3aae66d7fa16366e7b7a5aabcb9 Mon Sep 17 00:00:00 2001 From: Nils Bars Date: Mon, 12 Feb 2024 21:18:50 +0100 Subject: [PATCH 33/68] Print panics via defmt per default for the stm32f0 example --- examples/stm32f0/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 71b0eb683..c74980dc4 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" -panic-probe = "0.3" +panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } From 56e6b6bee6bd6087b35857e8aa1a011f6e83f703 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Mon, 12 Feb 2024 23:24:21 +0100 Subject: [PATCH 34/68] refactor(boot): move page erase index out of state --- embassy-boot/src/firmware_updater/asynch.rs | 14 +++++--------- embassy-boot/src/firmware_updater/blocking.rs | 14 +++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index d31eff005..b76668136 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, state: FirmwareState<'d, STATE>, + last_erased_dfu_page_index: Option, } #[cfg(target_os = "none")] @@ -56,6 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { Self { dfu: config.dfu, state: FirmwareState::new(config.state, aligned), + last_erased_dfu_page_index: None, } } @@ -72,7 +74,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { /// proceed with updating the firmware as it must be signed with a /// corresponding private key (otherwise it could be malicious firmware). /// - /// Mark to trigger firmware swap on next boot if verify suceeds. + /// Mark to trigger firmware swap on next boot if verify succeeds. /// /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have /// been generated from a SHA-512 digest of the firmware bytes. @@ -213,14 +215,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { let sector_end = sector_start + DFU::ERASE_SIZE; // Determine if the current sector needs to be erased before writing. let need_erase = self - .state .last_erased_dfu_page_index .map_or(true, |last_erased_sector| current_sector != last_erased_sector); // If the sector needs to be erased, erase it and update the last erased sector index. if need_erase { self.dfu.erase(sector_start as u32, sector_end as u32).await?; - self.state.last_erased_dfu_page_index = Some(current_sector); + self.last_erased_dfu_page_index = Some(current_sector); } // Calculate the size of the data chunk that can be written in the current iteration. @@ -258,7 +259,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { pub struct FirmwareState<'d, STATE> { state: STATE, aligned: &'d mut [u8], - last_erased_dfu_page_index: Option, } impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { @@ -280,11 +280,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { /// and follow the alignment rules for the flash being read from and written to. pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE)); - Self { - state, - aligned, - last_erased_dfu_page_index: None, - } + Self { state, aligned } } // Make sure we are running a booted firmware to avoid reverting to a bad state. diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 5b8076f81..eb96a9523 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, state: BlockingFirmwareState<'d, STATE>, + last_erased_dfu_page_index: Option, } #[cfg(target_os = "none")] @@ -91,6 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> Self { dfu: config.dfu, state: BlockingFirmwareState::new(config.state, aligned), + last_erased_dfu_page_index: None, } } @@ -107,7 +109,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> /// proceed with updating the firmware as it must be signed with a /// corresponding private key (otherwise it could be malicious firmware). /// - /// Mark to trigger firmware swap on next boot if verify suceeds. + /// Mark to trigger firmware swap on next boot if verify succeeds. /// /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have /// been generated from a SHA-512 digest of the firmware bytes. @@ -248,14 +250,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> let sector_end = sector_start + DFU::ERASE_SIZE; // Determine if the current sector needs to be erased before writing. let need_erase = self - .state .last_erased_dfu_page_index .map_or(true, |last_erased_sector| current_sector != last_erased_sector); // If the sector needs to be erased, erase it and update the last erased sector index. if need_erase { self.dfu.erase(sector_start as u32, sector_end as u32)?; - self.state.last_erased_dfu_page_index = Some(current_sector); + self.last_erased_dfu_page_index = Some(current_sector); } // Calculate the size of the data chunk that can be written in the current iteration. @@ -293,7 +294,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> pub struct BlockingFirmwareState<'d, STATE> { state: STATE, aligned: &'d mut [u8], - last_erased_dfu_page_index: Option, } impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { @@ -315,11 +315,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { /// and written to. pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { assert_eq!(aligned.len(), STATE::WRITE_SIZE); - Self { - state, - aligned, - last_erased_dfu_page_index: None, - } + Self { state, aligned } } // Make sure we are running a booted firmware to avoid reverting to a bad state. From 7dd974aa0dd88ad319971b81083f63a190d278bf Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Mon, 12 Feb 2024 23:28:04 +0100 Subject: [PATCH 35/68] refactor(boot): use sector instead of page for consistency --- embassy-boot/src/firmware_updater/asynch.rs | 16 ++++++++-------- embassy-boot/src/firmware_updater/blocking.rs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index b76668136..3d211be65 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -13,7 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, state: FirmwareState<'d, STATE>, - last_erased_dfu_page_index: Option, + last_erased_dfu_sector_index: Option, } #[cfg(target_os = "none")] @@ -57,7 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { Self { dfu: config.dfu, state: FirmwareState::new(config.state, aligned), - last_erased_dfu_page_index: None, + last_erased_dfu_sector_index: None, } } @@ -180,7 +180,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { /// It handles sector erasures and data writes while verifying the device is in a proper state /// for firmware updates. The function ensures that only unerased sectors are erased before /// writing and efficiently handles the writing process across sector boundaries and in - /// various configurations (data size, page size, etc.). + /// various configurations (data size, sector size, etc.). /// /// # Arguments /// @@ -215,13 +215,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { let sector_end = sector_start + DFU::ERASE_SIZE; // Determine if the current sector needs to be erased before writing. let need_erase = self - .last_erased_dfu_page_index + .last_erased_dfu_sector_index .map_or(true, |last_erased_sector| current_sector != last_erased_sector); // If the sector needs to be erased, erase it and update the last erased sector index. if need_erase { self.dfu.erase(sector_start as u32, sector_end as u32).await?; - self.last_erased_dfu_page_index = Some(current_sector); + self.last_erased_dfu_sector_index = Some(current_sector); } // Calculate the size of the data chunk that can be written in the current iteration. @@ -380,7 +380,7 @@ mod tests { } #[test] - fn can_verify_sha1_page_bigger_than_chunk() { + fn can_verify_sha1_sector_bigger_than_chunk() { let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); let state = Partition::new(&flash, 0, 4096); let dfu = Partition::new(&flash, 65536, 65536); @@ -404,7 +404,7 @@ mod tests { } #[test] - fn can_verify_sha1_page_smaller_than_chunk() { + fn can_verify_sha1_sector_smaller_than_chunk() { let flash = Mutex::::new(MemFlash::<131072, 1024, 8>::default()); let state = Partition::new(&flash, 0, 4096); let dfu = Partition::new(&flash, 65536, 65536); @@ -428,7 +428,7 @@ mod tests { } #[test] - fn can_verify_sha1_cross_page_boundary() { + fn can_verify_sha1_cross_sector_boundary() { let flash = Mutex::::new(MemFlash::<131072, 1024, 8>::default()); let state = Partition::new(&flash, 0, 4096); let dfu = Partition::new(&flash, 65536, 65536); diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index eb96a9523..35772a856 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -13,7 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, state: BlockingFirmwareState<'d, STATE>, - last_erased_dfu_page_index: Option, + last_erased_dfu_sector_index: Option, } #[cfg(target_os = "none")] @@ -92,7 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> Self { dfu: config.dfu, state: BlockingFirmwareState::new(config.state, aligned), - last_erased_dfu_page_index: None, + last_erased_dfu_sector_index: None, } } @@ -215,7 +215,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> /// It handles sector erasures and data writes while verifying the device is in a proper state /// for firmware updates. The function ensures that only unerased sectors are erased before /// writing and efficiently handles the writing process across sector boundaries and in - /// various configurations (data size, page size, etc.). + /// various configurations (data size, sector size, etc.). /// /// # Arguments /// @@ -250,13 +250,13 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> let sector_end = sector_start + DFU::ERASE_SIZE; // Determine if the current sector needs to be erased before writing. let need_erase = self - .last_erased_dfu_page_index + .last_erased_dfu_sector_index .map_or(true, |last_erased_sector| current_sector != last_erased_sector); // If the sector needs to be erased, erase it and update the last erased sector index. if need_erase { self.dfu.erase(sector_start as u32, sector_end as u32)?; - self.last_erased_dfu_page_index = Some(current_sector); + self.last_erased_dfu_sector_index = Some(current_sector); } // Calculate the size of the data chunk that can be written in the current iteration. @@ -420,7 +420,7 @@ mod tests { } #[test] - fn can_verify_sha1_page_bigger_than_chunk() { + fn can_verify_sha1_sector_bigger_than_chunk() { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); @@ -446,7 +446,7 @@ mod tests { } #[test] - fn can_verify_sha1_page_smaller_than_chunk() { + fn can_verify_sha1_sector_smaller_than_chunk() { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); @@ -472,7 +472,7 @@ mod tests { } #[test] - fn can_verify_sha1_cross_page_boundary() { + fn can_verify_sha1_cross_sector_boundary() { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 1024, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); From 739c69bd637baf471585648db4d253089301d6c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Feb 2024 00:58:18 +0100 Subject: [PATCH 36/68] stm32/rcc: some f3 fixes. --- embassy-stm32/src/rcc/f3.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 0a5e67b4a..580aa389f 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -222,8 +222,8 @@ pub(crate) unsafe fn init(config: Config) { // Set prescalers // CFGR has been written before (PLL, PLL48) don't overwrite these settings RCC.cfgr().modify(|w| { - w.set_ppre2(config.apb1_pre); - w.set_ppre1(config.apb2_pre); + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); w.set_hpre(config.ahb_pre); }); @@ -234,6 +234,7 @@ pub(crate) unsafe fn init(config: Config) { // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings RCC.cfgr().modify(|w| w.set_sw(config.sys)); + while RCC.cfgr().read().sws() != config.sys {} let rtc = config.ls.init(); From b7c147445a98ced9557ca6c0950f6083d0cf09af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Feb 2024 21:54:53 +0100 Subject: [PATCH 37/68] stm32/rcc: port F1 to new API. --- embassy-stm32/src/rcc/f1.rs | 390 +++++++++++++++---------- examples/stm32f1/src/bin/hello.rs | 4 +- examples/stm32f1/src/bin/usb_serial.rs | 20 +- tests/stm32/.cargo/config.toml | 4 +- tests/stm32/src/common.rs | 18 ++ 5 files changed, 266 insertions(+), 170 deletions(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 7f0adab27..9fdd4c11c 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -1,191 +1,257 @@ -use core::convert::TryFrom; - use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::*; +use crate::pac::rcc::vals::Pllsrc; +#[cfg(any(rcc_f1, rcc_f1cl))] +use crate::pac::rcc::vals::Usbpre; +pub use crate::pac::rcc::vals::{ + Adcpre as ADCPrescaler, Hpre as AHBPrescaler, Pllmul as PllMul, Pllxtpre as PllPreDiv, Ppre as APBPrescaler, + Sw as Sysclk, +}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(8_000_000); -/// Configuration of the clocks -/// -#[non_exhaustive] -#[derive(Default)] -pub struct Config { - pub hse: Option, +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, +} - pub sys_ck: Option, - pub hclk: Option, - pub pclk1: Option, - pub pclk2: Option, - pub adcclk: Option, - pub pllxtpre: bool, +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum PllSource { + HSE, + HSI, +} + +#[derive(Clone, Copy)] +pub struct Pll { + pub src: PllSource, + + /// PLL pre-divider. + /// + /// On some F3 chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. + pub prediv: PllPreDiv, + + /// PLL multiplication factor. + pub mul: PllMul, +} + +/// Clocks configutation +#[non_exhaustive] +pub struct Config { + pub hsi: bool, + pub hse: Option, + pub sys: Sysclk, + + pub pll: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + + pub adc_pre: ADCPrescaler, pub ls: super::LsConfig, } -pub(crate) unsafe fn init(config: Config) { - let pllxtpre_div = if config.pllxtpre { 2 } else { 1 }; - let pllsrcclk = config.hse.map(|hse| hse.0 / pllxtpre_div).unwrap_or(HSI_FREQ.0 / 2); +impl Default for Config { + fn default() -> Self { + Self { + hsi: true, + hse: None, + sys: Sysclk::HSI, + pll: None, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + ls: Default::default(), - let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); - let pllmul = sysclk / pllsrcclk; - - let (pllmul_bits, real_sysclk) = if pllmul == 1 { - (None, config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0)) - } else { - let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16); - (Some(pllmul as u8 - 2), pllsrcclk * pllmul) - }; - - assert!(real_sysclk <= 72_000_000); - - let hpre_bits = config - .hclk - .map(|hclk| match real_sysclk / hclk.0 { - 0 => unreachable!(), - 1 => 0b0111, - 2 => 0b1000, - 3..=5 => 0b1001, - 6..=11 => 0b1010, - 12..=39 => 0b1011, - 40..=95 => 0b1100, - 96..=191 => 0b1101, - 192..=383 => 0b1110, - _ => 0b1111, - }) - .unwrap_or(0b0111); - - let hclk = if hpre_bits >= 0b1100 { - real_sysclk / (1 << (hpre_bits - 0b0110)) - } else { - real_sysclk / (1 << (hpre_bits - 0b0111)) - }; - - assert!(hclk <= 72_000_000); - - let ppre1_bits = config - .pclk1 - .map(|pclk1| match hclk / pclk1.0 { - 0 => unreachable!(), - 1 => 0b011, - 2 => 0b100, - 3..=5 => 0b101, - 6..=11 => 0b110, - _ => 0b111, - }) - .unwrap_or(0b011); - - let ppre1 = 1 << (ppre1_bits - 0b011); - let pclk1 = hclk / u32::try_from(ppre1).unwrap(); - let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; - - assert!(pclk1 <= 36_000_000); - - let ppre2_bits = config - .pclk2 - .map(|pclk2| match hclk / pclk2.0 { - 0 => unreachable!(), - 1 => 0b011, - 2 => 0b100, - 3..=5 => 0b101, - 6..=11 => 0b110, - _ => 0b111, - }) - .unwrap_or(0b011); - - let ppre2 = 1 << (ppre2_bits - 0b011); - let pclk2 = hclk / u32::try_from(ppre2).unwrap(); - let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; - - assert!(pclk2 <= 72_000_000); - - FLASH.acr().write(|w| { - w.set_latency(if real_sysclk <= 24_000_000 { - Latency::WS0 - } else if real_sysclk <= 48_000_000 { - Latency::WS1 - } else { - Latency::WS2 - }); - // the prefetch buffer is enabled by default, let's keep it enabled - w.set_prftbe(true); - }); - - // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the - // PLL output frequency is a supported one. - // usbpre == false: divide clock by 1.5, otherwise no division - #[cfg(not(rcc_f100))] - let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) { - (Some(_), Some(_), 72_000_000) => (false, true), - (Some(_), Some(_), 48_000_000) => (true, true), - _ => (true, false), - }; - - let apre_bits: u8 = config - .adcclk - .map(|adcclk| match pclk2 / adcclk.0 { - 0..=2 => 0b00, - 3..=4 => 0b01, - 5..=7 => 0b10, - _ => 0b11, - }) - .unwrap_or(0b11); - - let apre = (apre_bits + 1) << 1; - let adcclk = pclk2 / unwrap!(u32::try_from(apre)); - - assert!(adcclk <= 14_000_000); - - if config.hse.is_some() { - // enable HSE and wait for it to be ready - RCC.cr().modify(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} + // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz) + adc_pre: ADCPrescaler::DIV6, + } } +} - if let Some(pllmul_bits) = pllmul_bits { - let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; - RCC.cfgr() - .modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag))); +/// Initialize and Set the clock frequencies +pub(crate) unsafe fn init(config: Config) { + // Configure HSI + let hsi = match config.hsi { + false => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + true => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + Some(HSI_FREQ) + } + }; + + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } + + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + // Enable PLL + let pll = config.pll.map(|pll| { + let (src_val, src_freq) = match pll.src { + PllSource::HSI => { + if pll.prediv != PllPreDiv::DIV2 { + panic!("if PLL source is HSI, PLL prediv must be 2."); + } + (Pllsrc::HSI_DIV2, unwrap!(hsi)) + } + PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), + }; + let in_freq = src_freq / pll.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let out_freq = in_freq * pll.mul; + assert!(max::PLL_OUT.contains(&out_freq)); - // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { - w.set_pllmul(Pllmul::from_bits(pllmul_bits)); - w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8)); + w.set_pllmul(pll.mul); + w.set_pllsrc(src_val); + w.set_pllxtpre(pll.prediv); }); - RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} - } - // Only needed for stm32f103? - RCC.cfgr().modify(|w| { - w.set_adcpre(Adcpre::from_bits(apre_bits)); - w.set_ppre2(Ppre::from_bits(ppre2_bits)); - w.set_ppre1(Ppre::from_bits(ppre1_bits)); - w.set_hpre(Hpre::from_bits(hpre_bits)); - #[cfg(not(rcc_f100))] - w.set_usbpre(Usbpre::from_bits(usbpre as u8)); - w.set_sw(if pllmul_bits.is_some() { - Sw::PLL1_P - } else if config.hse.is_some() { - Sw::HSE - } else { - Sw::HSI - }); + out_freq }); + #[cfg(any(rcc_f1, rcc_f1cl))] + let usb = match pll { + Some(Hertz(72_000_000)) => { + RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1_5)); + Some(Hertz(48_000_000)) + } + Some(Hertz(48_000_000)) => { + RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1)); + Some(Hertz(48_000_000)) + } + _ => None, + }; + + // Configure sysclk + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_P => unwrap!(pll), + _ => unreachable!(), + }; + + let hclk = sys / config.ahb_pre; + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); + + assert!(max::HCLK.contains(&hclk)); + assert!(max::PCLK1.contains(&pclk1)); + assert!(max::PCLK2.contains(&pclk2)); + + let adc = pclk2 / config.adc_pre; + assert!(max::ADC.contains(&adc)); + + // Set latency based on HCLK frquency + let latency = match hclk.0 { + ..=24_000_000 => Latency::WS0, + ..=48_000_000 => Latency::WS1, + _ => Latency::WS2, + }; + FLASH.acr().modify(|w| { + w.set_latency(latency); + // RM0316: "The prefetch buffer must be kept on when using a prescaler + // different from 1 on the AHB clock.", "Half-cycle access cannot be + // used when there is a prescaler different from 1 on the AHB clock" + if config.ahb_pre != AHBPrescaler::DIV1 { + w.set_hlfcya(false); + w.set_prftbe(true); + } + }); + + // Set prescalers + // CFGR has been written before (PLL, PLL48) don't overwrite these settings + RCC.cfgr().modify(|w| { + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); + w.set_hpre(config.ahb_pre); + w.set_adcpre(config.adc_pre); + }); + + // Wait for the new prescalers to kick in + // "The clocks are divided with the new prescaler factor from + // 1 to 16 AHB cycles after write" + cortex_m::asm::delay(16); + + // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings + RCC.cfgr().modify(|w| w.set_sw(config.sys)); + while RCC.cfgr().read().sws() != config.sys {} + let rtc = config.ls.init(); set_clocks!( - sys: Some(Hertz(real_sysclk)), - pclk1: Some(Hertz(pclk1)), - pclk2: Some(Hertz(pclk2)), - pclk1_tim: Some(Hertz(pclk1 * timer_mul1)), - pclk2_tim: Some(Hertz(pclk2 * timer_mul2)), - hclk1: Some(Hertz(hclk)), - adc: Some(Hertz(adcclk)), + hsi: hsi, + hse: hse, + pll1_p: pll, + sys: Some(sys), + pclk1: Some(pclk1), + pclk2: Some(pclk2), + pclk1_tim: Some(pclk1_tim), + pclk2_tim: Some(pclk2_tim), + hclk1: Some(hclk), + adc: Some(adc), rtc: rtc, + #[cfg(any(rcc_f1, rcc_f1cl))] + usb: usb, + lse: None, ); } + +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + #[cfg(not(rcc_f1cl))] + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(16_000_000); + #[cfg(not(rcc_f1cl))] + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); + + #[cfg(rcc_f1cl)] + pub(crate) const HSE_OSC: RangeInclusive = Hertz(3_000_000)..=Hertz(25_000_000); + #[cfg(rcc_f1cl)] + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(50_000_000); + + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(72_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(36_000_000); + pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(72_000_000); + + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); + pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(72_000_000); + + pub(crate) const ADC: RangeInclusive = Hertz(0)..=Hertz(14_000_000); +} diff --git a/examples/stm32f1/src/bin/hello.rs b/examples/stm32f1/src/bin/hello.rs index 7b761ecc1..3c295612c 100644 --- a/examples/stm32f1/src/bin/hello.rs +++ b/examples/stm32f1/src/bin/hello.rs @@ -3,15 +3,13 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - let mut config = Config::default(); - config.rcc.sys_ck = Some(Hertz(36_000_000)); + let config = Config::default(); let _p = embassy_stm32::init(config); loop { diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index e28381893..1ae6c1dee 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -21,9 +21,23 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.hse = Some(Hertz(8_000_000)); - config.rcc.sys_ck = Some(Hertz(48_000_000)); - config.rcc.pclk1 = Some(Hertz(24_000_000)); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + // Oscillator for bluepill, Bypass for nucleos. + mode: HseMode::Oscillator, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + } let mut p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 8e32b4cee..528bd3451 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -14,9 +14,9 @@ rustflags = [ ] [build] -target = "thumbv6m-none-eabi" +#target = "thumbv6m-none-eabi" #target = "thumbv7m-none-eabi" -#target = "thumbv7em-none-eabi" +target = "thumbv7em-none-eabi" #target = "thumbv8m.main-none-eabihf" [env] diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 36fe8a235..182ad6298 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -247,6 +247,24 @@ pub fn config() -> Config { config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; } + #[cfg(feature = "stm32f103c8")] + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL9, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV1; + } + #[cfg(feature = "stm32f207zg")] { use embassy_stm32::rcc::*; From ccd2c574c37d26d09f67d6410d79e7e3e16e6119 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Feb 2024 01:15:20 +0100 Subject: [PATCH 38/68] stm32/rcc: port F0 to new API. --- ci.sh | 7 + embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/f0.rs | 338 +++++++++++++++++++---------------- embassy-stm32/src/rcc/mco.rs | 6 +- embassy-stm32/src/rcc/mod.rs | 16 +- 5 files changed, 207 insertions(+), 164 deletions(-) diff --git a/ci.sh b/ci.sh index 58a288441..7ecca92af 100755 --- a/ci.sh +++ b/ci.sh @@ -88,6 +88,13 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index cc76408f3..4c27164ce 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a3ad0b738292ae40af201d79b28db60fe876e11" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7734584b2007766b1c5a6a7f2654fdb442fa211a" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-8a3ad0b738292ae40af201d79b28db60fe876e11", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7734584b2007766b1c5a6a7f2654fdb442fa211a", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs index 1042a1cb2..d6f995e45 100644 --- a/embassy-stm32/src/rcc/f0.rs +++ b/embassy-stm32/src/rcc/f0.rs @@ -1,27 +1,64 @@ -use stm32_metapac::flash::vals::Latency; - -use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; +use crate::pac::flash::vals::Latency; +use crate::pac::rcc::vals::Pllsrc; +pub use crate::pac::rcc::vals::{ + Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Prediv as PllPreDiv, Sw as Sysclk, +}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(8_000_000); -/// Configuration of the clocks -/// -/// hse takes precedence over hsi48 if both are enabled +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum PllSource { + HSE, + HSI, + #[cfg(rcc_f0v4)] + HSI48, +} + +#[derive(Clone, Copy)] +pub struct Pll { + pub src: PllSource, + + /// PLL pre-divider. + /// + /// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. + pub prediv: PllPreDiv, + + /// PLL multiplication factor. + pub mul: PllMul, +} + +/// Clocks configutation #[non_exhaustive] pub struct Config { - pub hse: Option, - pub bypass_hse: bool, - pub usb_pll: bool, - + pub hsi: bool, + pub hse: Option, #[cfg(crs)] pub hsi48: Option, + pub sys: Sysclk, - pub sys_ck: Option, - pub hclk: Option, - pub pclk: Option, + pub pll: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, pub ls: super::LsConfig, } @@ -29,168 +66,167 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - hse: Default::default(), - bypass_hse: Default::default(), - usb_pll: Default::default(), + hsi: true, + hse: None, #[cfg(crs)] hsi48: Some(Default::default()), - sys_ck: Default::default(), - hclk: Default::default(), - pclk: Default::default(), + sys: Sysclk::HSI, + pll: None, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, ls: Default::default(), } } } +/// Initialize and Set the clock frequencies pub(crate) unsafe fn init(config: Config) { - let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI_FREQ.0); + // Configure HSI + let hsi = match config.hsi { + false => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + true => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + Some(HSI_FREQ) + } + }; + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } + + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + // configure HSI48 #[cfg(crs)] let hsi48 = config.hsi48.map(|config| super::init_hsi48(config)); #[cfg(not(crs))] let hsi48: Option = None; - let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| { - if hsi48.is_some() { - return (48_000_000, true); - } - (HSI_FREQ.0, false) - }); + // Enable PLL + let pll = config.pll.map(|pll| { + let (src_val, src_freq) = match pll.src { + #[cfg(not(any(rcc_f0v1, rcc_f0v2)))] + PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)), + #[cfg(any(rcc_f0v1, rcc_f0v2))] + PllSource::HSI => { + if pll.prediv != PllPreDiv::DIV2 { + panic!("if PLL source is HSI, PLL prediv must be 2."); + } + (Pllsrc::HSI_DIV2, unwrap!(hsi)) + } + PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), + #[cfg(rcc_f0v4)] + PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), + }; + let in_freq = src_freq / pll.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let out_freq = in_freq * pll.mul; + assert!(max::PLL_OUT.contains(&out_freq)); - let (pllmul_bits, real_sysclk) = if sysclk == src_clk { - (None, sysclk) - } else { - let prediv = if config.hse.is_some() { 1 } else { 2 }; - let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2; - let pllmul = pllmul.max(2).min(16); - - let pllmul_bits = pllmul as u8 - 2; - let real_sysclk = pllmul * src_clk / prediv; - (Some(pllmul_bits), real_sysclk) - }; - - let hpre_bits = config - .hclk - .map(|hclk| match real_sysclk / hclk.0 { - 0 => unreachable!(), - 1 => 0b0111, - 2 => 0b1000, - 3..=5 => 0b1001, - 6..=11 => 0b1010, - 12..=39 => 0b1011, - 40..=95 => 0b1100, - 96..=191 => 0b1101, - 192..=383 => 0b1110, - _ => 0b1111, - }) - .unwrap_or(0b0111); - let hclk = real_sysclk / (1 << (hpre_bits - 0b0111)); - - let ppre_bits = config - .pclk - .map(|pclk| match hclk / pclk.0 { - 0 => unreachable!(), - 1 => 0b011, - 2 => 0b100, - 3..=5 => 0b101, - 6..=11 => 0b110, - _ => 0b111, - }) - .unwrap_or(0b011); - - let ppre: u8 = 1 << (ppre_bits - 0b011); - let pclk = hclk / u32::from(ppre); - - let timer_mul = if ppre == 1 { 1 } else { 2 }; - - FLASH.acr().write(|w| { - w.set_latency(if real_sysclk <= 24_000_000 { - Latency::WS0 - } else { - Latency::WS1 + RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); + RCC.cfgr().modify(|w| { + w.set_pllmul(pll.mul); + w.set_pllsrc(src_val); }); - }); - - match (config.hse.is_some(), use_hsi48) { - (true, _) => { - RCC.cr().modify(|w| { - w.set_csson(true); - w.set_hseon(true); - w.set_hsebyp(config.bypass_hse); - }); - while !RCC.cr().read().hserdy() {} - - if pllmul_bits.is_some() { - RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV)) - } - } - // use_hsi48 will always be false for stm32f0x0 - #[cfg(not(stm32f0x0))] - (false, true) => { - RCC.cr2().modify(|w| w.set_hsi48on(true)); - while !RCC.cr2().read().hsi48rdy() {} - - if pllmul_bits.is_some() { - RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV)) - } - } - _ => { - RCC.cr().modify(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - if pllmul_bits.is_some() { - RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2)) - } - } - } - - if config.usb_pll { - RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P)); - } - // TODO: Option to use CRS (Clock Recovery) - - if let Some(pllmul_bits) = pllmul_bits { - RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits))); - RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} - RCC.cfgr().modify(|w| { - w.set_ppre(Ppre::from_bits(ppre_bits)); - w.set_hpre(Hpre::from_bits(hpre_bits)); - w.set_sw(Sw::PLL1_P) - }); - } else { - RCC.cfgr().modify(|w| { - w.set_ppre(Ppre::from_bits(ppre_bits)); - w.set_hpre(Hpre::from_bits(hpre_bits)); + out_freq + }); - if config.hse.is_some() { - w.set_sw(Sw::HSE); - } else if use_hsi48 { - #[cfg(not(stm32f0x0))] - w.set_sw(Sw::HSI48); - } else { - w.set_sw(Sw::HSI) - } - }) - } + // Configure sysclk + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_P => unwrap!(pll), + #[cfg(rcc_f0v4)] + Sysclk::HSI48 => unwrap!(hsi48), + #[allow(unreachable_patterns)] + _ => unreachable!(), + }; + + let hclk = sys / config.ahb_pre; + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + + assert!(max::HCLK.contains(&hclk)); + assert!(max::PCLK1.contains(&pclk1)); + + // Set latency based on HCLK frquency + let latency = match hclk.0 { + ..=24_000_000 => Latency::WS0, + _ => Latency::WS1, + }; + FLASH.acr().modify(|w| { + w.set_latency(latency); + w.set_prftbe(true); + }); + + // Set prescalers + // CFGR has been written before (PLL, PLL48) don't overwrite these settings + RCC.cfgr().modify(|w| { + w.set_ppre(config.apb1_pre); + w.set_hpre(config.ahb_pre); + }); + + // Wait for the new prescalers to kick in + // "The clocks are divided with the new prescaler factor from + // 1 to 16 AHB cycles after write" + cortex_m::asm::delay(16); + + // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings + RCC.cfgr().modify(|w| w.set_sw(config.sys)); + while RCC.cfgr().read().sws() != config.sys {} let rtc = config.ls.init(); set_clocks!( - hsi: None, - lse: None, - sys: Some(Hertz(real_sysclk)), - pclk1: Some(Hertz(pclk)), - pclk2: Some(Hertz(pclk)), - pclk1_tim: Some(Hertz(pclk * timer_mul)), - pclk2_tim: Some(Hertz(pclk * timer_mul)), - hclk1: Some(Hertz(hclk)), - rtc: rtc, + hsi: hsi, + hse: hse, + pll1_p: pll, + sys: Some(sys), + pclk1: Some(pclk1), + pclk2: Some(pclk1), + pclk1_tim: Some(pclk1_tim), + pclk2_tim: Some(pclk1_tim), + hclk1: Some(hclk), + #[cfg(all(not(rcc_f37), adc3_common))] + adc34: Some(adc34), + #[cfg(stm32f334)] + hrtim: hrtim, hsi48: hsi48, - - // TODO: - pll1_p: None, + rtc: rtc, + lse: None, ); } + +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(32_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(32_000_000); + + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(24_000_000); + pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(48_000_000); +} diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index db0df9fac..654943bc1 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -4,7 +4,7 @@ use embassy_hal_internal::into_ref; use crate::gpio::sealed::AFType; use crate::gpio::Speed; -#[cfg(not(any(stm32f1, rcc_f3v1, rcc_f37)))] +#[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; #[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))] pub use crate::pac::rcc::vals::Mcosel as McoSource; @@ -13,7 +13,7 @@ pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; use crate::pac::RCC; use crate::{peripherals, Peripheral}; -#[cfg(any(stm32f1, rcc_f3v1, rcc_f37))] +#[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum McoPrescaler { DIV1, @@ -43,7 +43,7 @@ macro_rules! impl_peri { r.modify(|w| { w.$set_source(source); - #[cfg(not(any(stm32f1, rcc_f3v1, rcc_f37)))] + #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] w.$set_prescaler(_prescaler); }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 05937cc5d..b7eca0615 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -18,17 +18,17 @@ mod hsi48; #[cfg(crs)] pub use hsi48::*; -#[cfg_attr(rcc_f0, path = "f0.rs")] -#[cfg_attr(any(stm32f1), path = "f1.rs")] -#[cfg_attr(any(stm32f3), path = "f3.rs")] +#[cfg_attr(stm32f0, path = "f0.rs")] +#[cfg_attr(stm32f1, path = "f1.rs")] +#[cfg_attr(stm32f3, path = "f3.rs")] #[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")] -#[cfg_attr(rcc_c0, path = "c0.rs")] -#[cfg_attr(rcc_g0, path = "g0.rs")] -#[cfg_attr(rcc_g4, path = "g4.rs")] +#[cfg_attr(stm32c0, path = "c0.rs")] +#[cfg_attr(stm32g0, path = "g0.rs")] +#[cfg_attr(stm32g4, path = "g4.rs")] #[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")] #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")] -#[cfg_attr(rcc_u5, path = "u5.rs")] -#[cfg_attr(rcc_wba, path = "wba.rs")] +#[cfg_attr(stm32u5, path = "u5.rs")] +#[cfg_attr(stm32wba, path = "wba.rs")] mod _version; pub use _version::*; From d8b4922b3ced8645a6225dcb0e8b873364bc8337 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:33:04 -0500 Subject: [PATCH 39/68] Add STM32 HMAC function. --- embassy-stm32/src/hash/mod.rs | 73 ++++++++++++++++++++++++++------ examples/stm32f7/src/bin/hash.rs | 2 +- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index f0c2c839a..bae538b5f 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -100,8 +100,9 @@ pub enum DataType { /// Stores the state of the HASH peripheral for suspending/resuming /// digest calculation. -pub struct Context { +pub struct Context<'c> { first_word_sent: bool, + key_sent: bool, buffer: [u8; HASH_BUFFER_LEN], buflen: usize, algo: Algorithm, @@ -110,8 +111,11 @@ pub struct Context { str: u32, cr: u32, csr: [u32; NUM_CONTEXT_REGS], + key: HmacKey<'c>, } +type HmacKey<'k> = Option<&'k [u8]>; + /// HASH driver. pub struct Hash<'d, T: Instance, D = NoDma> { _peripheral: PeripheralRef<'d, T>, @@ -140,10 +144,11 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { } /// Starts computation of a new hash and returns the saved peripheral state. - pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> { // Define a context for this new computation. let mut ctx = Context { first_word_sent: false, + key_sent: false, buffer: [0; HASH_BUFFER_LEN], buflen: 0, algo: algorithm, @@ -152,6 +157,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { str: 0, cr: 0, csr: [0; NUM_CONTEXT_REGS], + key, }; // Set the data type in the peripheral. @@ -181,6 +187,14 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { #[cfg(any(hash_v3, hash_v4))] T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); + // Configure HMAC mode if a key is provided. + if let Some(key) = ctx.key { + T::regs().cr().modify(|w| w.set_mode(true)); + if key.len() > 64 { + T::regs().cr().modify(|w| w.set_lkey(true)); + } + } + T::regs().cr().modify(|w| w.set_init(true)); // Store and return the state of the peripheral. @@ -191,18 +205,30 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { /// 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]) { + pub fn update_blocking<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) { + // Restore the peripheral state. + self.load_context(&ctx); + + // Load the HMAC key if provided. + if !ctx.key_sent { + if let Some(key) = ctx.key { + self.accumulate_blocking(key); + T::regs().str().write(|w| w.set_dcal(true)); + // Block waiting for digest. + while !T::regs().sr().read().dcis() {} + } + ctx.key_sent = true; + } + 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(); + self.store_context(ctx); return; } - // Restore the peripheral state. - self.load_context(&ctx); - let mut ilen_remaining = input.len(); let mut input_start = 0; @@ -261,21 +287,30 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { /// 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]) + pub async fn update<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) where D: crate::hash::Dma, { + // Restore the peripheral state. + self.load_context(&ctx); + + // Load the HMAC key if provided. + if !ctx.key_sent { + if let Some(key) = ctx.key { + self.accumulate(key).await; + } + ctx.key_sent = true; + } + 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(); + self.store_context(ctx); return; } - // Restore the peripheral state. - self.load_context(&ctx); - // Enable multiple DMA transfers. T::regs().cr().modify(|w| w.set_mdmat(true)); @@ -319,7 +354,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { /// 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 { + pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize { // Restore the peripheral state. self.load_context(&ctx); @@ -333,6 +368,13 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { // Block waiting for digest. while !T::regs().sr().read().dcis() {} + // Load the HMAC key if provided. + if let Some(key) = ctx.key { + self.accumulate_blocking(key); + T::regs().str().write(|w| w.set_dcal(true)); + while !T::regs().sr().read().dcis() {} + } + // Return the digest. let digest_words = match ctx.algo { Algorithm::SHA1 => 5, @@ -370,7 +412,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { /// 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 + pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize where D: crate::hash::Dma, { @@ -384,6 +426,11 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { self.accumulate(&ctx.buffer[0..ctx.buflen]).await; ctx.buflen = 0; + // Load the HMAC key if provided. + if let Some(key) = ctx.key { + self.accumulate(key).await; + } + // Wait for completion. poll_fn(|cx| { // Check if already done. @@ -484,7 +531,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { } /// Save the peripheral state to a context. - fn store_context(&mut self, ctx: &mut Context) { + fn store_context<'c>(&mut self, ctx: &mut Context<'c>) { // Block waiting for data in ready. while !T::regs().sr().read().dinis() {} diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 96e50f84b..cbb880353 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -26,7 +26,7 @@ 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); + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None); hw_hasher.update(&mut context, test_1).await; hw_hasher.update(&mut context, test_2).await; let mut hw_digest: [u8; 32] = [0; 32]; From 37c869827e96fb17838f89a082f12c08f3177fff Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:33:04 -0500 Subject: [PATCH 40/68] Update STM32 hash test. --- tests/stm32/src/bin/hash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index cfcf3d976..1b89f46e8 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -38,11 +38,11 @@ async fn main(_spawner: Spawner) { 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); + let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None); 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); + let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8, None); 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); From f0f1f2d14c6cb82ee1a4f452bdece2f98479c39f Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:33:04 -0500 Subject: [PATCH 41/68] Added HMAC example. --- examples/stm32f7/Cargo.toml | 1 + examples/stm32f7/src/bin/hash.rs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a612c2554..736e81723 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -29,6 +29,7 @@ critical-section = "1.1" embedded-storage = "0.3.1" static_cell = "2" sha2 = { version = "0.10.8", default-features = false } +hmac = "0.12.1" [profile.release] debug = 2 diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index cbb880353..c2d1a7158 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -6,9 +6,12 @@ use embassy_executor::Spawner; use embassy_stm32::hash::*; use embassy_stm32::{bind_interrupts, hash, peripherals, Config}; use embassy_time::Instant; +use hmac::{Hmac, Mac}; use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; +type HmacSha256 = Hmac; + bind_interrupts!(struct Irqs { HASH_RNG => hash::InterruptHandler; }); @@ -52,5 +55,24 @@ async fn main(_spawner: Spawner) -> ! { info!("Software Execution Time: {:?}", sw_execution_time); assert_eq!(hw_digest, sw_digest[..]); + let hmac_key: [u8; 64] = [0x55; 64]; + + // Compute HMAC in hardware. + let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key)); + hw_hasher.update(&mut sha256hmac_context, test_1).await; + hw_hasher.update(&mut sha256hmac_context, test_2).await; + let mut hw_hmac: [u8; 32] = [0; 32]; + hw_hasher.finish(sha256hmac_context, &mut hw_hmac).await; + + // Compute HMAC in software. + let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap(); + sw_mac.update(test_1); + sw_mac.update(test_2); + let sw_hmac = sw_mac.finalize().into_bytes(); + + info!("Hardware HMAC: {:?}", hw_hmac); + info!("Software HMAC: {:?}", sw_hmac[..]); + assert_eq!(hw_hmac, sw_hmac[..]); + loop {} } From 14a678fe4542d7c400d0d68b2859c54f2246abe4 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 12 Feb 2024 20:33:04 -0500 Subject: [PATCH 42/68] Fixed HMAC blocking mode. --- embassy-stm32/src/hash/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index bae538b5f..b47814f8b 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -215,7 +215,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { self.accumulate_blocking(key); T::regs().str().write(|w| w.set_dcal(true)); // Block waiting for digest. - while !T::regs().sr().read().dcis() {} + while !T::regs().sr().read().dinis() {} } ctx.key_sent = true; } @@ -365,16 +365,16 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { //Start the digest calculation. T::regs().str().write(|w| w.set_dcal(true)); - // Block waiting for digest. - while !T::regs().sr().read().dcis() {} - // Load the HMAC key if provided. if let Some(key) = ctx.key { + while !T::regs().sr().read().dinis() {} self.accumulate_blocking(key); T::regs().str().write(|w| w.set_dcal(true)); - while !T::regs().sr().read().dcis() {} } + // Block until digest computation is complete. + while !T::regs().sr().read().dcis() {} + // Return the digest. let digest_words = match ctx.algo { Algorithm::SHA1 => 5, From f9af0096bd6cd74853b8e5406d6135de2f5c8274 Mon Sep 17 00:00:00 2001 From: Ralf Date: Mon, 12 Feb 2024 19:29:29 +0100 Subject: [PATCH 43/68] FAQ add hint to embassy-time linker error to include HAL in linking --- docs/modules/ROOT/pages/faq.adoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index 7fb81e2ca..d55e63130 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -118,6 +118,13 @@ features = [ ] ---- +If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is passed to the linker by adding this line to your source: + +[source,rust] +---- +use embassy_stm32 as _; +---- + == Error: `Only one package in the dependency graph may specify the same links value.` You have multiple versions of the same crate in your dependency tree. This means that some of your From 4a0b1cbadb252f186ef41c465936bb3ac4dfcf3f Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 13 Feb 2024 15:23:50 +0100 Subject: [PATCH 44/68] Update docs/modules/ROOT/pages/faq.adoc --- docs/modules/ROOT/pages/faq.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index d55e63130..6b5e6d009 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -118,7 +118,7 @@ features = [ ] ---- -If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is passed to the linker by adding this line to your source: +If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source: [source,rust] ---- From f0045b92173a46e45be1724d28e1a59045c9c3f6 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:17:19 -0500 Subject: [PATCH 45/68] Added HMAC to STM32 hash test. --- tests/stm32/Cargo.toml | 1 + tests/stm32/src/bin/hash.rs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index fc4420687..d94045737 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -76,6 +76,7 @@ portable-atomic = { version = "1.5", features = [] } chrono = { version = "^0.4", default-features = false, optional = true} sha2 = { version = "0.10.8", default-features = false } +hmac = "0.12.1" # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 1b89f46e8..d1cfac5ce 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -9,9 +9,12 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::hash::*; use embassy_stm32::{bind_interrupts, hash, peripherals}; +use hmac::{Hmac, Mac}; use sha2::{Digest, Sha224, Sha256}; use {defmt_rtt as _, panic_probe as _}; +type HmacSha256 = Hmac; + #[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))] bind_interrupts!(struct Irqs { HASH_RNG => hash::InterruptHandler; @@ -73,6 +76,25 @@ async fn main(_spawner: Spawner) { info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]); defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]); + let hmac_key: [u8; 64] = [0x55; 64]; + + // Compute HMAC in hardware. + let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key)); + hw_hasher.update_blocking(&mut sha256hmac_context, test_1); + hw_hasher.update_blocking(&mut sha256hmac_context, test_2); + let mut hw_hmac: [u8; 32] = [0; 32]; + hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac); + + // Compute HMAC in software. + let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap(); + sw_mac.update(test_1); + sw_mac.update(test_2); + let sw_hmac = sw_mac.finalize().into_bytes(); + + info!("Hardware HMAC: {:?}", hw_hmac); + info!("Software HMAC: {:?}", sw_hmac[..]); + defmt::assert!(hw_hmac == sw_hmac[..]); + info!("Test OK"); cortex_m::asm::bkpt(); } From 0ceb313b6f0827c1b9544f8b87e721c616de8cc2 Mon Sep 17 00:00:00 2001 From: Michael de Silva Date: Wed, 14 Feb 2024 07:22:52 +0530 Subject: [PATCH 46/68] FIX: Correct typo in stm32 gpio --- embassy-stm32/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 1051a13c8..00e3e1727 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -249,7 +249,7 @@ impl From for vals::Pupdr { /// Speed settings /// -/// These vary dpeending on the chip, ceck the reference manual or datasheet for details. +/// These vary depending on the chip, check the reference manual or datasheet for details. #[allow(missing_docs)] #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From bbe1eebc534f57c74f1735e5d99ed3c4136636bd Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Wed, 14 Feb 2024 17:17:27 +0800 Subject: [PATCH 47/68] Add missing TIM for time-driver; reorder time-driver selection when use "time-drvier-any". --- embassy-stm32/build.rs | 41 ++++++++------------ embassy-stm32/src/time_driver.rs | 66 +++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 35023bf1f..5241dbb27 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -183,40 +183,33 @@ fn main() { let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { None => "", + Some("tim1") => "TIM1", Some("tim2") => "TIM2", Some("tim3") => "TIM3", Some("tim4") => "TIM4", Some("tim5") => "TIM5", + Some("tim8") => "TIM8", Some("tim9") => "TIM9", - Some("tim11") => "TIM11", Some("tim12") => "TIM12", Some("tim15") => "TIM15", + Some("tim20") => "TIM20", Some("tim21") => "TIM21", Some("tim22") => "TIM22", + Some("tim23") => "TIM23", + Some("tim24") => "TIM24", Some("any") => { - if singletons.contains(&"TIM2".to_string()) { - "TIM2" - } else if singletons.contains(&"TIM3".to_string()) { - "TIM3" - } else if singletons.contains(&"TIM4".to_string()) { - "TIM4" - } else if singletons.contains(&"TIM5".to_string()) { - "TIM5" - } else if singletons.contains(&"TIM9".to_string()) { - "TIM9" - } else if singletons.contains(&"TIM11".to_string()) { - "TIM11" - } else if singletons.contains(&"TIM12".to_string()) { - "TIM12" - } else if singletons.contains(&"TIM15".to_string()) { - "TIM15" - } else if singletons.contains(&"TIM21".to_string()) { - "TIM21" - } else if singletons.contains(&"TIM22".to_string()) { - "TIM22" - } else { - panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM9, TIM11, TIM12 or TIM15.") - } + // Order of TIM candidators: + // 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV + // 2. In same catagory: larger TIM number first + [ + "TIM22", "TIM21", "TIM12", "TIM9", // 2CH + "TIM15", // 2CH_CMP + "TIM19", "TIM4", "TIM3", // GP16 + "TIM24", "TIM23", "TIM5", "TIM2", // GP32 + "TIM20", "TIM8", "TIM1", //ADV + ] + .iter() + .find(|tim| singletons.contains(&tim.to_string())).expect("time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.") } _ => panic!("unknown time_driver {:?}", time_driver), }; diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 29ff4a736..a1f54307d 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use core::cell::Cell; use core::convert::TryInto; use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; @@ -22,18 +24,22 @@ use crate::{interrupt, peripherals}; // As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not -// candidates for use as an embassy-time driver provider. +// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) // // The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number // available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: // CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. -#[cfg(not(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22)))] -const ALARM_COUNT: usize = 3; - -#[cfg(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] -const ALARM_COUNT: usize = 1; +cfg_if::cfg_if! { + if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] { + const ALARM_COUNT: usize = 1; + } else { + const ALARM_COUNT: usize = 3; + } +} +#[cfg(time_drvier_tim1)] +type T = peripherals::TIM1; #[cfg(time_driver_tim2)] type T = peripherals::TIM2; #[cfg(time_driver_tim3)] @@ -42,6 +48,8 @@ type T = peripherals::TIM3; type T = peripherals::TIM4; #[cfg(time_driver_tim5)] type T = peripherals::TIM5; +#[cfg(time_driver_tim8)] +type T = peripherals::TIM8; #[cfg(time_driver_tim9)] type T = peripherals::TIM9; #[cfg(time_driver_tim11)] @@ -50,12 +58,26 @@ type T = peripherals::TIM11; type T = peripherals::TIM12; #[cfg(time_driver_tim15)] type T = peripherals::TIM15; +#[cfg(time_driver_tim20)] +type T = peripherals::TIM20; #[cfg(time_driver_tim21)] type T = peripherals::TIM21; #[cfg(time_driver_tim22)] type T = peripherals::TIM22; +#[cfg(time_driver_tim23)] +type T = peripherals::TIM23; +#[cfg(time_driver_tim24)] +type T = peripherals::TIM24; foreach_interrupt! { + (TIM1, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim1)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; (TIM2, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim2)] #[cfg(feature = "rt")] @@ -88,16 +110,16 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM9, timer, $block:ident, UP, $irq:ident) => { - #[cfg(time_driver_tim9)] + (TIM8, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim8)] #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() } }; - (TIM11, timer, $block:ident, UP, $irq:ident) => { - #[cfg(time_driver_tim11)] + (TIM9, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim9)] #[cfg(feature = "rt")] #[interrupt] fn $irq() { @@ -120,6 +142,14 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; + (TIM20, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim20)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; (TIM21, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim21)] #[cfg(feature = "rt")] @@ -136,6 +166,22 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; + (TIM23, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim23)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; + (TIM24, timer, $block:ident, UP, $irq:ident) => { + #[cfg(time_driver_tim24)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; } // Clock timekeeping works with something we call "periods", which are time intervals From 1860e2269311df018a47a9a52f9f942c0285c97b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 14 Feb 2024 00:10:59 +0100 Subject: [PATCH 48/68] stm32/rcc: unify f0, f1, f3. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/f0.rs | 232 -------------------- embassy-stm32/src/rcc/{f3.rs => f013.rs} | 163 +++++++++++--- embassy-stm32/src/rcc/f1.rs | 257 ----------------------- embassy-stm32/src/rcc/{f.rs => f247.rs} | 0 embassy-stm32/src/rcc/mod.rs | 6 +- 6 files changed, 137 insertions(+), 525 deletions(-) delete mode 100644 embassy-stm32/src/rcc/f0.rs rename embassy-stm32/src/rcc/{f3.rs => f013.rs} (65%) delete mode 100644 embassy-stm32/src/rcc/f1.rs rename embassy-stm32/src/rcc/{f.rs => f247.rs} (100%) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4c27164ce..c384f14f1 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7734584b2007766b1c5a6a7f2654fdb442fa211a" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cc1a1603e61881a6f0a1a47c12c16c57c245ba8" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7734584b2007766b1c5a6a7f2654fdb442fa211a", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cc1a1603e61881a6f0a1a47c12c16c57c245ba8", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs deleted file mode 100644 index d6f995e45..000000000 --- a/embassy-stm32/src/rcc/f0.rs +++ /dev/null @@ -1,232 +0,0 @@ -use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::Pllsrc; -pub use crate::pac::rcc::vals::{ - Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Prediv as PllPreDiv, Sw as Sysclk, -}; -use crate::pac::{FLASH, RCC}; -use crate::time::Hertz; - -/// HSI speed -pub const HSI_FREQ: Hertz = Hertz(8_000_000); - -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum HseMode { - /// crystal/ceramic oscillator (HSEBYP=0) - Oscillator, - /// external analog clock (low swing) (HSEBYP=1) - Bypass, -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Hse { - /// HSE frequency. - pub freq: Hertz, - /// HSE mode. - pub mode: HseMode, -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum PllSource { - HSE, - HSI, - #[cfg(rcc_f0v4)] - HSI48, -} - -#[derive(Clone, Copy)] -pub struct Pll { - pub src: PllSource, - - /// PLL pre-divider. - /// - /// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. - pub prediv: PllPreDiv, - - /// PLL multiplication factor. - pub mul: PllMul, -} - -/// Clocks configutation -#[non_exhaustive] -pub struct Config { - pub hsi: bool, - pub hse: Option, - #[cfg(crs)] - pub hsi48: Option, - pub sys: Sysclk, - - pub pll: Option, - - pub ahb_pre: AHBPrescaler, - pub apb1_pre: APBPrescaler, - - pub ls: super::LsConfig, -} - -impl Default for Config { - fn default() -> Self { - Self { - hsi: true, - hse: None, - #[cfg(crs)] - hsi48: Some(Default::default()), - sys: Sysclk::HSI, - pll: None, - ahb_pre: AHBPrescaler::DIV1, - apb1_pre: APBPrescaler::DIV1, - ls: Default::default(), - } - } -} - -/// Initialize and Set the clock frequencies -pub(crate) unsafe fn init(config: Config) { - // Configure HSI - let hsi = match config.hsi { - false => { - RCC.cr().modify(|w| w.set_hsion(false)); - None - } - true => { - RCC.cr().modify(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - Some(HSI_FREQ) - } - }; - - // Configure HSE - let hse = match config.hse { - None => { - RCC.cr().modify(|w| w.set_hseon(false)); - None - } - Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } - - RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); - RCC.cr().modify(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - Some(hse.freq) - } - }; - - // configure HSI48 - #[cfg(crs)] - let hsi48 = config.hsi48.map(|config| super::init_hsi48(config)); - #[cfg(not(crs))] - let hsi48: Option = None; - - // Enable PLL - let pll = config.pll.map(|pll| { - let (src_val, src_freq) = match pll.src { - #[cfg(not(any(rcc_f0v1, rcc_f0v2)))] - PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)), - #[cfg(any(rcc_f0v1, rcc_f0v2))] - PllSource::HSI => { - if pll.prediv != PllPreDiv::DIV2 { - panic!("if PLL source is HSI, PLL prediv must be 2."); - } - (Pllsrc::HSI_DIV2, unwrap!(hsi)) - } - PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), - #[cfg(rcc_f0v4)] - PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), - }; - let in_freq = src_freq / pll.prediv; - assert!(max::PLL_IN.contains(&in_freq)); - let out_freq = in_freq * pll.mul; - assert!(max::PLL_OUT.contains(&out_freq)); - - RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); - RCC.cfgr().modify(|w| { - w.set_pllmul(pll.mul); - w.set_pllsrc(src_val); - }); - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - - out_freq - }); - - // Configure sysclk - let sys = match config.sys { - Sysclk::HSI => unwrap!(hsi), - Sysclk::HSE => unwrap!(hse), - Sysclk::PLL1_P => unwrap!(pll), - #[cfg(rcc_f0v4)] - Sysclk::HSI48 => unwrap!(hsi48), - #[allow(unreachable_patterns)] - _ => unreachable!(), - }; - - let hclk = sys / config.ahb_pre; - let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); - - assert!(max::HCLK.contains(&hclk)); - assert!(max::PCLK1.contains(&pclk1)); - - // Set latency based on HCLK frquency - let latency = match hclk.0 { - ..=24_000_000 => Latency::WS0, - _ => Latency::WS1, - }; - FLASH.acr().modify(|w| { - w.set_latency(latency); - w.set_prftbe(true); - }); - - // Set prescalers - // CFGR has been written before (PLL, PLL48) don't overwrite these settings - RCC.cfgr().modify(|w| { - w.set_ppre(config.apb1_pre); - w.set_hpre(config.ahb_pre); - }); - - // Wait for the new prescalers to kick in - // "The clocks are divided with the new prescaler factor from - // 1 to 16 AHB cycles after write" - cortex_m::asm::delay(16); - - // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings - RCC.cfgr().modify(|w| w.set_sw(config.sys)); - while RCC.cfgr().read().sws() != config.sys {} - - let rtc = config.ls.init(); - - set_clocks!( - hsi: hsi, - hse: hse, - pll1_p: pll, - sys: Some(sys), - pclk1: Some(pclk1), - pclk2: Some(pclk1), - pclk1_tim: Some(pclk1_tim), - pclk2_tim: Some(pclk1_tim), - hclk1: Some(hclk), - #[cfg(all(not(rcc_f37), adc3_common))] - adc34: Some(adc34), - #[cfg(stm32f334)] - hrtim: hrtim, - hsi48: hsi48, - rtc: rtc, - lse: None, - ); -} - -mod max { - use core::ops::RangeInclusive; - - use crate::time::Hertz; - - pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(32_000_000); - pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(32_000_000); - - pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(48_000_000); - pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(48_000_000); - - pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(24_000_000); - pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(48_000_000); -} diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f013.rs similarity index 65% rename from embassy-stm32/src/rcc/f3.rs rename to embassy-stm32/src/rcc/f013.rs index 580aa389f..c2933186c 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -1,9 +1,14 @@ use crate::pac::flash::vals::Latency; -pub use crate::pac::rcc::vals::{ - Adcpres as AdcPllPrescaler, Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Prediv as PllPreDiv, - Sw as Sysclk, -}; -use crate::pac::rcc::vals::{Pllsrc, Usbpre}; +#[cfg(stm32f1)] +pub use crate::pac::rcc::vals::Adcpre as ADCPrescaler; +#[cfg(stm32f3)] +pub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler; +use crate::pac::rcc::vals::Pllsrc; +#[cfg(stm32f1)] +pub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv; +#[cfg(any(stm32f0, stm32f3))] +pub use crate::pac::rcc::vals::Prediv as PllPreDiv; +pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; @@ -30,6 +35,8 @@ pub struct Hse { pub enum PllSource { HSE, HSI, + #[cfg(rcc_f0v4)] + HSI48, } #[derive(Clone, Copy)] @@ -38,19 +45,21 @@ pub struct Pll { /// PLL pre-divider. /// - /// On some F3 chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. + /// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. pub prediv: PllPreDiv, /// PLL multiplication factor. pub mul: PllMul, } +#[cfg(all(stm32f3, not(rcc_f37)))] #[derive(Clone, Copy)] pub enum AdcClockSource { Pll(AdcPllPrescaler), Hclk(AdcHclkPrescaler), } +#[cfg(all(stm32f3, not(rcc_f37)))] #[derive(Clone, Copy, PartialEq, Eq)] pub enum AdcHclkPrescaler { Div1, @@ -58,6 +67,7 @@ pub enum AdcHclkPrescaler { Div4, } +#[cfg(stm32f334)] #[derive(Clone, Copy, PartialEq, Eq)] pub enum HrtimClockSource { BusClk, @@ -69,17 +79,23 @@ pub enum HrtimClockSource { pub struct Config { pub hsi: bool, pub hse: Option, + #[cfg(crs)] + pub hsi48: Option, pub sys: Sysclk, pub pll: Option, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, + #[cfg(not(stm32f0))] pub apb2_pre: APBPrescaler, - #[cfg(not(rcc_f37))] + #[cfg(stm32f1)] + pub adc_pre: ADCPrescaler, + + #[cfg(all(stm32f3, not(rcc_f37)))] pub adc: AdcClockSource, - #[cfg(all(not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] pub adc34: AdcClockSource, #[cfg(stm32f334)] pub hrtim: HrtimClockSource, @@ -92,16 +108,24 @@ impl Default for Config { Self { hsi: true, hse: None, + #[cfg(crs)] + hsi48: Some(Default::default()), sys: Sysclk::HSI, pll: None, ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, + #[cfg(not(stm32f0))] apb2_pre: APBPrescaler::DIV1, ls: Default::default(), - #[cfg(not(rcc_f37))] + #[cfg(stm32f1)] + // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz) + adc_pre: ADCPrescaler::DIV6, + + + #[cfg(all(stm32f3, not(rcc_f37)))] adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), - #[cfg(all(not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), #[cfg(stm32f334)] hrtim: HrtimClockSource::BusClk, @@ -143,13 +167,18 @@ pub(crate) unsafe fn init(config: Config) { } }; + // configure HSI48 + #[cfg(crs)] + let hsi48 = config.hsi48.map(|config| super::init_hsi48(config)); + #[cfg(not(crs))] + let hsi48: Option = None; + // Enable PLL - // RM0316: "Reserved, must be kept at reset value." let pll = config.pll.map(|pll| { let (src_val, src_freq) = match pll.src { - #[cfg(rcc_f3v3)] + #[cfg(any(rcc_f0v3, rcc_f0v4, rcc_f3v3))] PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)), - #[cfg(not(rcc_f3v3))] + #[cfg(not(any(rcc_f0v3, rcc_f0v4, rcc_f3v3)))] PllSource::HSI => { if pll.prediv != PllPreDiv::DIV2 { panic!("if PLL source is HSI, PLL prediv must be 2."); @@ -157,16 +186,21 @@ pub(crate) unsafe fn init(config: Config) { (Pllsrc::HSI_DIV2, unwrap!(hsi)) } PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), + #[cfg(rcc_f0v4)] + PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), }; let in_freq = src_freq / pll.prediv; assert!(max::PLL_IN.contains(&in_freq)); let out_freq = in_freq * pll.mul; assert!(max::PLL_OUT.contains(&out_freq)); + #[cfg(not(stm32f1))] RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); RCC.cfgr().modify(|w| { w.set_pllmul(pll.mul); w.set_pllsrc(src_val); + #[cfg(stm32f1)] + w.set_pllxtpre(pll.prediv); }); RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} @@ -174,17 +208,16 @@ pub(crate) unsafe fn init(config: Config) { out_freq }); + #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))] let usb = match pll { - Some(Hertz(72_000_000)) => { - RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1_5)); - Some(Hertz(48_000_000)) - } - Some(Hertz(48_000_000)) => { - RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1)); - Some(Hertz(48_000_000)) - } + Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5), + Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1), _ => None, - }; + } + .map(|usbpre| { + RCC.cfgr().modify(|w| w.set_usbpre(usbpre)); + Hertz(48_000_000) + }); // Configure sysclk let sys = match config.sys { @@ -196,13 +229,28 @@ pub(crate) unsafe fn init(config: Config) { let hclk = sys / config.ahb_pre; let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + #[cfg(not(stm32f0))] let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); + #[cfg(stm32f0)] + let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); assert!(max::HCLK.contains(&hclk)); assert!(max::PCLK1.contains(&pclk1)); + #[cfg(not(stm32f0))] assert!(max::PCLK2.contains(&pclk2)); + #[cfg(stm32f1)] + let adc = pclk2 / config.adc_pre; + #[cfg(stm32f1)] + assert!(max::ADC.contains(&adc)); + // Set latency based on HCLK frquency + #[cfg(stm32f0)] + let latency = match hclk.0 { + ..=24_000_000 => Latency::WS0, + _ => Latency::WS1, + }; + #[cfg(any(stm32f1, stm32f3))] let latency = match hclk.0 { ..=24_000_000 => Latency::WS0, ..=48_000_000 => Latency::WS1, @@ -213,18 +261,28 @@ pub(crate) unsafe fn init(config: Config) { // RM0316: "The prefetch buffer must be kept on when using a prescaler // different from 1 on the AHB clock.", "Half-cycle access cannot be // used when there is a prescaler different from 1 on the AHB clock" + #[cfg(stm32f3)] if config.ahb_pre != AHBPrescaler::DIV1 { w.set_hlfcya(false); w.set_prftbe(true); } + #[cfg(not(stm32f3))] + w.set_prftbe(true); }); // Set prescalers // CFGR has been written before (PLL, PLL48) don't overwrite these settings - RCC.cfgr().modify(|w| { - w.set_ppre1(config.apb1_pre); - w.set_ppre2(config.apb2_pre); + RCC.cfgr().modify(|w: &mut stm32_metapac::rcc::regs::Cfgr| { + #[cfg(not(stm32f0))] + { + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); + } + #[cfg(stm32f0)] + w.set_ppre(config.apb1_pre); w.set_hpre(config.ahb_pre); + #[cfg(stm32f1)] + w.set_adcpre(config.adc_pre); }); // Wait for the new prescalers to kick in @@ -238,10 +296,10 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); - #[cfg(not(rcc_f37))] + #[cfg(all(stm32f3, not(rcc_f37)))] use crate::pac::adccommon::vals::Ckmode; - #[cfg(not(rcc_f37))] + #[cfg(all(stm32f3, not(rcc_f37)))] let adc = match config.adc { AdcClockSource::Pll(adcpres) => { RCC.cfgr2().modify(|w| w.set_adc12pres(adcpres)); @@ -265,7 +323,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - #[cfg(all(not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] let adc34 = match config.adc34 { AdcClockSource::Pll(adcpres) => { RCC.cfgr2().modify(|w| w.set_adc34pres(adcpres)); @@ -316,18 +374,63 @@ pub(crate) unsafe fn init(config: Config) { pclk1_tim: Some(pclk1_tim), pclk2_tim: Some(pclk2_tim), hclk1: Some(hclk), - #[cfg(not(rcc_f37))] + #[cfg(all(stm32f3, not(rcc_f37)))] adc: Some(adc), - #[cfg(all(not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] adc34: Some(adc34), #[cfg(stm32f334)] hrtim: hrtim, rtc: rtc, + hsi48: hsi48, + #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))] usb: usb, lse: None, ); } +#[cfg(stm32f0)] +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(32_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(32_000_000); + + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(24_000_000); + pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(48_000_000); +} + +#[cfg(stm32f1)] +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + #[cfg(not(rcc_f1cl))] + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(16_000_000); + #[cfg(not(rcc_f1cl))] + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); + + #[cfg(rcc_f1cl)] + pub(crate) const HSE_OSC: RangeInclusive = Hertz(3_000_000)..=Hertz(25_000_000); + #[cfg(rcc_f1cl)] + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(50_000_000); + + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(72_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(36_000_000); + pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(72_000_000); + + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); + pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(72_000_000); + + pub(crate) const ADC: RangeInclusive = Hertz(0)..=Hertz(14_000_000); +} + +#[cfg(stm32f3)] mod max { use core::ops::RangeInclusive; diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs deleted file mode 100644 index 9fdd4c11c..000000000 --- a/embassy-stm32/src/rcc/f1.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::Pllsrc; -#[cfg(any(rcc_f1, rcc_f1cl))] -use crate::pac::rcc::vals::Usbpre; -pub use crate::pac::rcc::vals::{ - Adcpre as ADCPrescaler, Hpre as AHBPrescaler, Pllmul as PllMul, Pllxtpre as PllPreDiv, Ppre as APBPrescaler, - Sw as Sysclk, -}; -use crate::pac::{FLASH, RCC}; -use crate::time::Hertz; - -/// HSI speed -pub const HSI_FREQ: Hertz = Hertz(8_000_000); - -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum HseMode { - /// crystal/ceramic oscillator (HSEBYP=0) - Oscillator, - /// external analog clock (low swing) (HSEBYP=1) - Bypass, -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Hse { - /// HSE frequency. - pub freq: Hertz, - /// HSE mode. - pub mode: HseMode, -} - -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum PllSource { - HSE, - HSI, -} - -#[derive(Clone, Copy)] -pub struct Pll { - pub src: PllSource, - - /// PLL pre-divider. - /// - /// On some F3 chips, this must be 2 if `src == HSI`. Init will panic if this is not the case. - pub prediv: PllPreDiv, - - /// PLL multiplication factor. - pub mul: PllMul, -} - -/// Clocks configutation -#[non_exhaustive] -pub struct Config { - pub hsi: bool, - pub hse: Option, - pub sys: Sysclk, - - pub pll: Option, - - pub ahb_pre: AHBPrescaler, - pub apb1_pre: APBPrescaler, - pub apb2_pre: APBPrescaler, - - pub adc_pre: ADCPrescaler, - - pub ls: super::LsConfig, -} - -impl Default for Config { - fn default() -> Self { - Self { - hsi: true, - hse: None, - sys: Sysclk::HSI, - pll: None, - ahb_pre: AHBPrescaler::DIV1, - apb1_pre: APBPrescaler::DIV1, - apb2_pre: APBPrescaler::DIV1, - ls: Default::default(), - - // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz) - adc_pre: ADCPrescaler::DIV6, - } - } -} - -/// Initialize and Set the clock frequencies -pub(crate) unsafe fn init(config: Config) { - // Configure HSI - let hsi = match config.hsi { - false => { - RCC.cr().modify(|w| w.set_hsion(false)); - None - } - true => { - RCC.cr().modify(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - Some(HSI_FREQ) - } - }; - - // Configure HSE - let hse = match config.hse { - None => { - RCC.cr().modify(|w| w.set_hseon(false)); - None - } - Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } - - RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); - RCC.cr().modify(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - Some(hse.freq) - } - }; - - // Enable PLL - let pll = config.pll.map(|pll| { - let (src_val, src_freq) = match pll.src { - PllSource::HSI => { - if pll.prediv != PllPreDiv::DIV2 { - panic!("if PLL source is HSI, PLL prediv must be 2."); - } - (Pllsrc::HSI_DIV2, unwrap!(hsi)) - } - PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), - }; - let in_freq = src_freq / pll.prediv; - assert!(max::PLL_IN.contains(&in_freq)); - let out_freq = in_freq * pll.mul; - assert!(max::PLL_OUT.contains(&out_freq)); - - RCC.cfgr().modify(|w| { - w.set_pllmul(pll.mul); - w.set_pllsrc(src_val); - w.set_pllxtpre(pll.prediv); - }); - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - - out_freq - }); - - #[cfg(any(rcc_f1, rcc_f1cl))] - let usb = match pll { - Some(Hertz(72_000_000)) => { - RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1_5)); - Some(Hertz(48_000_000)) - } - Some(Hertz(48_000_000)) => { - RCC.cfgr().modify(|w| w.set_usbpre(Usbpre::DIV1)); - Some(Hertz(48_000_000)) - } - _ => None, - }; - - // Configure sysclk - let sys = match config.sys { - Sysclk::HSI => unwrap!(hsi), - Sysclk::HSE => unwrap!(hse), - Sysclk::PLL1_P => unwrap!(pll), - _ => unreachable!(), - }; - - let hclk = sys / config.ahb_pre; - let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); - let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); - - assert!(max::HCLK.contains(&hclk)); - assert!(max::PCLK1.contains(&pclk1)); - assert!(max::PCLK2.contains(&pclk2)); - - let adc = pclk2 / config.adc_pre; - assert!(max::ADC.contains(&adc)); - - // Set latency based on HCLK frquency - let latency = match hclk.0 { - ..=24_000_000 => Latency::WS0, - ..=48_000_000 => Latency::WS1, - _ => Latency::WS2, - }; - FLASH.acr().modify(|w| { - w.set_latency(latency); - // RM0316: "The prefetch buffer must be kept on when using a prescaler - // different from 1 on the AHB clock.", "Half-cycle access cannot be - // used when there is a prescaler different from 1 on the AHB clock" - if config.ahb_pre != AHBPrescaler::DIV1 { - w.set_hlfcya(false); - w.set_prftbe(true); - } - }); - - // Set prescalers - // CFGR has been written before (PLL, PLL48) don't overwrite these settings - RCC.cfgr().modify(|w| { - w.set_ppre1(config.apb1_pre); - w.set_ppre2(config.apb2_pre); - w.set_hpre(config.ahb_pre); - w.set_adcpre(config.adc_pre); - }); - - // Wait for the new prescalers to kick in - // "The clocks are divided with the new prescaler factor from - // 1 to 16 AHB cycles after write" - cortex_m::asm::delay(16); - - // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings - RCC.cfgr().modify(|w| w.set_sw(config.sys)); - while RCC.cfgr().read().sws() != config.sys {} - - let rtc = config.ls.init(); - - set_clocks!( - hsi: hsi, - hse: hse, - pll1_p: pll, - sys: Some(sys), - pclk1: Some(pclk1), - pclk2: Some(pclk2), - pclk1_tim: Some(pclk1_tim), - pclk2_tim: Some(pclk2_tim), - hclk1: Some(hclk), - adc: Some(adc), - rtc: rtc, - #[cfg(any(rcc_f1, rcc_f1cl))] - usb: usb, - lse: None, - ); -} - -mod max { - use core::ops::RangeInclusive; - - use crate::time::Hertz; - - #[cfg(not(rcc_f1cl))] - pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(16_000_000); - #[cfg(not(rcc_f1cl))] - pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); - - #[cfg(rcc_f1cl)] - pub(crate) const HSE_OSC: RangeInclusive = Hertz(3_000_000)..=Hertz(25_000_000); - #[cfg(rcc_f1cl)] - pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(50_000_000); - - pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(72_000_000); - pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(36_000_000); - pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(72_000_000); - - pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(25_000_000); - pub(crate) const PLL_OUT: RangeInclusive = Hertz(16_000_000)..=Hertz(72_000_000); - - pub(crate) const ADC: RangeInclusive = Hertz(0)..=Hertz(14_000_000); -} diff --git a/embassy-stm32/src/rcc/f.rs b/embassy-stm32/src/rcc/f247.rs similarity index 100% rename from embassy-stm32/src/rcc/f.rs rename to embassy-stm32/src/rcc/f247.rs diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index b7eca0615..0f3467151 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -18,10 +18,8 @@ mod hsi48; #[cfg(crs)] pub use hsi48::*; -#[cfg_attr(stm32f0, path = "f0.rs")] -#[cfg_attr(stm32f1, path = "f1.rs")] -#[cfg_attr(stm32f3, path = "f3.rs")] -#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")] +#[cfg_attr(any(stm32f0, stm32f1, stm32f3), path = "f013.rs")] +#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f247.rs")] #[cfg_attr(stm32c0, path = "c0.rs")] #[cfg_attr(stm32g0, path = "g0.rs")] #[cfg_attr(stm32g4, path = "g4.rs")] From 50b8100fd30fd13ac706889b0951e6ec25c77821 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Thu, 15 Feb 2024 12:34:51 +0200 Subject: [PATCH 49/68] nrf: Implement chunked DMA transfers for SPIM peripheral On some chips (notably nrf52832), the maximum DMA transfer is 255 bytes, which has caused subtle issues while interfacing with various devices over SPI bus. --- embassy-nrf/src/spim.rs | 112 ++++++++++++++++++++++++---------------- embassy-nrf/src/util.rs | 13 +++++ 2 files changed, 81 insertions(+), 44 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 8937159df..2d70732a8 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -17,7 +17,7 @@ use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::typelevel::Interrupt; -use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; +use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{interrupt, pac, Peripheral}; /// SPIM error @@ -25,10 +25,6 @@ use crate::{interrupt, pac, Peripheral}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - /// Supplied TX buffer overflows EasyDMA transmit buffer - TxBufferTooLong, - /// Supplied RX buffer overflows EasyDMA receive buffer - RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. BufferNotInRAM, } @@ -74,9 +70,13 @@ impl interrupt::typelevel::Handler for InterruptHandl let s = T::state(); #[cfg(feature = "_nrf52832_anomaly_109")] - if r.events_started.read().bits() != 0 { - s.waker.wake(); - r.intenclr.write(|w| w.started().clear()); + { + // Ideally we should call this only during the first chunk transfer, + // but so far calling this every time doesn't seem to be causing any issues. + if r.events_started.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.started().clear()); + } } if r.events_end.read().bits() != 0 { @@ -209,35 +209,39 @@ impl<'d, T: Instance> Spim<'d, T> { spim } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - slice_in_ram_or(tx, Error::BufferNotInRAM)?; - // NOTE: RAM slice check for rx is not necessary, as a mutable - // slice can only be built from data located in RAM. - + fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) { compiler_fence(Ordering::SeqCst); let r = T::regs(); - // Set up the DMA write. - let (ptr, tx_len) = slice_ptr_parts(tx); - if tx_len > EASY_DMA_SIZE { - return Err(Error::TxBufferTooLong); + fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) { + if total > offset { + (ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length)) + } else { + (ptr, 0) + } } - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); - // Set up the DMA read. - let (ptr, rx_len) = slice_ptr_parts_mut(rx); - if rx_len > EASY_DMA_SIZE { - return Err(Error::RxBufferTooLong); - } - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); + let (ptr, len) = slice_ptr_parts_mut(rx); + let (rx_ptr, rx_len) = xfer_params(ptr as _, len as _, offset, length); + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx_ptr) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); + // Set up the DMA write. + let (ptr, len) = slice_ptr_parts(tx); + let (tx_ptr, tx_len) = xfer_params(ptr as _, len as _, offset, length); + r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx_ptr) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) }); + + /* + trace!("XFER: offset: {}, length: {}", offset, length); + trace!("RX(len: {}, ptr: {=u32:02x})", rx_len, rx_ptr as u32); + trace!("TX(len: {}, ptr: {=u32:02x})", tx_len, tx_ptr as u32); + */ + #[cfg(feature = "_nrf52832_anomaly_109")] - { + if offset == 0 { let s = T::state(); r.events_started.reset(); @@ -260,21 +264,32 @@ impl<'d, T: Instance> Spim<'d, T> { // Start SPI transaction. r.tasks_start.write(|w| unsafe { w.bits(1) }); - - Ok(()) } - fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - self.prepare(rx, tx)?; + fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) { + self.prepare_dma_transfer(rx, tx, offset, length); #[cfg(feature = "_nrf52832_anomaly_109")] - while let Poll::Pending = self.nrf52832_dma_workaround_status() {} + if offset == 0 { + while self.nrf52832_dma_workaround_status().is_pending() {} + } // Wait for 'end' event. while T::regs().events_end.read().bits() == 0 {} compiler_fence(Ordering::SeqCst); + } + fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { + slice_in_ram_or(tx, Error::BufferNotInRAM)?; + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. + + let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx)); + for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) { + let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE); + self.blocking_inner_from_ram_chunk(rx, tx, offset, length); + } Ok(()) } @@ -287,22 +302,23 @@ impl<'d, T: Instance> Spim<'d, T> { tx_ram_buf.copy_from_slice(tx); self.blocking_inner_from_ram(rx, tx_ram_buf) } - Err(error) => Err(error), } } - async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - self.prepare(rx, tx)?; + async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) { + self.prepare_dma_transfer(rx, tx, offset, length); #[cfg(feature = "_nrf52832_anomaly_109")] - poll_fn(|cx| { - let s = T::state(); + if offset == 0 { + poll_fn(|cx| { + let s = T::state(); - s.waker.register(cx.waker()); + s.waker.register(cx.waker()); - self.nrf52832_dma_workaround_status() - }) - .await; + self.nrf52832_dma_workaround_status() + }) + .await; + } // Wait for 'end' event. poll_fn(|cx| { @@ -316,7 +332,18 @@ impl<'d, T: Instance> Spim<'d, T> { .await; compiler_fence(Ordering::SeqCst); + } + async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { + slice_in_ram_or(tx, Error::BufferNotInRAM)?; + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. + + let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx)); + for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) { + let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE); + self.async_inner_from_ram_chunk(rx, tx, offset, length).await; + } Ok(()) } @@ -329,7 +356,6 @@ impl<'d, T: Instance> Spim<'d, T> { tx_ram_buf.copy_from_slice(tx); self.async_inner_from_ram(rx, tx_ram_buf).await } - Err(error) => Err(error), } } @@ -528,8 +554,6 @@ mod eh02 { impl embedded_hal_1::spi::Error for Error { fn kind(&self) -> embedded_hal_1::spi::ErrorKind { match *self { - Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, - Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, } } diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index b408c517b..6cdb97f08 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs @@ -4,6 +4,19 @@ use core::mem; const SRAM_LOWER: usize = 0x2000_0000; const SRAM_UPPER: usize = 0x3000_0000; +// #![feature(const_slice_ptr_len)] +// https://github.com/rust-lang/rust/issues/71146 +pub(crate) fn slice_ptr_len(ptr: *const [T]) -> usize { + use core::ptr::NonNull; + let ptr = ptr.cast_mut(); + if let Some(ptr) = NonNull::new(ptr) { + ptr.len() + } else { + // We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null. + NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len() + } +} + // TODO: replace transmutes with core::ptr::metadata once it's stable pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (*const T, usize) { unsafe { mem::transmute(slice) } From 5b7eff65417e491fa7908dfd8b62013efb55d30b Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Thu, 15 Feb 2024 23:56:26 +0100 Subject: [PATCH 50/68] [embassy-stm32]: started stm32g4 RCC refactor * Copied API from f.rs where applicable * HSE and HSI independantly configurable * Boost mode set by user rather * Added HSE, pll1_q and pll1_p frequencies to set_clocks call * Stubbed max module based on f.rs, needs cleanup --- embassy-stm32/src/rcc/g4.rs | 204 +++++++++++++++---------- examples/stm32g4/src/bin/adc.rs | 16 +- examples/stm32g4/src/bin/pll.rs | 16 +- examples/stm32g4/src/bin/usb_serial.rs | 33 ++-- 4 files changed, 160 insertions(+), 109 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3e20bf6af..edd14ab23 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,10 +1,10 @@ use stm32_metapac::flash::vals::Latency; -use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; +use stm32_metapac::rcc::vals::{Adcsel, Sw}; use stm32_metapac::FLASH; pub use crate::pac::rcc::vals::{ - Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, - Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler, + Adcsel as AdcClockSource, Clk48sel, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllPreDiv, + Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler, Sw as Sysclk, }; use crate::pac::{PWR, RCC}; use crate::time::Hertz; @@ -12,28 +12,20 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); -/// System clock mux source -#[derive(Clone, Copy)] -pub enum ClockSrc { - HSE(Hertz), - HSI, - PLL, +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, } -/// PLL clock input source -#[derive(Clone, Copy, Debug)] -pub enum PllSource { - HSI, - HSE(Hertz), -} - -impl Into for PllSource { - fn into(self) -> Pllsrc { - match self { - PllSource::HSE(..) => Pllsrc::HSE, - PllSource::HSI => Pllsrc::HSI, - } - } +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, } /// PLL Configuration @@ -43,22 +35,22 @@ impl Into for PllSource { /// frequency ranges for each of these settings. pub struct Pll { /// PLL Source clock selection. - pub source: PllSource, + pub source: Pllsrc, /// PLL pre-divider - pub prediv_m: PllM, + pub prediv: PllPreDiv, /// PLL multiplication factor for VCO - pub mul_n: PllN, + pub mul: PllMul, /// PLL division factor for P clock (ADC Clock) - pub div_p: Option, + pub divp: Option, /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) - pub div_q: Option, + pub divq: Option, /// PLL division factor for R clock (SYSCLK) - pub div_r: Option, + pub divr: Option, } /// Sets the source for the 48MHz clock to the USB and RNG peripherals. @@ -73,39 +65,53 @@ pub enum Clock48MhzSrc { } /// Clocks configutation +#[non_exhaustive] pub struct Config { - pub mux: ClockSrc, + pub hsi: bool, + pub hse: Option, + pub sys: Sysclk, + pub hsi48: Option, + + pub pll: Option, + + /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration + /// MUST turn on the PLLR output. pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub low_power_run: bool, - /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration - /// MUST turn on the PLLR output. - pub pll: Option, + /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. - pub clock_48mhz_src: Option, + pub clk48_src: Option, + + pub ls: super::LsConfig, + pub adc12_clock_source: AdcClockSource, pub adc345_clock_source: AdcClockSource, pub fdcan_clock_source: FdCanClockSource, - pub ls: super::LsConfig, + pub boost: bool, } impl Default for Config { #[inline] fn default() -> Config { Config { - mux: ClockSrc::HSI, + hsi: true, + hse: None, + sys: Sysclk::HSI, + hsi48: Some(Default::default()), + pll: None, ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1, low_power_run: false, - pll: None, - clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), + clk48_src: Some(Clock48MhzSrc::Hsi48(Default::default())), + ls: Default::default(), adc12_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE, fdcan_clock_source: FdCanClockSource::PCLK1, - ls: Default::default(), + boost: false, } } } @@ -117,34 +123,60 @@ pub struct PllFreq { } pub(crate) unsafe fn init(config: Config) { + // Configure HSI + let hsi = match config.hsi { + false => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + true => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + Some(HSI_FREQ) + } + }; + + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } + + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + let pll_freq = config.pll.map(|pll_config| { let src_freq = match pll_config.source { - PllSource::HSI => { - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - HSI_FREQ - } - PllSource::HSE(freq) => { - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - freq - } + Pllsrc::HSI => unwrap!(hsi), + Pllsrc::HSE => unwrap!(hse), + _ => unreachable!(), }; + // TODO: check PLL input, internal and output frequencies for validity + // Disable PLL before configuration RCC.cr().modify(|w| w.set_pllon(false)); while RCC.cr().read().pllrdy() {} - let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n; + let internal_freq = src_freq / pll_config.prediv * pll_config.mul; RCC.pllcfgr().write(|w| { - w.set_plln(pll_config.mul_n); - w.set_pllm(pll_config.prediv_m); + w.set_plln(pll_config.mul); + w.set_pllm(pll_config.prediv); w.set_pllsrc(pll_config.source.into()); }); - let pll_p_freq = pll_config.div_p.map(|div_p| { + let pll_p_freq = pll_config.divp.map(|div_p| { RCC.pllcfgr().modify(|w| { w.set_pllp(div_p); w.set_pllpen(true); @@ -152,7 +184,7 @@ pub(crate) unsafe fn init(config: Config) { internal_freq / div_p }); - let pll_q_freq = pll_config.div_q.map(|div_q| { + let pll_q_freq = pll_config.divq.map(|div_q| { RCC.pllcfgr().modify(|w| { w.set_pllq(div_q); w.set_pllqen(true); @@ -160,7 +192,7 @@ pub(crate) unsafe fn init(config: Config) { internal_freq / div_q }); - let pll_r_freq = pll_config.div_r.map(|div_r| { + let pll_r_freq = pll_config.divr.map(|div_r| { RCC.pllcfgr().modify(|w| { w.set_pllr(div_r); w.set_pllren(true); @@ -179,22 +211,10 @@ pub(crate) unsafe fn init(config: Config) { } }); - let (sys_clk, sw) = match config.mux { - ClockSrc::HSI => { - // Enable HSI - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ, Sw::HSI) - } - ClockSrc::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - - (freq, Sw::HSE) - } - ClockSrc::PLL => { + let (sys_clk, sw) = match config.sys { + Sysclk::HSI => (HSI_FREQ, Sw::HSI), + Sysclk::HSE => (unwrap!(hse), Sw::HSE), + Sysclk::PLL1_R => { assert!(pll_freq.is_some()); assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); @@ -202,10 +222,9 @@ pub(crate) unsafe fn init(config: Config) { assert!(freq <= 170_000_000); - if freq >= 150_000_000 { - // Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234) + if config.boost { + // Enable Core Boost mode ([RM0440] p234) PWR.cr5().modify(|w| w.set_r1mode(false)); - // Set flash wait state in boost mode based on frequency ([RM0440] p191) if freq <= 36_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); } else if freq <= 68_000_000 { @@ -218,8 +237,8 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); } } else { + // Enable Core Boost mode ([RM0440] p234) PWR.cr5().modify(|w| w.set_r1mode(true)); - // Set flash wait state in normal mode based on frequency ([RM0440] p191) if freq <= 30_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); } else if freq <= 60_000_000 { @@ -235,6 +254,7 @@ pub(crate) unsafe fn init(config: Config) { (Hertz(freq), Sw::PLL1_R) } + _ => unreachable!(), }; RCC.cfgr().modify(|w| { @@ -263,7 +283,7 @@ pub(crate) unsafe fn init(config: Config) { }; // Setup the 48 MHz clock if needed - if let Some(clock_48mhz_src) = config.clock_48mhz_src { + if let Some(clock_48mhz_src) = config.clk48_src { let source = match clock_48mhz_src { Clock48MhzSrc::PllQ => { // Make sure the PLLQ is enabled and running at 48Mhz @@ -317,9 +337,33 @@ pub(crate) unsafe fn init(config: Config) { pclk2_tim: Some(apb2_tim_freq), adc: adc12_ck, adc34: adc345_ck, - pll1_p: None, - pll1_q: None, // TODO - hse: None, // TODO + pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p), + pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_p), + hse: hse, rtc: rtc, ); } + +// TODO: if necessary, make more of these gated behind cfg attrs +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + /// HSE 4-48MHz (RM0440 p280) + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(48_000_000); + + /// External Clock ?-48MHz (RM0440 p280) + pub(crate) const HSE_BYP: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + + /// SYSCLK ?-170MHz (RM0440 p282) + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); + + /// PLL Output frequency ?-170MHz (RM0440 p281) + pub(crate) const PCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); + + // Left over from f.rs, remove if not necessary + pub(crate) const HCLK: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000); + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); + pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); +} diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 35324d931..99e3ef63b 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; -use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSource}; +use embassy_stm32::rcc::{AdcClockSource, Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; use embassy_stm32::Config; use embassy_time::{Delay, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,17 +14,17 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.pll = Some(Pll { - source: PllSource::HSI, - prediv_m: PllM::DIV4, - mul_n: PllN::MUL85, - div_p: None, - div_q: None, + source: Pllsrc::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL85, + divp: None, + divq: None, // Main system clock at 170 MHz - div_r: Some(PllR::DIV2), + divr: Some(PllRDiv::DIV2), }); config.rcc.adc12_clock_source = AdcClockSource::SYS; - config.rcc.mux = ClockSrc::PLL; + config.rcc.sys = Sysclk::PLL1_R; let mut p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 46ebe0b0d..5274de79d 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSource}; +use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -13,16 +13,16 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.pll = Some(Pll { - source: PllSource::HSI, - prediv_m: PllM::DIV4, - mul_n: PllN::MUL85, - div_p: None, - div_q: None, + source: Pllsrc::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL85, + divp: None, + divq: None, // Main system clock at 170 MHz - div_r: Some(PllR::DIV2), + divr: Some(PllRDiv::DIV2), }); - config.rcc.mux = ClockSrc::PLL; + config.rcc.sys = Sysclk::PLL1_R; let _p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index c26fa76b7..d9207e4cd 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -3,7 +3,9 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSource}; +use embassy_stm32::rcc::{ + Clock48MhzSrc, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk, +}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{self, Driver, Instance}; use embassy_stm32::{bind_interrupts, peripherals, Config}; @@ -24,25 +26,30 @@ async fn main(_spawner: Spawner) { // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. const USE_HSI48: bool = true; - let plldivq = if USE_HSI48 { None } else { Some(PllQ::DIV6) }; + let plldivq = if USE_HSI48 { None } else { Some(PllQDiv::DIV6) }; - config.rcc.pll = Some(Pll { - source: PllSource::HSE(Hertz(8_000_000)), - prediv_m: PllM::DIV2, - mul_n: PllN::MUL72, - div_p: None, - div_q: plldivq, - // Main system clock at 144 MHz - div_r: Some(PllR::DIV2), + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Oscillator, }); - config.rcc.mux = ClockSrc::PLL; + config.rcc.pll = Some(Pll { + source: Pllsrc::HSE, + prediv: PllPreDiv::DIV2, + mul: PllMul::MUL72, + divp: None, + divq: plldivq, + // Main system clock at 144 MHz + divr: Some(PllRDiv::DIV2), + }); + + config.rcc.sys = Sysclk::PLL1_R; if USE_HSI48 { // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. - config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); + config.rcc.clk48_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); } else { - config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); + config.rcc.clk48_src = Some(Clock48MhzSrc::PllQ); } let p = embassy_stm32::init(config); From bd0b450ca4ff36b2b1fe0b3b422cd478f9201ad0 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 15 Feb 2024 17:43:20 -0500 Subject: [PATCH 51/68] Improve rp2040 i2c slave This commit takes the fixes and error reporting improvements from jcdickinson's work and applies them without overlaying a software state machine on top of the hardware state machine. Also allows configuration of response to 'general call' writes. --- embassy-rp/src/i2c_slave.rs | 234 +++++++++++++++++++------------ examples/rp/src/bin/i2c_slave.rs | 2 +- 2 files changed, 147 insertions(+), 89 deletions(-) diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index 721b7a1f6..d3cc20f39 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -4,6 +4,7 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::into_ref; +use embassy_time::{block_for, Duration}; use pac::i2c; use crate::i2c::{ @@ -21,6 +22,16 @@ pub enum Error { Abort(AbortReason), /// User passed in a response buffer that was 0 length InvalidResponseBufferLength, + /// The response buffer length was too short to contain the message + /// + /// The length parameter will always be the length of the buffer, and is + /// provided as a convenience for matching alongside `Command::Write`. + PartialWrite(usize), + /// The response buffer length was too short to contain the message + /// + /// The length parameter will always be the length of the buffer, and is + /// provided as a convenience for matching alongside `Command::GeneralCall`. + PartialGeneralCall(usize), } /// Received command @@ -56,17 +67,23 @@ pub enum ReadStatus { pub struct Config { /// Target Address pub addr: u16, + /// Control if the peripheral should ack to and report general calls. + pub general_call: bool, } impl Default for Config { fn default() -> Self { - Self { addr: 0x55 } + Self { + addr: 0x55, + general_call: true, + } } } /// I2CSlave driver. pub struct I2cSlave<'d, T: Instance> { phantom: PhantomData<&'d mut T>, + pending_byte: Option, } impl<'d, T: Instance> I2cSlave<'d, T> { @@ -96,7 +113,19 @@ impl<'d, T: Instance> I2cSlave<'d, T> { w.set_master_mode(false); w.set_ic_slave_disable(false); w.set_tx_empty_ctrl(true); + w.set_rx_fifo_full_hld_ctrl(true); + + // This typically makes no sense for a slave, but it is used to + // tune spike suppression, according to the datasheet. + w.set_speed(pac::i2c::vals::Speed::FAST); + + // Generate stop interrupts for general calls + // This also causes stop interrupts for other devices on the bus but those will not be + // propagated up to the application. + w.set_stop_det_ifaddressed(!config.general_call); }); + p.ic_ack_general_call() + .write(|w| w.set_ack_gen_call(config.general_call)); // Set FIFO watermarks to 1 to make things simpler. This is encoded // by a register value of 0. Rx watermark should never change, but Tx watermark will be @@ -119,7 +148,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { phantom: PhantomData } + Self { + phantom: PhantomData, + pending_byte: None, + } } /// Calls `f` to check if we are ready or not. @@ -133,8 +165,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> { future::poll_fn(|cx| { let r = f(self); - trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0); - if r.is_pending() { T::waker().register(cx.waker()); g(self); @@ -146,14 +176,36 @@ impl<'d, T: Instance> I2cSlave<'d, T> { } #[inline(always)] - fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { + fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) { let p = T::regs(); - let len = p.ic_rxflr().read().rxflr() as usize; - let end = offset + len; - for i in offset..end { - buffer[i] = p.ic_data_cmd().read().dat(); + + for b in &mut buffer[*offset..] { + if let Some(pending) = self.pending_byte.take() { + *b = pending; + *offset += 1; + continue; + } + + let status = p.ic_status().read(); + if !status.rfne() { + break; + } + + let dat = p.ic_data_cmd().read(); + if *offset != 0 && dat.first_data_byte() { + // The RP2040 state machine will keep placing bytes into the + // FIFO, even if they are part of a subsequent write transaction. + // + // Unfortunately merely reading ic_data_cmd will consume that + // byte, the first byte of the next transaction, so we need + // to store it elsewhere + self.pending_byte = Some(dat.dat()); + break; + } + + *b = dat.dat(); + *offset += 1; } - end } #[inline(always)] @@ -165,52 +217,62 @@ impl<'d, T: Instance> I2cSlave<'d, T> { } /// Wait asynchronously for commands from an I2C master. - /// `buffer` is provided in case master does a 'write' and is unused for 'read'. + /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'. pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { let p = T::regs(); - p.ic_clr_intr().read(); // set rx fifo watermark to 1 byte p.ic_rx_tl().write(|w| w.set_rx_tl(0)); let mut len = 0; - let ret = self - .wait_on( - |me| { - let stat = p.ic_raw_intr_stat().read(); - if p.ic_rxflr().read().rxflr() > 0 { - len = me.drain_fifo(buffer, len); - // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise - p.ic_rx_tl().write(|w| w.set_rx_tl(11)); - } + self.wait_on( + |me| { + let stat = p.ic_raw_intr_stat().read(); - if stat.restart_det() && stat.rd_req() { - Poll::Ready(Ok(Command::WriteRead(len))) - } else if stat.gen_call() && stat.stop_det() && len > 0 { - Poll::Ready(Ok(Command::GeneralCall(len))) - } else if stat.stop_det() { - Poll::Ready(Ok(Command::Write(len))) - } else if stat.rd_req() { - Poll::Ready(Ok(Command::Read)) + if p.ic_rxflr().read().rxflr() > 0 { + me.drain_fifo(buffer, &mut len); + // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise + p.ic_rx_tl().write(|w| w.set_rx_tl(11)); + } + + if buffer.len() == len { + if stat.gen_call() { + return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len()))); } else { - Poll::Pending + return Poll::Ready(Err(Error::PartialWrite(buffer.len()))); } - }, - |_me| { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_restart_det(true); - w.set_m_gen_call(true); - w.set_m_rd_req(true); - w.set_m_rx_full(true); - }); - }, - ) - .await; + } - p.ic_clr_intr().read(); - - ret + if stat.restart_det() && stat.rd_req() { + p.ic_clr_restart_det().read(); + Poll::Ready(Ok(Command::WriteRead(len))) + } else if stat.gen_call() && stat.stop_det() && len > 0 { + p.ic_clr_gen_call().read(); + p.ic_clr_stop_det().read(); + Poll::Ready(Ok(Command::GeneralCall(len))) + } else if stat.stop_det() && len > 0 { + p.ic_clr_stop_det().read(); + Poll::Ready(Ok(Command::Write(len))) + } else if stat.rd_req() { + p.ic_clr_stop_det().read(); + p.ic_clr_restart_det().read(); + p.ic_clr_gen_call().read(); + Poll::Ready(Ok(Command::Read)) + } else { + Poll::Pending + } + }, + |_me| { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_restart_det(true); + w.set_m_gen_call(true); + w.set_m_rd_req(true); + w.set_m_rx_full(true); + }); + }, + ) + .await } /// Respond to an I2C master READ command, asynchronously. @@ -223,47 +285,47 @@ impl<'d, T: Instance> I2cSlave<'d, T> { let mut chunks = buffer.chunks(FIFO_SIZE as usize); - let ret = self - .wait_on( - |me| { - if let Err(abort_reason) = me.read_and_clear_abort_reason() { - if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { - return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); - } else { - return Poll::Ready(Err(abort_reason)); - } - } - - if let Some(chunk) = chunks.next() { - me.write_to_fifo(chunk); - - Poll::Pending + self.wait_on( + |me| { + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { + p.ic_clr_intr().read(); + return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); } else { - let stat = p.ic_raw_intr_stat().read(); - - if stat.rx_done() && stat.stop_det() { - Poll::Ready(Ok(ReadStatus::Done)) - } else if stat.rd_req() { - Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) - } else { - Poll::Pending - } + return Poll::Ready(Err(abort_reason)); } - }, - |_me| { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_rx_done(true); - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }) - }, - ) - .await; + } - p.ic_clr_intr().read(); + if let Some(chunk) = chunks.next() { + me.write_to_fifo(chunk); - ret + p.ic_clr_rd_req().read(); + + Poll::Pending + } else { + let stat = p.ic_raw_intr_stat().read(); + + if stat.rx_done() && stat.stop_det() { + p.ic_clr_rx_done().read(); + p.ic_clr_stop_det().read(); + Poll::Ready(Ok(ReadStatus::Done)) + } else if stat.rd_req() && stat.tx_empty() { + Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) + } else { + Poll::Pending + } + } + }, + |_me| { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_rx_done(true); + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await } /// Respond to reads with the fill byte until the controller stops asking @@ -294,10 +356,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> { let p = T::regs(); let mut abort_reason = p.ic_tx_abrt_source().read(); - // Mask off fifo flush count - let tx_flush_cnt = abort_reason.tx_flush_cnt(); - abort_reason.set_tx_flush_cnt(0); - // Mask off master_dis abort_reason.set_abrt_master_dis(false); @@ -314,8 +372,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> { AbortReason::NoAcknowledge } else if abort_reason.arb_lost() { AbortReason::ArbitrationLoss - } else if abort_reason.abrt_slvflush_txfifo() { - AbortReason::TxNotEmpty(tx_flush_cnt) + } else if abort_reason.tx_flush_cnt() > 0 { + AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt()) } else { AbortReason::Other(abort_reason.0) }; diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs index ac470d2be..9fffb4646 100644 --- a/examples/rp/src/bin/i2c_slave.rs +++ b/examples/rp/src/bin/i2c_slave.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) { let c_sda = p.PIN_1; let c_scl = p.PIN_0; let mut config = i2c::Config::default(); - config.frequency = 5_000; + config.frequency = 1_000_000; let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); unwrap!(spawner.spawn(controller_task(controller))); From 396041ad1a954a09bb2cf45ea685ca8a1bbd8623 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 00:04:35 +0100 Subject: [PATCH 52/68] Commented out currently unused constants --- embassy-stm32/src/rcc/g4.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index edd14ab23..c9ac2bb0b 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -357,13 +357,13 @@ mod max { pub(crate) const HSE_BYP: RangeInclusive = Hertz(0)..=Hertz(48_000_000); /// SYSCLK ?-170MHz (RM0440 p282) - pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); + //pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); /// PLL Output frequency ?-170MHz (RM0440 p281) - pub(crate) const PCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); + //pub(crate) const PCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); // Left over from f.rs, remove if not necessary - pub(crate) const HCLK: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000); - pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); - pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); + //pub(crate) const HCLK: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000); + //pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); + //pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); } From a75007f5ccff25e5bc3afcc806ceb197f61be0bf Mon Sep 17 00:00:00 2001 From: DafabHoid Date: Fri, 16 Feb 2024 00:30:24 +0100 Subject: [PATCH 53/68] cyw43: Reuse buf to reduce stack usage --- cyw43/src/runner.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs index b2a9e3e80..c72cf0def 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -242,13 +242,12 @@ where cmd, iface, }) => { - self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; + self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await; self.check_status(&mut buf).await; } Either3::Second(packet) => { trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); - let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); // There MUST be 2 bytes of padding between the SDPCM and BDC headers. @@ -480,9 +479,8 @@ where self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 } - async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); + async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) { + let buf8 = slice8_mut(buf); let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); From 72e92647e2634958d2229f35264a3dc696c3d33e Mon Sep 17 00:00:00 2001 From: Adam Snaider Date: Thu, 15 Feb 2024 18:10:20 -0500 Subject: [PATCH 54/68] Add unsafe constructor for AnyPin --- embassy-rp/src/gpio.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 93b29bbf9..a121a8036 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -889,6 +889,17 @@ pub struct AnyPin { pin_bank: u8, } +impl AnyPin { + /// Unsafely create a new type-erased pin. + /// + /// # Safety + /// + /// You must ensure that you’re only using one instance of this type at a time. + pub unsafe fn steal(pin_bank: u8) -> Self { + Self { pin_bank } + } +} + impl_peripheral!(AnyPin); impl Pin for AnyPin {} From ae02467434a344abbbb367123044c5e407c80a14 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 16 Feb 2024 02:07:21 +0100 Subject: [PATCH 55/68] stm32: update metapac. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/build.rs | 76 ++++++++++++------------------------ embassy-stm32/src/rcc/wba.rs | 2 +- 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index c384f14f1..389ed0041 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cc1a1603e61881a6f0a1a47c12c16c57c245ba8" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cc1a1603e61881a6f0a1a47c12c16c57c245ba8", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 35023bf1f..ee88d4541 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -5,8 +5,7 @@ use std::{env, fs}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; -use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet}; -use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA}; +use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccKernelClock, StopMode, METADATA}; fn main() { let target = env::var("TARGET").unwrap(); @@ -414,38 +413,6 @@ fn main() { .find(|r| r.kind == "rcc") .unwrap(); - // ======== - // Generate rcc fieldset and enum maps - let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = { - let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items; - let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect(); - let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect(); - - rcc_blocks - .iter() - .filter_map(|b| match &b.inner { - BlockItemInner::Register(register) => register.fieldset.map(|f| (b.name, f)), - _ => None, - }) - .filter_map(|(b, f)| { - rcc_fieldsets.get(f).map(|f| { - ( - b, - f.fields - .iter() - .filter_map(|f| { - let enumm = f.enumm?; - let enumm = rcc_enums.get(enumm)?; - - Some((f.name, *enumm)) - }) - .collect(), - ) - }) - }) - .collect() - }; - // ======== // Generate RccPeripheral impls @@ -494,8 +461,8 @@ fn main() { let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; let pname = format_ident!("{}", p.name); - let en_reg = format_ident!("{}", en.register); - let set_en_field = format_ident!("set_{}", en.field); + let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); + let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); let refcount = force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1; @@ -523,21 +490,25 @@ fn main() { (TokenStream::new(), TokenStream::new()) }; - let mux_for = |mux: Option<&'static PeripheralRccRegister>| { - let mux = mux?; - let fieldset = rcc_enum_map.get(mux.register)?; - let enumm = fieldset.get(mux.field)?; + let clock_frequency = match &rcc.kernel_clock { + PeripheralRccKernelClock::Mux(mux) => { + let ir = &rcc_registers.ir; + let fieldset_name = mux.register.to_ascii_lowercase(); + let fieldset = ir + .fieldsets + .iter() + .find(|i| i.name.eq_ignore_ascii_case(&fieldset_name)) + .unwrap(); + let field_name = mux.field.to_ascii_lowercase(); + let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap(); + let enum_name = field.enumm.unwrap(); + let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap(); - Some((mux, *enumm)) - }; + let fieldset_name = format_ident!("{}", fieldset_name); + let field_name = format_ident!("{}", field_name); + let enum_name = format_ident!("{}", enum_name); - let clock_frequency = match mux_for(rcc.mux.as_ref()) { - Some((mux, rcc_enumm)) => { - let fieldset_name = format_ident!("{}", mux.register); - let field_name = format_ident!("{}", mux.field); - let enum_name = format_ident!("{}", rcc_enumm.name); - - let match_arms: TokenStream = rcc_enumm + let match_arms: TokenStream = enumm .variants .iter() .filter(|v| v.name != "DISABLE") @@ -561,9 +532,10 @@ fn main() { } } } - None => { - let clock_name = format_ident!("{}", rcc.clock); - clock_names.insert(rcc.clock.to_string()); + PeripheralRccKernelClock::Clock(clock) => { + let clock = clock.to_ascii_lowercase(); + let clock_name = format_ident!("{}", clock); + clock_names.insert(clock.to_string()); quote! { unsafe { crate::rcc::get_freqs().#clock_name.unwrap() } } diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 47ce4783c..fbf2d1cf9 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs @@ -49,7 +49,7 @@ impl Default for Config { apb2_pre: APBPrescaler::DIV1, apb7_pre: APBPrescaler::DIV1, ls: Default::default(), - adc_clock_source: AdcClockSource::HCLK1, + adc_clock_source: AdcClockSource::HCLK4, voltage_scale: VoltageScale::RANGE2, } } From f41b0f6ba95d0323f058a1ec2a2dc9b6f3317fee Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 15 Feb 2024 23:06:57 -0500 Subject: [PATCH 56/68] Remove unused imports --- embassy-rp/src/i2c_slave.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index d3cc20f39..979686627 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -4,7 +4,6 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::into_ref; -use embassy_time::{block_for, Duration}; use pac::i2c; use crate::i2c::{ From bcb0be21c180d4107e636083f5a1543a1f51447b Mon Sep 17 00:00:00 2001 From: Frank Plowman Date: Fri, 16 Feb 2024 17:24:58 +0000 Subject: [PATCH 57/68] embassy-nrf: Fix PDM gain register value derivation Co-authored-by: Sol Harter --- embassy-nrf/src/pdm.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 24fa29a4a..64d414d8f 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -4,6 +4,7 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -145,15 +146,14 @@ impl<'d, T: Instance> Pdm<'d, T> { } fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { - let gain_left = gain_left - .saturating_add(I7F1::from_bits(40)) - .saturating_to_num::() - .clamp(0, 0x50); - let gain_right = gain_right - .saturating_add(I7F1::from_bits(40)) - .saturating_to_num::() - .clamp(0, 0x50); - + let gain_to_bits = |gain: I7F1| -> u8 { + let gain = gain.saturating_add(I7F1::from_bits(0x28)) + .to_bits() + .clamp(0, 0x50); + unsafe { mem::transmute(gain) } + }; + let gain_left = gain_to_bits(gain_left); + let gain_right = gain_to_bits(gain_right); r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); } From 32e4c93954abb3add1c9b2c8fff627c497d47258 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 19:58:19 +0100 Subject: [PATCH 58/68] Removed dangling doc comments --- embassy-stm32/src/rcc/g4.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index c9ac2bb0b..5ac933af4 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -356,10 +356,10 @@ mod max { /// External Clock ?-48MHz (RM0440 p280) pub(crate) const HSE_BYP: RangeInclusive = Hertz(0)..=Hertz(48_000_000); - /// SYSCLK ?-170MHz (RM0440 p282) + // SYSCLK ?-170MHz (RM0440 p282) //pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); - /// PLL Output frequency ?-170MHz (RM0440 p281) + // PLL Output frequency ?-170MHz (RM0440 p281) //pub(crate) const PCLK: RangeInclusive = Hertz(0)..=Hertz(170_000_000); // Left over from f.rs, remove if not necessary From ae748339999034854a78629c2e2f160d0d8417f9 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 20:32:35 +0100 Subject: [PATCH 59/68] Removed redundant HSI48 configuration --- embassy-stm32/src/rcc/g4.rs | 32 ++++++++------------------ examples/stm32g4/src/bin/usb_serial.rs | 7 +++--- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 5ac933af4..7ca741fc7 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -3,8 +3,9 @@ use stm32_metapac::rcc::vals::{Adcsel, Sw}; use stm32_metapac::FLASH; pub use crate::pac::rcc::vals::{ - Adcsel as AdcClockSource, Clk48sel, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllPreDiv, - Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler, Sw as Sysclk, + Adcsel as AdcClockSource, Clk48sel as Clk48Src, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, + Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler, + Sw as Sysclk, }; use crate::pac::{PWR, RCC}; use crate::time::Hertz; @@ -53,17 +54,6 @@ pub struct Pll { pub divr: Option, } -/// Sets the source for the 48MHz clock to the USB and RNG peripherals. -pub enum Clock48MhzSrc { - /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the - /// oscillator to comply with the USB specification for oscillator tolerance. - Hsi48(super::Hsi48Config), - /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the - /// PLL needs to be using the HSE source to comply with the USB specification for oscillator - /// tolerance. - PllQ, -} - /// Clocks configutation #[non_exhaustive] pub struct Config { @@ -82,7 +72,7 @@ pub struct Config { pub low_power_run: bool, /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. - pub clk48_src: Option, + pub clk48_src: Clk48Src, pub ls: super::LsConfig, @@ -106,7 +96,7 @@ impl Default for Config { apb1_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1, low_power_run: false, - clk48_src: Some(Clock48MhzSrc::Hsi48(Default::default())), + clk48_src: Clk48Src::HSI48, ls: Default::default(), adc12_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE, @@ -283,19 +273,17 @@ pub(crate) unsafe fn init(config: Config) { }; // Setup the 48 MHz clock if needed - if let Some(clock_48mhz_src) = config.clk48_src { - let source = match clock_48mhz_src { - Clock48MhzSrc::PllQ => { + { + let source = match config.clk48_src { + Clk48Src::PLL1_Q => { // Make sure the PLLQ is enabled and running at 48Mhz let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); crate::pac::rcc::vals::Clk48sel::PLL1_Q } - Clock48MhzSrc::Hsi48(config) => { - super::init_hsi48(config); - crate::pac::rcc::vals::Clk48sel::HSI48 - } + Clk48Src::HSI48 => crate::pac::rcc::vals::Clk48sel::HSI48, + _ => unreachable!(), }; RCC.ccipr().modify(|w| w.set_clk48sel(source)); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index d9207e4cd..353ac1799 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -4,7 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::rcc::{ - Clock48MhzSrc, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk, + Clk48Src, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk, }; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{self, Driver, Instance}; @@ -47,9 +47,10 @@ async fn main(_spawner: Spawner) { if USE_HSI48 { // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. - config.rcc.clk48_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); + config.rcc.clk48_src = Clk48Src::HSI48; } else { - config.rcc.clk48_src = Some(Clock48MhzSrc::PllQ); + config.rcc.clk48_src = Clk48Src::PLL1_Q; } let p = embassy_stm32::init(config); From 25a95503f661f064e57854df8f831ad681868a4c Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 20:41:04 +0100 Subject: [PATCH 60/68] Configured HSI48 if enabled, assert is enabled if chosen as clk48 source --- embassy-stm32/src/rcc/g4.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 7ca741fc7..22dfa25c6 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -145,6 +145,11 @@ pub(crate) unsafe fn init(config: Config) { } }; + // Configure HSI48 if required + if let Some(hsi48_config) = config.hsi48 { + super::init_hsi48(hsi48_config); + } + let pll_freq = config.pll.map(|pll_config| { let src_freq = match pll_config.source { Pllsrc::HSI => unwrap!(hsi), @@ -272,7 +277,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - // Setup the 48 MHz clock if needed + // Configure the 48MHz clock source for USB and RNG peripherals. { let source = match config.clk48_src { Clk48Src::PLL1_Q => { @@ -282,7 +287,11 @@ pub(crate) unsafe fn init(config: Config) { crate::pac::rcc::vals::Clk48sel::PLL1_Q } - Clk48Src::HSI48 => crate::pac::rcc::vals::Clk48sel::HSI48, + Clk48Src::HSI48 => { + // Make sure HSI48 is enabled + assert!(config.hsi48.is_some()); + crate::pac::rcc::vals::Clk48sel::HSI48 + } _ => unreachable!(), }; From e465dacf739e499f64b7943f4674edfe060f83b7 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 21:34:12 +0100 Subject: [PATCH 61/68] Added documentation, fixed and refined boost and flash read latency config --- embassy-stm32/src/rcc/g4.rs | 69 +++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 22dfa25c6..e2afd5260 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -13,6 +13,7 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); +/// HSE Mode #[derive(Clone, Copy, Eq, PartialEq)] pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -21,6 +22,7 @@ pub enum HseMode { Bypass, } +/// HSE Configuration #[derive(Clone, Copy, Eq, PartialEq)] pub struct Hse { /// HSE frequency. @@ -57,11 +59,19 @@ pub struct Pll { /// Clocks configutation #[non_exhaustive] pub struct Config { + /// HSI Enable pub hsi: bool, + + /// HSE Configuration pub hse: Option, + + /// System Clock Configuration pub sys: Sysclk, + + /// HSI48 Configuration pub hsi48: Option, + /// PLL Configuration pub pll: Option, /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration @@ -69,17 +79,26 @@ pub struct Config { pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, + pub low_power_run: bool, /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. pub clk48_src: Clk48Src, + /// Low-Speed Clock Configuration pub ls: super::LsConfig, + /// Clock Source for ADCs 1 and 2 pub adc12_clock_source: AdcClockSource, + + /// Clock Source for ADCs 3, 4 and 5 pub adc345_clock_source: AdcClockSource, + + /// Clock Source for FDCAN pub fdcan_clock_source: FdCanClockSource, + /// Enable range1 boost mode + /// Recommended when the SYSCLK frequency is greater than 150MHz. pub boost: bool, } @@ -217,36 +236,6 @@ pub(crate) unsafe fn init(config: Config) { assert!(freq <= 170_000_000); - if config.boost { - // Enable Core Boost mode ([RM0440] p234) - PWR.cr5().modify(|w| w.set_r1mode(false)); - if freq <= 36_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); - } else if freq <= 68_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); - } else if freq <= 102_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); - } else if freq <= 136_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); - } else { - FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); - } - } else { - // Enable Core Boost mode ([RM0440] p234) - PWR.cr5().modify(|w| w.set_r1mode(true)); - if freq <= 30_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); - } else if freq <= 60_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); - } else if freq <= 80_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); - } else if freq <= 120_000_000 { - FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); - } else { - FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); - } - } - (Hertz(freq), Sw::PLL1_R) } _ => unreachable!(), @@ -261,6 +250,26 @@ pub(crate) unsafe fn init(config: Config) { let ahb_freq = sys_clk / config.ahb_pre; + // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) + PWR.cr5().modify(|w| w.set_r1mode(!config.boost)); + + // Configure flash read access latency based on boost mode and frequency (RM0440 p98) + FLASH.acr().modify(|w| { + w.set_latency(match (config.boost, ahb_freq.0) { + (true, ..=34_000_000) => Latency::WS0, + (true, ..=68_000_000) => Latency::WS1, + (true, ..=102_000_000) => Latency::WS2, + (true, ..=136_000_000) => Latency::WS3, + (true, _) => Latency::WS4, + + (false, ..=36_000_000) => Latency::WS0, + (false, ..=60_000_000) => Latency::WS1, + (false, ..=90_000_000) => Latency::WS2, + (false, ..=120_000_000) => Latency::WS3, + (false, _) => Latency::WS4, + }) + }); + let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { From c5f39d5c89240d17b0d7c8ee48fc48757c163bba Mon Sep 17 00:00:00 2001 From: Frank Plowman Date: Fri, 16 Feb 2024 20:42:14 +0000 Subject: [PATCH 62/68] embassy-nrf: Use fully-qualified `core::mem::transmute` --- embassy-nrf/src/pdm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 64d414d8f..d03315fb6 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -4,7 +4,6 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -150,7 +149,7 @@ impl<'d, T: Instance> Pdm<'d, T> { let gain = gain.saturating_add(I7F1::from_bits(0x28)) .to_bits() .clamp(0, 0x50); - unsafe { mem::transmute(gain) } + unsafe { core::mem::transmute(gain) } }; let gain_left = gain_to_bits(gain_left); let gain_right = gain_to_bits(gain_right); From 07987aea4e2706d8a7fa252ba3f6e31d576537ee Mon Sep 17 00:00:00 2001 From: Frank Plowman Date: Fri, 16 Feb 2024 20:45:58 +0000 Subject: [PATCH 63/68] embassy-nrf: Fix various typos and make style more consistent --- embassy-nrf/src/pdm.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index d03315fb6..dcaeffe0f 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! Pulse Density Modulation (PDM) mirophone driver. +//! Pulse Density Modulation (PDM) mirophone driver #![macro_use] @@ -26,7 +26,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; pub use crate::pac::pdm::ratio::RATIO_A as Ratio; use crate::{interrupt, Peripheral}; -/// Interrupt handler. +/// Interrupt handler pub struct InterruptHandler { _phantom: PhantomData, } @@ -56,12 +56,12 @@ pub struct Pdm<'d, T: Instance> { _peri: PeripheralRef<'d, T>, } -/// PDM error. +/// PDM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - /// Buffer is too long. + /// Buffer is too long BufferTooLong, /// Buffer is empty BufferZeroLength, @@ -75,13 +75,13 @@ static DUMMY_BUFFER: [i16; 1] = [0; 1]; /// The state of a continuously running sampler. While it reflects /// the progress of a sampler, it also signals what should be done -/// next. For example, if the sampler has stopped then the Pdm implementation -/// can then tear down its infrastructure. +/// next. For example, if the sampler has stopped then the PDM implementation +/// can then tear down its infrastructure #[derive(PartialEq)] pub enum SamplerState { - /// The sampler processed the samples and is ready for more. + /// The sampler processed the samples and is ready for more Sampled, - /// The sampler is done processing samples. + /// The sampler is done processing samples Stopped, } @@ -162,12 +162,12 @@ impl<'d, T: Instance> Pdm<'d, T> { Self::_set_gain(T::regs(), gain_left, gain_right) } - /// Start sampling microphon data into a dummy buffer - /// Usefull to start the microphon and keep it active between recording samples + /// Start sampling microphone data into a dummy buffer. + /// Useful to start the microphone and keep it active between recording samples. pub async fn start(&mut self) { let r = T::regs(); - // start dummy sampling because microphon needs some setup time + // start dummy sampling because microphone needs some setup time r.sample .ptr .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); @@ -178,14 +178,14 @@ impl<'d, T: Instance> Pdm<'d, T> { r.tasks_start.write(|w| unsafe { w.bits(1) }); } - /// Stop sampling microphon data inta a dummy buffer + /// Stop sampling microphone data inta a dummy buffer pub async fn stop(&mut self) { let r = T::regs(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.events_started.reset(); } - /// Sample data into the given buffer. + /// Sample data into the given buffer pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { if buffer.len() == 0 { return Err(Error::BufferZeroLength); @@ -302,7 +302,7 @@ impl<'d, T: Instance> Pdm<'d, T> { }); // Don't reorder the start event before the previous writes. Hopefully self - // wouldn't happen anyway. + // wouldn't happen anyway compiler_fence(Ordering::SeqCst); r.tasks_start.write(|w| unsafe { w.bits(1) }); @@ -313,11 +313,11 @@ impl<'d, T: Instance> Pdm<'d, T> { let drop = OnDrop::new(|| { r.tasks_stop.write(|w| unsafe { w.bits(1) }); - // N.B. It would be better if this were async, but Drop only support sync code. + // N.B. It would be better if this were async, but Drop only support sync code while r.events_stopped.read().bits() != 0 {} }); - // Wait for events and complete when the sampler indicates it has had enough. + // Wait for events and complete when the sampler indicates it has had enough poll_fn(|cx| { let r = T::regs(); @@ -330,7 +330,7 @@ impl<'d, T: Instance> Pdm<'d, T> { r.intenset.write(|w| w.end().set()); if !done { - // Discard the last buffer after the user requested a stop. + // Discard the last buffer after the user requested a stop if sampler(&bufs[current_buffer]) == SamplerState::Sampled { let next_buffer = 1 - current_buffer; current_buffer = next_buffer; @@ -404,7 +404,7 @@ impl Default for Config { } } -/// PDM operation mode. +/// PDM operation mode #[derive(PartialEq)] pub enum OperationMode { /// Mono (1 channel) @@ -475,9 +475,9 @@ pub(crate) mod sealed { } } -/// PDM peripheral instance. +/// PDM peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { - /// Interrupt for this peripheral. + /// Interrupt for this peripheral type Interrupt: interrupt::typelevel::Interrupt; } From 7d111191689460ddfdce08dec7195cb9fa1b598b Mon Sep 17 00:00:00 2001 From: Frank Plowman Date: Fri, 16 Feb 2024 20:47:19 +0000 Subject: [PATCH 64/68] embassy-nrf: Don't break lines; make rustfmt happy --- embassy-nrf/src/pdm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index dcaeffe0f..6ddc4dc0a 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -146,9 +146,7 @@ impl<'d, T: Instance> Pdm<'d, T> { fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { let gain_to_bits = |gain: I7F1| -> u8 { - let gain = gain.saturating_add(I7F1::from_bits(0x28)) - .to_bits() - .clamp(0, 0x50); + let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50); unsafe { core::mem::transmute(gain) } }; let gain_left = gain_to_bits(gain_left); From a24087c36c60e97f8b0aaefe57111c3a2edd6e8a Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 16 Feb 2024 21:52:58 +0100 Subject: [PATCH 65/68] Configured SYSCLK after boost mode, added comments --- embassy-stm32/src/rcc/g4.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index e2afd5260..0c1a1e4b1 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -241,16 +241,13 @@ pub(crate) unsafe fn init(config: Config) { _ => unreachable!(), }; - RCC.cfgr().modify(|w| { - w.set_sw(sw); - w.set_hpre(config.ahb_pre); - w.set_ppre1(config.apb1_pre); - w.set_ppre2(config.apb2_pre); - }); - + // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let ahb_freq = sys_clk / config.ahb_pre; // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) + // TODO: according to RM0440 p235, when switching from range1-normal to range1-boost, it’s necessary to divide + // SYSCLK by 2 using the AHB prescaler, set boost and flash read latency, switch system frequency, wait 1us and + // reconfigure the AHB prescaler as desired. Unclear whether this is always necessary. PWR.cr5().modify(|w| w.set_r1mode(!config.boost)); // Configure flash read access latency based on boost mode and frequency (RM0440 p98) @@ -270,6 +267,14 @@ pub(crate) unsafe fn init(config: Config) { }) }); + // Now that boost mode and flash read access latency are configured, set up SYSCLK + RCC.cfgr().modify(|w| { + w.set_sw(sw); + w.set_hpre(config.ahb_pre); + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); + }); + let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::DIV1 => (ahb_freq, ahb_freq), pre => { From 6d7458dac7e768425342910e04a75c85e667cb82 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Sat, 17 Feb 2024 00:30:16 +0100 Subject: [PATCH 66/68] Refinements * Implemented boost mode dance (RM0440 p234-245, 6.5.1) * Enabled boost mode in usb_serial example, tested on hardware * Removed hard requirement of a valid 48MHz source (HSI48 is checked if requested, PLL passed through as-is and assumed to be valid) * Used calc_pclk to calculate APB frequencies * Refactored 48MHz configuration code to remove unnecessary let and block * Renamed ahb_freq to hclk for clarity and consistency --- embassy-stm32/src/rcc/g4.rs | 67 ++++++++++++-------------- examples/stm32g4/src/bin/usb_serial.rs | 1 + 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 0c1a1e4b1..382ffbead 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -242,17 +242,25 @@ pub(crate) unsafe fn init(config: Config) { }; // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. - let ahb_freq = sys_clk / config.ahb_pre; + let hclk = sys_clk / config.ahb_pre; // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) - // TODO: according to RM0440 p235, when switching from range1-normal to range1-boost, it’s necessary to divide - // SYSCLK by 2 using the AHB prescaler, set boost and flash read latency, switch system frequency, wait 1us and - // reconfigure the AHB prescaler as desired. Unclear whether this is always necessary. - PWR.cr5().modify(|w| w.set_r1mode(!config.boost)); + if config.boost { + // RM0440 p235 + // “The sequence to switch from Range1 normal mode to Range1 boost mode is: + // 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency. + RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2)); + // 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode) + PWR.cr5().modify(|w| w.set_r1mode(false)); + + // Below: + // 3. Adjust wait states according to new freq target + // 4. Configure and switch to new frequency + } // Configure flash read access latency based on boost mode and frequency (RM0440 p98) FLASH.acr().modify(|w| { - w.set_latency(match (config.boost, ahb_freq.0) { + w.set_latency(match (config.boost, hclk.0) { (true, ..=34_000_000) => Latency::WS0, (true, ..=68_000_000) => Latency::WS1, (true, ..=102_000_000) => Latency::WS2, @@ -267,6 +275,11 @@ pub(crate) unsafe fn init(config: Config) { }) }); + if config.boost { + // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. + cortex_m::asm::delay(16); + } + // Now that boost mode and flash read access latency are configured, set up SYSCLK RCC.cfgr().modify(|w| { w.set_sw(sw); @@ -275,30 +288,16 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre2(config.apb2_pre); }); - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) - } - }; - - let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) - } - }; + let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre); + let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre); // Configure the 48MHz clock source for USB and RNG peripherals. - { - let source = match config.clk48_src { + RCC.ccipr().modify(|w| { + w.set_clk48sel(match config.clk48_src { Clk48Src::PLL1_Q => { - // Make sure the PLLQ is enabled and running at 48Mhz - let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); - assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); - + // Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock. + // Peripherals which require one (USB, RNG) should check that they‘re driven by a valid 48MHz + // clock at init. crate::pac::rcc::vals::Clk48sel::PLL1_Q } Clk48Src::HSI48 => { @@ -307,10 +306,8 @@ pub(crate) unsafe fn init(config: Config) { crate::pac::rcc::vals::Clk48sel::HSI48 } _ => unreachable!(), - }; - - RCC.ccipr().modify(|w| w.set_clk48sel(source)); - } + }) + }); RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); @@ -339,9 +336,9 @@ pub(crate) unsafe fn init(config: Config) { set_clocks!( sys: Some(sys_clk), - hclk1: Some(ahb_freq), - hclk2: Some(ahb_freq), - hclk3: Some(ahb_freq), + hclk1: Some(hclk), + hclk2: Some(hclk), + hclk3: Some(hclk), pclk1: Some(apb1_freq), pclk1_tim: Some(apb1_tim_freq), pclk2: Some(apb2_freq), @@ -355,7 +352,7 @@ pub(crate) unsafe fn init(config: Config) { ); } -// TODO: if necessary, make more of these gated behind cfg attrs +// TODO: if necessary, make more of these, gated behind cfg attrs mod max { use core::ops::RangeInclusive; diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 353ac1799..989fef5b0 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -44,6 +44,7 @@ async fn main(_spawner: Spawner) { }); config.rcc.sys = Sysclk::PLL1_R; + config.rcc.boost = true; // BOOST! if USE_HSI48 { // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. From 0e80dc4cd929f201dd35b569aa0aadd891c58682 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 17 Feb 2024 02:36:48 +0100 Subject: [PATCH 67/68] tests/stm32: add stm32f091rc, stm32h503rb. --- tests/stm32/Cargo.toml | 2 ++ tests/stm32/build.rs | 2 ++ tests/stm32/src/bin/hash.rs | 1 + tests/stm32/src/common.rs | 58 ++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d94045737..8554682a4 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -30,6 +30,8 @@ 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"] +stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] +stm32h503rb = ["embassy-stm32/stm32h503rb", "rng"] hash = [] eth = ["embassy-executor/task-arena-size-16384"] diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index f32a7b2f8..bc5589164 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -16,6 +16,8 @@ fn main() -> Result<(), Box> { feature = "stm32l073rz", // wrong ram size in stm32-data feature = "stm32wl55jc", + // no VTOR, so interrupts can't work when running from RAM + feature = "stm32f091rc", )) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index d1cfac5ce..8cc5d593f 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -24,6 +24,7 @@ bind_interrupts!(struct Irqs { feature = "stm32wba52cg", feature = "stm32l552ze", feature = "stm32h563zi", + feature = "stm32h503rb", feature = "stm32u5a5zj", feature = "stm32u585ai" ))] diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 182ad6298..50a7f9bae 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -54,6 +54,10 @@ teleprobe_meta::target!(b"nucleo-stm32l496zg"); teleprobe_meta::target!(b"nucleo-stm32wl55jc"); #[cfg(feature = "stm32wba52cg")] teleprobe_meta::target!(b"nucleo-stm32wba52cg"); +#[cfg(feature = "stm32f091rc")] +teleprobe_meta::target!(b"nucleo-stm32f091rc"); +#[cfg(feature = "stm32h503rb")] +teleprobe_meta::target!(b"nucleo-stm32h503rb"); macro_rules! define_peris { ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => { @@ -85,6 +89,12 @@ macro_rules! define_peris { }; } +#[cfg(feature = "stm32f091rc")] +define_peris!( + UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); #[cfg(feature = "stm32f103c8")] define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, @@ -157,6 +167,12 @@ define_peris!( 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;}, ); +#[cfg(feature = "stm32h503rb")] +define_peris!( + UART = USART1, UART_TX = PB14, UART_RX = PB15, 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 = {USART1 => embassy_stm32::usart::InterruptHandler;}, +); #[cfg(feature = "stm32c031c6")] define_peris!( UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, @@ -247,6 +263,22 @@ pub fn config() -> Config { config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; } + #[cfg(feature = "stm32f091rc")] + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL6, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + } #[cfg(feature = "stm32f103c8")] { use embassy_stm32::rcc::*; @@ -264,7 +296,6 @@ pub fn config() -> Config { config.rcc.apb1_pre = APBPrescaler::DIV2; config.rcc.apb2_pre = APBPrescaler::DIV1; } - #[cfg(feature = "stm32f207zg")] { use embassy_stm32::rcc::*; @@ -400,6 +431,31 @@ pub fn config() -> Config { config.rcc.voltage_scale = VoltageScale::Scale0; } + #[cfg(feature = "stm32h503rb")] + { + use embassy_stm32::rcc::*; + config.rcc.hsi = None; + config.rcc.hsi48 = Some(Default::default()); // needed for RNG + config.rcc.hse = Some(Hse { + freq: Hertz(24_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV6, + mul: PllMul::MUL125, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV2), + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.apb3_pre = APBPrescaler::DIV1; + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.voltage_scale = VoltageScale::Scale0; + } + #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))] { use embassy_stm32::rcc::*; From cb7863aea548b8cda65ca3eb14e543c226dbedb8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 17 Feb 2024 03:37:51 +0100 Subject: [PATCH 68/68] tests/stm32: actually add stm32f091rc, stm32h503rb. --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index 7ecca92af..ef706bdc5 100755 --- a/ci.sh +++ b/ci.sh @@ -230,6 +230,8 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --out-dir out/tests/stm32f091rc \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ --- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \