From d8b265856f73ae0e7985a6c2fe1a8f0fb811328d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:52:48 +0100 Subject: [PATCH 001/112] Add f4 flash sector computation to hal-common to allow for tests --- embassy-hal-common/src/lib.rs | 1 + embassy-hal-common/src/stm32/flash.rs | 113 ++++++++++++++++++++++++++ embassy-hal-common/src/stm32/mod.rs | 1 + 3 files changed, 115 insertions(+) create mode 100644 embassy-hal-common/src/stm32/flash.rs create mode 100644 embassy-hal-common/src/stm32/mod.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index b2a35cd3..1b253719 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -10,4 +10,5 @@ mod macros; mod peripheral; pub mod ratio; pub mod ring_buffer; +pub mod stm32; pub use peripheral::{Peripheral, PeripheralRef}; diff --git a/embassy-hal-common/src/stm32/flash.rs b/embassy-hal-common/src/stm32/flash.rs new file mode 100644 index 00000000..46c42aff --- /dev/null +++ b/embassy-hal-common/src/stm32/flash.rs @@ -0,0 +1,113 @@ +pub mod f4 { + const FLASH_BASE: u32 = 0x08_00_00_00; + pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; + + #[derive(Debug, PartialEq)] + pub struct FlashSector { + pub index: u8, + pub size: u32, + } + + pub fn get_sector(addr: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = addr - FLASH_BASE; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { + get_single_bank_sector(offset) + } else { + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + ..sector + } + } + } + } + + fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB + + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::f4::*; + + #[test] + fn can_get_sector_single_bank() { + let assert_sector = |index: u8, size: u32, addr: u32| { + assert_eq!(FlashSector { index, size }, get_sector(addr, false, 1024 * 1024)) + }; + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } + + #[test] + fn can_get_sector_dual_bank() { + let assert_sector = |index: u8, size: u32, addr: u32| { + assert_eq!(FlashSector { index, size }, get_sector(addr, true, 1024 * 1024)) + }; + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, LARGE_SECTOR_SIZE, 0x0807_FFFF); + + assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_FFFF); + + assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + + assert_sector(17, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-hal-common/src/stm32/mod.rs b/embassy-hal-common/src/stm32/mod.rs new file mode 100644 index 00000000..2e50f82b --- /dev/null +++ b/embassy-hal-common/src/stm32/mod.rs @@ -0,0 +1 @@ +pub mod flash; From cccceb88f2c91adcbdc6c1d07d785a97742f996b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:57:15 +0100 Subject: [PATCH 002/112] Generate flash regions during build --- embassy-stm32/build.rs | 76 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index dbfc1370..89281912 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -5,7 +5,7 @@ use std::{env, fs}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use stm32_metapac::metadata::METADATA; +use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; fn main() { let chip_name = match env::vars() @@ -103,6 +103,68 @@ fn main() { } }); + // ======== + // Generate FLASH regions + let mut flash_regions = TokenStream::new(); + let flash_memory_regions = METADATA + .memory + .iter() + .filter(|x| x.kind == MemoryRegionKind::Flash || x.kind == MemoryRegionKind::Otp); + for region in flash_memory_regions.clone() { + let region_name = format_ident!("{}", region.name); + let base = region.address as usize; + let size = region.size as usize; + let settings = region.settings.as_ref().unwrap(); + let erase_size = settings.erase_size as usize; + let write_size = settings.write_size as usize; + let erase_value = settings.erase_value; + + flash_regions.extend(quote! { + pub struct #region_name(()); + }); + + flash_regions.extend(quote! { + impl crate::flash::FlashRegion for #region_name { + const BASE: usize = #base; + const SIZE: usize = #size; + const ERASE_SIZE: usize = #erase_size; + const WRITE_SIZE: usize = #write_size; + const ERASE_VALUE: u8 = #erase_value; + } + }); + } + + let (fields, inits): (Vec, Vec) = flash_memory_regions + .map(|f| { + let field_name = format_ident!("{}", f.name.to_lowercase()); + let field_type = format_ident!("{}", f.name); + let field = quote! { + pub #field_name: #field_type + }; + let init = quote! { + #field_name: #field_type(()) + }; + + (field, init) + }) + .unzip(); + + flash_regions.extend(quote! { + pub struct FlashRegions { + #(#fields),* + } + + impl FlashRegions { + pub(crate) const fn take() -> Self { + Self { + #(#inits),* + } + } + } + }); + + g.extend(quote! { pub mod flash_regions { #flash_regions } }); + // ======== // Generate DMA IRQs. @@ -558,11 +620,22 @@ fn main() { // ======== // Write foreach_foo! macrotables + let mut flash_regions_table: Vec> = Vec::new(); let mut interrupts_table: Vec> = Vec::new(); let mut peripherals_table: Vec> = Vec::new(); let mut pins_table: Vec> = Vec::new(); let mut dma_channels_table: Vec> = Vec::new(); + for m in METADATA + .memory + .iter() + .filter(|m| m.kind == MemoryRegionKind::Flash || m.kind == MemoryRegionKind::Otp) + { + let mut row = Vec::new(); + row.push(m.name.to_string()); + flash_regions_table.push(row); + } + let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; let gpio_stride = 0x400; @@ -659,6 +732,7 @@ fn main() { let mut m = String::new(); + make_table(&mut m, "foreach_flash_region", &flash_regions_table); make_table(&mut m, "foreach_interrupt", &interrupts_table); make_table(&mut m, "foreach_peripheral", &peripherals_table); make_table(&mut m, "foreach_pin", &pins_table); From 6b44027eab273d1589eb289044d1bd4d172477f6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:58:40 +0100 Subject: [PATCH 003/112] Add FlashRegion trait and implement embedded_storage traits for each region --- embassy-stm32/src/flash/mod.rs | 162 +++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b7166a43..b6cecfdb 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,10 +1,10 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; -const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; #[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] @@ -23,60 +23,63 @@ impl<'d> Flash<'d> { Self { _inner: p } } + pub fn into_regions(self) -> FlashRegions { + FlashRegions::take() + } + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { + if offset as usize + bytes.len() > FLASH_SIZE { return Err(Error::Size); } - let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + let first_address = FLASH_BASE as u32 + offset; + + let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize + buf.len() > FLASH_END { + if offset as usize + buf.len() > FLASH_SIZE { return Err(Error::Size); } - if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { return Err(Error::Unaligned); } - trace!("Writing {} bytes at 0x{:x}", buf.len(), offset); - self.clear_all_err(); + let first_address = FLASH_BASE as u32 + offset; + trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); unsafe { + family::clear_all_err(); + family::unlock(); - let res = family::blocking_write(offset, buf); + let res = family::blocking_write(first_address, buf); family::lock(); res } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let from = FLASH_BASE as u32 + from; - let to = FLASH_BASE as u32 + to; - if to < from || to as usize > FLASH_END { + if to < from || to as usize > FLASH_SIZE { return Err(Error::Size); } - if (from as usize % ERASE_SIZE) != 0 || (to as usize % ERASE_SIZE) != 0 { + if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { return Err(Error::Unaligned); } - self.clear_all_err(); + let from_address = FLASH_BASE as u32 + from; + let to_address = FLASH_BASE as u32 + to; unsafe { + family::clear_all_err(); + family::unlock(); - let res = family::blocking_erase(from, to); + let res = family::blocking_erase(from_address, to_address); family::lock(); res } } - - fn clear_all_err(&mut self) { - unsafe { family::clear_all_err() }; - } } impl Drop for Flash<'_> { @@ -85,6 +88,69 @@ impl Drop for Flash<'_> { } } +pub trait FlashRegion { + const BASE: usize; + const SIZE: usize; + const ERASE_SIZE: usize; + const WRITE_SIZE: usize; + const ERASE_VALUE: u8; + + fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + if offset as usize + bytes.len() > Self::SIZE { + return Err(Error::Size); + } + + let first_address = Self::BASE as u32 + offset; + + let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + if offset as usize + buf.len() > Self::SIZE { + return Err(Error::Size); + } + if offset as usize % Self::WRITE_SIZE != 0 || buf.len() as usize % Self::WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let first_address = Self::BASE as u32 + offset; + trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); + + critical_section::with(|_| unsafe { + family::clear_all_err(); + + family::unlock(); + let res = family::blocking_write(first_address, buf); + family::lock(); + res + }) + } + + fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + if to < from || to as usize > Self::SIZE { + return Err(Error::Size); + } + if (from as usize % Self::ERASE_SIZE) != 0 || (to as usize % Self::ERASE_SIZE) != 0 { + return Err(Error::Unaligned); + } + + let from_address = Self::BASE as u32 + from; + let to_address = Self::BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); + + critical_section::with(|_| unsafe { + family::clear_all_err(); + + family::unlock(); + let res = family::blocking_erase(from_address, to_address); + family::lock(); + res + }) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -97,10 +163,6 @@ pub enum Error { Parallelism, } -impl<'d> ErrorType for Flash<'d> { - type Error = Error; -} - impl NorFlashError for Error { fn kind(&self) -> NorFlashErrorKind { match self { @@ -111,27 +173,35 @@ impl NorFlashError for Error { } } -impl<'d> ReadNorFlash for Flash<'d> { - const READ_SIZE: usize = WRITE_SIZE; +foreach_flash_region! { + ($name:ident) => { + impl ErrorType for crate::_generated::flash_regions::$name { + type Error = Error; + } - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } + impl ReadNorFlash for crate::_generated::flash_regions::$name { + const READ_SIZE: usize = ::WRITE_SIZE; - fn capacity(&self) -> usize { - FLASH_SIZE - } -} - -impl<'d> NorFlash for Flash<'d> { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + ::SIZE + } + } + + impl NorFlash for crate::_generated::flash_regions::$name { + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } + } + }; } From 6c73b23f384b4814ad9d13e8d108ef71464f72af Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:59:40 +0100 Subject: [PATCH 004/112] Align F4 family --- embassy-stm32/src/flash/f4.rs | 70 ++++++++++------------------------- 1 file changed, 19 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 9e23a8ad..d739c46b 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,12 +1,16 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; +use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; + +use super::{FlashRegion, FLASH_SIZE, MAINC}; use crate::flash::Error; use crate::pac; -const SECOND_BANK_SECTOR_START: u32 = 12; +pub(crate) const MAX_WRITE_SIZE: usize = MAINC::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = MAINC::ERASE_SIZE; unsafe fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { @@ -34,7 +38,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); @@ -42,10 +46,12 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); + let mut offset = first_address; + for chunk in buf.chunks(MAX_WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(offset as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); offset += val.len() as u32; // prevents parallelism errors @@ -65,50 +71,12 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -struct FlashSector { - index: u8, - size: u32, -} - -fn get_sector(addr: u32, dual_bank: bool) -> FlashSector { - let offset = addr - FLASH_BASE as u32; - - let bank_size = match dual_bank { - true => FLASH_SIZE / 2, - false => FLASH_SIZE, - } as u32; - - let bank = offset / bank_size; - let offset_in_bank = offset % bank_size; - - let index_in_bank = if offset_in_bank >= ERASE_SIZE as u32 / 2 { - 4 + offset_in_bank / ERASE_SIZE as u32 - } else { - offset_in_bank / (ERASE_SIZE as u32 / 8) - }; - - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - let size = match index_in_bank { - 0..=3 => 16 * 1024, - 4 => 64 * 1024, - _ => 128 * 1024, - }; - - let index = if bank == 1 { - SECOND_BANK_SECTOR_START + index_in_bank - } else { - index_in_bank - } as u8; - - FlashSector { index, size } -} - -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let mut addr = from; +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + let mut addr = from_address; let dual_bank = is_dual_bank(); - while addr < to { - let sector = get_sector(addr, dual_bank); + while addr < to_address { + let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); erase_sector(sector.index)?; addr += sector.size; } @@ -117,8 +85,8 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { } unsafe fn erase_sector(sector: u8) -> Result<(), Error> { - let bank = sector / SECOND_BANK_SECTOR_START as u8; - let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8); + let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; + let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); trace!("Erasing sector: {}", sector); From 7edd72f8f542e81143d2375f1783418404d1dc58 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:05:37 +0100 Subject: [PATCH 005/112] Align F3 family --- embassy-stm32/src/flash/f3.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 1cb08ee1..294fcffc 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,9 +1,14 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; +use super::FlashRegion; use crate::flash::Error; use crate::pac; +pub(crate) const MAX_WRITE_SIZE: usize = super::MAINA::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::MAINA::ERASE_SIZE; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -13,15 +18,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| w.set_pg(true)); let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(2) { - write_volatile(offset as *mut u16, u16::from_le_bytes(chunk[0..2].try_into().unwrap())); - offset += chunk.len() as u32; + let mut address = first_address; + let chunks = buf.chunks_exact(size_of::()); + assert!(chunks.remainder().is_empty()); + for chunk in chunks { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; ret = blocking_wait_ready(); if ret.is_err() { @@ -36,8 +43,8 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { pac::FLASH.cr().modify(|w| { w.set_per(true); }); From 99c4346579cd000128d6b14aca98968bc4bbad22 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:25:12 +0100 Subject: [PATCH 006/112] Add f7 computation to hal common and add tests --- .../src/stm32/{flash.rs => flash/f4.rs} | 82 +++++++++---------- embassy-hal-common/src/stm32/flash/f7.rs | 57 +++++++++++++ embassy-hal-common/src/stm32/flash/mod.rs | 2 + 3 files changed, 99 insertions(+), 42 deletions(-) rename embassy-hal-common/src/stm32/{flash.rs => flash/f4.rs} (61%) create mode 100644 embassy-hal-common/src/stm32/flash/f7.rs create mode 100644 embassy-hal-common/src/stm32/flash/mod.rs diff --git a/embassy-hal-common/src/stm32/flash.rs b/embassy-hal-common/src/stm32/flash/f4.rs similarity index 61% rename from embassy-hal-common/src/stm32/flash.rs rename to embassy-hal-common/src/stm32/flash/f4.rs index 46c42aff..44d6c08c 100644 --- a/embassy-hal-common/src/stm32/flash.rs +++ b/embassy-hal-common/src/stm32/flash/f4.rs @@ -1,62 +1,60 @@ -pub mod f4 { - const FLASH_BASE: u32 = 0x08_00_00_00; - pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; - pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; - pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; - pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; +const FLASH_BASE: u32 = 0x0800_0000; +pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; +pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; +pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; +pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - #[derive(Debug, PartialEq)] - pub struct FlashSector { - pub index: u8, - pub size: u32, - } +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub size: u32, +} - pub fn get_sector(addr: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = addr - FLASH_BASE; - if !dual_bank { +pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = address - FLASH_BASE; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { get_single_bank_sector(offset) } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - ..sector - } + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + ..sector } } } +} - fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB +fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - size: MEDIUM_SECTOR_SIZE, - } + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, } } #[cfg(test)] mod tests { - use super::f4::*; + use super::*; #[test] fn can_get_sector_single_bank() { diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs new file mode 100644 index 00000000..90938e6a --- /dev/null +++ b/embassy-hal-common/src/stm32/flash/f7.rs @@ -0,0 +1,57 @@ +const FLASH_BASE: u32 = 0x0800_0000; +pub(crate) const SMALL_SECTOR_SIZE: u32 = 32 * 1024; +pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; +pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub size: u32, +} + +pub fn get_sector(address: u32) -> FlashSector { + // First 4 sectors are 32KB, then one 128KB, and rest are 256KB + let offset = address - FLASH_BASE; + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector() { + let assert_sector = |index: u8, size: u32, addr: u32| assert_eq!(FlashSector { index, size }, get_sector(addr)); + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-hal-common/src/stm32/flash/mod.rs b/embassy-hal-common/src/stm32/flash/mod.rs new file mode 100644 index 00000000..1452b491 --- /dev/null +++ b/embassy-hal-common/src/stm32/flash/mod.rs @@ -0,0 +1,2 @@ +pub mod f4; +pub mod f7; From a8567f06174c7a3fc2c708a2acb3763d9a59c8b7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:26:00 +0100 Subject: [PATCH 007/112] Align F7 family --- embassy-stm32/src/flash/f7.rs | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index dd0d8439..ee0513fa 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,10 +1,17 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use embassy_hal_common::stm32::flash::f7::get_sector; + +use super::FlashRegion; use crate::flash::Error; use crate::pac; +pub(crate) const MAX_WRITE_SIZE: usize = super::MAINC::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::MAINC::ERASE_SIZE; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -14,7 +21,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); @@ -22,11 +29,13 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + let mut address = first_address; + for chunk in buf.chunks(MAX_WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; // prevents parallelism errors fence(Ordering::SeqCst); @@ -45,20 +54,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; - - let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; - - for sector in start_sector..end_sector { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + let start_sector = get_sector(from_address); + let end_sector = get_sector(to_address); + for sector in start_sector.index..end_sector.index { let ret = erase_sector(sector as u8); if ret.is_err() { return ret; From c848bd9c9c0589e987918fb72647b9002f0eb4e4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:02:42 +0100 Subject: [PATCH 008/112] Align with removal of MemoryRegionKind::Otp --- embassy-stm32/build.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 89281912..ca55681f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -109,9 +109,9 @@ fn main() { let flash_memory_regions = METADATA .memory .iter() - .filter(|x| x.kind == MemoryRegionKind::Flash || x.kind == MemoryRegionKind::Otp); + .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", region.name); + let region_name = format_ident!("{}", region.name.replace("_", "")); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -136,8 +136,9 @@ fn main() { let (fields, inits): (Vec, Vec) = flash_memory_regions .map(|f| { - let field_name = format_ident!("{}", f.name.to_lowercase()); - let field_type = format_ident!("{}", f.name); + let trimmed_name = f.name.replace("_", ""); + let field_name = format_ident!("{}", trimmed_name.to_lowercase()); + let field_type = format_ident!("{}", trimmed_name); let field = quote! { pub #field_name: #field_type }; @@ -629,10 +630,10 @@ fn main() { for m in METADATA .memory .iter() - .filter(|m| m.kind == MemoryRegionKind::Flash || m.kind == MemoryRegionKind::Otp) + .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let mut row = Vec::new(); - row.push(m.name.to_string()); + row.push(m.name.replace("_", "")); flash_regions_table.push(row); } From 47e07584cacf9b632736185d95be2f703df9a8f8 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:03:00 +0100 Subject: [PATCH 009/112] Align H7 family --- embassy-stm32/src/flash/h7.rs | 41 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7de95ac1..6ab8e7b7 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,13 +1,19 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; +use super::{FlashRegion, FLASH_SIZE}; use crate::flash::Error; use crate::pac; +const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { - super::FLASH_SIZE / 2 > super::ERASE_SIZE + FLASH_SIZE / 2 > ERASE_SIZE } pub(crate) unsafe fn lock() { @@ -27,8 +33,8 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { + let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) @@ -45,12 +51,13 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - 'outer: for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - trace!("Writing at {:x}", offset); - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + let mut address = first_address; + 'outer: for chunk in buf.chunks(WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; ret = blocking_wait_ready(bank); bank.sr().modify(|w| { @@ -76,20 +83,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error } pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let from = from - super::FLASH_BASE as u32; - let to = to - super::FLASH_BASE as u32; + let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let (start, end) = if to <= super::FLASH_SIZE as u32 { - let start_sector = from / super::ERASE_SIZE as u32; - let end_sector = to / super::ERASE_SIZE as u32; - (start_sector, end_sector) - } else { - error!("Attempting to write outside of defined sectors {:x} {:x}", from, to); - return Err(Error::Unaligned); - }; - - trace!("Erasing sectors from {} to {}", start, end); - for sector in start..end { + for sector in start_sector..end_sector { let bank = if sector >= 8 { 1 } else { 0 }; let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); if ret.is_err() { From 47d5f127bb307f441bc7765d6d053ce41168ce53 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:30:24 +0100 Subject: [PATCH 010/112] Align L family --- embassy-stm32/src/flash/l.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 5048a331..9ab732b8 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,9 +1,15 @@ use core::convert::TryInto; use core::ptr::write_volatile; +use super::FlashRegion; use crate::flash::Error; use crate::pac; +const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -33,17 +39,17 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { + let mut address = first_address; + for chunk in buf.chunks(WRITE_SIZE) { for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); + address += val.len() as u32; } ret = blocking_wait_ready(); @@ -60,8 +66,8 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + for page in (from_address..to_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -74,7 +80,7 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32; + let idx = (page - super::FLASH_BASE as u32) / ERASE_SIZE as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; From 73ccc04231adb4c4e2f00c3ecaea8481afb218d4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:39:10 +0100 Subject: [PATCH 011/112] Change region type name --- embassy-stm32/build.rs | 15 ++++++++++----- embassy-stm32/src/flash/f3.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 12 ++++++------ embassy-stm32/src/flash/f7.rs | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ca55681f..393efc42 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -111,7 +111,7 @@ fn main() { .iter() .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", region.name.replace("_", "")); + let region_name = format_ident!("{}", get_flash_region_name(region.name)); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -120,6 +120,7 @@ fn main() { let erase_value = settings.erase_value; flash_regions.extend(quote! { + #[allow(non_camel_case_types)] pub struct #region_name(()); }); @@ -136,9 +137,9 @@ fn main() { let (fields, inits): (Vec, Vec) = flash_memory_regions .map(|f| { - let trimmed_name = f.name.replace("_", ""); - let field_name = format_ident!("{}", trimmed_name.to_lowercase()); - let field_type = format_ident!("{}", trimmed_name); + let region_name = get_flash_region_name(f.name); + let field_name = format_ident!("{}", region_name.to_lowercase()); + let field_type = format_ident!("{}", region_name); let field = quote! { pub #field_name: #field_type }; @@ -633,7 +634,7 @@ fn main() { .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let mut row = Vec::new(); - row.push(m.name.replace("_", "")); + row.push(get_flash_region_name(m.name)); flash_regions_table.push(row); } @@ -886,3 +887,7 @@ macro_rules! {} {{ ) .unwrap(); } + +fn get_flash_region_name(name: &str) -> String { + name.replace("BANK_", "BANK").replace("REGION_", "REGION") +} diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 294fcffc..b24dfb4a 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -6,8 +6,8 @@ use super::FlashRegion; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::MAINA::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::MAINA::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d739c46b..0d9d405b 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -5,12 +5,12 @@ use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; -use super::{FlashRegion, FLASH_SIZE, MAINC}; +use super::{FlashRegion, FLASH_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = MAINC::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = MAINC::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; unsafe fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { @@ -46,13 +46,13 @@ pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<() let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = first_address; + let mut address = first_address; for chunk in buf.chunks(MAX_WRITE_SIZE) { let vals = chunk.chunks_exact(size_of::()); assert!(vals.remainder().is_empty()); for val in vals { - write_volatile(offset as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - offset += val.len() as u32; + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; // prevents parallelism errors fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index ee0513fa..8b8076e0 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -9,8 +9,8 @@ use super::FlashRegion; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::MAINC::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::MAINC::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); From 245147634bfbdcd325eea20be19286708bb29c9f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:03:06 +0100 Subject: [PATCH 012/112] Add region start to flash sectors --- embassy-hal-common/src/stm32/flash/f4.rs | 90 +++++++++++++----------- embassy-hal-common/src/stm32/flash/f7.rs | 42 ++++++----- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/embassy-hal-common/src/stm32/flash/f4.rs b/embassy-hal-common/src/stm32/flash/f4.rs index 44d6c08c..a8069ddd 100644 --- a/embassy-hal-common/src/stm32/flash/f4.rs +++ b/embassy-hal-common/src/stm32/flash/f4.rs @@ -7,6 +7,7 @@ pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, + pub start: u32, pub size: u32, } @@ -22,7 +23,8 @@ pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector let sector = get_single_bank_sector(offset - bank_size); FlashSector { index: SECOND_BANK_SECTOR_OFFSET + sector.index, - ..sector + start: sector.start + bank_size, + size: sector.size, } } } @@ -30,25 +32,31 @@ pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector fn get_single_bank_sector(offset: u32) -> FlashSector { // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { 0 => { if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, + index: small_sector_index as u8, + start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, size: SMALL_SECTOR_SIZE, } } else { FlashSector { index: 4, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, size: MEDIUM_SECTOR_SIZE, } } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } } } @@ -58,54 +66,54 @@ mod tests { #[test] fn can_get_sector_single_bank() { - let assert_sector = |index: u8, size: u32, addr: u32| { - assert_eq!(FlashSector { index, size }, get_sector(addr, false, 1024 * 1024)) + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(11, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(11, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } #[test] fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, size: u32, addr: u32| { - assert_eq!(FlashSector { index, size }, get_sector(addr, true, 1024 * 1024)) + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(17, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs index 90938e6a..2f586ade 100644 --- a/embassy-hal-common/src/stm32/flash/f7.rs +++ b/embassy-hal-common/src/stm32/flash/f7.rs @@ -6,6 +6,7 @@ pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, + pub start: u32, pub size: u32, } @@ -15,21 +16,28 @@ pub fn get_sector(address: u32) -> FlashSector { match offset / LARGE_SECTOR_SIZE { 0 => { if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, + index: small_sector_index as u8, + start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, size: SMALL_SECTOR_SIZE, } } else { FlashSector { index: 4, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, size: MEDIUM_SECTOR_SIZE, } } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } } } @@ -39,19 +47,21 @@ mod tests { #[test] fn can_get_sector() { - let assert_sector = |index: u8, size: u32, addr: u32| assert_eq!(FlashSector { index, size }, get_sector(addr)); + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_7FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_8000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0802_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0804_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(7, LARGE_SECTOR_SIZE, 0x080C_0000); - assert_sector(7, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } From bc69eb596e2496a5eb0cf1252ada12f2710aaff2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:04:45 +0100 Subject: [PATCH 013/112] Add is_eraseable_range and split write into consecutive parts --- embassy-stm32/src/flash/f3.rs | 61 ++++++++++++------------ embassy-stm32/src/flash/f4.rs | 84 ++++++++++++++++----------------- embassy-stm32/src/flash/f7.rs | 80 ++++++++++++++++---------------- embassy-stm32/src/flash/h7.rs | 72 ++++++++++++++-------------- embassy-stm32/src/flash/l.rs | 62 ++++++++++++------------- embassy-stm32/src/flash/mod.rs | 85 +++++++++++++++++++++++----------- 6 files changed, 236 insertions(+), 208 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index b24dfb4a..99ac1a15 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,13 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -18,33 +18,35 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 2); + pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - let chunks = buf.chunks_exact(size_of::()); - assert!(chunks.remainder().is_empty()); - for chunk in chunks { - write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); - address += chunk.len() as u32; - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for chunk in buf.chunks(2) { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -71,7 +73,6 @@ pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Resul return ret; } } - Ok(()) } @@ -89,7 +90,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0d9d405b..7428fd57 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,23 +1,19 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; -use super::{FlashRegion, FLASH_SIZE}; +use super::{FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - -unsafe fn is_dual_bank() -> bool { +fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { - pac::FLASH.optcr().read().db1m() + unsafe { pac::FLASH.optcr().read().db1m() } } else { false } @@ -38,49 +34,53 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let mut addr = from_address; - let dual_bank = is_dual_bank(); +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - while addr < to_address { - let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); - erase_sector(sector.index)?; - addr += sector.size; +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -116,7 +116,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 8b8076e0..16b68458 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,17 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f7::get_sector; -use super::FlashRegion; +use super::WRITE_SIZE; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,49 +17,51 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let start_sector = get_sector(from_address); - let end_sector = get_sector(to_address); - for sector in start_sector.index..end_sector.index { - let ret = erase_sector(sector as u8); - if ret.is_err() { - return ret; - } +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -106,7 +104,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 6ab8e7b7..21a9e45d 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,15 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::{FlashRegion, FLASH_SIZE}; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { @@ -33,59 +31,60 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); +} + +pub(crate) unsafe fn end_write() {} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + // We cannot have the write setup sequence in begin_write as it depends on the address + let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) }; - bank.cr().write(|w| { w.set_pg(true); w.set_psize(2); // 32 bits at once }); - cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - 'outer: for chunk in buf.chunks(WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; + let mut res = None; + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; - ret = blocking_wait_ready(bank); - bank.sr().modify(|w| { - if w.eop() { - w.set_eop(true); - } - }); - if ret.is_err() { - break 'outer; - } + res = Some(blocking_wait_ready(bank)); + bank.sr().modify(|w| { + if w.eop() { + w.set_eop(true); } + }); + if res.unwrap().is_err() { + break; } - ret - }; + } bank.cr().write(|w| w.set_pg(false)); cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - ret + res.unwrap() } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; for sector in start_sector..end_sector { let bank = if sector >= 8 { 1 } else { 0 }; let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); @@ -93,7 +92,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { return ret; } } - Ok(()) } @@ -157,7 +155,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 9ab732b8..57989625 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,14 +1,12 @@ -use core::convert::TryInto; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] @@ -39,35 +37,37 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - address += val.len() as u32; - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -155,7 +155,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b6cecfdb..c704909a 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,8 +1,10 @@ use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::{Mutex, MutexGuard}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; @@ -17,6 +19,8 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +static REGION_LOCK: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); @@ -33,7 +37,6 @@ impl<'d> Flash<'d> { } let first_address = FLASH_BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -43,39 +46,56 @@ impl<'d> Flash<'d> { if offset as usize + buf.len() > FLASH_SIZE { return Err(Error::Size); } - if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { + if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { return Err(Error::Unaligned); } - let first_address = FLASH_BASE as u32 + offset; - trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); + let start_address = FLASH_BASE as u32 + offset; + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + // No need to take lock here as we only have one mut flash reference. unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res } } + unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { + family::begin_write(); + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; + if res.is_err() { + family::end_write(); + return res; + } + address += WRITE_SIZE as u32; + } + + family::end_write(); + Ok(()) + } + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { if to < from || to as usize > FLASH_SIZE { return Err(Error::Size); } - if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { + + let start_address = FLASH_BASE as u32 + from; + let end_address = FLASH_BASE as u32 + to; + if !family::is_eraseable_range(start_address, end_address) { return Err(Error::Unaligned); } - - let from_address = FLASH_BASE as u32 + from; - let to_address = FLASH_BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res } @@ -101,7 +121,6 @@ pub trait FlashRegion { } let first_address = Self::BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -115,17 +134,19 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let first_address = Self::BASE as u32 + offset; - trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); + let start_address = Self::BASE as u32 + offset; + trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res - }) + } } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { @@ -136,18 +157,28 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let from_address = Self::BASE as u32 + from; - let to_address = Self::BASE as u32 + to; - trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); + let start_address = Self::BASE as u32 + from; + let end_address = Self::BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res - }) + } + } +} + +fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { + loop { + if let Ok(guard) = REGION_LOCK.try_lock() { + return guard; + } } } From e8fc7a66a344776463026fba8c91231951fcf2b4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:32:32 +0100 Subject: [PATCH 014/112] Ensure flash module and FlashRegion trait is always defined --- embassy-stm32/src/flash/other.rs | 7 +++++++ embassy-stm32/src/lib.rs | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs new file mode 100644 index 00000000..c9836d09 --- /dev/null +++ b/embassy-stm32/src/flash/other.rs @@ -0,0 +1,7 @@ +pub trait FlashRegion { + const BASE: usize; + const SIZE: usize; + const ERASE_SIZE: usize; + const WRITE_SIZE: usize; + const ERASE_VALUE: u8; +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f6..ddd5c0fd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -47,6 +47,13 @@ pub mod crc; flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 ))] pub mod flash; +#[cfg(not(any( + flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 +)))] +pub mod flash { + mod other; + pub use other::FlashRegion; +} pub mod pwm; #[cfg(rng)] pub mod rng; From e9a5b31fa83f7a261a79a810666039381553037f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 17:00:52 +0100 Subject: [PATCH 015/112] Implement drop for FlashRegions --- embassy-stm32/src/flash/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index c704909a..1294ace4 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -108,6 +108,12 @@ impl Drop for Flash<'_> { } } +impl Drop for FlashRegions { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + pub trait FlashRegion { const BASE: usize; const SIZE: usize; From d6ce1c4325179a813d47f2f068c178fe858ac49f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 11:31:45 +0200 Subject: [PATCH 016/112] Support running tests in embassy-stm32 and move impl from common back to stm32 --- embassy-hal-common/src/lib.rs | 1 - embassy-hal-common/src/stm32/flash/f4.rs | 119 ---------------------- embassy-hal-common/src/stm32/flash/f7.rs | 67 ------------ embassy-hal-common/src/stm32/flash/mod.rs | 2 - embassy-hal-common/src/stm32/mod.rs | 1 - embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/flash/f4.rs | 116 ++++++++++++++++++++- embassy-stm32/src/flash/f7.rs | 64 +++++++++++- embassy-stm32/src/flash/mod.rs | 7 ++ 9 files changed, 186 insertions(+), 197 deletions(-) delete mode 100644 embassy-hal-common/src/stm32/flash/f4.rs delete mode 100644 embassy-hal-common/src/stm32/flash/f7.rs delete mode 100644 embassy-hal-common/src/stm32/flash/mod.rs delete mode 100644 embassy-hal-common/src/stm32/mod.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 1b253719..b2a35cd3 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -10,5 +10,4 @@ mod macros; mod peripheral; pub mod ratio; pub mod ring_buffer; -pub mod stm32; pub use peripheral::{Peripheral, PeripheralRef}; diff --git a/embassy-hal-common/src/stm32/flash/f4.rs b/embassy-hal-common/src/stm32/flash/f4.rs deleted file mode 100644 index a8069ddd..00000000 --- a/embassy-hal-common/src/stm32/flash/f4.rs +++ /dev/null @@ -1,119 +0,0 @@ -const FLASH_BASE: u32 = 0x0800_0000; -pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; -pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; -pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; -pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = address - FLASH_BASE; - if !dual_bank { - get_single_bank_sector(offset) - } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - start: sector.start + bank_size, - size: sector.size, - } - } - } -} - -fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_get_sector_single_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } - - #[test] - fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } -} diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs deleted file mode 100644 index 2f586ade..00000000 --- a/embassy-hal-common/src/stm32/flash/f7.rs +++ /dev/null @@ -1,67 +0,0 @@ -const FLASH_BASE: u32 = 0x0800_0000; -pub(crate) const SMALL_SECTOR_SIZE: u32 = 32 * 1024; -pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; -pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -pub fn get_sector(address: u32) -> FlashSector { - // First 4 sectors are 32KB, then one 128KB, and rest are 256KB - let offset = address - FLASH_BASE; - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_get_sector() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); - assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); - assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); - assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); - - assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); - assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); - assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } -} diff --git a/embassy-hal-common/src/stm32/flash/mod.rs b/embassy-hal-common/src/stm32/flash/mod.rs deleted file mode 100644 index 1452b491..00000000 --- a/embassy-hal-common/src/stm32/flash/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod f4; -pub mod f7; diff --git a/embassy-hal-common/src/stm32/mod.rs b/embassy-hal-common/src/stm32/mod.rs deleted file mode 100644 index 2e50f82b..00000000 --- a/embassy-hal-common/src/stm32/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod flash; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b66d724d..1dd6202d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { version = "1", features = ["rt"] } +stm32-metapac = "1" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -69,12 +69,16 @@ seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } +[dev-dependencies] +critical-section = { version = "1.1", features = ["std"] } + [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" stm32-metapac = { version = "1", default-features = false, features = ["metadata"]} [features] +default = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 7428fd57..0fdfecb9 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,12 +2,15 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; - -use super::{FLASH_SIZE, WRITE_SIZE}; +use super::{FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +const SMALL_SECTOR_SIZE: u32 = 16 * 1024; +const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; +const LARGE_SECTOR_SIZE: u32 = 128 * 1024; +const SECOND_BANK_SECTOR_OFFSET: u8 = 12; + fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration @@ -141,3 +144,110 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = address - FLASH_BASE as u32; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { + get_single_bank_sector(offset) + } else { + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + start: sector.start + bank_size, + size: sector.size, + } + } + } +} + +fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; + FlashSector { + index: small_sector_index as u8, + start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector_single_bank() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } + + #[test] + fn can_get_sector_dual_bank() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 16b68458..0d3b738c 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -2,12 +2,14 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::stm32::flash::f7::get_sector; - -use super::WRITE_SIZE; +use super::{FlashSector, FLASH_BASE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +const SMALL_SECTOR_SIZE: u32 = 32 * 1024; +const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; +const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -129,3 +131,59 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +fn get_sector(address: u32) -> FlashSector { + // First 4 sectors are 32KB, then one 128KB, and rest are 256KB + let offset = address - FLASH_BASE as u32; + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; + FlashSector { + index: small_sector_index as u8, + start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1294ace4..6906bd09 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -19,6 +19,13 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub start: u32, + pub size: u32, +} + static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { From 6806bb969278acc9d3cde34897453b29807157c1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 11:52:18 +0200 Subject: [PATCH 017/112] Expose flash region settings as an array --- embassy-stm32/build.rs | 42 ++++++++++++++++++++++------------ embassy-stm32/src/flash/mod.rs | 40 +++++++++++++++++--------------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 393efc42..53f20978 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -3,7 +3,7 @@ use std::fmt::Write as _; use std::path::PathBuf; use std::{env, fs}; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; @@ -106,12 +106,15 @@ fn main() { // ======== // Generate FLASH regions let mut flash_regions = TokenStream::new(); - let flash_memory_regions = METADATA + let flash_memory_regions: Vec<_> = METADATA .memory .iter() - .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); - for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", get_flash_region_name(region.name)); + .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) + .collect(); + for region in flash_memory_regions.iter() { + let region_name = get_flash_region_name(region.name); + let region_type = format_ident!("{}", region_name); + let settings_name = format_ident!("{}_SETTINGS", region_name); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -121,21 +124,26 @@ fn main() { flash_regions.extend(quote! { #[allow(non_camel_case_types)] - pub struct #region_name(()); + pub struct #region_type(()); }); flash_regions.extend(quote! { - impl crate::flash::FlashRegion for #region_name { - const BASE: usize = #base; - const SIZE: usize = #size; - const ERASE_SIZE: usize = #erase_size; - const WRITE_SIZE: usize = #write_size; - const ERASE_VALUE: u8 = #erase_value; + pub const #settings_name: crate::flash::FlashRegionSettings = crate::flash::FlashRegionSettings { + base: #base, + size: #size, + erase_size: #erase_size, + write_size: #write_size, + erase_value: #erase_value, + }; + + impl crate::flash::FlashRegion for #region_type { + const SETTINGS: crate::flash::FlashRegionSettings = #settings_name; } }); } - let (fields, inits): (Vec, Vec) = flash_memory_regions + let (fields, (inits, settings)): (Vec, (Vec, Vec)) = flash_memory_regions + .iter() .map(|f| { let region_name = get_flash_region_name(f.name); let field_name = format_ident!("{}", region_name.to_lowercase()); @@ -146,11 +154,13 @@ fn main() { let init = quote! { #field_name: #field_type(()) }; + let settings_name = format_ident!("{}_SETTINGS", region_name); - (field, init) + (field, (init, settings_name)) }) .unzip(); + let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { pub struct FlashRegions { #(#fields),* @@ -163,6 +173,10 @@ fn main() { } } } + + pub const FLASH_REGIONS: [&crate::flash::FlashRegionSettings; #regions_len] = [ + #(&#settings),* + ]; }); g.extend(quote! { pub mod flash_regions { #flash_regions } }); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 6906bd09..29db2d13 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -19,6 +19,14 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +pub struct FlashRegionSettings { + pub base: usize, + pub size: usize, + pub erase_size: usize, + pub write_size: usize, + pub erase_value: u8, +} + #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, @@ -122,32 +130,28 @@ impl Drop for FlashRegions { } pub trait FlashRegion { - const BASE: usize; - const SIZE: usize; - const ERASE_SIZE: usize; - const WRITE_SIZE: usize; - const ERASE_VALUE: u8; + const SETTINGS: FlashRegionSettings; fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > Self::SIZE { + if offset as usize + bytes.len() > Self::SETTINGS.size { return Err(Error::Size); } - let first_address = Self::BASE as u32 + offset; + let first_address = Self::SETTINGS.base as u32 + offset; let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > Self::SIZE { + if offset as usize + buf.len() > Self::SETTINGS.size { return Err(Error::Size); } - if offset as usize % Self::WRITE_SIZE != 0 || buf.len() as usize % Self::WRITE_SIZE != 0 { + if offset as usize % Self::SETTINGS.write_size != 0 || buf.len() as usize % Self::SETTINGS.write_size != 0 { return Err(Error::Unaligned); } - let start_address = Self::BASE as u32 + offset; + let start_address = Self::SETTINGS.base as u32 + offset; trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); // Protect agains simultaneous write/erase to multiple regions. @@ -163,15 +167,15 @@ pub trait FlashRegion { } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > Self::SIZE { + if to < from || to as usize > Self::SETTINGS.size { return Err(Error::Size); } - if (from as usize % Self::ERASE_SIZE) != 0 || (to as usize % Self::ERASE_SIZE) != 0 { + if (from as usize % Self::SETTINGS.erase_size) != 0 || (to as usize % Self::SETTINGS.erase_size) != 0 { return Err(Error::Unaligned); } - let start_address = Self::BASE as u32 + from; - let end_address = Self::BASE as u32 + to; + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); // Protect agains simultaneous write/erase to multiple regions. @@ -224,20 +228,20 @@ foreach_flash_region! { } impl ReadNorFlash for crate::_generated::flash_regions::$name { - const READ_SIZE: usize = ::WRITE_SIZE; + const READ_SIZE: usize = ::SETTINGS.write_size; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { - ::SIZE + ::SETTINGS.size } } impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; + const WRITE_SIZE: usize = ::SETTINGS.write_size; + const ERASE_SIZE: usize = ::SETTINGS.erase_size; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.blocking_erase(from, to) From 4ee3d15519aaf3a290fd78063b88d182ff3aab53 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 12:10:24 +0200 Subject: [PATCH 018/112] Keep peripheral lifetime when calling into_regions() --- embassy-stm32/build.rs | 8 +++++--- embassy-stm32/src/flash/mod.rs | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 53f20978..f5bdadf5 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -162,13 +162,15 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { - pub struct FlashRegions { + pub struct FlashRegions<'d> { + _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } - impl FlashRegions { - pub(crate) const fn take() -> Self { + impl<'d> FlashRegions<'d> { + pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { + _inner: p, #(#inits),* } } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 29db2d13..1d1f034a 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -16,7 +16,7 @@ use crate::Peripheral; mod family; pub struct Flash<'d> { - _inner: PeripheralRef<'d, FLASH>, + inner: PeripheralRef<'d, FLASH>, } pub struct FlashRegionSettings { @@ -39,11 +39,13 @@ static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); - Self { _inner: p } + Self { inner: p } } - pub fn into_regions(self) -> FlashRegions { - FlashRegions::take() + pub fn into_regions(self) -> FlashRegions<'d> { + let mut flash = self; + let p = unsafe { flash.inner.clone_unchecked() }; + FlashRegions::new(p) } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { @@ -123,7 +125,7 @@ impl Drop for Flash<'_> { } } -impl Drop for FlashRegions { +impl Drop for FlashRegions<'_> { fn drop(&mut self) { unsafe { family::lock() }; } From 69944675a3c35a8479fa3b1499246c0f3f971d95 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 12:49:13 +0200 Subject: [PATCH 019/112] Expose get_sector in favor of is_eraseable_range --- embassy-stm32/src/flash/f3.rs | 18 ++++++++++------ embassy-stm32/src/flash/f4.rs | 37 ++++++++++++++++---------------- embassy-stm32/src/flash/f7.rs | 19 +++++----------- embassy-stm32/src/flash/h7.rs | 18 ++++++++++------ embassy-stm32/src/flash/l.rs | 18 ++++++++++------ embassy-stm32/src/flash/mod.rs | 14 +++++++++++- embassy-stm32/src/flash/other.rs | 14 +++++++----- 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 99ac1a15..3da3962e 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,11 +3,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, BANK1, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -41,10 +41,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { for page in (start_address..end_address).step_by(ERASE_SIZE) { pac::FLASH.cr().modify(|w| { @@ -107,3 +103,13 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0fdfecb9..306359be 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -63,24 +63,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let dual_bank = is_dual_bank(); - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let dual_bank = is_dual_bank(); let mut address = start_address; while address < end_address { - let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + let sector = get_sector(address); erase_sector(sector.index)?; address += sector.size; } @@ -145,7 +131,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { +pub(crate) fn get_sector(address: u32) -> FlashSector { + get_sector_inner(address, is_dual_bank(), FLASH_SIZE) +} + +fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { let offset = address - FLASH_BASE as u32; if !dual_bank { get_single_bank_sector(offset) @@ -187,7 +177,10 @@ fn get_single_bank_sector(offset: u32) -> FlashSector { let large_sector_index = i - 1; FlashSector { index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + start: FLASH_BASE as u32 + + 4 * SMALL_SECTOR_SIZE + + MEDIUM_SECTOR_SIZE + + large_sector_index * LARGE_SECTOR_SIZE, size: LARGE_SECTOR_SIZE, } } @@ -201,7 +194,10 @@ mod tests { #[test] fn can_get_sector_single_bank() { let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) + assert_eq!( + FlashSector { index, start, size }, + get_sector_inner(addr, false, 1024 * 1024) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); @@ -221,7 +217,10 @@ mod tests { #[test] fn can_get_sector_dual_bank() { let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) + assert_eq!( + FlashSector { index, start, size }, + get_sector_inner(addr, true, 1024 * 1024) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0d3b738c..588fc7a5 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -45,18 +45,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { let mut address = start_address; while address < end_address { @@ -132,7 +120,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -fn get_sector(address: u32) -> FlashSector { +pub(crate) fn get_sector(address: u32) -> FlashSector { // First 4 sectors are 32KB, then one 128KB, and rest are 256KB let offset = address - FLASH_BASE as u32; match offset / LARGE_SECTOR_SIZE { @@ -156,7 +144,10 @@ fn get_sector(address: u32) -> FlashSector { let large_sector_index = i - 1; FlashSector { index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + start: FLASH_BASE as u32 + + 4 * SMALL_SECTOR_SIZE + + MEDIUM_SECTOR_SIZE + + large_sector_index * LARGE_SECTOR_SIZE, size: LARGE_SECTOR_SIZE, } } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 21a9e45d..732af853 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,11 +3,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { @@ -78,10 +78,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; @@ -194,3 +190,13 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 57989625..56a21885 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,11 +2,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] @@ -62,10 +62,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { for page in (start_address..end_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] @@ -191,3 +187,13 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1d1f034a..29cf3cc5 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -104,7 +104,7 @@ impl<'d> Flash<'d> { let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - if !family::is_eraseable_range(start_address, end_address) { + if !is_eraseable_range(start_address, end_address) { return Err(Error::Unaligned); } trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); @@ -193,6 +193,18 @@ pub trait FlashRegion { } } +fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index c9836d09..4ffb4cc3 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -1,7 +1,11 @@ pub trait FlashRegion { - const BASE: usize; - const SIZE: usize; - const ERASE_SIZE: usize; - const WRITE_SIZE: usize; - const ERASE_VALUE: u8; + const SETTINGS: FlashRegionSettings; +} + +pub struct FlashRegionSettings { + pub base: usize, + pub size: usize, + pub erase_size: usize, + pub write_size: usize, + pub erase_value: u8, } From ddbd5098658612e1421cdd081956c3e6ee3c92f8 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:37:10 +0200 Subject: [PATCH 020/112] Move as much logic from families to shared module as possible --- embassy-stm32/src/flash/f3.rs | 40 +++++---- embassy-stm32/src/flash/f4.rs | 15 +--- embassy-stm32/src/flash/f7.rs | 14 +--- embassy-stm32/src/flash/h7.rs | 17 +--- embassy-stm32/src/flash/l.rs | 83 +++++++++---------- embassy-stm32/src/flash/mod.rs | 147 ++++++++++++++------------------- 6 files changed, 129 insertions(+), 187 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 3da3962e..7b339ccc 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -41,33 +41,31 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - for page in (start_address..end_address).step_by(ERASE_SIZE) { - pac::FLASH.cr().modify(|w| { - w.set_per(true); - }); +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + }); - pac::FLASH.ar().write(|w| w.set_far(page)); + pac::FLASH.ar().write(|w| w.set_far(sector.first)); - pac::FLASH.cr().modify(|w| { - w.set_strt(true); - }); + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = blocking_wait_ready(); - if !pac::FLASH.sr().read().eop() { - trace!("FLASH: EOP not set"); - ret = Err(Error::Prog); - } else { - pac::FLASH.sr().write(|w| w.set_eop(true)); - } + if !pac::FLASH.sr().read().eop() { + trace!("FLASH: EOP not set"); + ret = Err(Error::Prog); + } else { + pac::FLASH.sr().write(|w| w.set_eop(true)); + } - pac::FLASH.cr().modify(|w| w.set_per(false)); + pac::FLASH.cr().modify(|w| w.set_per(false)); - clear_all_err(); - if ret.is_err() { - return ret; - } + clear_all_err(); + if ret.is_err() { + return ret; } Ok(()) } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 306359be..cb420c69 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -63,17 +63,8 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - erase_sector(sector.index)?; - address += sector.size; - } - Ok(()) -} - -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let sector = sector.index; let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); @@ -132,7 +123,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - get_sector_inner(address, is_dual_bank(), FLASH_SIZE) + get_sector_inner(address, is_dual_bank(), FLASH_SIZE as u32) } fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 588fc7a5..eba7df46 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -45,20 +45,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - erase_sector(sector.index)?; - address += sector.size; - } - Ok(()) -} - -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index) }); pac::FLASH.cr().modify(|w| { diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 732af853..28999999 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -78,20 +78,9 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - for sector in start_sector..end_sector { - let bank = if sector >= 8 { 1 } else { 0 }; - let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); - if ret.is_err() { - return ret; - } - } - Ok(()) -} - -unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let bank = pac::FLASH::bank(if sector.index >= 8 { 1 } else { 0 }); + let sector = sector.index % 8; bank.cr().modify(|w| { w.set_ser(true); w.set_snb(sector) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 56a21885..c8d060f0 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -62,55 +62,50 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - for page in (start_address..end_address).step_by(ERASE_SIZE) { - #[cfg(any(flash_l0, flash_l1))] - { - pac::FLASH.pecr().modify(|w| { - w.set_erase(true); - w.set_prog(true); - }); - - write_volatile(page as *mut u32, 0xFFFFFFFF); - } - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - { - let idx = (page - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - - #[cfg(flash_l4)] - let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; - - pac::FLASH.cr().modify(|w| { - w.set_per(true); - w.set_pnb(idx as u8); - #[cfg(any(flash_wl, flash_wb))] - w.set_strt(true); - #[cfg(any(flash_l4))] - w.set_start(true); - #[cfg(any(flash_l4))] - w.set_bker(bank); - }); - } - - let ret: Result<(), Error> = blocking_wait_ready(); - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().modify(|w| w.set_per(false)); - - #[cfg(any(flash_l0, flash_l1))] +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + #[cfg(any(flash_l0, flash_l1))] + { pac::FLASH.pecr().modify(|w| { - w.set_erase(false); - w.set_prog(false); + w.set_erase(true); + w.set_prog(true); }); - clear_all_err(); - if ret.is_err() { - return ret; - } + write_volatile(sector.start as *mut u32, 0xFFFFFFFF); } - Ok(()) + #[cfg(any(flash_wl, flash_wb, flash_l4))] + { + let idx = (sector.start - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + + #[cfg(flash_l4)] + let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; + + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + #[cfg(any(flash_wl, flash_wb))] + w.set_strt(true); + #[cfg(any(flash_l4))] + w.set_start(true); + #[cfg(any(flash_l4))] + w.set_bker(bank); + }); + } + + let ret: Result<(), Error> = blocking_wait_ready(); + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().modify(|w| w.set_per(false)); + + #[cfg(any(flash_l0, flash_l1))] + pac::FLASH.pecr().modify(|w| { + w.set_erase(false); + w.set_prog(false); + }); + + clear_all_err(); + + ret } pub(crate) unsafe fn clear_all_err() { diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 29cf3cc5..89fdabd4 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,3 +1,4 @@ +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; @@ -14,7 +15,6 @@ use crate::Peripheral; #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] mod family; - pub struct Flash<'d> { inner: PeripheralRef<'d, FLASH>, } @@ -49,73 +49,93 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > FLASH_SIZE { + Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) + } + + fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { return Err(Error::Size); } - let first_address = FLASH_BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > FLASH_SIZE { - return Err(Error::Size); - } - if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - let start_address = FLASH_BASE as u32 + offset; - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); // No need to take lock here as we only have one mut flash reference. - unsafe { - family::clear_all_err(); - family::unlock(); - let res = Flash::blocking_write_all(start_address, buf); - family::lock(); - res - } + unsafe { Flash::blocking_write_inner(start_address, buf) } } - unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { - family::begin_write(); - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; - if res.is_err() { - family::end_write(); - return res; - } - address += WRITE_SIZE as u32; + unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); } - family::end_write(); + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + family::clear_all_err(); + family::unlock(); + family::begin_write(); + + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; + address += WRITE_SIZE as u32; + } Ok(()) } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > FLASH_SIZE { - return Err(Error::Size); - } - let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - if !is_eraseable_range(start_address, end_address) { + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { return Err(Error::Unaligned); } + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - unsafe { - family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(start_address, end_address); + family::clear_all_err(); + family::unlock(); + + let _ = OnDrop::new(|| { family::lock(); - res + }); + + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + family::blocking_erase_sector(§or)?; + address += sector.size; } + Ok(()) } } @@ -135,76 +155,35 @@ pub trait FlashRegion { const SETTINGS: FlashRegionSettings; fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > Self::SETTINGS.size { - return Err(Error::Size); - } - - let first_address = Self::SETTINGS.base as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) } fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > Self::SETTINGS.size { - return Err(Error::Size); - } - if offset as usize % Self::SETTINGS.write_size != 0 || buf.len() as usize % Self::SETTINGS.write_size != 0 { - return Err(Error::Unaligned); - } - let start_address = Self::SETTINGS.base as u32 + offset; - trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); // Protect agains simultaneous write/erase to multiple regions. let _guard = take_lock_spin(); unsafe { family::clear_all_err(); - family::unlock(); - let res = Flash::blocking_write_all(start_address, buf); - family::lock(); - res + Flash::blocking_write_inner(start_address, buf) } } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > Self::SETTINGS.size { - return Err(Error::Size); - } - if (from as usize % Self::SETTINGS.erase_size) != 0 || (to as usize % Self::SETTINGS.erase_size) != 0 { - return Err(Error::Unaligned); - } - let start_address = Self::SETTINGS.base as u32 + from; let end_address = Self::SETTINGS.base as u32 + to; - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); // Protect agains simultaneous write/erase to multiple regions. let _guard = take_lock_spin(); unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(start_address, end_address); - family::lock(); - res + Flash::blocking_erase_inner(start_address, end_address) } } } -fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { From b7dfc8de10ceddd6c2e8c078e529eb5e266ea7db Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:52:52 +0200 Subject: [PATCH 021/112] Let flash module be conditionally included --- embassy-stm32/build.rs | 2 ++ embassy-stm32/src/flash/l.rs | 4 +--- embassy-stm32/src/flash/other.rs | 11 ----------- embassy-stm32/src/lib.rs | 11 +---------- 4 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f5bdadf5..d179f5e0 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -162,11 +162,13 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { + #[cfg(flash)] pub struct FlashRegions<'d> { _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } + #[cfg(flash)] impl<'d> FlashRegions<'d> { pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c8d060f0..edcf2b2f 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,8 +6,6 @@ use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; - pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -75,7 +73,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / BANK1::SETTINGS.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs deleted file mode 100644 index 4ffb4cc3..00000000 --- a/embassy-stm32/src/flash/other.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; -} - -pub struct FlashRegionSettings { - pub base: usize, - pub size: usize, - pub erase_size: usize, - pub write_size: usize, - pub erase_value: u8, -} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ddd5c0fd..2d49c85b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,17 +43,8 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(any( - flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 -))] +#[cfg(flash)] pub mod flash; -#[cfg(not(any( - flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 -)))] -pub mod flash { - mod other; - pub use other::FlashRegion; -} pub mod pwm; #[cfg(rng)] pub mod rng; From 5a12fd6c75a41b976b2c935cbd8269fd03000a9a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:57:33 +0200 Subject: [PATCH 022/112] Add unimplemented family section --- embassy-stm32/src/flash/mod.rs | 95 ++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 89fdabd4..d4e74553 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -9,12 +9,45 @@ pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; -#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] +#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] mod family; + +#[cfg(not(any( + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 +)))] +mod family { + use super::{Error, FlashSector}; + + pub(crate) unsafe fn lock() { + unimplemented!(); + } + pub(crate) unsafe fn unlock() { + unimplemented!(); + } + pub(crate) unsafe fn begin_write() { + unimplemented!(); + } + pub(crate) unsafe fn end_write() { + unimplemented!(); + } + pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + unimplemented!(); + } + pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { + unimplemented!(); + } + pub(crate) unsafe fn clear_all_err() { + unimplemented!(); + } + pub(crate) fn get_sector(_address: u32) -> FlashSector { + unimplemented!(); + } +} + pub struct Flash<'d> { inner: PeripheralRef<'d, FLASH>, } @@ -34,6 +67,33 @@ pub struct FlashSector { pub size: u32, } +pub trait FlashRegion { + const SETTINGS: FlashRegionSettings; + + fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + } + + fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + let start_address = Self::SETTINGS.base as u32 + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_write_inner(start_address, buf) } + } + + fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } +} + static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { @@ -151,39 +211,6 @@ impl Drop for FlashRegions<'_> { } } -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; - - fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) - } - - fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { - family::clear_all_err(); - Flash::blocking_write_inner(start_address, buf) - } - } - - fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { - family::clear_all_err(); - Flash::blocking_erase_inner(start_address, end_address) - } - } -} - fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { From 15e17472207fe587c38ba6cd2c2214ae4e88ee32 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 14:10:16 +0200 Subject: [PATCH 023/112] Fix build of not implemented family --- embassy-stm32/src/flash/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index d4e74553..1186a182 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -20,7 +20,7 @@ mod family; flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 )))] mod family { - use super::{Error, FlashSector}; + use super::{Error, FlashSector, WRITE_SIZE}; pub(crate) unsafe fn lock() { unimplemented!(); From fc8c83e00ad6ec810bc73e750e8e943b3e725156 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 14:50:19 +0200 Subject: [PATCH 024/112] Fix h7 compile error --- embassy-stm32/src/flash/h7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 28999999..82dd4b28 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let bank = pac::FLASH::bank(if sector.index >= 8 { 1 } else { 0 }); + let bank = pac::FLASH.bank(if sector.index >= 8 { 1 } else { 0 }); let sector = sector.index % 8; bank.cr().modify(|w| { w.set_ser(true); From 68c260edeb8822411ac03d569c1c0d91be2b98a5 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 15:03:48 +0200 Subject: [PATCH 025/112] Use stm32-metapac v2 --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1af439eb..4f660187 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "1" +stm32-metapac = "2" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" From ef1890e9110c8ef3553e6a2d0979dfb52520b025 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 15:45:18 +0200 Subject: [PATCH 026/112] Remove flash operations from FlashRegion trait and move to common module --- embassy-stm32/src/flash/common.rs | 177 ++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 200 ++---------------------------- 2 files changed, 186 insertions(+), 191 deletions(-) create mode 100644 embassy-stm32/src/flash/common.rs diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs new file mode 100644 index 00000000..f92236bb --- /dev/null +++ b/embassy-stm32/src/flash/common.rs @@ -0,0 +1,177 @@ +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::{Mutex, MutexGuard}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; + +use super::{family, Error, FlashRegion}; +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use crate::Peripheral; + +pub struct Flash<'d> { + inner: PeripheralRef<'d, crate::peripherals::FLASH>, +} + +impl<'d> Flash<'d> { + pub fn new(p: impl Peripheral

+ 'd) -> Self { + into_ref!(p); + Self { inner: p } + } + + pub fn into_regions(self) -> FlashRegions<'d> { + let mut flash = self; + let p = unsafe { flash.inner.clone_unchecked() }; + FlashRegions::new(p) + } + + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) + } + + fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + let start_address = FLASH_BASE as u32 + offset; + + // No need to take lock here as we only have one mut flash reference. + + unsafe { Flash::blocking_write_inner(start_address, buf) } + } + + unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + family::clear_all_err(); + family::unlock(); + family::begin_write(); + + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; + address += WRITE_SIZE as u32; + } + Ok(()) + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let start_address = FLASH_BASE as u32 + from; + let end_address = FLASH_BASE as u32 + to; + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + family::clear_all_err(); + family::unlock(); + + let _ = OnDrop::new(|| { + family::lock(); + }); + + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + family::blocking_erase_sector(§or)?; + address += sector.size; + } + Ok(()) + } +} + +impl Drop for Flash<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + +static REGION_LOCK: Mutex = Mutex::new(()); + +fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { + loop { + if let Ok(guard) = REGION_LOCK.try_lock() { + return guard; + } + } +} + +foreach_flash_region! { + ($name:ident) => { + impl ErrorType for crate::_generated::flash_regions::$name { + type Error = Error; + } + + impl ReadNorFlash for crate::_generated::flash_regions::$name { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + } + + fn capacity(&self) -> usize { + ::SETTINGS.size + } + } + + impl NorFlash for crate::_generated::flash_regions::$name { + const WRITE_SIZE: usize = ::SETTINGS.write_size; + const ERASE_SIZE: usize = ::SETTINGS.erase_size; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let start_address = Self::SETTINGS.base as u32 + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_write_inner(start_address, bytes) } + } + } + }; +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1186a182..ec7c6694 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,13 +1,4 @@ -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::{Mutex, MutexGuard}; -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; - -pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; -use crate::peripherals::FLASH; -use crate::Peripheral; +use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] @@ -48,8 +39,14 @@ mod family { } } -pub struct Flash<'d> { - inner: PeripheralRef<'d, FLASH>, +#[cfg(flash)] +mod common; + +#[cfg(flash)] +pub use common::*; + +pub trait FlashRegion { + const SETTINGS: FlashRegionSettings; } pub struct FlashRegionSettings { @@ -67,158 +64,12 @@ pub struct FlashSector { pub size: u32, } -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; - - fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) - } - - fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } -} - -static REGION_LOCK: Mutex = Mutex::new(()); - -impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); - Self { inner: p } - } - - pub fn into_regions(self) -> FlashRegions<'d> { - let mut flash = self; - let p = unsafe { flash.inner.clone_unchecked() }; - FlashRegions::new(p) - } - - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) - } - - fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - - let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) - } - - pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - - // No need to take lock here as we only have one mut flash reference. - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); - - family::clear_all_err(); - family::unlock(); - family::begin_write(); - - let _ = OnDrop::new(|| { - family::end_write(); - family::lock(); - }); - - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; - address += WRITE_SIZE as u32; - } - Ok(()) - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + from; - let end_address = FLASH_BASE as u32 + to; - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } - - unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - - family::clear_all_err(); - family::unlock(); - - let _ = OnDrop::new(|| { - family::lock(); - }); - - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - family::blocking_erase_sector(§or)?; - address += sector.size; - } - Ok(()) - } -} - -impl Drop for Flash<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - impl Drop for FlashRegions<'_> { fn drop(&mut self) { unsafe { family::lock() }; } } -fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { - loop { - if let Ok(guard) = REGION_LOCK.try_lock() { - return guard; - } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -240,36 +91,3 @@ impl NorFlashError for Error { } } } - -foreach_flash_region! { - ($name:ident) => { - impl ErrorType for crate::_generated::flash_regions::$name { - type Error = Error; - } - - impl ReadNorFlash for crate::_generated::flash_regions::$name { - const READ_SIZE: usize = ::SETTINGS.write_size; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } - - fn capacity(&self) -> usize { - ::SETTINGS.size - } - } - - impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::SETTINGS.write_size; - const ERASE_SIZE: usize = ::SETTINGS.erase_size; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } - } - }; -} From def576ac4688fb2113aacca46d5bfb4001c8dc1a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 04:24:41 +0200 Subject: [PATCH 027/112] Remove FlashRegion trait and rename Settings to FlashRegion --- embassy-stm32/build.rs | 53 +++++++++-------- embassy-stm32/src/flash/common.rs | 98 ++++++++++++++++++++++--------- embassy-stm32/src/flash/mod.rs | 16 ++--- 3 files changed, 103 insertions(+), 64 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d179f5e0..8f34f510 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -112,64 +112,58 @@ fn main() { .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) .collect(); for region in flash_memory_regions.iter() { - let region_name = get_flash_region_name(region.name); - let region_type = format_ident!("{}", region_name); - let settings_name = format_ident!("{}_SETTINGS", region_name); - let base = region.address as usize; - let size = region.size as usize; + let region_name = format_ident!("{}", get_flash_region_name(region.name)); + let base = region.address; + let size = region.size; let settings = region.settings.as_ref().unwrap(); - let erase_size = settings.erase_size as usize; - let write_size = settings.write_size as usize; + let erase_size = settings.erase_size; + let write_size = settings.write_size; let erase_value = settings.erase_value; flash_regions.extend(quote! { - #[allow(non_camel_case_types)] - pub struct #region_type(()); - }); - - flash_regions.extend(quote! { - pub const #settings_name: crate::flash::FlashRegionSettings = crate::flash::FlashRegionSettings { + pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { base: #base, size: #size, erase_size: #erase_size, write_size: #write_size, erase_value: #erase_value, }; + }); - impl crate::flash::FlashRegion for #region_type { - const SETTINGS: crate::flash::FlashRegionSettings = #settings_name; - } + let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); + flash_regions.extend(quote! { + pub struct #region_type(pub(crate) &'static crate::flash::FlashRegion); }); } - let (fields, (inits, settings)): (Vec, (Vec, Vec)) = flash_memory_regions + let (fields, (inits, region_names)): (Vec, (Vec, Vec)) = flash_memory_regions .iter() .map(|f| { let region_name = get_flash_region_name(f.name); let field_name = format_ident!("{}", region_name.to_lowercase()); - let field_type = format_ident!("{}", region_name); + let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { pub #field_name: #field_type }; + let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(()) + #field_name: #field_type(&#region_name) }; - let settings_name = format_ident!("{}_SETTINGS", region_name); - (field, (init, settings_name)) + (field, (init, region_name)) }) .unzip(); let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashRegions<'d> { + pub struct FlashLayout<'d> { _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } #[cfg(flash)] - impl<'d> FlashRegions<'d> { + impl<'d> FlashLayout<'d> { pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { _inner: p, @@ -178,8 +172,8 @@ fn main() { } } - pub const FLASH_REGIONS: [&crate::flash::FlashRegionSettings; #regions_len] = [ - #(&#settings),* + pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ + #(&#region_names),* ]; }); @@ -651,8 +645,11 @@ fn main() { .iter() .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { + let settings = m.settings.as_ref().unwrap(); let mut row = Vec::new(); - row.push(get_flash_region_name(m.name)); + row.push(get_flash_region_type_name(m.name)); + row.push(settings.write_size.to_string()); + row.push(settings.erase_size.to_string()); flash_regions_table.push(row); } @@ -906,6 +903,10 @@ macro_rules! {} {{ .unwrap(); } +fn get_flash_region_type_name(name: &str) -> String { + name.replace("BANK_", "Bank").replace("REGION_", "Region") +} + fn get_flash_region_name(name: &str) -> String { name.replace("BANK_", "BANK").replace("REGION_", "REGION") } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index f92236bb..b190a5a0 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -2,7 +2,6 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use super::{family, Error, FlashRegion}; pub use crate::_generated::flash_regions::*; @@ -19,10 +18,8 @@ impl<'d> Flash<'d> { Self { inner: p } } - pub fn into_regions(self) -> FlashRegions<'d> { - let mut flash = self; - let p = unsafe { flash.inner.clone_unchecked() }; - FlashRegions::new(p) + pub fn into_regions(self) -> FlashLayout<'d> { + FlashLayout::new(self.release()) } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { @@ -114,6 +111,11 @@ impl<'d> Flash<'d> { } Ok(()) } + + pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { + let mut flash = self; + unsafe { flash.inner.clone_unchecked() } + } } impl Drop for Flash<'_> { @@ -132,45 +134,85 @@ fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { } } +impl FlashRegion { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + unsafe { self.blocking_read_inner(offset, bytes) } + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { self.blocking_write_inner(offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { self.blocking_erase_inner(from, to) } + } + + unsafe fn blocking_read_inner(&self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Flash::blocking_read_inner(self.base + offset, bytes) + } + + unsafe fn blocking_write_inner(&self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let start_address = self.base + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + Flash::blocking_write_inner(start_address, bytes) + } + + unsafe fn blocking_erase_inner(&self, from: u32, to: u32) -> Result<(), Error> { + let start_address = self.base + from; + let end_address = self.base + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + Flash::blocking_erase_inner(start_address, end_address) + } +} + foreach_flash_region! { - ($name:ident) => { - impl ErrorType for crate::_generated::flash_regions::$name { + ($type_name:ident, $write_size:ident, $erase_size:ident) => { + impl crate::_generated::flash_regions::$type_name { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + unsafe { self.0.blocking_read_inner(offset, bytes) } + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { self.0.blocking_write_inner(offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { self.0.blocking_erase_inner(from, to) } + } + } + + impl ErrorType for crate::_generated::flash_regions::$type_name { type Error = Error; } - impl ReadNorFlash for crate::_generated::flash_regions::$name { + impl ReadNorFlash for crate::_generated::flash_regions::$type_name { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + unsafe { self.0.blocking_read_inner(offset, bytes) } } fn capacity(&self) -> usize { - ::SETTINGS.size + self.0.size as usize } } - impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::SETTINGS.write_size; - const ERASE_SIZE: usize = ::SETTINGS.erase_size; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } + impl NorFlash for crate::_generated::flash_regions::$type_name { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let start_address = Self::SETTINGS.base as u32 + offset; + unsafe { self.0.blocking_write_inner(offset, bytes) } + } - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_write_inner(start_address, bytes) } + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + unsafe { self.0.blocking_erase_inner(from, to) } } } }; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index ec7c6694..1e7d4c65 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -45,15 +45,11 @@ mod common; #[cfg(flash)] pub use common::*; -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; -} - -pub struct FlashRegionSettings { - pub base: usize, - pub size: usize, - pub erase_size: usize, - pub write_size: usize, +pub struct FlashRegion { + pub base: u32, + pub size: u32, + pub erase_size: u32, + pub write_size: u32, pub erase_value: u8, } @@ -64,7 +60,7 @@ pub struct FlashSector { pub size: u32, } -impl Drop for FlashRegions<'_> { +impl Drop for FlashLayout<'_> { fn drop(&mut self) { unsafe { family::lock() }; } From 91d8afd371c20d21765713a45625f62ce25d97b6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 05:27:57 +0200 Subject: [PATCH 028/112] Add AltFlashLayout for supported F4 chips --- embassy-stm32/build.rs | 16 +++++-- embassy-stm32/src/flash/common.rs | 4 +- embassy-stm32/src/flash/f4.rs | 75 +++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 58 +++++++++++++----------- 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ec9b6dc0..6b203a3f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -910,10 +910,18 @@ macro_rules! {} {{ .unwrap(); } -fn get_flash_region_type_name(name: &str) -> String { - name.replace("BANK_", "Bank").replace("REGION_", "Region") +fn get_flash_region_name(name: &str) -> String { + let name = name.replace("BANK_", "BANK").replace("REGION_", "REGION"); + if name.contains("REGION") { + name + } else { + name + "_REGION" + } } -fn get_flash_region_name(name: &str) -> String { - name.replace("BANK_", "BANK").replace("REGION_", "REGION") +fn get_flash_region_type_name(name: &str) -> String { + get_flash_region_name(name) + .replace("BANK", "Bank") + .replace("REGION", "Region") + .replace("_", "") } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index b190a5a0..c239d967 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -3,9 +3,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use super::{family, Error, FlashRegion}; -pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::Peripheral; pub struct Flash<'d> { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index cb420c69..257aae2d 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -11,7 +11,82 @@ const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; const LARGE_SECTOR_SIZE: u32 = 128 * 1024; const SECOND_BANK_SECTOR_OFFSET: u8 = 12; +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +mod alt_regions { + use embassy_hal_common::PeripheralRef; + use stm32_metapac::FLASH_SIZE; + + use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; + use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashRegion}; + use crate::peripherals::FLASH; + + pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { + base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION1 + }; + pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { + base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION2 + }; + pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { + base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + + pub type AltBank1Region1 = Bank1Region1; + pub type AltBank1Region2 = Bank1Region2; + pub struct AltBank1Region3(&'static FlashRegion); + pub struct AltBank2Region1(&'static FlashRegion); + pub struct AltBank2Region2(&'static FlashRegion); + pub struct AltBank2Region3(&'static FlashRegion); + + pub struct AltFlashLayout<'d> { + _inner: PeripheralRef<'d, FLASH>, + pub bank1_region1: AltBank1Region1, + pub bank1_region2: AltBank1Region2, + pub bank1_region3: AltBank1Region3, + pub bank2_region1: AltBank2Region1, + pub bank2_region2: AltBank2Region2, + pub bank2_region3: AltBank2Region3, + } + + impl<'d> Flash<'d> { + pub fn into_alt_regions(self) -> AltFlashLayout<'d> { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + AltFlashLayout { + _inner: self.release(), + bank1_region1: Bank1Region1(&BANK1_REGION1), + bank1_region2: Bank1Region2(&BANK1_REGION2), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3), + } + } + } + + impl Drop for AltFlashLayout<'_> { + fn drop(&mut self) { + unsafe { + super::lock(); + crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) + }; + } + } +} + +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub use alt_regions::AltFlashLayout; + fn is_dual_bank() -> bool { + // let asd: super::Bank1Region1; + // let sad = &super::BANK_1_REGION_1; + match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1e7d4c65..7dc71471 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,5 +1,35 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; +#[cfg(flash)] +mod common; + +#[cfg(flash)] +pub use common::*; + +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; + +pub struct FlashRegion { + pub base: u32, + pub size: u32, + pub erase_size: u32, + pub write_size: u32, + pub erase_value: u8, +} + +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub start: u32, + pub size: u32, +} + +impl Drop for FlashLayout<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] @@ -39,32 +69,8 @@ mod family { } } -#[cfg(flash)] -mod common; - -#[cfg(flash)] -pub use common::*; - -pub struct FlashRegion { - pub base: u32, - pub size: u32, - pub erase_size: u32, - pub write_size: u32, - pub erase_value: u8, -} - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} +#[allow(unused_imports)] +pub use family::*; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From e3c4e00be0469030f163568efa2902d73e2b8a4c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 06:01:56 +0200 Subject: [PATCH 029/112] Align families --- embassy-stm32/src/flash/common.rs | 6 +++++ embassy-stm32/src/flash/f3.rs | 10 +++---- embassy-stm32/src/flash/f4.rs | 3 --- embassy-stm32/src/flash/h7.rs | 14 ++++------ embassy-stm32/src/flash/l.rs | 8 +++--- embassy-stm32/src/flash/mod.rs | 44 +++++++------------------------ embassy-stm32/src/flash/other.rs | 27 +++++++++++++++++++ embassy-stm32/src/lib.rs | 1 - 8 files changed, 55 insertions(+), 58 deletions(-) create mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c239d967..6534e1b8 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -122,6 +122,12 @@ impl Drop for Flash<'_> { } } +impl Drop for FlashLayout<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + static REGION_LOCK: Mutex = Mutex::new(()); fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 7b339ccc..5ac1d8fd 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,12 +3,10 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -46,7 +44,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_per(true); }); - pac::FLASH.ar().write(|w| w.set_far(sector.first)); + pac::FLASH.ar().write(|w| w.set_far(sector.start)); pac::FLASH.cr().modify(|w| { w.set_strt(true); @@ -103,11 +101,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 257aae2d..52ef7ad5 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -84,9 +84,6 @@ mod alt_regions { pub use alt_regions::AltFlashLayout; fn is_dual_bank() -> bool { - // let asd: super::Bank1Region1; - // let sad = &super::BANK_1_REGION_1; - match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 82dd4b28..360ad77d 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,15 +3,12 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; -const SECOND_BANK_OFFSET: usize = 0x0010_0000; - const fn is_dual_bank() -> bool { - FLASH_SIZE / 2 > ERASE_SIZE + FLASH_REGIONS.len() == 2 } pub(crate) unsafe fn lock() { @@ -24,7 +21,6 @@ pub(crate) unsafe fn lock() { pub(crate) unsafe fn unlock() { pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); - if is_dual_bank() { pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); @@ -39,7 +35,7 @@ pub(crate) unsafe fn end_write() {} pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address - let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { + let bank = if start_address < BANK1_REGION.end() { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) @@ -181,11 +177,11 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index edcf2b2f..f4b11011 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,7 +2,7 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -73,7 +73,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / BANK1::SETTINGS.erase_size as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; @@ -182,11 +182,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 7dc71471..110b8149 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -24,9 +24,9 @@ pub struct FlashSector { pub size: u32, } -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; +impl FlashRegion { + pub const fn end(&self) -> u32 { + self.base + self.size } } @@ -35,40 +35,14 @@ impl Drop for FlashLayout<'_> { #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr( + not(any( + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 + )), + path = "other.rs" +)] mod family; -#[cfg(not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 -)))] -mod family { - use super::{Error, FlashSector, WRITE_SIZE}; - - pub(crate) unsafe fn lock() { - unimplemented!(); - } - pub(crate) unsafe fn unlock() { - unimplemented!(); - } - pub(crate) unsafe fn begin_write() { - unimplemented!(); - } - pub(crate) unsafe fn end_write() { - unimplemented!(); - } - pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { - unimplemented!(); - } - pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { - unimplemented!(); - } - pub(crate) unsafe fn clear_all_err() { - unimplemented!(); - } - pub(crate) fn get_sector(_address: u32) -> FlashSector { - unimplemented!(); - } -} - #[allow(unused_imports)] pub use family::*; diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs new file mode 100644 index 00000000..fd355122 --- /dev/null +++ b/embassy-stm32/src/flash/other.rs @@ -0,0 +1,27 @@ +#[allow(unused)] +use super::{Error, FlashSector, WRITE_SIZE}; + +pub(crate) unsafe fn lock() { + unimplemented!(); +} +pub(crate) unsafe fn unlock() { + unimplemented!(); +} +pub(crate) unsafe fn begin_write() { + unimplemented!(); +} +pub(crate) unsafe fn end_write() { + unimplemented!(); +} +pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn clear_all_err() { + unimplemented!(); +} +pub(crate) fn get_sector(_address: u32) -> FlashSector { + unimplemented!(); +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 75ff2d75..3f2d078f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,7 +43,6 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(flash)] pub mod flash; pub mod pwm; #[cfg(quadspi)] From e7129371d0d543394c070671490a796ddbb10e3f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 08:32:36 +0200 Subject: [PATCH 030/112] Let sector computation be shared across families --- ci.sh | 1 + embassy-stm32/build.rs | 13 +++ embassy-stm32/src/flash/common.rs | 35 +++++- embassy-stm32/src/flash/f3.rs | 16 +-- embassy-stm32/src/flash/f4.rs | 181 ++++++++++++------------------ embassy-stm32/src/flash/f7.rs | 95 +++++++++------- embassy-stm32/src/flash/h7.rs | 21 ++-- embassy-stm32/src/flash/l.rs | 18 +-- embassy-stm32/src/flash/mod.rs | 15 ++- embassy-stm32/src/flash/other.rs | 12 +- 10 files changed, 208 insertions(+), 199 deletions(-) diff --git a/ci.sh b/ci.sh index d86c9352..b9dddad3 100755 --- a/ci.sh +++ b/ci.sh @@ -49,6 +49,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 6b203a3f..00740924 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -113,6 +113,18 @@ fn main() { .collect(); for region in flash_memory_regions.iter() { let region_name = format_ident!("{}", get_flash_region_name(region.name)); + let bank = format_ident!( + "{}", + if region.name.starts_with("BANK_1") { + "Bank1" + } else if region.name.starts_with("BANK_2") { + "Bank2" + } else if region.name == "OTP" { + "Otp" + } else { + unimplemented!() + } + ); let base = region.address; let size = region.size; let settings = region.settings.as_ref().unwrap(); @@ -122,6 +134,7 @@ fn main() { flash_regions.extend(quote! { pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { + bank: crate::flash::FlashBank::#bank, base: #base, size: #size, erase_size: #erase_size, diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 6534e1b8..31dd1136 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -3,7 +3,8 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use super::{family, Error, FlashLayout, FlashRegion, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use crate::flash::FlashBank; use crate::Peripheral; pub struct Flash<'d> { @@ -79,10 +80,12 @@ impl<'d> Flash<'d> { } unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + let regions = family::get_flash_regions(); + // Test if the address range is aligned at sector base addresses let mut address = start_address; while address < end_address { - let sector = family::get_sector(address); + let sector = get_sector(address, regions); if sector.start != address { return Err(Error::Unaligned); } @@ -103,7 +106,8 @@ impl<'d> Flash<'d> { let mut address = start_address; while address < end_address { - let sector = family::get_sector(address); + let sector = get_sector(address, regions); + trace!("Erasing sector: {}", sector); family::blocking_erase_sector(§or)?; address += sector.size; } @@ -138,6 +142,31 @@ fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { } } +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { unsafe { self.blocking_read_inner(offset, bytes) } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 5ac1d8fd..c1ed33f9 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,10 +3,14 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -99,13 +103,3 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 52ef7ad5..61ac8afd 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,22 +2,17 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SMALL_SECTOR_SIZE: u32 = 16 * 1024; -const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; -const LARGE_SECTOR_SIZE: u32 = 128 * 1024; -const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; - use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashRegion}; + use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { @@ -25,19 +20,31 @@ mod alt_regions { ..BANK1_REGION3 }; pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, ..BANK1_REGION1 }; pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, ..BANK1_REGION2 }; pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, size: 3 * BANK1_REGION3.erase_size, ..BANK1_REGION3 }; + pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [ + &BANK1_REGION1, + &BANK1_REGION2, + &ALT_BANK1_REGION3, + &ALT_BANK2_REGION1, + &ALT_BANK2_REGION2, + &ALT_BANK2_REGION3, + ]; + pub type AltBank1Region1 = Bank1Region1; pub type AltBank1Region2 = Bank1Region2; pub struct AltBank1Region3(&'static FlashRegion); @@ -81,25 +88,22 @@ mod alt_regions { } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub use alt_regions::AltFlashLayout; +pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; -fn is_dual_bank() -> bool { - match FLASH_SIZE / 1024 { - // 1 MB devices depend on configuration - 1024 => { - if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { - unsafe { pac::FLASH.optcr().read().db1m() } - } else { - false - } - } - // 2 MB devices are always dual bank - 2048 => true, - // All other devices are single bank - _ => false, +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { + if unsafe { pac::FLASH.optcr().read().db1m() } { + &ALT_FLASH_REGIONS + } else { + &FLASH_REGIONS } } +#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -136,11 +140,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let sector = sector.index; - let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; - let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); - - trace!("Erasing sector: {}", sector); + let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; pac::FLASH.cr().modify(|w| { w.set_ser(true); @@ -194,72 +194,27 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -pub(crate) fn get_sector(address: u32) -> FlashSector { - get_sector_inner(address, is_dual_bank(), FLASH_SIZE as u32) -} - -fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = address - FLASH_BASE as u32; - if !dual_bank { - get_single_bank_sector(offset) - } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - start: sector.start + bank_size, - size: sector.size, - } - } - } -} - -fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 - + 4 * SMALL_SECTOR_SIZE - + MEDIUM_SECTOR_SIZE - + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - #[cfg(test)] mod tests { use super::*; + use crate::flash::{get_sector, FlashBank}; #[test] + #[cfg(stm32f429)] fn can_get_sector_single_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { assert_eq!( - FlashSector { index, start, size }, - get_sector_inner(addr, false, 1024 * 1024) + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) ) }; @@ -275,41 +230,43 @@ mod tests { assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } - #[test] - fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { assert_eq!( - FlashSector { index, start, size }, - get_sector_inner(addr, true, 1024 * 1024) + FlashSector { + bank, + index_in_bank, + start, + size + }, + get_sector(address, &ALT_FLASH_REGIONS) ) }; - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index eba7df46..7111f5cc 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -2,13 +2,13 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashSector, FLASH_BASE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SMALL_SECTOR_SIZE: u32 = 32 * 1024; -const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; -const LARGE_SECTOR_SIZE: u32 = 256 * 1024; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -48,7 +48,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index) + w.set_snb(sector.index_in_bank) }); pac::FLASH.cr().modify(|w| { @@ -110,48 +110,61 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -pub(crate) fn get_sector(address: u32) -> FlashSector { - // First 4 sectors are 32KB, then one 128KB, and rest are 256KB - let offset = address - FLASH_BASE as u32; - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 - + 4 * SMALL_SECTOR_SIZE - + MEDIUM_SECTOR_SIZE - + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - #[cfg(test)] mod tests { use super::*; + use crate::flash::{get_sector, FlashBank}; #[test] + #[cfg(stm32f732)] fn can_get_sector() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + } + + #[test] + #[cfg(stm32f769)] + fn can_get_sector() { + const SMALL_SECTOR_SIZE: u32 = 32 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; + const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 360ad77d..5ea57ccd 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,7 +3,7 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -11,6 +11,10 @@ const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 } +pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { @@ -75,11 +79,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let bank = pac::FLASH.bank(if sector.index >= 8 { 1 } else { 0 }); - let sector = sector.index % 8; + let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index_in_bank) }); bank.cr().modify(|w| { @@ -175,13 +178,3 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f4b11011..f5ebc0a5 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,10 +2,14 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -73,7 +77,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / BANK1_REGION.erase_size as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; @@ -180,13 +184,3 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 110b8149..794d32cc 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -10,6 +10,7 @@ pub use crate::_generated::flash_regions::*; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; pub struct FlashRegion { + pub bank: FlashBank, pub base: u32, pub size: u32, pub erase_size: u32, @@ -19,15 +20,27 @@ pub struct FlashRegion { #[derive(Debug, PartialEq)] pub struct FlashSector { - pub index: u8, + pub bank: FlashBank, + pub index_in_bank: u8, pub start: u32, pub size: u32, } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum FlashBank { + Bank1 = 0, + Bank2 = 1, + Otp, +} + impl FlashRegion { pub const fn end(&self) -> u32 { self.base + self.size } + + pub const fn sectors(&self) -> u8 { + (self.size / self.erase_size) as u8 + } } #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index fd355122..d329dcab 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -1,5 +1,10 @@ -#[allow(unused)] -use super::{Error, FlashSector, WRITE_SIZE}; +#![allow(unused)] + +use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; + +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} pub(crate) unsafe fn lock() { unimplemented!(); @@ -22,6 +27,3 @@ pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), pub(crate) unsafe fn clear_all_err() { unimplemented!(); } -pub(crate) fn get_sector(_address: u32) -> FlashSector { - unimplemented!(); -} From 760d4a72cbb8d008646e751e70fa212f65f26068 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:05:13 +0200 Subject: [PATCH 031/112] Ensure that embedded_storage traits are actually implemented --- embassy-stm32/src/flash/common.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 31dd1136..47e94f75 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -205,7 +205,7 @@ impl FlashRegion { } foreach_flash_region! { - ($type_name:ident, $write_size:ident, $erase_size:ident) => { + ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { unsafe { self.0.blocking_read_inner(offset, bytes) } @@ -220,11 +220,11 @@ foreach_flash_region! { } } - impl ErrorType for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name { type Error = Error; } - impl ReadNorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -236,7 +236,7 @@ foreach_flash_region! { } } - impl NorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; From 02caec9482cbd8ce62c17ebc3fcaa13c3f47c1ee Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:07:23 +0200 Subject: [PATCH 032/112] Skip unknown banks --- embassy-stm32/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 00740924..4ca3ef3f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -113,7 +113,7 @@ fn main() { .collect(); for region in flash_memory_regions.iter() { let region_name = format_ident!("{}", get_flash_region_name(region.name)); - let bank = format_ident!( + let bank_variant = format_ident!( "{}", if region.name.starts_with("BANK_1") { "Bank1" @@ -122,7 +122,7 @@ fn main() { } else if region.name == "OTP" { "Otp" } else { - unimplemented!() + continue; } ); let base = region.address; @@ -134,7 +134,7 @@ fn main() { flash_regions.extend(quote! { pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { - bank: crate::flash::FlashBank::#bank, + bank: crate::flash::FlashBank::#bank_variant, base: #base, size: #size, erase_size: #erase_size, From a78e10e00362bf0f5649e200fa62c75d6f3808d0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:17:14 +0200 Subject: [PATCH 033/112] Add defmt support to new flash types --- embassy-stm32/src/flash/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 794d32cc..231ff1f9 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -9,6 +9,8 @@ pub use common::*; pub use crate::_generated::flash_regions::*; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { pub bank: FlashBank, pub base: u32, @@ -19,6 +21,7 @@ pub struct FlashRegion { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashSector { pub bank: FlashBank, pub index_in_bank: u8, @@ -27,6 +30,7 @@ pub struct FlashSector { } #[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FlashBank { Bank1 = 0, Bank2 = 1, From f3dcb5eb22dfac21024e77023d1967b4eaa0b176 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 15:13:44 +0200 Subject: [PATCH 034/112] Wrap write/erase operations in cs --- embassy-stm32/src/flash/common.rs | 209 +++++++++++++----------------- 1 file changed, 91 insertions(+), 118 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 47e94f75..59429e34 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,7 +1,5 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::{Mutex, MutexGuard}; use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::flash::FlashBank; @@ -22,96 +20,21 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) - } - - fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - - let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) + let start_address = FLASH_BASE as u32 + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { let start_address = FLASH_BASE as u32 + offset; - // No need to take lock here as we only have one mut flash reference. - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); - - family::clear_all_err(); - family::unlock(); - family::begin_write(); - - let _ = OnDrop::new(|| { - family::end_write(); - family::lock(); - }); - - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; - address += WRITE_SIZE as u32; - } - Ok(()) + unsafe { blocking_write(start_address, buf) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } - - unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { - let regions = family::get_flash_regions(); - - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - - family::clear_all_err(); - family::unlock(); - - let _ = OnDrop::new(|| { - family::lock(); - }); - - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - trace!("Erasing sector: {}", sector); - family::blocking_erase_sector(§or)?; - address += sector.size; - } - Ok(()) + unsafe { blocking_erase(start_address, end_address) } } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { @@ -132,14 +55,79 @@ impl Drop for FlashLayout<'_> { } } -static REGION_LOCK: Mutex = Mutex::new(()); - -fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { - loop { - if let Ok(guard) = REGION_LOCK.try_lock() { - return guard; - } +fn blocking_read(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); } + + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) +} + +unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + critical_section::with(|_| { + family::clear_all_err(); + family::unlock(); + family::begin_write(); + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + family::blocking_write(address, chunk.try_into().unwrap()) + })?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let regions = family::get_flash_regions(); + + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + trace!("Erasing sector: {}", sector); + + critical_section::with(|_| { + family::clear_all_err(); + family::unlock(); + let _ = OnDrop::new(|| { + family::lock(); + }); + family::blocking_erase_sector(§or) + })?; + address += sector.size; + } + Ok(()) } pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { @@ -169,38 +157,19 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - unsafe { self.blocking_read_inner(offset, bytes) } + let start_address = self.base + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { self.blocking_write_inner(offset, bytes) } + let start_address = self.base + offset; + unsafe { blocking_write(start_address, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { self.blocking_erase_inner(from, to) } - } - - unsafe fn blocking_read_inner(&self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(self.base + offset, bytes) - } - - unsafe fn blocking_write_inner(&self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.base + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - Flash::blocking_write_inner(start_address, bytes) - } - - unsafe fn blocking_erase_inner(&self, from: u32, to: u32) -> Result<(), Error> { let start_address = self.base + from; let end_address = self.base + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - Flash::blocking_erase_inner(start_address, end_address) + unsafe { blocking_erase(start_address, end_address) } } } @@ -208,15 +177,19 @@ foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - unsafe { self.0.blocking_read_inner(offset, bytes) } + let start_address = self.0.base + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { self.0.blocking_write_inner(offset, bytes) } + let start_address = self.0.base + offset; + unsafe { blocking_write(start_address, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { self.0.blocking_erase_inner(from, to) } + let start_address = self.0.base + from; + let end_address = self.0.base + to; + unsafe { blocking_erase(start_address, end_address) } } } @@ -228,7 +201,7 @@ foreach_flash_region! { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - unsafe { self.0.blocking_read_inner(offset, bytes) } + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -241,11 +214,11 @@ foreach_flash_region! { const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - unsafe { self.0.blocking_write_inner(offset, bytes) } + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - unsafe { self.0.blocking_erase_inner(from, to) } + self.blocking_erase(from, to) } } }; From 80972f1e0e6b6d409cc4d86202608c22e5ee3e5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 30 Mar 2023 17:55:55 +0200 Subject: [PATCH 035/112] executor,sync: add support for turbo-wakers. This is a `core` patch to make wakers 1 word (the task pointer) instead of 2 (task pointer + vtable). It allows having the "waker optimization" we had a while back on `WakerRegistration/AtomicWaker`, but EVERYWHERE, without patching all crates. Advantages: - Less memory usage. - Faster. - `AtomicWaker` can actually use atomics to load/store the waker, No critical section needed. - No `dyn` call, which means `cargo-call-stack` can now see through wakes. Disadvantages: - You have to patch `core`... - Breaks all executors and other things that create wakers, unless they opt in to using the new `from_ptr` API. How to use: - Run this shell script to patch `core`. https://gist.github.com/Dirbaio/c67da7cf318515181539122c9d32b395 - Enable `build-std` - Enable `build-std-features = core/turbowakers` - Enable feature `turbowakers` in `embassy-executor`, `embassy-sync`. - Make sure you have no other crate creating wakers other than `embassy-executor`. These will panic at runtime. Note that the patched `core` is equivalent to the unpached one when the `turbowakers` feature is not enabled, so it should be fine to leave it there. --- embassy-executor/Cargo.toml | 2 + embassy-executor/src/raw/mod.rs | 1 + embassy-executor/src/raw/waker_turbo.rs | 34 +++++++++++++++ embassy-sync/Cargo.toml | 1 + embassy-sync/src/waitqueue/atomic_waker.rs | 41 +++++++++++++++++++ .../src/waitqueue/atomic_waker_turbo.rs | 30 ++++++++++++++ embassy-sync/src/waitqueue/mod.rs | 8 +++- .../{waker.rs => waker_registration.rs} | 40 ------------------ 8 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 embassy-executor/src/raw/waker_turbo.rs create mode 100644 embassy-sync/src/waitqueue/atomic_waker.rs create mode 100644 embassy-sync/src/waitqueue/atomic_waker_turbo.rs rename embassy-sync/src/waitqueue/{waker.rs => waker_registration.rs} (63%) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index c2868eb9..8ad3fd69 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -38,6 +38,8 @@ wasm = ["dep:wasm-bindgen", "dep:js-sys"] # Enable nightly-only features nightly = [] +turbowakers = [] + integrated-timers = ["dep:embassy-time"] # Trace interrupt invocations with rtos-trace. diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 15ff18fc..72c367c3 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -11,6 +11,7 @@ mod run_queue; #[cfg(feature = "integrated-timers")] mod timer_queue; pub(crate) mod util; +#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; use core::future::Future; diff --git a/embassy-executor/src/raw/waker_turbo.rs b/embassy-executor/src/raw/waker_turbo.rs new file mode 100644 index 00000000..435a0ff7 --- /dev/null +++ b/embassy-executor/src/raw/waker_turbo.rs @@ -0,0 +1,34 @@ +use core::ptr::NonNull; +use core::task::Waker; + +use super::{wake_task, TaskHeader, TaskRef}; + +pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { + Waker::from_turbo_ptr(NonNull::new_unchecked(p.as_ptr() as _)) +} + +/// Get a task pointer from a waker. +/// +/// This can be used as an optimization in wait queues to store task pointers +/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps +/// avoid dynamic dispatch. +/// +/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task). +/// +/// # Panics +/// +/// Panics if the waker is not created by the Embassy executor. +pub fn task_from_waker(waker: &Waker) -> TaskRef { + let ptr = waker.as_turbo_ptr().as_ptr(); + + // safety: our wakers are always created with `TaskRef::as_ptr` + unsafe { TaskRef::from_ptr(ptr as *const TaskHeader) } +} + +#[inline(never)] +#[no_mangle] +fn _turbo_wake(ptr: NonNull<()>) { + // safety: our wakers are always created with `TaskRef::as_ptr` + let task = unsafe { TaskRef::from_ptr(ptr.as_ptr() as *const TaskHeader) }; + wake_task(task) +} diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 7b5d3ce4..e4871e71 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -25,6 +25,7 @@ features = ["nightly"] [features] nightly = ["embedded-io/async"] std = [] +turbowakers = [] [dependencies] defmt = { version = "0.3", optional = true } diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs new file mode 100644 index 00000000..63fe04a6 --- /dev/null +++ b/embassy-sync/src/waitqueue/atomic_waker.rs @@ -0,0 +1,41 @@ +use core::cell::Cell; +use core::task::Waker; + +use crate::blocking_mutex::raw::CriticalSectionRawMutex; +use crate::blocking_mutex::Mutex; + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: Mutex>>, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + cell.set(match cell.replace(None) { + Some(w2) if (w2.will_wake(w)) => Some(w2), + _ => Some(w.clone()), + }) + }) + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + if let Some(w) = cell.replace(None) { + w.wake_by_ref(); + cell.set(Some(w)); + } + }) + } +} diff --git a/embassy-sync/src/waitqueue/atomic_waker_turbo.rs b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs new file mode 100644 index 00000000..5c6a96ec --- /dev/null +++ b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs @@ -0,0 +1,30 @@ +use core::ptr; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicPtr, Ordering}; +use core::task::Waker; + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: AtomicPtr<()>, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: AtomicPtr::new(ptr::null_mut()), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + self.waker.store(w.as_turbo_ptr().as_ptr() as _, Ordering::Release); + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + if let Some(ptr) = NonNull::new(self.waker.load(Ordering::Acquire)) { + unsafe { Waker::from_turbo_ptr(ptr) }.wake(); + } + } +} diff --git a/embassy-sync/src/waitqueue/mod.rs b/embassy-sync/src/waitqueue/mod.rs index 6661a6b6..6b0b0c64 100644 --- a/embassy-sync/src/waitqueue/mod.rs +++ b/embassy-sync/src/waitqueue/mod.rs @@ -1,7 +1,11 @@ //! Async low-level wait queues -mod waker; -pub use waker::*; +#[cfg_attr(feature = "turbowakers", path = "atomic_waker_turbo.rs")] +mod atomic_waker; +pub use atomic_waker::*; + +mod waker_registration; +pub use waker_registration::*; mod multi_waker; pub use multi_waker::*; diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker_registration.rs similarity index 63% rename from embassy-sync/src/waitqueue/waker.rs rename to embassy-sync/src/waitqueue/waker_registration.rs index 9ce94a08..9b666e7c 100644 --- a/embassy-sync/src/waitqueue/waker.rs +++ b/embassy-sync/src/waitqueue/waker_registration.rs @@ -1,10 +1,6 @@ -use core::cell::Cell; use core::mem; use core::task::Waker; -use crate::blocking_mutex::raw::CriticalSectionRawMutex; -use crate::blocking_mutex::Mutex; - /// Utility struct to register and wake a waker. #[derive(Debug, Default)] pub struct WakerRegistration { @@ -54,39 +50,3 @@ impl WakerRegistration { self.waker.is_some() } } - -/// Utility struct to register and wake a waker. -pub struct AtomicWaker { - waker: Mutex>>, -} - -impl AtomicWaker { - /// Create a new `AtomicWaker`. - pub const fn new() -> Self { - Self { - waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), - } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&self, w: &Waker) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - cell.set(match cell.replace(None) { - Some(w2) if (w2.will_wake(w)) => Some(w2), - _ => Some(w.clone()), - }) - }) - } - - /// Wake the registered waker, if any. - pub fn wake(&self) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - if let Some(w) = cell.replace(None) { - w.wake_by_ref(); - cell.set(Some(w)); - } - }) - } -} From 373760a56b1bad13ebcec19247ee3ee4ae4cb07c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 08:05:37 +0200 Subject: [PATCH 036/112] Split bootloader implementation into multiple files --- embassy-boot/boot/src/boot_loader.rs | 526 +++++++++ embassy-boot/boot/src/firmware_updater.rs | 537 ++++++++++ embassy-boot/boot/src/firmware_writer.rs | 97 ++ embassy-boot/boot/src/lib.rs | 1183 +-------------------- embassy-boot/boot/src/partition.rs | 22 + 5 files changed, 1194 insertions(+), 1171 deletions(-) create mode 100644 embassy-boot/boot/src/boot_loader.rs create mode 100644 embassy-boot/boot/src/firmware_updater.rs create mode 100644 embassy-boot/boot/src/firmware_writer.rs create mode 100644 embassy-boot/boot/src/partition.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs new file mode 100644 index 00000000..ad673511 --- /dev/null +++ b/embassy-boot/boot/src/boot_loader.rs @@ -0,0 +1,526 @@ +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; + +use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +/// Errors returned by bootloader +#[derive(PartialEq, Eq, Debug)] +pub enum BootError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Invalid bootloader magic + BadMagic, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for BootError { + fn format(&self, fmt: defmt::Formatter) { + match self { + BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), + BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), + } + } +} + +impl From for BootError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + BootError::Flash(error.kind()) + } +} + +/// Extension of the embedded-storage flash type information with block size and erase value. +pub trait Flash: NorFlash + ReadNorFlash { + /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase + /// size of the flash, but for external QSPI flash modules, this can be lower. + const BLOCK_SIZE: usize; + /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. + const ERASE_VALUE: u8 = 0xFF; +} + +/// Trait defining the flash handles used for active and DFU partition +pub trait FlashConfig { + /// Flash type used for the state partition. + type STATE: Flash; + /// Flash type used for the active partition. + type ACTIVE: Flash; + /// Flash type used for the dfu partition. + type DFU: Flash; + + /// Return flash instance used to write/read to/from active partition. + fn active(&mut self) -> &mut Self::ACTIVE; + /// Return flash instance used to write/read to/from dfu partition. + fn dfu(&mut self) -> &mut Self::DFU; + /// Return flash instance used to write/read to/from bootloader state. + fn state(&mut self) -> &mut Self::STATE; +} + +/// BootLoader works with any flash implementing embedded_storage and can also work with +/// different page sizes and flash write sizes. +pub struct BootLoader { + // Page with current state of bootloader. The state partition has the following format: + // | Range | Description | + // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + // | WRITE_SIZE - N | Progress index used while swapping or reverting | + state: Partition, + // Location of the partition which will be booted from + active: Partition, + // Location of the partition which will be swapped in when requested + dfu: Partition, +} + +impl BootLoader { + /// Create a new instance of a bootloader with the given partitions. + /// + /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. + /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. + pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + Self { active, dfu, state } + } + + /// Return the boot address for the active partition. + pub fn boot_address(&self) -> usize { + self.active.from + } + + /// Perform necessary boot preparations like swapping images. + /// + /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap + /// algorithm to work correctly. + /// + /// SWAPPING + /// + /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. + /// The swap index contains the copy progress, as to allow continuation of the copy process on + /// power failure. The index counter is represented within 1 or more pages (depending on total + /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE) + /// contains a zero value. This ensures that index updates can be performed atomically and + /// avoid a situation where the wrong index value is set (page write size is "atomic"). + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 0 | 1 | 2 | 3 | - | + /// | DFU | 0 | 3 | 2 | 1 | X | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// The algorithm starts by copying 'backwards', and after the first step, the layout is + /// as follows: + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 1 | 1 | 2 | 1 | - | + /// | DFU | 1 | 3 | 2 | 1 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// The next iteration performs the same steps + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 2 | 1 | 2 | 1 | - | + /// | DFU | 2 | 3 | 2 | 2 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// And again until we're done + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 3 | 3 | 2 | 1 | - | + /// | DFU | 3 | 3 | 1 | 2 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// REVERTING + /// + /// The reverting algorithm uses the swap index to discover that images were swapped, but that + /// the application failed to mark the boot successful. In this case, the revert algorithm will + /// run. + /// + /// The revert index is located separately from the swap index, to ensure that revert can continue + /// on power failure. + /// + /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + //*/ + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 1 | - | + /// | DFU | 3 | 3 | 1 | 2 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 1 | - | + /// | DFU | 3 | 3 | 2 | 2 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 3 | - | + /// | DFU | 3 | 3 | 2 | 1 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + pub fn prepare_boot( + &mut self, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result { + // Ensure we have enough progress pages to store copy progress + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); + assert_eq!(magic.len(), P::STATE::WRITE_SIZE); + + // Copy contents from partition N to active + let state = self.read_state(p, magic)?; + if state == State::Swap { + // + // Check if we already swapped. If we're in the swap state, this means we should revert + // since the app has failed to mark boot as successful + // + if !self.is_swapped(p, magic, page)? { + trace!("Swapping"); + self.swap(p, magic, page)?; + trace!("Swapping done"); + } else { + trace!("Reverting"); + self.revert(p, magic, page)?; + + // Overwrite magic and reset progress + let fstate = p.state(); + magic.fill(!P::STATE::ERASE_VALUE); + fstate.write(self.state.from as u32, magic)?; + fstate.erase(self.state.from as u32, self.state.to as u32)?; + + magic.fill(BOOT_MAGIC); + fstate.write(self.state.from as u32, magic)?; + } + } + Ok(state) + } + + fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + let progress = self.current_progress(p, magic)?; + + Ok(progress >= page_count * 2) + } + + fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { + let write_size = aligned.len(); + let max_index = ((self.state.len() - write_size) / write_size) - 1; + aligned.fill(!P::STATE::ERASE_VALUE); + + let flash = config.state(); + for i in 0..max_index { + flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + + if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + return Ok(i); + } + } + Ok(max_index) + } + + fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { + let flash = p.state(); + let write_size = magic.len(); + let w = self.state.from + write_size + idx * write_size; + + let aligned = magic; + aligned.fill(!P::STATE::ERASE_VALUE); + flash.write(w as u32, aligned)?; + Ok(()) + } + + fn active_addr(&self, n: usize, page_size: usize) -> usize { + self.active.from + n * page_size + } + + fn dfu_addr(&self, n: usize, page_size: usize) -> usize { + self.dfu.from + n * page_size + } + + fn copy_page_once_to_active( + &mut self, + idx: usize, + from_page: usize, + to_page: usize, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { + let mut offset = from_page; + for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { + p.dfu().read(offset as u32, chunk)?; + offset += chunk.len(); + } + + p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; + + let mut offset = to_page; + for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { + p.active().write(offset as u32, chunk)?; + offset += chunk.len(); + } + self.update_progress(idx, p, magic)?; + } + Ok(()) + } + + fn copy_page_once_to_dfu( + &mut self, + idx: usize, + from_page: usize, + to_page: usize, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { + let mut offset = from_page; + for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { + p.active().read(offset as u32, chunk)?; + offset += chunk.len(); + } + + p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; + + let mut offset = to_page; + for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { + p.dfu().write(offset as u32, chunk)?; + offset += chunk.len(); + } + self.update_progress(idx, p, magic)?; + } + Ok(()) + } + + fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + trace!("Page count: {}", page_count); + for page_num in 0..page_count { + trace!("COPY PAGE {}", page_num); + // Copy active page to the 'next' DFU page. + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - page_num, page_size); + //trace!("Copy active {} to dfu {}", active_page, dfu_page); + self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; + + // Copy DFU page to the active page + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); + //trace!("Copy dfy {} to active {}", dfu_page, active_page); + self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + } + + Ok(()) + } + + fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + for page_num in 0..page_count { + // Copy the bad active page to the DFU page + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num, page_size); + self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; + + // Copy the DFU page back to the active page + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num + 1, page_size); + self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + } + + Ok(()) + } + + fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { + let flash = config.state(); + flash.read(self.state.from as u32, magic)?; + + if !magic.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } +} + +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + +/// A flash wrapper implementing the Flash and embedded_storage traits. +pub struct BootFlash +where + F: NorFlash + ReadNorFlash, +{ + flash: F, +} + +impl BootFlash +where + F: NorFlash + ReadNorFlash, +{ + /// Create a new instance of a bootable flash + pub fn new(flash: F) -> Self { + Self { flash } + } +} + +impl Flash for BootFlash +where + F: NorFlash + ReadNorFlash, +{ + const BLOCK_SIZE: usize = BLOCK_SIZE; + const ERASE_VALUE: u8 = ERASE_VALUE; +} + +impl ErrorType for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + type Error = F::Error; +} + +impl NorFlash for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + const WRITE_SIZE: usize = F::WRITE_SIZE; + const ERASE_SIZE: usize = F::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + F::erase(&mut self.flash, from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + F::write(&mut self.flash, offset, bytes) + } +} + +impl ReadNorFlash for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + F::read(&mut self.flash, offset, bytes) + } + + fn capacity(&self) -> usize { + F::capacity(&self.flash) + } +} + +/// Convenience provider that uses a single flash for all partitions. +pub struct SingleFlashConfig<'a, F> +where + F: Flash, +{ + flash: &'a mut F, +} + +impl<'a, F> SingleFlashConfig<'a, F> +where + F: Flash, +{ + /// Create a provider for a single flash. + pub fn new(flash: &'a mut F) -> Self { + Self { flash } + } +} + +impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> +where + F: Flash, +{ + type STATE = F; + type ACTIVE = F; + type DFU = F; + + fn active(&mut self) -> &mut Self::STATE { + self.flash + } + fn dfu(&mut self) -> &mut Self::ACTIVE { + self.flash + } + fn state(&mut self) -> &mut Self::DFU { + self.flash + } +} + +/// Convenience flash provider that uses separate flash instances for each partition. +pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + active: &'a mut ACTIVE, + state: &'a mut STATE, + dfu: &'a mut DFU, +} + +impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + /// Create a new flash provider with separate configuration for all three partitions. + pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { + Self { active, state, dfu } + } +} + +impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + type STATE = STATE; + type ACTIVE = ACTIVE; + type DFU = DFU; + + fn active(&mut self) -> &mut Self::ACTIVE { + self.active + } + fn dfu(&mut self) -> &mut Self::DFU { + self.dfu + } + fn state(&mut self) -> &mut Self::STATE { + self.state + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } +} diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs new file mode 100644 index 00000000..2d871227 --- /dev/null +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -0,0 +1,537 @@ +use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; + +use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +/// Errors returned by FirmwareUpdater +#[derive(Debug)] +pub enum FirmwareUpdaterError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Signature errors. + Signature(signature::Error), +} + +#[cfg(feature = "defmt")] +impl defmt::Format for FirmwareUpdaterError { + fn format(&self, fmt: defmt::Formatter) { + match self { + FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), + FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + } + } +} + +impl From for FirmwareUpdaterError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + FirmwareUpdaterError::Flash(error.kind()) + } +} + +/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to +/// 'mess up' the internal bootloader state +pub struct FirmwareUpdater { + state: Partition, + dfu: Partition, +} + +impl Default for FirmwareUpdater { + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let dfu = unsafe { + Partition::new( + &__bootloader_dfu_start as *const u32 as usize, + &__bootloader_dfu_end as *const u32 as usize, + ) + }; + let state = unsafe { + Partition::new( + &__bootloader_state_start as *const u32 as usize, + &__bootloader_state_end as *const u32 as usize, + ) + }; + + trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + FirmwareUpdater::new(dfu, state) + } +} + +impl FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub const fn new(dfu: Partition, state: Partition) -> Self { + Self { dfu, state } + } + + /// Return the length of the DFU area + pub fn firmware_len(&self) -> usize { + self.dfu.len() + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { + flash.read(self.state.from as u32, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// 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. + /// + /// 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. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _flash).await + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, flash).await + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, flash).await + } + + async fn set_magic( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + flash.read(self.state.from as u32, aligned).await?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned).await?; + flash.erase(self.state.from as u32, self.state.to as u32).await?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned).await?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + flash + .erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + ) + .await?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self.dfu) + .write_block(offset, data, flash, block_size) + .await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub async fn prepare_update( + &mut self, + flash: &mut F, + ) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self.dfu)) + } + + // + // Blocking API + // + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { + flash.read(self.state.from as u32, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// 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. + /// + /// 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. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub fn verify_and_mark_updated_blocking( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub fn mark_updated_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + flash.read(self.state.from as u32, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned)?; + flash.erase(self.state.from as u32, self.state.to as u32)?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + flash.erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + )?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking( + &mut self, + flash: &mut F, + ) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self.dfu)) + } +} diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs new file mode 100644 index 00000000..f992021b --- /dev/null +++ b/embassy-boot/boot/src/firmware_writer.rs @@ -0,0 +1,97 @@ +use embedded_storage::nor_flash::NorFlash; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; + +use crate::Partition; + +/// FirmwareWriter allows writing blocks to an already erased flash. +pub struct FirmwareWriter(pub(crate) Partition); + +impl FirmwareWriter { + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_block( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.from + offset, + data.len() + ); + + let mut write_offset = self.0.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk).await?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_block_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.from + offset, + data.len() + ); + + let mut write_offset = self.0.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk)?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 0df44f36..a2259411 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -5,34 +5,18 @@ #![doc = include_str!("../README.md")] mod fmt; -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; +mod boot_loader; +mod firmware_updater; +mod firmware_writer; +mod partition; -const BOOT_MAGIC: u8 = 0xD0; -const SWAP_MAGIC: u8 = 0xF0; +pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; +pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; +pub use firmware_writer::FirmwareWriter; +pub use partition::Partition; -/// A region in flash used by the bootloader. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Partition { - /// Start of the flash region. - pub from: usize, - /// End of the flash region. - pub to: usize, -} - -impl Partition { - /// Create a new partition with the provided range - pub const fn new(from: usize, to: usize) -> Self { - Self { from, to } - } - - /// Return the length of the partition - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> usize { - self.to - self.from - } -} +pub(crate) const BOOT_MAGIC: u8 = 0xD0; +pub(crate) const SWAP_MAGIC: u8 = 0xF0; /// The state of the bootloader after running prepare. #[derive(PartialEq, Eq, Debug)] @@ -44,34 +28,6 @@ pub enum State { Swap, } -/// Errors returned by bootloader -#[derive(PartialEq, Eq, Debug)] -pub enum BootError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Invalid bootloader magic - BadMagic, -} - -#[cfg(feature = "defmt")] -impl defmt::Format for BootError { - fn format(&self, fmt: defmt::Formatter) { - match self { - BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), - BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), - } - } -} - -impl From for BootError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - BootError::Flash(error.kind()) - } -} - /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. #[repr(align(32))] pub struct AlignedBuffer(pub [u8; N]); @@ -88,1118 +44,12 @@ impl AsMut<[u8]> for AlignedBuffer { } } -/// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash + ReadNorFlash { - /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase - /// size of the flash, but for external QSPI flash modules, this can be lower. - const BLOCK_SIZE: usize; - /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. - const ERASE_VALUE: u8 = 0xFF; -} - -/// Trait defining the flash handles used for active and DFU partition -pub trait FlashConfig { - /// Flash type used for the state partition. - type STATE: Flash; - /// Flash type used for the active partition. - type ACTIVE: Flash; - /// Flash type used for the dfu partition. - type DFU: Flash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -/// BootLoader works with any flash implementing embedded_storage and can also work with -/// different page sizes and flash write sizes. -pub struct BootLoader { - // Page with current state of bootloader. The state partition has the following format: - // | Range | Description | - // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | WRITE_SIZE - N | Progress index used while swapping or reverting | - state: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, -} - -impl BootLoader { - /// Create a new instance of a bootloader with the given partitions. - /// - /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. - /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { - Self { active, dfu, state } - } - - /// Return the boot address for the active partition. - pub fn boot_address(&self) -> usize { - self.active.from - } - - /// Perform necessary boot preparations like swapping images. - /// - /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap - /// algorithm to work correctly. - /// - /// SWAPPING - /// - /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. - /// The swap index contains the copy progress, as to allow continuation of the copy process on - /// power failure. The index counter is represented within 1 or more pages (depending on total - /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE) - /// contains a zero value. This ensures that index updates can be performed atomically and - /// avoid a situation where the wrong index value is set (page write size is "atomic"). - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 0 | 1 | 2 | 3 | - | - /// | DFU | 0 | 3 | 2 | 1 | X | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// The algorithm starts by copying 'backwards', and after the first step, the layout is - /// as follows: - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 1 | 1 | 2 | 1 | - | - /// | DFU | 1 | 3 | 2 | 1 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// The next iteration performs the same steps - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 2 | 1 | 2 | 1 | - | - /// | DFU | 2 | 3 | 2 | 2 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// And again until we're done - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 3 | 3 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// REVERTING - /// - /// The reverting algorithm uses the swap index to discover that images were swapped, but that - /// the application failed to mark the boot successful. In this case, the revert algorithm will - /// run. - /// - /// The revert index is located separately from the swap index, to ensure that revert can continue - /// on power failure. - /// - /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - //*/ - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 2 | 2 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 3 | - | - /// | DFU | 3 | 3 | 2 | 1 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - pub fn prepare_boot( - &mut self, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result { - // Ensure we have enough progress pages to store copy progress - assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); - assert_eq!(magic.len(), P::STATE::WRITE_SIZE); - - // Copy contents from partition N to active - let state = self.read_state(p, magic)?; - if state == State::Swap { - // - // Check if we already swapped. If we're in the swap state, this means we should revert - // since the app has failed to mark boot as successful - // - if !self.is_swapped(p, magic, page)? { - trace!("Swapping"); - self.swap(p, magic, page)?; - trace!("Swapping done"); - } else { - trace!("Reverting"); - self.revert(p, magic, page)?; - - // Overwrite magic and reset progress - let fstate = p.state(); - magic.fill(!P::STATE::ERASE_VALUE); - fstate.write(self.state.from as u32, magic)?; - fstate.erase(self.state.from as u32, self.state.to as u32)?; - - magic.fill(BOOT_MAGIC); - fstate.write(self.state.from as u32, magic)?; - } - } - Ok(state) - } - - fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - let progress = self.current_progress(p, magic)?; - - Ok(progress >= page_count * 2) - } - - fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { - let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 1; - aligned.fill(!P::STATE::ERASE_VALUE); - - let flash = config.state(); - for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; - - if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { - return Ok(i); - } - } - Ok(max_index) - } - - fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let flash = p.state(); - let write_size = magic.len(); - let w = self.state.from + write_size + idx * write_size; - - let aligned = magic; - aligned.fill(!P::STATE::ERASE_VALUE); - flash.write(w as u32, aligned)?; - Ok(()) - } - - fn active_addr(&self, n: usize, page_size: usize) -> usize { - self.active.from + n * page_size - } - - fn dfu_addr(&self, n: usize, page_size: usize) -> usize { - self.dfu.from + n * page_size - } - - fn copy_page_once_to_active( - &mut self, - idx: usize, - from_page: usize, - to_page: usize, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; - for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().read(offset as u32, chunk)?; - offset += chunk.len(); - } - - p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; - - let mut offset = to_page; - for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().write(offset as u32, chunk)?; - offset += chunk.len(); - } - self.update_progress(idx, p, magic)?; - } - Ok(()) - } - - fn copy_page_once_to_dfu( - &mut self, - idx: usize, - from_page: usize, - to_page: usize, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; - for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().read(offset as u32, chunk)?; - offset += chunk.len(); - } - - p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; - - let mut offset = to_page; - for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().write(offset as u32, chunk)?; - offset += chunk.len(); - } - self.update_progress(idx, p, magic)?; - } - Ok(()) - } - - fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - trace!("Page count: {}", page_count); - for page_num in 0..page_count { - trace!("COPY PAGE {}", page_num); - // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - page_num, page_size); - //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; - - // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); - //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; - } - - Ok(()) - } - - fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - for page_num in 0..page_count { - // Copy the bad active page to the DFU page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num, page_size); - self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; - - // Copy the DFU page back to the active page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num + 1, page_size); - self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; - } - - Ok(()) - } - - fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - let flash = config.state(); - flash.read(self.state.from as u32, magic)?; - - if !magic.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } -} - -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { - assert_eq!(active.len() % page_size, 0); - assert_eq!(dfu.len() % page_size, 0); - assert!(dfu.len() - active.len() >= page_size); - assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); -} - -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: Flash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: Flash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: Flash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash -where - F: NorFlash + ReadNorFlash, -{ - flash: F, -} - -impl BootFlash -where - F: NorFlash + ReadNorFlash, -{ - /// Create a new instance of a bootable flash - pub fn new(flash: F) -> Self { - Self { flash } - } -} - -impl Flash for BootFlash -where - F: NorFlash + ReadNorFlash, -{ - const BLOCK_SIZE: usize = BLOCK_SIZE; - const ERASE_VALUE: u8 = ERASE_VALUE; -} - -impl ErrorType for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - type Error = F::Error; -} - -impl NorFlash for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - const WRITE_SIZE: usize = F::WRITE_SIZE; - const ERASE_SIZE: usize = F::ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(&mut self.flash, from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(&mut self.flash, offset, bytes) - } -} - -impl ReadNorFlash for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(&mut self.flash, offset, bytes) - } - - fn capacity(&self) -> usize { - F::capacity(&self.flash) - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} -/// Errors returned by FirmwareUpdater -#[derive(Debug)] -pub enum FirmwareUpdaterError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Signature errors. - Signature(signature::Error), -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FirmwareUpdaterError { - fn format(&self, fmt: defmt::Formatter) { - match self { - FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), - FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), - } - } -} - -impl From for FirmwareUpdaterError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - FirmwareUpdaterError::Flash(error.kind()) - } -} - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { - state: Partition, - dfu: Partition, -} - -impl Default for FirmwareUpdater { - fn default() -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, - ) - }; - - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - FirmwareUpdater::new(dfu, state) - } -} - -impl FirmwareUpdater { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub const fn new(dfu: Partition, state: Partition) -> Self { - Self { dfu, state } - } - - /// Return the length of the DFU area - pub fn firmware_len(&self) -> usize { - self.dfu.len() - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result { - flash.read(self.state.from as u32, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// 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. - /// - /// 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. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub async fn verify_and_mark_updated( - &mut self, - _flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: usize, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); - } - - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); - } - - let message = digest.finalize(); - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic(_aligned, SWAP_MAGIC, _flash).await - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub async fn mark_updated( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, flash).await - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, flash).await - } - - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); - - flash.write(self.state.from as u32, aligned).await?; - flash.erase(self.state.from as u32, self.state.to as u32).await?; - - aligned.fill(magic); - flash.write(self.state.from as u32, aligned).await?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - flash - .erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - ) - .await?; - - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu) - .write_block(offset, data, flash, block_size) - .await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - flash: &mut F, - ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - - Ok(FirmwareWriter(self.dfu)) - } - - // - // Blocking API - // - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub fn get_state_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result { - flash.read(self.state.from as u32, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// 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. - /// - /// 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. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated_blocking( - &mut self, - _flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: usize, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); - } - - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); - } - - let message = digest.finalize(); - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub fn mark_updated_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, flash) - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, flash) - } - - fn set_magic_blocking( - &mut self, - aligned: &mut [u8], - magic: u8, - flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned)?; - - if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); - - flash.write(self.state.from as u32, aligned)?; - flash.erase(self.state.from as u32, self.state.to as u32)?; - - aligned.fill(magic); - flash.write(self.state.from as u32, aligned)?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - flash.erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - )?; - - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. - /// - /// Using this instead of `write_firmware_blocking` allows for an optimized - /// API in exchange for added complexity. - pub fn prepare_update_blocking( - &mut self, - flash: &mut F, - ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - - Ok(FirmwareWriter(self.dfu)) - } -} - -/// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter(Partition); - -impl FirmwareWriter { - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_block( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; - for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk).await?; - write_offset += chunk.len(); - } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ - - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_block_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; - for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk)?; - write_offset += chunk.len(); - } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ - - Ok(()) - } -} - #[cfg(test)] mod tests { use core::convert::Infallible; - use embedded_storage::nor_flash::ErrorType; - use embedded_storage_async::nor_flash::ReadNorFlash as AsyncReadNorFlash; + use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; + use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; use futures::executor::block_on; use super::*; @@ -1414,15 +264,6 @@ mod tests { } } - #[test] - #[should_panic] - fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); - } - #[test] #[cfg(feature = "_verify")] fn test_verify() { diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs new file mode 100644 index 00000000..46f80a23 --- /dev/null +++ b/embassy-boot/boot/src/partition.rs @@ -0,0 +1,22 @@ +/// A region in flash used by the bootloader. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Partition { + /// Start of the flash region. + pub from: usize, + /// End of the flash region. + pub to: usize, +} + +impl Partition { + /// Create a new partition with the provided range + pub const fn new(from: usize, to: usize) -> Self { + Self { from, to } + } + + /// Return the length of the partition + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + self.to - self.from + } +} From 42931b51f25ca22d7df3a6e8e98bfab7904eb11e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 10:18:19 +0200 Subject: [PATCH 037/112] Let bootloader partition have read/write/erase operations This change should not have any breaking changes. --- embassy-boot/boot/src/boot_loader.rs | 102 +++++++------ embassy-boot/boot/src/firmware_updater.rs | 174 +++++++--------------- embassy-boot/boot/src/firmware_writer.rs | 54 +------ embassy-boot/boot/src/partition.rs | 88 ++++++++++- 4 files changed, 195 insertions(+), 223 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index ad673511..e2e361e3 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -79,7 +79,7 @@ impl BootLoader { Self { active, dfu, state } } - /// Return the boot address for the active partition. + /// Return the offset of the active partition into the active flash. pub fn boot_address(&self) -> usize { self.active.from } @@ -193,13 +193,13 @@ impl BootLoader { self.revert(p, magic, page)?; // Overwrite magic and reset progress - let fstate = p.state(); + let state_flash = p.state(); magic.fill(!P::STATE::ERASE_VALUE); - fstate.write(self.state.from as u32, magic)?; - fstate.erase(self.state.from as u32, self.state.to as u32)?; + self.state.write_blocking(state_flash, 0, magic)?; + self.state.wipe_blocking(state_flash)?; magic.fill(BOOT_MAGIC); - fstate.write(self.state.from as u32, magic)?; + self.state.write_blocking(state_flash, 0, magic)?; } } Ok(state) @@ -218,9 +218,10 @@ impl BootLoader { let max_index = ((self.state.len() - write_size) / write_size) - 1; aligned.fill(!P::STATE::ERASE_VALUE); - let flash = config.state(); + let state_flash = config.state(); for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + self.state + .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(i); @@ -230,47 +231,39 @@ impl BootLoader { } fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let flash = p.state(); let write_size = magic.len(); - let w = self.state.from + write_size + idx * write_size; let aligned = magic; aligned.fill(!P::STATE::ERASE_VALUE); - flash.write(w as u32, aligned)?; + self.state + .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; Ok(()) } - fn active_addr(&self, n: usize, page_size: usize) -> usize { - self.active.from + n * page_size - } - - fn dfu_addr(&self, n: usize, page_size: usize) -> usize { - self.dfu.from + n * page_size - } - fn copy_page_once_to_active( &mut self, idx: usize, - from_page: usize, - to_page: usize, + from_offset: u32, + to_offset: u32, p: &mut P, magic: &mut [u8], page: &mut [u8], ) -> Result<(), BootError> { let buf = page; if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; + let mut offset = from_offset; for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().read(offset as u32, chunk)?; - offset += chunk.len(); + self.dfu.read_blocking(p.dfu(), offset, chunk)?; + offset += chunk.len() as u32; } - p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; + self.active + .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?; - let mut offset = to_page; + let mut offset = to_offset; for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().write(offset as u32, chunk)?; - offset += chunk.len(); + self.active.write_blocking(p.active(), offset, chunk)?; + offset += chunk.len() as u32; } self.update_progress(idx, p, magic)?; } @@ -280,26 +273,27 @@ impl BootLoader { fn copy_page_once_to_dfu( &mut self, idx: usize, - from_page: usize, - to_page: usize, + from_offset: u32, + to_offset: u32, p: &mut P, magic: &mut [u8], page: &mut [u8], ) -> Result<(), BootError> { let buf = page; if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; + let mut offset = from_offset; for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().read(offset as u32, chunk)?; - offset += chunk.len(); + self.active.read_blocking(p.active(), offset, chunk)?; + offset += chunk.len() as u32; } - p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; + self.dfu + .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?; - let mut offset = to_page; + let mut offset = to_offset; for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().write(offset as u32, chunk)?; - offset += chunk.len(); + self.dfu.write_blocking(p.dfu(), offset, chunk)?; + offset += chunk.len() as u32; } self.update_progress(idx, p, magic)?; } @@ -312,17 +306,20 @@ impl BootLoader { trace!("Page count: {}", page_count); for page_num in 0..page_count { trace!("COPY PAGE {}", page_num); + + let idx = page_num * 2; + // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - page_num, page_size); - //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; + let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + let dfu_to_offset = ((page_count - page_num) * page_size) as u32; + //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); - //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; + let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; } Ok(()) @@ -332,23 +329,24 @@ impl BootLoader { let page_size = page.len(); let page_count = self.active.len() / page_size; for page_num in 0..page_count { + let idx = page_count * 2 + page_num * 2; + // Copy the bad active page to the DFU page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num, page_size); - self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; + let active_from_offset = (page_num * page_size) as u32; + let dfu_to_offset = (page_num * page_size) as u32; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; // Copy the DFU page back to the active page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num + 1, page_size); - self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + let active_to_offset = (page_num * page_size) as u32; + let dfu_from_offset = ((page_num + 1) * page_size) as u32; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; } Ok(()) } fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - let flash = config.state(); - flash.read(self.state.from as u32, magic)?; + self.state.read_blocking(config.state(), 0, magic)?; if !magic.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 2d871227..af1ba114 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -84,10 +84,10 @@ impl FirmwareUpdater { /// `mark_booted`. pub async fn get_state( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result { - flash.read(self.state.from as u32, aligned).await?; + self.state.read(state_flash, 0, aligned).await?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -115,17 +115,16 @@ impl FirmwareUpdater { #[cfg(feature = "_verify")] pub async fn verify_and_mark_updated( &mut self, - _flash: &mut F, + _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -137,21 +136,10 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } public_key @@ -173,21 +161,10 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } let message = digest.finalize(); @@ -202,7 +179,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC, _flash).await + self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } /// Mark to trigger firmware swap on next boot. @@ -213,11 +190,11 @@ impl FirmwareUpdater { #[cfg(not(feature = "_verify"))] pub async fn mark_updated( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, flash).await + self.set_magic(aligned, SWAP_MAGIC, state_flash).await } /// Mark firmware boot successful and stop rollback on reset. @@ -227,29 +204,29 @@ impl FirmwareUpdater { /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. pub async fn mark_booted( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, flash).await + self.set_magic(aligned, BOOT_MAGIC, state_flash).await } async fn set_magic( &mut self, aligned: &mut [u8], magic: u8, - flash: &mut F, + state_flash: &mut F, ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned).await?; + self.state.read(state_flash, 0, aligned).await?; if aligned.iter().any(|&b| b != magic) { aligned.fill(0); - flash.write(self.state.from as u32, aligned).await?; - flash.erase(self.state.from as u32, self.state.to as u32).await?; + self.state.write(state_flash, 0, aligned).await?; + self.state.wipe(state_flash).await?; aligned.fill(magic); - flash.write(self.state.from as u32, aligned).await?; + self.state.write(state_flash, 0, aligned).await?; } Ok(()) } @@ -265,26 +242,17 @@ impl FirmwareUpdater { &mut self, offset: usize, data: &[u8], - flash: &mut F, + dfu_flash: &mut F, block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); - flash - .erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - ) + self.dfu + .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) .await?; - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - FirmwareWriter(self.dfu) - .write_block(offset, data, flash, block_size) + .write_block(offset, data, dfu_flash, block_size) .await?; Ok(()) @@ -297,11 +265,9 @@ impl FirmwareUpdater { /// exchange for added complexity. pub async fn prepare_update( &mut self, - flash: &mut F, + dfu_flash: &mut F, ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + self.dfu.wipe(dfu_flash).await?; Ok(FirmwareWriter(self.dfu)) } @@ -317,10 +283,10 @@ impl FirmwareUpdater { /// `mark_booted`. pub fn get_state_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result { - flash.read(self.state.from as u32, aligned)?; + self.state.read_blocking(state_flash, 0, aligned)?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -348,7 +314,7 @@ impl FirmwareUpdater { #[cfg(feature = "_verify")] pub fn verify_and_mark_updated_blocking( &mut self, - _flash: &mut F, + _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: usize, @@ -370,21 +336,10 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } public_key @@ -406,21 +361,10 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } let message = digest.finalize(); @@ -435,7 +379,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } /// Mark to trigger firmware swap on next boot. @@ -446,11 +390,11 @@ impl FirmwareUpdater { #[cfg(not(feature = "_verify"))] pub fn mark_updated_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) } /// Mark firmware boot successful and stop rollback on reset. @@ -460,29 +404,29 @@ impl FirmwareUpdater { /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. pub fn mark_booted_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) } fn set_magic_blocking( &mut self, aligned: &mut [u8], magic: u8, - flash: &mut F, + state_flash: &mut F, ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned)?; + self.state.read_blocking(state_flash, 0, aligned)?; if aligned.iter().any(|&b| b != magic) { aligned.fill(0); - flash.write(self.state.from as u32, aligned)?; - flash.erase(self.state.from as u32, self.state.to as u32)?; + self.state.write_blocking(state_flash, 0, aligned)?; + self.state.wipe_blocking(state_flash)?; aligned.fill(magic); - flash.write(self.state.from as u32, aligned)?; + self.state.write_blocking(state_flash, 0, aligned)?; } Ok(()) } @@ -498,23 +442,15 @@ impl FirmwareUpdater { &mut self, offset: usize, data: &[u8], - flash: &mut F, + dfu_flash: &mut F, block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); - flash.erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - )?; + self.dfu + .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; + FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; Ok(()) } @@ -528,9 +464,7 @@ impl FirmwareUpdater { &mut self, flash: &mut F, ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + self.dfu.wipe_blocking(flash)?; Ok(FirmwareWriter(self.dfu)) } diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs index f992021b..46079e73 100644 --- a/embassy-boot/boot/src/firmware_writer.rs +++ b/embassy-boot/boot/src/firmware_writer.rs @@ -21,32 +21,11 @@ impl FirmwareWriter { flash: &mut F, block_size: usize, ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; + let mut offset = offset as u32; for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk).await?; - write_offset += chunk.len(); + self.0.write(flash, offset, chunk).await?; + offset += chunk.len() as u32; } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ Ok(()) } @@ -65,32 +44,11 @@ impl FirmwareWriter { flash: &mut F, block_size: usize, ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; + let mut offset = offset as u32; for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk)?; - write_offset += chunk.len(); + self.0.write_blocking(flash, offset, chunk)?; + offset += chunk.len() as u32; } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ Ok(()) } diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 46f80a23..9918fb83 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -1,10 +1,13 @@ +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + /// A region in flash used by the bootloader. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { - /// Start of the flash region. + /// The offset into the flash where the partition starts. pub from: usize, - /// End of the flash region. + /// The offset into the flash where the partition ends. pub to: usize, } @@ -14,9 +17,88 @@ impl Partition { Self { from, to } } - /// Return the length of the partition + /// Return the size of the partition #[allow(clippy::len_without_is_empty)] pub const fn len(&self) -> usize { self.to - self.from } + + /// Read from the partition on the provided flash + pub(crate) async fn read( + &self, + flash: &mut F, + offset: u32, + bytes: &mut [u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes).await + } + + /// Write to the partition on the provided flash + pub(crate) async fn write( + &self, + flash: &mut F, + offset: u32, + bytes: &[u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes).await?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + pub(crate) async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to).await?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to).await?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Read from the partition on the provided flash + pub(crate) fn read_blocking( + &self, + flash: &mut F, + offset: u32, + bytes: &mut [u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes) + } + + /// Write to the partition on the provided flash + pub(crate) fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes)?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + pub(crate) fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to)?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to)?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } } From d9d6fd6d70f3f9971c6db65b6962199f9da7913c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 10:28:47 +0200 Subject: [PATCH 038/112] Add erase and wipe tests --- embassy-boot/boot/src/lib.rs | 3 +- embassy-boot/boot/src/partition.rs | 46 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index a2259411..4c28d7aa 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -313,7 +313,8 @@ mod tests { )) .is_ok()); } - struct MemFlash([u8; SIZE]); + + pub struct MemFlash(pub [u8; SIZE]); impl NorFlash for MemFlash diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 9918fb83..3ccd4dd7 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -102,3 +102,49 @@ impl Partition { Ok(()) } } + +#[cfg(test)] +mod tests { + use crate::tests::MemFlash; + use crate::Partition; + + #[test] + fn can_erase() { + let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let partition = Partition::new(256, 512); + + partition.erase_blocking(&mut flash, 64, 192).unwrap(); + + for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { + assert_eq!(0x00, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { + assert_eq!(0xFF, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { + assert_eq!(0x00, byte, "Index {}", index); + } + } + + #[test] + fn can_wipe() { + let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let partition = Partition::new(256, 512); + + partition.wipe_blocking(&mut flash).unwrap(); + + for (index, byte) in flash.0.iter().copied().enumerate().take(256) { + assert_eq!(0x00, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { + assert_eq!(0xFF, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { + assert_eq!(0x00, byte, "Index {}", index); + } + } +} From cfbe93c2805598652d85e21322b343032586b398 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 31 Mar 2023 10:43:30 +0200 Subject: [PATCH 039/112] Rework bufferedUart to get rid of PeripheralMutex in a similar fashion as nrf & rp. Also adds embedded-hal traits to bufferedUart --- embassy-stm32/src/usart/buffered.rs | 750 ++++++++++++++++++---------- embassy-stm32/src/usart/mod.rs | 9 + 2 files changed, 490 insertions(+), 269 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index cd7d72f9..6721c44a 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,51 +1,47 @@ -use core::cell::RefCell; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::slice; use core::task::Poll; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; -use embassy_hal_common::ring_buffer::RingBuffer; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_cortex_m::interrupt::Interrupt; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_sync::waitqueue::AtomicWaker; use super::*; -pub struct State<'d, T: BasicInstance>(StateStorage>); -impl<'d, T: BasicInstance> State<'d, T> { +pub struct State { + rx_waker: AtomicWaker, + rx_buf: RingBuffer, + + tx_waker: AtomicWaker, + tx_buf: RingBuffer, +} + +impl State { pub const fn new() -> Self { - Self(StateStorage::new()) + Self { + rx_buf: RingBuffer::new(), + tx_buf: RingBuffer::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } } } -struct StateInner<'d, T: BasicInstance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, -} - -unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} - pub struct BufferedUart<'d, T: BasicInstance> { - inner: RefCell>>, + rx: BufferedUartRx<'d, T>, + tx: BufferedUartTx<'d, T>, } -pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { - inner: &'u BufferedUart<'d, T>, +pub struct BufferedUartTx<'d, T: BasicInstance> { + phantom: PhantomData<&'d mut T>, } -pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { - inner: &'u BufferedUart<'d, T>, +pub struct BufferedUartRx<'d, T: BasicInstance> { + phantom: PhantomData<&'d mut T>, } -impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} - impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -57,11 +53,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } pub fn new_with_rtscts( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -86,12 +81,11 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } #[cfg(not(usart_v1))] pub fn new_with_de( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -113,11 +107,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } fn new_inner( - state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -128,8 +121,13 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { ) -> BufferedUart<'d, T> { into_ref!(_peri, rx, tx, irq); - let r = T::regs(); + let state = T::buffered_state(); + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + let r = T::regs(); unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); @@ -147,273 +145,297 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self { - inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), - })), + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, } } - pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { - (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + (self.rx, self.tx) } +} - async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { +impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { + async fn read(&self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx_waker.register(cx.waker()); - Poll::Pending + let state = T::buffered_state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let n = rx_reader.pop(|data| { + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n }); - - if do_pend { - inner.pend(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; } - res + // FIXME: + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + // let regs = T::regs(); + // unsafe { + // regs.uartimsc().write_set(|w| { + // w.set_rxim(true); + // w.set_rtim(true); + // }); + // } + + Poll::Ready(Ok(n)) }) .await + + // poll_fn(move |cx| { + // let state = T::buffered_state(); + + // let mut do_pend = false; + // compiler_fence(Ordering::SeqCst); + + // // We have data ready in buffer? Return it. + // let data = state.rx_buf.pop_buf(); + // if !data.is_empty() { + // let len = data.len().min(buf.len()); + // buf[..len].copy_from_slice(&data[..len]); + + // if state.rx_buf.is_full() { + // do_pend = true; + // } + // state.rx_buf.pop(len); + + // return Poll::Ready(Ok(len)); + // } + + // state.rx_waker.register(cx.waker()); + + // if do_pend { + // inner.pend(); + // } + + // Poll::Pending + // }) + // .await } - fn inner_blocking_read(&self, buf: &mut [u8]) -> Result { + fn blocking_read(&self, buf: &mut [u8]) -> Result { loop { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let n = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return len; - } - - 0 + let state = T::buffered_state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let n = rx_reader.pop(|data| { + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n }); - if do_pend { - inner.pend(); - } - if n > 0 { + // FIXME: + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + // let regs = T::regs(); + // unsafe { + // regs.uartimsc().write_set(|w| { + // w.set_rxim(true); + // w.set_rtim(true); + // }); + // } + return Ok(n); } } } - async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + async fn fill_buf(&self) -> Result<&[u8], Error> { poll_fn(move |cx| { - let mut inner = self.inner.borrow_mut(); - let (poll, empty) = inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (Poll::Ready(Ok(n)), empty) - }); - if empty { - inner.pend(); + let state = T::buffered_state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; } - poll + + let buf = unsafe { slice::from_raw_parts(p, n) }; + Poll::Ready(Ok(buf)) }) .await } - async fn inner_flush<'a>(&'a self) -> Result<(), Error> { + fn consume(&self, amt: usize) { + let state = T::buffered_state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let full = state.rx_buf.is_full(); + rx_reader.pop_done(amt); + if full { + unsafe { T::Interrupt::steal().pend() }; + } + } +} + +impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { + async fn write(&self, buf: &[u8]) -> Result { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } + let state = T::buffered_state(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let n = tx_writer.push(|data| { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + n + }); + if n == 0 { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } - Poll::Ready(Ok(())) - }) + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + unsafe { T::Interrupt::steal() }.pend(); + Poll::Ready(Ok(n)) }) .await } - fn inner_blocking_write(&self, buf: &[u8]) -> Result { + async fn flush(&self) -> Result<(), Error> { + poll_fn(move |cx| { + let state = T::buffered_state(); + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + .await + } + + fn blocking_write(&self, buf: &[u8]) -> Result { loop { - let mut inner = self.inner.borrow_mut(); - let (n, empty) = inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - return (0, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (n, empty) + let state = T::buffered_state(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let n = tx_writer.push(|data| { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + n }); - if empty { - inner.pend(); - } + if n != 0 { + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + unsafe { T::Interrupt::steal() }.pend(); return Ok(n); } } } - fn inner_blocking_flush(&self) -> Result<(), Error> { + fn blocking_flush(&self) -> Result<(), Error> { loop { - if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { + let state = T::buffered_state(); + if state.tx_buf.is_empty() { return Ok(()); } } } - - async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) - .await - } - - fn inner_consume(&self, amt: usize) { - let mut inner = self.inner.borrow_mut(); - let signal = inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - inner.pend(); - } - } } -impl<'d, T: BasicInstance> StateInner<'d, T> -where - Self: 'd, -{ - fn on_rx(&mut self) { - let r = T::regs(); +impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> { + fn drop(&mut self) { + let state = T::buffered_state(); unsafe { - let sr = sr(r).read(); - clear_interrupt_flags(r, sr); + state.rx_buf.deinit(); - // This read also clears the error and idle interrupt flags on v1. - let b = rdr(r).read_volatile(); - - if sr.rxne() { - if sr.pe() { - warn!("Parity error"); - } - if sr.fe() { - warn!("Framing error"); - } - if sr.ne() { - warn!("Noise error"); - } - if sr.ore() { - warn!("Overrun error"); - } - - let buf = self.rx.push_buf(); - if !buf.is_empty() { - buf[0] = b; - self.rx.push(1); - } else { - warn!("RX buffer full, discard received byte"); - } - - if self.rx.is_full() { - self.rx_waker.wake(); - } - } - - if sr.idle() { - self.rx_waker.wake(); - }; - } - } - - fn on_tx(&mut self) { - let r = T::regs(); - unsafe { - if sr(r).read().txe() { - let buf = self.tx.pop_buf(); - if !buf.is_empty() { - r.cr1().modify(|w| { - w.set_txeie(true); - }); - tdr(r).write_volatile(buf[0].into()); - self.tx.pop(1); - self.tx_waker.wake(); - } else { - // Disable interrupt until we have something to transmit again - r.cr1().modify(|w| { - w.set_txeie(false); - }); - } + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + T::Interrupt::steal().disable(); } } } } -impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); +impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { + fn drop(&mut self) { + let state = T::buffered_state(); + unsafe { + state.tx_buf.deinit(); + + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } + } + } +} + +unsafe fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let state = T::buffered_state(); + + // RX + unsafe { + let sr = sr(r).read(); + clear_interrupt_flags(r, sr); + + if sr.rxne() { + if sr.pe() { + warn!("Parity error"); + } + if sr.fe() { + warn!("Framing error"); + } + if sr.ne() { + warn!("Noise error"); + } + if sr.ore() { + warn!("Overrun error"); + } + + let mut rx_writer = state.rx_buf.writer(); + let buf = rx_writer.push_slice(); + if !buf.is_empty() { + // This read also clears the error and idle interrupt flags on v1. + buf[0] = rdr(r).read_volatile(); + rx_writer.push_done(1); + } else { + // FIXME: Should we disable any further RX interrupts when the buffer becomes full. + } + + if state.rx_buf.is_full() { + state.rx_waker.wake(); + } + } + + if sr.idle() { + state.rx_waker.wake(); + }; + } + + // TX + unsafe { + if sr(r).read().txe() { + let mut tx_reader = state.tx_buf.reader(); + let buf = tx_reader.pop_slice(); + if !buf.is_empty() { + r.cr1().modify(|w| { + w.set_txeie(true); + }); + tdr(r).write_volatile(buf[0].into()); + tx_reader.pop_done(1); + state.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.cr1().modify(|w| { + w.set_txeie(false); + }); + } + } } } @@ -427,94 +449,284 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } -impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> { type Error = Error; } -impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> { type Error = Error; } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf).await + self.rx.read(buf).await } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.inner_read(buf).await + Self::read(self, buf).await } } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner_fill_buf().await + self.rx.fill_buf().await } fn consume(&mut self, amt: usize) { - self.inner_consume(amt) + self.rx.consume(amt) } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner.inner_fill_buf().await + Self::fill_buf(self).await } fn consume(&mut self, amt: usize) { - self.inner.inner_consume(amt) + Self::consume(self, amt) } } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - self.inner_write(buf).await + self.tx.write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner_flush().await + self.tx.flush().await } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_write(buf).await + Self::write(self, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_flush().await + Self::flush(self).await } } impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_blocking_read(buf) + self.rx.blocking_read(buf) } } -impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.inner_blocking_read(buf) + self.blocking_read(buf) } } impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { - self.inner_blocking_write(buf) + self.tx.blocking_write(buf) } fn flush(&mut self) -> Result<(), Self::Error> { - self.inner_blocking_flush() + self.tx.blocking_flush() } } -impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_blocking_write(buf) + Self::blocking_write(self, buf) } fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_blocking_flush() + Self::blocking_flush(self) + } +} + +mod eh02 { + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::serial::Read for BufferedUart<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.tx.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> { + fn read(&mut self) -> nb::Result { + embedded_hal_02::serial::Read::read(self) + } + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer).map(drop) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.tx.blocking_write(buffer).map(drop) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.blocking_flush().map_err(nb::Error::Other) + } + } +} + +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; + + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> { + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + Self::write(buf) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Self::read(buf) + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUart<'d, T> { + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.tx.write(buf) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUart<'d, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + self.rx.read(buf) + } } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index f80323e3..a42eede1 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1112,6 +1112,9 @@ pub(crate) mod sealed { fn regs() -> Regs; fn state() -> &'static State; + + #[cfg(feature = "nightly")] + fn buffered_state() -> &'static buffered::State; } pub trait FullInstance: BasicInstance { @@ -1147,6 +1150,12 @@ macro_rules! impl_lpuart { static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); &STATE } + + #[cfg(feature = "nightly")] + fn buffered_state() -> &'static buffered::State { + static STATE: buffered::State = buffered::State::new(); + &STATE + } } impl BasicInstance for peripherals::$inst {} From 50b0fb1a376dbc650db785228b181cc3939ac87f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 15:47:45 +0200 Subject: [PATCH 040/112] Let get_flash_regions be public --- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/f4.rs | 4 ++-- embassy-stm32/src/flash/f7.rs | 2 +- embassy-stm32/src/flash/h7.rs | 2 +- embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index c1ed33f9..10a09c42 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -7,7 +7,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 61ac8afd..2f5b417c 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -91,7 +91,7 @@ mod alt_regions { pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { if unsafe { pac::FLASH.optcr().read().db1m() } { &ALT_FLASH_REGIONS } else { @@ -100,7 +100,7 @@ pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 7111f5cc..6427d5a0 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 5ea57ccd..4f38d50c 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -11,7 +11,7 @@ const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 } -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f5ebc0a5..7d9cc6ea 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index d329dcab..c151cb82 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -2,7 +2,7 @@ use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } From 472dc6b7d16ea10b9512090acb1b74aa24ed189f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 31 Mar 2023 15:50:37 +0200 Subject: [PATCH 041/112] Fix interrupt handling so it is similar to before the rework, and fix examples --- embassy-stm32/src/usart/buffered.rs | 154 ++++++++------------- examples/stm32f4/src/bin/usart_buffered.rs | 14 +- examples/stm32l0/src/bin/usart_irq.rs | 16 +-- 3 files changed, 62 insertions(+), 122 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 6721c44a..3e23e7ca 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -43,9 +43,9 @@ pub struct BufferedUartRx<'d, T: BasicInstance> { impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, @@ -53,14 +53,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } pub fn new_with_rtscts( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], @@ -81,15 +81,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } #[cfg(not(usart_v1))] pub fn new_with_de( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, de: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -107,14 +107,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } fn new_inner( _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, @@ -155,8 +155,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { } } - pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { - (self.rx, self.tx) + pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) { + (self.tx, self.rx) } } @@ -165,85 +165,46 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { poll_fn(move |cx| { let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; - let n = rx_reader.pop(|data| { - let n = data.len().min(buf.len()); - buf[..n].copy_from_slice(&data[..n]); - n - }); - if n == 0 { - state.rx_waker.register(cx.waker()); - return Poll::Pending; + let data = rx_reader.pop_slice(); + + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + unsafe { T::Interrupt::steal().pend() }; + } + + return Poll::Ready(Ok(len)); } - // FIXME: - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - // let regs = T::regs(); - // unsafe { - // regs.uartimsc().write_set(|w| { - // w.set_rxim(true); - // w.set_rtim(true); - // }); - // } - - Poll::Ready(Ok(n)) + state.rx_waker.register(cx.waker()); + Poll::Pending }) .await - - // poll_fn(move |cx| { - // let state = T::buffered_state(); - - // let mut do_pend = false; - // compiler_fence(Ordering::SeqCst); - - // // We have data ready in buffer? Return it. - // let data = state.rx_buf.pop_buf(); - // if !data.is_empty() { - // let len = data.len().min(buf.len()); - // buf[..len].copy_from_slice(&data[..len]); - - // if state.rx_buf.is_full() { - // do_pend = true; - // } - // state.rx_buf.pop(len); - - // return Poll::Ready(Ok(len)); - // } - - // state.rx_waker.register(cx.waker()); - - // if do_pend { - // inner.pend(); - // } - - // Poll::Pending - // }) - // .await } fn blocking_read(&self, buf: &mut [u8]) -> Result { loop { let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; - let n = rx_reader.pop(|data| { - let n = data.len().min(buf.len()); - buf[..n].copy_from_slice(&data[..n]); - n - }); + let data = rx_reader.pop_slice(); - if n > 0 { - // FIXME: - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - // let regs = T::regs(); - // unsafe { - // regs.uartimsc().write_set(|w| { - // w.set_rxim(true); - // w.set_rtim(true); - // }); - // } + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); - return Ok(n); + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + unsafe { T::Interrupt::steal().pend() }; + } + + return Ok(len); } } } @@ -279,22 +240,23 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { async fn write(&self, buf: &[u8]) -> Result { poll_fn(move |cx| { let state = T::buffered_state(); + let empty = state.tx_buf.is_empty(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; - let n = tx_writer.push(|data| { - let n = data.len().min(buf.len()); - data[..n].copy_from_slice(&buf[..n]); - n - }); - if n == 0 { + let data = tx_writer.push_slice(); + if data.is_empty() { state.tx_waker.register(cx.waker()); return Poll::Pending; } - // The TX interrupt only triggers when the there was data in the - // FIFO and the number of bytes drops below a threshold. When the - // FIFO was empty we have to manually pend the interrupt to shovel - // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + tx_writer.push_done(n); + + if empty { + unsafe { T::Interrupt::steal() }.pend(); + } + Poll::Ready(Ok(n)) }) .await @@ -316,19 +278,19 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { fn blocking_write(&self, buf: &[u8]) -> Result { loop { let state = T::buffered_state(); + let empty = state.tx_buf.is_empty(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; - let n = tx_writer.push(|data| { + let data = tx_writer.push_slice(); + if !data.is_empty() { let n = data.len().min(buf.len()); data[..n].copy_from_slice(&buf[..n]); - n - }); + tx_writer.push_done(n); + + if empty { + unsafe { T::Interrupt::steal() }.pend(); + } - if n != 0 { - // The TX interrupt only triggers when the there was data in the - // FIFO and the number of bytes drops below a threshold. When the - // FIFO was empty we have to manually pend the interrupt to shovel - // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); return Ok(n); } } diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index dd171fe1..a93f8bae 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State}; +use embassy_stm32::usart::{BufferedUart, Config}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; @@ -16,20 +16,10 @@ async fn main(_spawner: Spawner) { let config = Config::default(); - let mut state = State::new(); let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new( - &mut state, - p.USART3, - p.PD9, - p.PD8, - irq, - &mut tx_buf, - &mut rx_buf, - config, - ); + let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 8e84cd09..46534700 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State}; +use embassy_stm32::usart::{BufferedUart, Config}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -20,20 +20,8 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let mut state = State::new(); let irq = interrupt::take!(USART2); - let mut usart = unsafe { - BufferedUart::new( - &mut state, - p.USART2, - p.PA3, - p.PA2, - irq, - &mut TX_BUFFER, - &mut RX_BUFFER, - config, - ) - }; + let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); From 0e13fe992543980b1d05547bd2c7f7c08530a8d2 Mon Sep 17 00:00:00 2001 From: Thierry Fleury Date: Sat, 1 Apr 2023 11:44:49 +0200 Subject: [PATCH 042/112] Fix set_baudrate on RP-PICO --- embassy-rp/src/uart/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 682243a2..a945f229 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -405,10 +405,6 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { Parity::ParityEven => (true, true), }; - // PL011 needs a (dummy) line control register write to latch in the - // divisors. We don't want to actually change LCR contents here. - r.uartlcr_h().modify(|_| {}); - r.uartlcr_h().write(|w| { w.set_wlen(config.data_bits.bits()); w.set_stp2(config.stop_bits == StopBits::STOP2); @@ -458,6 +454,10 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + r.uartlcr_h().modify(|_| {}); } } } From f78aa4f936b8bcdd8237f070125e60595f582eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 1 Apr 2023 14:31:24 +0200 Subject: [PATCH 043/112] rp: Allow zero len reads for buffered uart Prevents the read methods from getting stuck forever. --- embassy-rp/src/uart/buffered.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 1a573b31..9aa0b571 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -175,6 +175,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -202,6 +206,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; From 268e29b153c9c8a6e3a798a01757669ee31e5409 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 16:59:21 +0200 Subject: [PATCH 044/112] Let the FlashRegion for region types be public --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 4ca3ef3f..67b081fe 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -145,7 +145,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { - pub struct #region_type(pub(crate) &'static crate::flash::FlashRegion); + pub struct #region_type(pub &'static crate::flash::FlashRegion); }); } From e11eebfa577b143b8a19fcf6d9d5398c3058c3eb Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 17:26:32 +0200 Subject: [PATCH 045/112] Ensure that ranges are validated with the region size --- embassy-stm32/src/flash/common.rs | 57 ++++++++++++------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 59429e34..287c54f5 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -20,21 +20,15 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - blocking_read(start_address, bytes) + blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - - unsafe { blocking_write(start_address, buf) } + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + from; - let end_address = FLASH_BASE as u32 + to; - - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(FLASH_BASE as u32, from, to) } } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { @@ -55,30 +49,29 @@ impl Drop for FlashLayout<'_> { } } -fn blocking_read(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { +fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { return Err(Error::Size); } + let start_address = base + offset; let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } -unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { +unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { return Err(Error::Size); } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 { return Err(Error::Unaligned); } - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + let mut address = base + offset; + trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { + for chunk in bytes.chunks(WRITE_SIZE) { critical_section::with(|_| { family::clear_all_err(); family::unlock(); @@ -94,7 +87,9 @@ unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { Ok(()) } -unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { +unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { + let start_address = base + from; + let end_address = base + to; let regions = family::get_flash_regions(); // Test if the address range is aligned at sector base addresses @@ -157,19 +152,15 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = self.base + offset; - blocking_read(start_address, bytes) + blocking_read(self.base, self.size, offset, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.base + offset; - unsafe { blocking_write(start_address, bytes) } + unsafe { blocking_write(self.base, self.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = self.base + from; - let end_address = self.base + to; - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(self.base, from, to) } } } @@ -177,19 +168,15 @@ foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = self.0.base + offset; - blocking_read(start_address, bytes) + blocking_read(self.0.base, self.0.size, offset, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.0.base + offset; - unsafe { blocking_write(start_address, bytes) } + unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = self.0.base + from; - let end_address = self.0.base + to; - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(self.0.base, from, to) } } } From dd88775871d2946df6e7e06bf2b536e94973dbaa Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 18:10:20 +0200 Subject: [PATCH 046/112] Ensure that flash locking is defered to after write --- embassy-stm32/src/flash/common.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 287c54f5..41c28c56 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,3 +1,4 @@ +use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -74,12 +75,18 @@ unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Res for chunk in bytes.chunks(WRITE_SIZE) { critical_section::with(|_| { family::clear_all_err(); + fence(Ordering::SeqCst); family::unlock(); + fence(Ordering::SeqCst); family::begin_write(); - let _ = OnDrop::new(|| { + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { family::end_write(); + fence(Ordering::SeqCst); family::lock(); }); + family::blocking_write(address, chunk.try_into().unwrap()) })?; address += WRITE_SIZE as u32; @@ -114,10 +121,14 @@ unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { critical_section::with(|_| { family::clear_all_err(); + fence(Ordering::SeqCst); family::unlock(); - let _ = OnDrop::new(|| { + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { family::lock(); }); + family::blocking_erase_sector(§or) })?; address += sector.size; From 7ef6a3cfb22ee1e497fcea37a019ca32d22298b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 2 Apr 2023 14:36:32 +0200 Subject: [PATCH 047/112] rp: Allow zero len writes for buffered uart Prevents the write methods from getting stuck forever. --- embassy-rp/src/uart/buffered.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9aa0b571..c620ed08 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -301,6 +301,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { fn write<'a>(buf: &'a [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + let state = T::state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { @@ -335,6 +339,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } pub fn blocking_write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { let state = T::state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; From b41ee47115509ba5ed302c684c0c36b6a3e3f76f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 01:11:42 +0200 Subject: [PATCH 048/112] executor: unify export mod. --- embassy-executor/src/lib.rs | 12 ++++-------- embassy-macros/src/macros/cortex_m_interrupt_take.rs | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 4c7e2f4c..8707995b 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -46,11 +46,13 @@ cfg_if::cfg_if! { } } +/// Implementation details for embassy macros. +/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. #[doc(hidden)] -/// Implementation details for embassy macros. DO NOT USE. -pub mod export { +pub mod _export { #[cfg(feature = "rtos-trace")] pub use rtos_trace::trace; + pub use static_cell::StaticCell; /// Expands the given block of code when `embassy-executor` is compiled with /// the `rtos-trace-interrupt` feature. @@ -75,9 +77,3 @@ pub mod raw; mod spawner; pub use spawner::*; - -/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. -#[doc(hidden)] -pub mod _export { - pub use static_cell::StaticCell; -} diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index e2ebf98c..4806d1c1 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -10,12 +10,12 @@ pub fn run(name: syn::Ident) -> Result { let (isr_enter, isr_exit) = ( quote! { ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_enter(); + ::embassy_executor::_export::trace::isr_enter(); } }, quote! { ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_exit(); + ::embassy_executor::_export::trace::isr_exit(); } }, ); From 94890e544e07eabbc806dd5c05419e4336b5bf32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 02:01:06 +0200 Subject: [PATCH 049/112] Update stm32-metapac. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4f660187..504caacb 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "2" +stm32-metapac = "3" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "3", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] From d3c4e4a20a05085eae8d568c7efdbe09bada9cf5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 01:18:27 +0200 Subject: [PATCH 050/112] executor: add Pender, rework Cargo features. This introduces a `Pender` struct with enum cases for thread-mode, interrupt-mode and custom callback executors. This avoids calls through function pointers when using only the thread or interrupt executors. Faster, and friendlier to `cargo-call-stack`. `embassy-executor` now has `arch-xxx` Cargo features to select the arch and to enable the builtin executors (thread and interrupt). --- docs/modules/ROOT/examples/basic/Cargo.toml | 2 +- .../layer-by-layer/blinky-async/Cargo.toml | 2 +- embassy-cortex-m/src/executor.rs | 116 --------- embassy-cortex-m/src/lib.rs | 2 +- embassy-executor/Cargo.toml | 25 +- embassy-executor/src/arch/cortex_m.rs | 242 ++++++++++++++---- embassy-executor/src/arch/riscv32.rs | 133 +++++----- embassy-executor/src/arch/std.rs | 152 ++++++----- embassy-executor/src/arch/wasm.rs | 136 +++++----- embassy-executor/src/arch/xtensa.rs | 135 +++++----- embassy-executor/src/lib.rs | 71 +++-- embassy-executor/src/raw/mod.rs | 97 +++++-- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f0/src/bin/multiprio.rs | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f3/src/bin/multiprio.rs | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/multiprio.rs | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 50 files changed, 667 insertions(+), 522 deletions(-) delete mode 100644 embassy-cortex-m/src/executor.rs diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index d9f8a285..e3e446e6 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index c9a963d4..a11a7e0b 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } -embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } +embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs deleted file mode 100644 index 558539e7..00000000 --- a/embassy-cortex-m/src/executor.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Executor specific to cortex-m devices. - -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; - -use atomic_polyfill::{AtomicBool, Ordering}; -use cortex_m::interrupt::InterruptNumber; -use cortex_m::peripheral::NVIC; -pub use embassy_executor::*; - -#[derive(Clone, Copy)] -struct N(u16); -unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } -} - -fn pend_by_number(n: u16) { - cortex_m::peripheral::NVIC::pend(N(n)) -} - -/// Interrupt mode executor. -/// -/// This executor runs tasks in interrupt mode. The interrupt handler is set up -/// to poll tasks, and when a task is woken the interrupt is pended from software. -/// -/// This allows running async tasks at a priority higher than thread mode. One -/// use case is to leave thread mode free for non-async tasks. Another use case is -/// to run multiple executors: one in thread mode for low priority tasks and another in -/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower -/// priority ones. -/// -/// It is even possible to run multiple interrupt mode executors at different priorities, -/// by assigning different priorities to the interrupts. For an example on how to do this, -/// See the 'multiprio' example for 'embassy-nrf'. -/// -/// To use it, you have to pick an interrupt that won't be used by the hardware. -/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). -/// If this is not the case, you may use an interrupt from any unused peripheral. -/// -/// It is somewhat more complex to use, it's recommended to use the thread-mode -/// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - started: AtomicBool, - executor: UnsafeCell>, -} - -unsafe impl Send for InterruptExecutor {} -unsafe impl Sync for InterruptExecutor {} - -impl InterruptExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new( - |ctx| pend_by_number(ctx as u16), - irq.number() as *mut (), - )) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - unsafe { NVIC::unmask(irq) } - - executor.spawner().make_send() - } -} diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs index fba23367..e4b713a0 100644 --- a/embassy-cortex-m/src/lib.rs +++ b/embassy-cortex-m/src/lib.rs @@ -5,6 +5,6 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub mod executor; +pub use embassy_executor as executor; pub mod interrupt; pub mod peripheral; diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 8ad3fd69..bb8a46c8 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,9 +31,22 @@ flavors = [ features = ["std", "nightly", "defmt"] [features] -default = [] -std = ["critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys"] + +# Architecture +_arch = [] # some arch was picked +arch-std = ["_arch", "critical-section/std"] +arch-cortex-m = ["_arch", "dep:cortex-m"] +arch-xtensa = ["_arch"] +arch-riscv32 = ["_arch"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] + +# Enable creating a `Pender` from an arbitrary function pointer callback. +pender-callback = [] + +# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) +executor-thread = [] +# Enable the interrupt-mode executor (available in Cortex-M only) +executor-interrupt = [] # Enable nightly-only features nightly = [] @@ -55,9 +68,11 @@ embassy-macros = { version = "0.1.0", path = "../embassy-macros" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" -cfg-if = "1.0.0" static_cell = "1.0" -# WASM dependencies +# arch-cortex-m dependencies +cortex-m = { version = "0.7.6", optional = true } + +# arch-wasm dependencies wasm-bindgen = { version = "0.2.82", optional = true } js-sys = { version = "0.3", optional = true } diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 4b27a264..d6a55c4c 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,59 +1,209 @@ -use core::arch::asm; -use core::marker::PhantomData; -use core::ptr; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::arch::asm; + use core::marker::PhantomData; -use super::{raw, Spawner}; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_cortex_m as main; -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), - not_send: PhantomData, + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; + + impl ThreadPender { + pub(crate) fn pend(self) { + unsafe { core::arch::asm!("sev") } } } - /// Run the executor. + /// Thread mode executor, using WFE/SEV. /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// This executor allows for ultra low power consumption for chips where `WFE` + /// triggers low-power sleep without extra steps. If your chip requires extra steps, + /// you may use [`raw::Executor`] directly to program custom behavior. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + asm!("wfe"); + }; + } + } + } +} + +#[cfg(feature = "executor-interrupt")] +pub use interrupt::*; +#[cfg(feature = "executor-interrupt")] +mod interrupt { + use core::cell::UnsafeCell; + use core::mem::MaybeUninit; + + use atomic_polyfill::{AtomicBool, Ordering}; + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + use crate::raw::{self, Pender, PenderInner}; + + #[derive(Clone, Copy)] + pub(crate) struct InterruptPender(u16); + + impl InterruptPender { + pub(crate) fn pend(self) { + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(self); + } + + #[cfg(armv6m)] + cortex_m::peripheral::NVIC::pend(self); + } + } + + unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { + fn number(self) -> u16 { + self.0 + } + } + + /// Interrupt mode executor. + /// + /// This executor runs tasks in interrupt mode. The interrupt handler is set up + /// to poll tasks, and when a task is woken the interrupt is pended from software. + /// + /// This allows running async tasks at a priority higher than thread mode. One + /// use case is to leave thread mode free for non-async tasks. Another use case is + /// to run multiple executors: one in thread mode for low priority tasks and another in + /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower + /// priority ones. + /// + /// It is even possible to run multiple interrupt mode executors at different priorities, + /// by assigning different priorities to the interrupts. For an example on how to do this, + /// See the 'multiprio' example for 'embassy-nrf'. + /// + /// To use it, you have to pick an interrupt that won't be used by the hardware. + /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). + /// If this is not the case, you may use an interrupt from any unused peripheral. + /// + /// It is somewhat more complex to use, it's recommended to use the thread-mode + /// [`Executor`] instead, if it works for your use case. + pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, + } + + unsafe impl Send for InterruptExecutor {} + unsafe impl Sync for InterruptExecutor {} + + impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } - loop { unsafe { - self.inner.poll(); - asm!("wfe"); - }; + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( + irq.number(), + ))))) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() } } } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index e97a56cd..f66daeae 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,72 +1,83 @@ -use core::marker::PhantomData; -use core::ptr; -use core::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + use core::sync::atomic::{AtomicBool, Ordering}; -/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -/// RISCV32 Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV + static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + /// RISCV32 Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here + } } } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 701f0eb1..4e4a178f 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,84 +1,100 @@ -use std::marker::PhantomData; -use std::sync::{Condvar, Mutex}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-std`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use std::marker::PhantomData; + use std::sync::{Condvar, Mutex}; -/// Single-threaded std-based executor. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, - signaler: &'static Signaler, -} + #[cfg(feature = "nightly")] + pub use embassy_macros::main_std as main; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); - Self { - inner: raw::Executor::new( - |p| unsafe { - let s = &*(p as *const () as *const Signaler); - s.signal() - }, - signaler as *const _ as _, - ), - not_send: PhantomData, - signaler, + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; + + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender(&'static Signaler); + + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + self.0.signal() } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - self.signaler.wait() - } + /// Single-threaded std-based executor. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + signaler: &'static Signaler, } -} -struct Signaler { - mutex: Mutex, - condvar: Condvar, -} + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let signaler = &*Box::leak(Box::new(Signaler::new())); + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), + not_send: PhantomData, + signaler, + } + } -impl Signaler { - fn new() -> Self { - Self { - mutex: Mutex::new(false), - condvar: Condvar::new(), + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + self.signaler.wait() + } } } - fn wait(&self) { - let mut signaled = self.mutex.lock().unwrap(); - while !*signaled { - signaled = self.condvar.wait(signaled).unwrap(); - } - *signaled = false; + struct Signaler { + mutex: Mutex, + condvar: Condvar, } - fn signal(&self) { - let mut signaled = self.mutex.lock().unwrap(); - *signaled = true; - self.condvar.notify_one(); + impl Signaler { + fn new() -> Self { + Self { + mutex: Mutex::new(false), + condvar: Condvar::new(), + } + } + + fn wait(&self) { + let mut signaled = self.mutex.lock().unwrap(); + while !*signaled { + signaled = self.condvar.wait(signaled).unwrap(); + } + *signaled = false; + } + + fn signal(&self) { + let mut signaled = self.mutex.lock().unwrap(); + *signaled = true; + self.condvar.notify_one(); + } } } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 98091cfb..08ab16b9 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,74 +1,88 @@ -use core::marker::PhantomData; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); -use js_sys::Promise; -use wasm_bindgen::prelude::*; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { -use super::raw::util::UninitCell; -use super::raw::{self}; -use super::Spawner; + use core::marker::PhantomData; -/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. -pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, -} + #[cfg(feature = "nightly")] + pub use embassy_macros::main_wasm as main; + use js_sys::Promise; + use wasm_bindgen::prelude::*; -pub(crate) struct WasmContext { - promise: Promise, - closure: UninitCell>, -} + use crate::raw::util::UninitCell; + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -impl WasmContext { - pub fn new() -> Self { - Self { - promise: Promise::resolve(&JsValue::undefined()), - closure: UninitCell::uninit(), - } + /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. + pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, } -} -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); - let inner = raw::Executor::new( - |p| unsafe { - let ctx = &*(p as *const () as *const WasmContext); - let _ = ctx.promise.then(ctx.closure.as_mut()); - }, - ctx as *const _ as _, - ); - Self { - inner, - not_send: PhantomData, - ctx, + pub(crate) struct WasmContext { + promise: Promise, + closure: UninitCell>, + } + + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender(&'static WasmContext); + + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { - unsafe { - let executor = &self.inner; - self.ctx.closure.write(Closure::new(move |_| { - executor.poll(); - })); - init(self.inner.spawner()); + impl WasmContext { + pub fn new() -> Self { + Self { + promise: Promise::resolve(&JsValue::undefined()), + closure: UninitCell::uninit(), + } + } + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let ctx = &*Box::leak(Box::new(WasmContext::new())); + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), + not_send: PhantomData, + ctx, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { + unsafe { + let executor = &self.inner; + self.ctx.closure.write(Closure::new(move |_| { + executor.poll(); + })); + init(self.inner.spawner()); + } } } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 4ee0d9f7..61ea92c1 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,73 +1,84 @@ -use core::marker::PhantomData; -use core::ptr; -use core::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + use core::sync::atomic::{AtomicBool, Ordering}; -/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -/// Xtensa Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa + static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - // TODO can we relax this? - critical_section::with(|_| { - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } - }); + /// Xtensa Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + // TODO can we relax this? + critical_section::with(|_| { + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } + }); + } } } } diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 8707995b..3ce687eb 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -1,5 +1,5 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] +#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] +#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -10,41 +10,35 @@ pub(crate) mod fmt; #[cfg(feature = "nightly")] pub use embassy_macros::task; -cfg_if::cfg_if! { - if #[cfg(cortex_m)] { - #[path="arch/cortex_m.rs"] - mod arch; - pub use arch::*; - #[cfg(feature = "nightly")] - pub use embassy_macros::main_cortex_m as main; - } - else if #[cfg(target_arch="riscv32")] { - #[path="arch/riscv32.rs"] - mod arch; - pub use arch::*; - #[cfg(feature = "nightly")] - pub use embassy_macros::main_riscv as main; - } - else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] { - #[path="arch/xtensa.rs"] - mod arch; - pub use arch::*; - } - else if #[cfg(feature="wasm")] { - #[path="arch/wasm.rs"] - mod arch; - pub use arch::*; - #[cfg(feature = "nightly")] - pub use embassy_macros::main_wasm as main; - } - else if #[cfg(feature="std")] { - #[path="arch/std.rs"] - mod arch; - pub use arch::*; - #[cfg(feature = "nightly")] - pub use embassy_macros::main_std as main; - } +macro_rules! check_at_most_one { + (@amo [$($feats:literal)*] [] [$($res:tt)*]) => { + #[cfg(any($($res)*))] + compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*)); + }; + (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => { + check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]); + }; + ($($f:literal),*$(,)?) => { + check_at_most_one!(@amo [$($f)*] [$($f)*] []); + }; } +check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",); + +#[cfg(feature = "_arch")] +#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")] +#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] +#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")] +#[cfg_attr(feature = "arch-std", path = "arch/std.rs")] +#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] +mod arch; + +#[cfg(feature = "_arch")] +pub use arch::*; + +pub mod raw; + +mod spawner; +pub use spawner::*; /// Implementation details for embassy macros. /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. @@ -72,8 +66,3 @@ pub mod _export { ($($tt:tt)*) => {}; } } - -pub mod raw; - -mod spawner; -pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 72c367c3..f6c66da5 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -19,7 +19,6 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; -use core::sync::atomic::AtomicPtr; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, Ordering}; @@ -290,10 +289,60 @@ impl TaskPool { } } +#[derive(Clone, Copy)] +pub(crate) enum PenderInner { + #[cfg(feature = "executor-thread")] + Thread(crate::arch::ThreadPender), + #[cfg(feature = "executor-interrupt")] + Interrupt(crate::arch::InterruptPender), + #[cfg(feature = "pender-callback")] + Callback { func: fn(*mut ()), context: *mut () }, +} + +unsafe impl Send for PenderInner {} +unsafe impl Sync for PenderInner {} + +/// Platform/architecture-specific action executed when an executor has pending work. +/// +/// When a task within an executor is woken, the `Pender` is called. This does a +/// platform/architecture-specific action to signal there is pending work in the executor. +/// When this happens, you must arrange for [`Executor::poll`] to be called. +/// +/// You can think of it as a waker, but for the whole executor. +pub struct Pender(pub(crate) PenderInner); + +impl Pender { + /// Create a `Pender` that will call an arbitrary function pointer. + /// + /// # Arguments + /// + /// - `func`: The function pointer to call. + /// - `context`: Opaque context pointer, that will be passed to the function pointer. + #[cfg(feature = "pender-callback")] + pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { + Self(PenderInner::Callback { + func, + context: context.into(), + }) + } +} + +impl Pender { + pub(crate) fn pend(&self) { + match self.0 { + #[cfg(feature = "executor-thread")] + PenderInner::Thread(x) => x.pend(), + #[cfg(feature = "executor-interrupt")] + PenderInner::Interrupt(x) => x.pend(), + #[cfg(feature = "pender-callback")] + PenderInner::Callback { func, context } => func(context), + } + } +} + pub(crate) struct SyncExecutor { run_queue: RunQueue, - signal_fn: fn(*mut ()), - signal_ctx: AtomicPtr<()>, + pender: Pender, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue: timer_queue::TimerQueue, @@ -302,16 +351,13 @@ pub(crate) struct SyncExecutor { } impl SyncExecutor { - pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + pub(crate) fn new(pender: Pender) -> Self { #[cfg(feature = "integrated-timers")] let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; - #[cfg(feature = "integrated-timers")] - driver::set_alarm_callback(alarm, signal_fn, signal_ctx); Self { run_queue: RunQueue::new(), - signal_fn, - signal_ctx: AtomicPtr::new(signal_ctx), + pender, #[cfg(feature = "integrated-timers")] timer_queue: timer_queue::TimerQueue::new(), @@ -332,10 +378,16 @@ impl SyncExecutor { trace::task_ready_begin(task.as_ptr() as u32); if self.run_queue.enqueue(cs, task) { - (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) + self.pender.pend(); } } + #[cfg(feature = "integrated-timers")] + fn alarm_callback(ctx: *mut ()) { + let this: &Self = unsafe { &*(ctx as *const Self) }; + this.pender.pend(); + } + pub(super) unsafe fn spawn(&'static self, task: TaskRef) { task.header().executor.set(Some(self)); @@ -351,6 +403,9 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { + #[cfg(feature = "integrated-timers")] + driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); + #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] @@ -417,14 +472,14 @@ impl SyncExecutor { /// /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// that "want to run"). -/// - You must supply a `signal_fn`. The executor will call it to notify you it has work +/// - You must supply a [`Pender`]. The executor will call it to notify you it has work /// to do. You must arrange for `poll()` to be called as soon as possible. /// -/// `signal_fn` can be called from *any* context: any thread, any interrupt priority +/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority /// level, etc. It may be called synchronously from any `Executor` method call as well. /// You must deal with this correctly. /// -/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates +/// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// the requirement for `poll` to not be called reentrantly. #[repr(transparent)] pub struct Executor { @@ -437,15 +492,15 @@ impl Executor { pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { mem::transmute(inner) } + /// Create a new executor. /// - /// When the executor has work to do, it will call `signal_fn` with - /// `signal_ctx` as argument. + /// When the executor has work to do, it will call the [`Pender`]. /// - /// See [`Executor`] docs for details on `signal_fn`. - pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + /// See [`Executor`] docs for details on `Pender`. + pub fn new(pender: Pender) -> Self { Self { - inner: SyncExecutor::new(signal_fn, signal_ctx), + inner: SyncExecutor::new(pender), _not_sync: PhantomData, } } @@ -468,16 +523,16 @@ impl Executor { /// This loops over all tasks that are queued to be polled (i.e. they're /// freshly spawned or they've been woken). Other tasks are not polled. /// - /// You must call `poll` after receiving a call to `signal_fn`. It is OK - /// to call `poll` even when not requested by `signal_fn`, but it wastes + /// You must call `poll` after receiving a call to the [`Pender`]. It is OK + /// to call `poll` even when not requested by the `Pender`, but it wastes /// energy. /// /// # Safety /// /// You must NOT call `poll` reentrantly on the same executor. /// - /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you - /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to + /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you + /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 88899325..e75c73cb 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8d826790..8de2d2eb 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index aa279fb7..083607de 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 1ec0643a..74f50851 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index a4eefe2a..898b9a47 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 36eada29..e142c848 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 67efda74..f0e92e1a 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 4b2e02dd..87689e9a 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index fecbfc51..a6708bf5 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d8c24dfa..7910b372 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -17,7 +17,7 @@ log = [ [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cc88d92c..3ece2406 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index e88ddf2f..4134db46 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ "defmt", ] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "defmt", "integrated-timers", diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1e8870ed..aea61eec 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 8087df09..ff08e378 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 0095a680..3b1d888f 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 89d99b6d..5c82c557 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index e0dc8c98..430a805f 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 53f369b3..387af783 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index afaf9a0c..ffb23231 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 69ebef78..38f11201 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 77df51ac..5d010f79 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 7a7bab5b..d967d850 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 77df51ac..5d010f79 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index ea4cbd80..74e7bf53 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index e7273c9f..03bdbcea 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 8a57a8ef..4e415035 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index a0413478..d0d6a949 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 86933a62..413d5c18 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 6e3b2103..cd9508d5 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 644c90b1..7c254eba 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index f880328d..1c662b9d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 2b02eda9..ebef0a4f 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index e27b4527..ddf9729e 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 690481bb..9fc7e0f4 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index e0e799a3..430d0b4c 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 2a4e8cf4..912749e5 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 572a9ce8..eb447be3 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 08a775ea..17b64079 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -15,7 +15,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } From bfebf7a43648e06b313234a2ddc7496eb526bc69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 08:02:43 +0200 Subject: [PATCH 051/112] Fix formatting of sector erase log --- embassy-stm32/src/flash/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 41c28c56..c48b2f2e 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -117,7 +117,7 @@ unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { let mut address = start_address; while address < end_address { let sector = get_sector(address, regions); - trace!("Erasing sector: {}", sector); + trace!("Erasing sector: {:?}", sector); critical_section::with(|_| { family::clear_all_err(); From b1e2195b49129bcccf42121a6f39248bab9e341f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 14:50:41 +0200 Subject: [PATCH 052/112] Remove FirmwareWriter FirmwareWriter currently has a "max-write-size" parameter, but this is a limitation that should be handled by chunking inside the NorFlash driver, and not "up here" in user code. In case that the driver (e.g. qspi driver) is unaware of any max-write limitations, one could simply add an intermediate NorFlash adapter providing the chunk'ing capability. --- embassy-boot/boot/src/firmware_updater.rs | 25 ++++------- embassy-boot/boot/src/firmware_writer.rs | 55 ----------------------- embassy-boot/boot/src/lib.rs | 8 ++-- 3 files changed, 12 insertions(+), 76 deletions(-) delete mode 100644 embassy-boot/boot/src/firmware_writer.rs diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114..dd22b2fa 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -1,7 +1,7 @@ use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; -use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; /// Errors returned by FirmwareUpdater #[derive(Debug)] @@ -243,7 +243,6 @@ impl FirmwareUpdater { offset: usize, data: &[u8], dfu_flash: &mut F, - block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); @@ -251,25 +250,23 @@ impl FirmwareUpdater { .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) .await?; - FirmwareWriter(self.dfu) - .write_block(offset, data, dfu_flash, block_size) - .await?; + self.dfu.write(dfu_flash, offset as u32, data).await?; Ok(()) } /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. + /// returning its `Partition`. /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. pub async fn prepare_update( &mut self, dfu_flash: &mut F, - ) -> Result { + ) -> Result { self.dfu.wipe(dfu_flash).await?; - Ok(FirmwareWriter(self.dfu)) + Ok(self.dfu) } // @@ -443,29 +440,25 @@ impl FirmwareUpdater { offset: usize, data: &[u8], dfu_flash: &mut F, - block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); self.dfu .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; - FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; + self.dfu.write_blocking(dfu_flash, offset as u32, data)?; Ok(()) } /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. + /// returning its `Partition`. /// /// Using this instead of `write_firmware_blocking` allows for an optimized /// API in exchange for added complexity. - pub fn prepare_update_blocking( - &mut self, - flash: &mut F, - ) -> Result { + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { self.dfu.wipe_blocking(flash)?; - Ok(FirmwareWriter(self.dfu)) + Ok(self.dfu) } } diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs deleted file mode 100644 index 46079e73..00000000 --- a/embassy-boot/boot/src/firmware_writer.rs +++ /dev/null @@ -1,55 +0,0 @@ -use embedded_storage::nor_flash::NorFlash; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; - -use crate::Partition; - -/// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter(pub(crate) Partition); - -impl FirmwareWriter { - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_block( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - let mut offset = offset as u32; - for chunk in data.chunks(block_size) { - self.0.write(flash, offset, chunk).await?; - offset += chunk.len() as u32; - } - - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_block_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - let mut offset = offset as u32; - for chunk in data.chunks(block_size) { - self.0.write_blocking(flash, offset, chunk)?; - offset += chunk.len() as u32; - } - - Ok(()) - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa..428e7ca2 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,12 +7,10 @@ mod fmt; mod boot_loader; mod firmware_updater; -mod firmware_writer; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; -pub use firmware_writer::FirmwareWriter; pub use partition::Partition; pub(crate) const BOOT_MAGIC: u8 = 0xD0; @@ -109,7 +107,7 @@ mod tests { let mut updater = FirmwareUpdater::new(DFU, STATE); let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); @@ -182,7 +180,7 @@ mod tests { let mut offset = 0; for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); @@ -235,7 +233,7 @@ mod tests { let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); From 8aaffe82e71dfb2b3845c95bbf59ef4a34c7096c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 14:59:55 +0200 Subject: [PATCH 053/112] Add incremental hash to FirmwareUpdater This adds support for computing any hash over the update in the dtu area by providing a closure to the hash update function. --- embassy-boot/boot/src/firmware_updater.rs | 67 ++++++++++++++--------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114..90157036 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -118,13 +118,13 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len()); + assert!(_update_len <= self.dfu.len() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -136,11 +136,8 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + .await?; public_key .verify(&digest.finalize(), &signature) @@ -161,11 +158,8 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + .await?; let message = digest.finalize(); let r = public_key.verify(&message, &signature); @@ -182,6 +176,22 @@ impl FirmwareUpdater { self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } + /// Iterate through the DFU and process all bytes with the provided closure. + pub async fn incremental_hash( + &mut self, + dfu_flash: &mut F, + update_len: u32, + aligned: &mut [u8], + mut update: impl FnMut(&[u8]), + ) -> Result<(), FirmwareUpdaterError> { + for offset in (0..update_len).step_by(aligned.len()) { + self.dfu.read(dfu_flash, offset, aligned).await?; + let len = core::cmp::min((update_len - offset) as usize, aligned.len()); + update(&aligned[..len]); + } + Ok(()) + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -317,14 +327,13 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len <= self.dfu.len() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -336,11 +345,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; public_key .verify(&digest.finalize(), &signature) @@ -361,11 +366,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; let message = digest.finalize(); let r = public_key.verify(&message, &signature); @@ -382,6 +383,22 @@ impl FirmwareUpdater { self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } + /// Iterate through the DFU and process all bytes with the provided closure. + pub fn incremental_hash_blocking( + &mut self, + dfu_flash: &mut F, + update_len: u32, + aligned: &mut [u8], + mut update: impl FnMut(&[u8]), + ) -> Result<(), FirmwareUpdaterError> { + for offset in (0..update_len).step_by(aligned.len()) { + self.dfu.read_blocking(dfu_flash, offset, aligned)?; + let len = core::cmp::min((update_len - offset) as usize, aligned.len()); + update(&aligned[..len]); + } + Ok(()) + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa..6d0e2d8c 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -308,7 +308,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len, + firmware_len as u32, &mut aligned, )) .is_ok()); From 7c11d85e1ea0d01e5e1b4d6564d258663d66509b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 15:33:20 +0200 Subject: [PATCH 054/112] Move MemFlash to separate module and add verify_erased_before_write verification --- embassy-boot/boot/src/lib.rs | 155 +++------------------ embassy-boot/boot/src/mem_flash.rs | 213 +++++++++++++++++++++++++++++ embassy-boot/boot/src/partition.rs | 18 +-- 3 files changed, 244 insertions(+), 142 deletions(-) create mode 100644 embassy-boot/boot/src/mem_flash.rs diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa..a5795781 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -8,6 +8,7 @@ mod fmt; mod boot_loader; mod firmware_updater; mod firmware_writer; +mod mem_flash; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; @@ -46,13 +47,10 @@ impl AsMut<[u8]> for AlignedBuffer { #[cfg(test)] mod tests { - use core::convert::Infallible; - - use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; - use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; use futures::executor::block_on; use super::*; + use crate::mem_flash::MemFlash; /* #[test] @@ -75,8 +73,8 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); - flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); + let mut flash = MemFlash::<131072, 4096, 4>::default(); + flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); let mut flash = SingleFlashConfig::new(&mut flash); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -95,14 +93,14 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); + let mut flash = MemFlash::<131072, 4096, 4>::random().with_limited_erase_before_write_verification(4..); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; let mut aligned = [0; 4]; for i in ACTIVE.from..ACTIVE.to { - flash.0[i] = original[i - ACTIVE.from]; + flash.mem[i] = original[i - ACTIVE.from]; } let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -124,12 +122,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i); } // Running again should cause a revert @@ -141,12 +139,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); + assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i); } // Last page is untouched for i in DFU.from..DFU.to - 4096 { - assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); + assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i); } // Mark as booted @@ -166,16 +164,16 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 16384); const DFU: Partition = Partition::new(0, 16384); - let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); - let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); - let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut active = MemFlash::<16384, 4096, 8>::random(); + let mut dfu = MemFlash::<16384, 2048, 8>::random(); + let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -203,12 +201,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -220,15 +218,15 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); - let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); - let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut active = MemFlash::<16384, 2048, 4>::random(); + let mut dfu = MemFlash::<16384, 4096, 8>::random(); + let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -255,12 +253,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -313,113 +311,4 @@ mod tests { )) .is_ok()); } - - pub struct MemFlash(pub [u8; SIZE]); - - impl NorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); - for i in from..to { - self.0[i] = 0xFF; - } - Ok(()) - } - - fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - assert!(data.len() % WRITE_SIZE == 0); - assert!(offset as usize % WRITE_SIZE == 0); - assert!(offset as usize + data.len() <= SIZE); - - self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); - - Ok(()) - } - } - - impl ErrorType - for MemFlash - { - type Error = Infallible; - } - - impl ReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let len = buf.len(); - buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl super::Flash - for MemFlash - { - const BLOCK_SIZE: usize = ERASE_SIZE; - const ERASE_VALUE: u8 = 0xFF; - } - - impl AsyncReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let len = buf.len(); - buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl AsyncNorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0); - for i in from..to { - self.0[i] = 0xFF; - } - Ok(()) - } - - async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - info!("Writing {} bytes to 0x{:x}", data.len(), offset); - assert!(data.len() % WRITE_SIZE == 0); - assert!(offset as usize % WRITE_SIZE == 0); - assert!( - offset as usize + data.len() <= SIZE, - "OFFSET: {}, LEN: {}, FLASH SIZE: {}", - offset, - data.len(), - SIZE - ); - - self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); - - Ok(()) - } - } } diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs new file mode 100644 index 00000000..e87ccd37 --- /dev/null +++ b/embassy-boot/boot/src/mem_flash.rs @@ -0,0 +1,213 @@ +#![allow(unused)] + +use core::ops::{Bound, Range, RangeBounds}; + +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +use crate::Flash; + +pub struct MemFlash { + pub mem: [u8; SIZE], + pub allow_same_write: bool, + pub verify_erased_before_write: Range, + pub pending_write_successes: Option, +} + +#[derive(Debug)] +pub struct MemFlashError; + +impl MemFlash { + pub const fn new(fill: u8) -> Self { + Self { + mem: [fill; SIZE], + allow_same_write: false, + verify_erased_before_write: 0..SIZE, + pending_write_successes: None, + } + } + + #[cfg(test)] + pub fn random() -> Self { + let mut mem = [0; SIZE]; + for byte in mem.iter_mut() { + *byte = rand::random::(); + } + Self { + mem, + allow_same_write: false, + verify_erased_before_write: 0..SIZE, + pending_write_successes: None, + } + } + + #[must_use] + pub fn allow_same_write(self, allow: bool) -> Self { + Self { + allow_same_write: allow, + ..self + } + } + + #[must_use] + pub fn with_limited_erase_before_write_verification>(self, verified_range: R) -> Self { + let start = match verified_range.start_bound() { + Bound::Included(start) => *start, + Bound::Excluded(start) => *start + 1, + Bound::Unbounded => 0, + }; + let end = match verified_range.end_bound() { + Bound::Included(end) => *end - 1, + Bound::Excluded(end) => *end, + Bound::Unbounded => self.mem.len(), + }; + Self { + verify_erased_before_write: start..end, + ..self + } + } +} + +impl Default + for MemFlash +{ + fn default() -> Self { + Self::new(0xFF) + } +} + +impl Flash + for MemFlash +{ + const BLOCK_SIZE: usize = ERASE_SIZE; + const ERASE_VALUE: u8 = 0xFF; +} + +impl ErrorType + for MemFlash +{ + type Error = MemFlashError; +} + +impl NorFlashError for MemFlashError { + fn kind(&self) -> NorFlashErrorKind { + NorFlashErrorKind::Other + } +} + +impl ReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let len = bytes.len(); + bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +impl NorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let from = from as usize; + let to = to as usize; + assert!(from % ERASE_SIZE == 0); + assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); + for i in from..to { + self.mem[i] = 0xFF; + } + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let offset = offset as usize; + assert!(bytes.len() % WRITE_SIZE == 0); + assert!(offset % WRITE_SIZE == 0); + assert!(offset + bytes.len() <= SIZE); + + if let Some(pending_successes) = self.pending_write_successes { + if pending_successes > 0 { + self.pending_write_successes = Some(pending_successes - 1); + } else { + return Err(MemFlashError); + } + } + + for ((offset, mem_byte), new_byte) in self + .mem + .iter_mut() + .enumerate() + .skip(offset) + .take(bytes.len()) + .zip(bytes) + { + if self.allow_same_write && mem_byte == new_byte { + // Write does not change the flash memory which is allowed + } else { + if self.verify_erased_before_write.contains(&offset) { + assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); + } + *mem_byte &= *new_byte; + } + } + + Ok(()) + } +} + +impl AsyncReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + ::read(self, offset, bytes) + } + + fn capacity(&self) -> usize { + ::capacity(self) + } +} + +impl AsyncNorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + ::erase(self, from, to) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + ::write(self, offset, bytes) + } +} + +#[cfg(test)] +mod tests { + use core::ops::Range; + + use embedded_storage::nor_flash::NorFlash; + + use super::MemFlash; + + #[test] + fn writes_only_flip_bits_from_1_to_0() { + let mut flash = MemFlash::<16, 16, 1>::default().with_limited_erase_before_write_verification(0..0); + + flash.write(0, &[0x55]).unwrap(); + flash.write(0, &[0xAA]).unwrap(); + + assert_eq!(0x00, flash.mem[0]); + } +} diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 3ccd4dd7..1157e8cd 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -105,45 +105,45 @@ impl Partition { #[cfg(test)] mod tests { - use crate::tests::MemFlash; + use crate::mem_flash::MemFlash; use crate::Partition; #[test] fn can_erase() { - let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let mut flash = MemFlash::<1024, 64, 4>::new(0x00); let partition = Partition::new(256, 512); partition.erase_blocking(&mut flash, 64, 192).unwrap(); - for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { + for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { assert_eq!(0x00, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { assert_eq!(0xFF, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { assert_eq!(0x00, byte, "Index {}", index); } } #[test] fn can_wipe() { - let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let mut flash = MemFlash::<1024, 64, 4>::new(0x00); let partition = Partition::new(256, 512); partition.wipe_blocking(&mut flash).unwrap(); - for (index, byte) in flash.0.iter().copied().enumerate().take(256) { + for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { assert_eq!(0x00, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { assert_eq!(0xFF, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { assert_eq!(0x00, byte, "Index {}", index); } } From 4ce1c5f27dbb06641abddd2b10aebae1511a894b Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 16:41:25 +0200 Subject: [PATCH 055/112] Add MCO support for L4 and F4 families --- embassy-stm32/build.rs | 17 +++- embassy-stm32/src/rcc/f4.rs | 165 ++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/l4.rs | 133 +++++++++++++++++++++++++ examples/stm32f4/src/bin/mco.rs | 31 ++++++ examples/stm32l4/src/bin/mco.rs | 27 ++++++ 5 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 examples/stm32f4/src/bin/mco.rs create mode 100644 examples/stm32l4/src/bin/mco.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 3780c5a4..481fec67 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -50,10 +50,13 @@ fn main() { // We *shouldn't* have singletons for these, but the HAL currently requires // singletons, for using with RccPeripheral to enable/disable clocks to them. "rcc" => { - if r.version.starts_with("h7") { + if r.version.starts_with("h7") || r.version.starts_with("f4") { singletons.push("MCO1".to_string()); singletons.push("MCO2".to_string()); } + if r.version.starts_with("l4") { + singletons.push("MCO".to_string()); + } singletons.push(p.name.to_string()); } //"dbgmcu" => {} @@ -258,6 +261,7 @@ fn main() { (("i2c", "SCL"), quote!(crate::i2c::SclPin)), (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)), + (("rcc", "MCO"), quote!(crate::rcc::McoPin)), (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)), (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)), (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)), @@ -447,13 +451,22 @@ fn main() { // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now - if regs.version.starts_with("h7") { + if regs.version.starts_with("h7") || regs.version.starts_with("f4") { peri = format_ident!("{}", pin.signal.replace("_", "")); } else { continue; } } + if pin.signal == "MCO" { + // Supported in H7 only for now + if regs.version.starts_with("l4") { + peri = format_ident!("MCO"); + } else { + continue; + } + } + g.extend(quote! { pin_trait_impl!(#tr, #peri, #pin_name, #af); }) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 200bcce9..d0e0d585 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -1,8 +1,16 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; + use super::sealed::RccPeripheral; +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; +use crate::{peripherals, Peripheral}; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); @@ -96,6 +104,163 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 } } +pub enum McoClock { + DIV1, + DIV2, + DIV3, + DIV4, + DIV5, +} + +impl McoClock { + fn into_raw(&self) -> Mcopre { + match self { + McoClock::DIV1 => Mcopre::DIV1, + McoClock::DIV2 => Mcopre::DIV2, + McoClock::DIV3 => Mcopre::DIV3, + McoClock::DIV4 => Mcopre::DIV4, + McoClock::DIV5 => Mcopre::DIV5, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco1Source { + Hsi, + Lse, + Hse, + Pll, +} + +impl Default for Mco1Source { + fn default() -> Self { + Self::Hsi + } +} + +pub trait McoSource { + type Raw; + + fn into_raw(&self) -> Self::Raw; +} + +impl McoSource for Mco1Source { + type Raw = Mco1; + fn into_raw(&self) -> Self::Raw { + match self { + Mco1Source::Hsi => Mco1::HSI, + Mco1Source::Lse => Mco1::LSE, + Mco1Source::Hse => Mco1::HSE, + Mco1Source::Pll => Mco1::PLL, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco2Source { + SysClk, + Plli2s, + Hse, + Pll, +} + +impl Default for Mco2Source { + fn default() -> Self { + Self::SysClk + } +} + +impl McoSource for Mco2Source { + type Raw = Mco2; + fn into_raw(&self) -> Self::Raw { + match self { + Mco2Source::SysClk => Mco2::SYSCLK, + Mco2Source::Plli2s => Mco2::PLLI2S, + Mco2Source::Hse => Mco2::HSE, + Mco2Source::Pll => Mco2::PLL, + } + } +} + +pub(crate) mod sealed { + use stm32_metapac::rcc::vals::Mcopre; + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +impl sealed::McoInstance for peripherals::MCO1 { + type Source = Mco1; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mco1(source); + w.set_mco1pre(prescaler); + }); + match source { + Mco1::PLL => { + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } + Mco1::HSI => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + _ => {} + } + } +} +impl McoInstance for peripherals::MCO1 {} + +impl sealed::McoInstance for peripherals::MCO2 { + type Source = Mco2; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mco2(source); + w.set_mco2pre(prescaler); + }); + match source { + Mco2::PLL => { + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } + Mco2::PLLI2S => { + RCC.cr().modify(|w| w.set_plli2son(true)); + while !RCC.cr().read().plli2srdy() {} + } + _ => {} + } + } +} +impl McoInstance for peripherals::MCO2 {} + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: impl McoSource, + prescaler: McoClock, + ) -> Self { + into_ref!(pin); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} + unsafe fn flash_setup(sysclk: u32) { use crate::pac::flash::vals::Latency; diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index e650490f..c1bf7d0c 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,7 +1,15 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; + +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; +use crate::{peripherals, Peripheral}; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); @@ -298,6 +306,131 @@ impl Default for Config { } } +pub enum McoClock { + DIV1, + DIV2, + DIV4, + DIV8, + DIV16, +} + +impl McoClock { + fn into_raw(&self) -> Mcopre { + match self { + McoClock::DIV1 => Mcopre::DIV1, + McoClock::DIV2 => Mcopre::DIV2, + McoClock::DIV4 => Mcopre::DIV4, + McoClock::DIV8 => Mcopre::DIV8, + McoClock::DIV16 => Mcopre::DIV16, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco1Source { + Disabled, + Lse, + Lsi, + Hse, + Hsi16, + PllClk, + SysClk, + Msi, + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Hsi48, +} + +impl Default for Mco1Source { + fn default() -> Self { + Self::Hsi16 + } +} + +pub trait McoSource { + type Raw; + + fn into_raw(&self) -> Self::Raw; +} + +impl McoSource for Mco1Source { + type Raw = Mcosel; + fn into_raw(&self) -> Self::Raw { + match self { + Mco1Source::Disabled => Mcosel::NOCLOCK, + Mco1Source::Lse => Mcosel::LSE, + Mco1Source::Lsi => Mcosel::LSI, + Mco1Source::Hse => Mcosel::HSE, + Mco1Source::Hsi16 => Mcosel::HSI16, + Mco1Source::PllClk => Mcosel::PLL, + Mco1Source::SysClk => Mcosel::SYSCLK, + Mco1Source::Msi => Mcosel::MSI, + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Mco1Source::Hsi48 => Mcosel::HSI48, + } + } +} + +pub(crate) mod sealed { + use stm32_metapac::rcc::vals::Mcopre; + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +impl sealed::McoInstance for peripherals::MCO { + type Source = Mcosel; + + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mcosel(source); + w.set_mcopre(prescaler); + }); + + match source { + Mcosel::HSI16 => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Mcosel::HSI48 => { + RCC.crrcr().modify(|w| w.set_hsi48on(true)); + while !RCC.crrcr().read().hsi48rdy() {} + } + _ => {} + } + } +} + +impl McoInstance for peripherals::MCO {} + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: impl McoSource, + prescaler: McoClock, + ) -> Self { + into_ref!(pin); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} + pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw) = match config.mux { ClockSrc::MSI(range) => { diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs new file mode 100644 index 00000000..5d780f94 --- /dev/null +++ b/examples/stm32f4/src/bin/mco.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + + let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1); + let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4); + let mut led = Output::new(p.PB7, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs new file mode 100644 index 00000000..dea0c66e --- /dev/null +++ b/examples/stm32l4/src/bin/mco.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1); + + let mut led = Output::new(p.PB14, Level::High, Speed::Low); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} From 932b80ca8a5d49bcd05d523fe7f7320e2b960e85 Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 16:52:57 +0200 Subject: [PATCH 056/112] run fmt --- examples/stm32f4/src/bin/mco.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs index 5d780f94..2b9ceebc 100644 --- a/examples/stm32f4/src/bin/mco.rs +++ b/examples/stm32f4/src/bin/mco.rs @@ -14,7 +14,6 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1); let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4); let mut led = Output::new(p.PB7, Level::High, Speed::Low); From 1349dabe1aae491d83b83d27a80da7216333bb52 Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 17:55:05 +0200 Subject: [PATCH 057/112] add compilation time exclusion for stm32f410 --- embassy-stm32/src/rcc/f4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index d0e0d585..2a17eb9b 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -228,6 +228,7 @@ impl sealed::McoInstance for peripherals::MCO2 { RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} } + #[cfg(not(stm32f410))] Mco2::PLLI2S => { RCC.cr().modify(|w| w.set_plli2son(true)); while !RCC.cr().read().plli2srdy() {} From ae26a080260d81cf8dec17de538e101010af13c0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 31 Mar 2023 14:52:35 +0200 Subject: [PATCH 058/112] Add HIL test for timer on nrf --- tests/nrf/src/bin/timer.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/nrf/src/bin/timer.rs diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs new file mode 100644 index 00000000..9b9b5fb2 --- /dev/null +++ b/tests/nrf/src/bin/timer.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert, info}; +use embassy_executor::Spawner; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = embassy_nrf::init(Default::default()); + info!("Hello World!"); + + let start = Instant::now(); + Timer::after(Duration::from_millis(100)).await; + let end = Instant::now(); + let ms = (end - start).as_millis(); + info!("slept for {} ms", ms); + assert!(ms >= 99); + assert!(ms < 110); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From ef361d2e88d7991f563781ecb34c75d4ac0dfa7a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 23:15:19 +0200 Subject: [PATCH 059/112] Update Rust nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index da75fa53..22abacde 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-02-07" +channel = "nightly-2023-04-02" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From df3a1e1b9d337c17e08faf41c6e3c50c35eb9a6c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 07:18:29 +0200 Subject: [PATCH 060/112] Avoid write to not-erased magic This introduces an additional marker to the state partition right after the magic which indicates whether the current progress is valid or not. Validation in tests that we never write without an erase is added. There is currently a FIXME in the FirmwareUpdater. Let me know if we should take the erase value as a parameter. I opened a feature request in embedded-storage to get this value in the trait. Before this, the assumption about ERASE_VALUE=0xFF was the same. --- embassy-boot/boot/src/boot_loader.rs | 43 ++++++++++------ embassy-boot/boot/src/firmware_updater.rs | 34 +++++++++++-- embassy-boot/boot/src/lib.rs | 6 +-- embassy-boot/boot/src/mem_flash.rs | 61 +---------------------- 4 files changed, 63 insertions(+), 81 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index e2e361e3..9d047f77 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -31,7 +31,7 @@ where } /// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash + ReadNorFlash { +pub trait Flash: NorFlash { /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase /// size of the flash, but for external QSPI flash modules, this can be lower. const BLOCK_SIZE: usize; @@ -60,9 +60,11 @@ pub trait FlashConfig { /// different page sizes and flash write sizes. pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: - // | Range | Description | - // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | WRITE_SIZE - N | Progress index used while swapping or reverting | + // All ranges are in multiples of WRITE_SIZE bytes. + // | Range | Description | + // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | + // | 2..2 + N | Progress index used while swapping or reverting | state: Partition, // Location of the partition which will be booted from active: Partition, @@ -192,12 +194,17 @@ impl BootLoader { trace!("Reverting"); self.revert(p, magic, page)?; - // Overwrite magic and reset progress let state_flash = p.state(); + + // Invalidate progress magic.fill(!P::STATE::ERASE_VALUE); - self.state.write_blocking(state_flash, 0, magic)?; + self.state + .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?; + + // Clear magic and progress self.state.wipe_blocking(state_flash)?; + // Set magic magic.fill(BOOT_MAGIC); self.state.write_blocking(state_flash, 0, magic)?; } @@ -215,28 +222,34 @@ impl BootLoader { fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 1; + let max_index = ((self.state.len() - write_size) / write_size) - 2; aligned.fill(!P::STATE::ERASE_VALUE); let state_flash = config.state(); - for i in 0..max_index { + + self.state + .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?; + if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + // Progress is invalid + return Ok(max_index); + } + + for index in 0..max_index { self.state - .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; + .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { - return Ok(i); + return Ok(index); } } Ok(max_index) } - fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let write_size = magic.len(); - + fn update_progress(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { let aligned = magic; aligned.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; + .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; Ok(()) } @@ -360,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s assert_eq!(active.len() % page_size, 0); assert_eq!(dfu.len() % page_size, 0); assert!(dfu.len() - active.len() >= page_size); - assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); + assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size); } /// A flash wrapper implementing the Flash and embedded_storage traits. diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114..fe3c0452 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -220,11 +220,24 @@ impl FirmwareUpdater { self.state.read(state_flash, 0, aligned).await?; if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); + // Read progress validity + self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - self.state.write(state_flash, 0, aligned).await?; + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + } + + // Clear magic and progress self.state.wipe(state_flash).await?; + // Set magic aligned.fill(magic); self.state.write(state_flash, 0, aligned).await?; } @@ -420,11 +433,24 @@ impl FirmwareUpdater { self.state.read_blocking(state_flash, 0, aligned)?; if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); + // Read progress validity + self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - self.state.write_blocking(state_flash, 0, aligned)?; + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + } + + // Clear magic and progress self.state.wipe_blocking(state_flash)?; + // Set magic aligned.fill(magic); self.state.write_blocking(state_flash, 0, aligned)?; } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index a5795781..597ce3fa 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -93,7 +93,7 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>::random().with_limited_erase_before_write_verification(4..); + let mut flash = MemFlash::<131072, 4096, 4>::random(); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; @@ -166,7 +166,7 @@ mod tests { let mut active = MemFlash::<16384, 4096, 8>::random(); let mut dfu = MemFlash::<16384, 2048, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); + let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; @@ -220,7 +220,7 @@ mod tests { let mut aligned = [0; 4]; let mut active = MemFlash::<16384, 2048, 4>::random(); let mut dfu = MemFlash::<16384, 4096, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); + let mut state = MemFlash::<4096, 128, 4>::random(); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index e87ccd37..828aad9d 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -9,8 +9,6 @@ use crate::Flash; pub struct MemFlash { pub mem: [u8; SIZE], - pub allow_same_write: bool, - pub verify_erased_before_write: Range, pub pending_write_successes: Option, } @@ -21,8 +19,6 @@ impl MemFla pub const fn new(fill: u8) -> Self { Self { mem: [fill; SIZE], - allow_same_write: false, - verify_erased_before_write: 0..SIZE, pending_write_successes: None, } } @@ -35,37 +31,9 @@ impl MemFla } Self { mem, - allow_same_write: false, - verify_erased_before_write: 0..SIZE, pending_write_successes: None, } } - - #[must_use] - pub fn allow_same_write(self, allow: bool) -> Self { - Self { - allow_same_write: allow, - ..self - } - } - - #[must_use] - pub fn with_limited_erase_before_write_verification>(self, verified_range: R) -> Self { - let start = match verified_range.start_bound() { - Bound::Included(start) => *start, - Bound::Excluded(start) => *start + 1, - Bound::Unbounded => 0, - }; - let end = match verified_range.end_bound() { - Bound::Included(end) => *end - 1, - Bound::Excluded(end) => *end, - Bound::Unbounded => self.mem.len(), - }; - Self { - verify_erased_before_write: start..end, - ..self - } - } } impl Default @@ -150,14 +118,8 @@ impl NorFla .take(bytes.len()) .zip(bytes) { - if self.allow_same_write && mem_byte == new_byte { - // Write does not change the flash memory which is allowed - } else { - if self.verify_erased_before_write.contains(&offset) { - assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); - } - *mem_byte &= *new_byte; - } + assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); + *mem_byte = *new_byte; } Ok(()) @@ -192,22 +154,3 @@ impl AsyncN ::write(self, offset, bytes) } } - -#[cfg(test)] -mod tests { - use core::ops::Range; - - use embedded_storage::nor_flash::NorFlash; - - use super::MemFlash; - - #[test] - fn writes_only_flip_bits_from_1_to_0() { - let mut flash = MemFlash::<16, 16, 1>::default().with_limited_erase_before_write_verification(0..0); - - flash.write(0, &[0x55]).unwrap(); - flash.write(0, &[0xAA]).unwrap(); - - assert_eq!(0x00, flash.mem[0]); - } -} From 7c6936a2e398e43ea3dc89736dc385402822933f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:24:30 +0200 Subject: [PATCH 061/112] Let hash functions take a digest::Digest trait ... and add adapters for current Sha512 implementations that does not inplement the Digest trait --- embassy-boot/boot/Cargo.toml | 4 +- .../boot/src/digest_adapters/ed25519_dalek.rs | 30 +++++ embassy-boot/boot/src/digest_adapters/mod.rs | 5 + .../boot/src/digest_adapters/salty.rs | 29 +++++ embassy-boot/boot/src/firmware_updater.rs | 108 ++++++++++++------ embassy-boot/boot/src/lib.rs | 1 + 6 files changed, 140 insertions(+), 37 deletions(-) create mode 100644 embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs create mode 100644 embassy-boot/boot/src/digest_adapters/mod.rs create mode 100644 embassy-boot/boot/src/digest_adapters/salty.rs diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 3312c2f9..c4ebdaff 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -24,6 +24,7 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } +digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } @@ -37,6 +38,7 @@ log = "0.4" env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } +sha1 = "0.10.5" [dev-dependencies.ed25519-dalek] default_features = false @@ -47,4 +49,4 @@ ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] #Internal features -_verify = [] \ No newline at end of file +_verify = [] diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs new file mode 100644 index 00000000..a184d1c5 --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs @@ -0,0 +1,30 @@ +use digest::typenum::U64; +use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; +use ed25519_dalek::Digest as _; + +pub struct Sha512(ed25519_dalek::Sha512); + +impl Default for Sha512 { + fn default() -> Self { + Self(ed25519_dalek::Sha512::new()) + } +} + +impl Update for Sha512 { + fn update(&mut self, data: &[u8]) { + self.0.update(data) + } +} + +impl FixedOutput for Sha512 { + fn finalize_into(self, out: &mut digest::Output) { + let result = self.0.finalize(); + out.as_mut_slice().copy_from_slice(result.as_slice()) + } +} + +impl OutputSizeUser for Sha512 { + type OutputSize = U64; +} + +impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/digest_adapters/mod.rs b/embassy-boot/boot/src/digest_adapters/mod.rs new file mode 100644 index 00000000..9b4b4b60 --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/mod.rs @@ -0,0 +1,5 @@ +#[cfg(feature = "ed25519-dalek")] +pub(crate) mod ed25519_dalek; + +#[cfg(feature = "ed25519-salty")] +pub(crate) mod salty; diff --git a/embassy-boot/boot/src/digest_adapters/salty.rs b/embassy-boot/boot/src/digest_adapters/salty.rs new file mode 100644 index 00000000..2b5dcf3a --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/salty.rs @@ -0,0 +1,29 @@ +use digest::typenum::U64; +use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; + +pub struct Sha512(salty::Sha512); + +impl Default for Sha512 { + fn default() -> Self { + Self(salty::Sha512::new()) + } +} + +impl Update for Sha512 { + fn update(&mut self, data: &[u8]) { + self.0.update(data) + } +} + +impl FixedOutput for Sha512 { + fn finalize_into(self, out: &mut digest::Output) { + let result = self.0.finalize(); + out.as_mut_slice().copy_from_slice(result.as_slice()) + } +} + +impl OutputSizeUser for Sha512 { + type OutputSize = U64; +} + +impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 22e3e6b0..2d1b2698 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -1,3 +1,4 @@ +use digest::Digest; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; @@ -128,25 +129,27 @@ impl FirmwareUpdater { #[cfg(feature = "ed25519-dalek")] { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) .await?; - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)? } #[cfg(feature = "ed25519-salty")] { use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; fn into_signature_error(_: E) -> FirmwareUpdaterError { FirmwareUpdaterError::Signature(signature::Error::default()) @@ -157,11 +160,10 @@ impl FirmwareUpdater { let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) .await?; - let message = digest.finalize(); let r = public_key.verify(&message, &signature); trace!( "Verifying with public key {}, signature {} and message {} yields ok: {}", @@ -176,19 +178,21 @@ impl FirmwareUpdater { self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } - /// Iterate through the DFU and process all bytes with the provided closure. - pub async fn incremental_hash( + /// Verify the update in DFU with any digest. + pub async fn hash( &mut self, dfu_flash: &mut F, update_len: u32, - aligned: &mut [u8], - mut update: impl FnMut(&[u8]), + chunk_buf: &mut [u8], + output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - for offset in (0..update_len).step_by(aligned.len()) { - self.dfu.read(dfu_flash, offset, aligned).await?; - let len = core::cmp::min((update_len - offset) as usize, aligned.len()); - update(&aligned[..len]); + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read(dfu_flash, offset, chunk_buf).await?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); } + output.copy_from_slice(digest.finalize().as_slice()); Ok(()) } @@ -334,24 +338,26 @@ impl FirmwareUpdater { #[cfg(feature = "ed25519-dalek")] { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)? } #[cfg(feature = "ed25519-salty")] { use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; fn into_signature_error(_: E) -> FirmwareUpdaterError { FirmwareUpdaterError::Signature(signature::Error::default()) @@ -362,10 +368,9 @@ impl FirmwareUpdater { let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - let message = digest.finalize(); let r = public_key.verify(&message, &signature); trace!( "Verifying with public key {}, signature {} and message {} yields ok: {}", @@ -380,19 +385,21 @@ impl FirmwareUpdater { self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } - /// Iterate through the DFU and process all bytes with the provided closure. - pub fn incremental_hash_blocking( + /// Verify the update in DFU with any digest. + pub fn hash_blocking( &mut self, dfu_flash: &mut F, update_len: u32, - aligned: &mut [u8], - mut update: impl FnMut(&[u8]), + chunk_buf: &mut [u8], + output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - for offset in (0..update_len).step_by(aligned.len()) { - self.dfu.read_blocking(dfu_flash, offset, aligned)?; - let len = core::cmp::min((update_len - offset) as usize, aligned.len()); - update(&aligned[..len]); + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); } + output.copy_from_slice(digest.finalize().as_slice()); Ok(()) } @@ -479,3 +486,32 @@ impl FirmwareUpdater { Ok(self.dfu) } } + +#[cfg(test)] +mod tests { + use futures::executor::block_on; + use sha1::{Digest, Sha1}; + + use super::*; + use crate::tests::MemFlash; + + #[test] + fn can_verify() { + const STATE: Partition = Partition::new(0, 4096); + const DFU: Partition = Partition::new(65536, 131072); + + let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]); + + 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(DFU, STATE); + block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 6888a805..da905547 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -6,6 +6,7 @@ mod fmt; mod boot_loader; +mod digest_adapters; mod firmware_updater; mod partition; From 5e19fb6fb976db869696eaa5e8a2cdba751055b1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:36:50 +0200 Subject: [PATCH 062/112] Fix compile error when verification is enabled --- embassy-boot/boot/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index cb12f9dc..d53c613a 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -286,13 +286,13 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const DFU: Partition = Partition::new(4096, 8192); - let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); + let mut flash = MemFlash::<8192, 4096, 4>::default(); let firmware_len = firmware.len(); let mut write_buf = [0; 4096]; write_buf[0..firmware_len].copy_from_slice(firmware); - NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); + DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); // On with the test From 803c09c300a9aeb412e08c37723cd9de3caf89e9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:50:53 +0200 Subject: [PATCH 063/112] Expose read/write/erase on partition --- embassy-boot/boot/src/partition.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 3ccd4dd7..217c457f 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -24,7 +24,7 @@ impl Partition { } /// Read from the partition on the provided flash - pub(crate) async fn read( + pub async fn read( &self, flash: &mut F, offset: u32, @@ -35,12 +35,7 @@ impl Partition { } /// Write to the partition on the provided flash - pub(crate) async fn write( - &self, - flash: &mut F, - offset: u32, - bytes: &[u8], - ) -> Result<(), F::Error> { + pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.write(offset, bytes).await?; trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); @@ -48,7 +43,7 @@ impl Partition { } /// Erase part of the partition on the provided flash - pub(crate) async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { let from = self.from as u32 + from; let to = self.from as u32 + to; flash.erase(from, to).await?; @@ -66,18 +61,13 @@ impl Partition { } /// Read from the partition on the provided flash - pub(crate) fn read_blocking( - &self, - flash: &mut F, - offset: u32, - bytes: &mut [u8], - ) -> Result<(), F::Error> { + pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.read(offset, bytes) } /// Write to the partition on the provided flash - pub(crate) fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.write(offset, bytes)?; trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); @@ -85,7 +75,7 @@ impl Partition { } /// Erase part of the partition on the provided flash - pub(crate) fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { let from = self.from as u32 + from; let to = self.from as u32 + to; flash.erase(from, to)?; From 8256ac104405400a15aa9a6a2d9afe38a552a98b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 19:07:45 +0200 Subject: [PATCH 064/112] Use MemFlash::default() in sha1 verify test --- embassy-boot/boot/src/firmware_updater.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 819e2020..fffb9a50 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -519,14 +519,14 @@ mod tests { use sha1::{Digest, Sha1}; use super::*; - use crate::tests::MemFlash; + use crate::mem_flash::MemFlash; #[test] - fn can_verify() { + fn can_verify_sha1() { const STATE: Partition = Partition::new(0, 4096); const DFU: Partition = Partition::new(65536, 131072); - let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]); + let mut flash = MemFlash::<131072, 4096, 8>::default(); let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; From 9242ad89d436422fd5abdafadb2faf845e730a16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 20:25:55 +0200 Subject: [PATCH 065/112] Remove magic buffer argument from prepare_boot and use the aligned page buffer instead --- embassy-boot/boot/src/boot_loader.rs | 134 +++++++++++++++------------ embassy-boot/boot/src/lib.rs | 22 +---- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 9d047f77..69807559 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -56,6 +56,16 @@ pub trait FlashConfig { fn state(&mut self) -> &mut Self::STATE; } +trait FlashConfigEx { + fn page_size() -> usize; +} + +impl FlashConfigEx for T { + fn page_size() -> usize { + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + } +} + /// BootLoader works with any flash implementing embedded_storage and can also work with /// different page sizes and flash write sizes. pub struct BootLoader { @@ -91,6 +101,9 @@ impl BootLoader { /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap /// algorithm to work correctly. /// + /// The provided aligned_buf argument must satisfy any alignment requirements + /// given by the partition flashes. All flash operations will use this buffer. + /// /// SWAPPING /// /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. @@ -169,87 +182,89 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot( - &mut self, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result { + pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); - assert_eq!(magic.len(), P::STATE::WRITE_SIZE); + assert_eq!(aligned_buf.len(), P::page_size()); + assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, magic)?; + let state = self.read_state(p, aligned_buf)?; if state == State::Swap { // // Check if we already swapped. If we're in the swap state, this means we should revert // since the app has failed to mark boot as successful // - if !self.is_swapped(p, magic, page)? { + if !self.is_swapped(p, aligned_buf)? { trace!("Swapping"); - self.swap(p, magic, page)?; + self.swap(p, aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, magic, page)?; + self.revert(p, aligned_buf)?; let state_flash = p.state(); + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; // Invalidate progress - magic.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?; + .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress self.state.wipe_blocking(state_flash)?; // Set magic - magic.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, magic)?; + state_word.fill(BOOT_MAGIC); + self.state.write_blocking(state_flash, 0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - let progress = self.current_progress(p, magic)?; + fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + let page_count = self.active.len() / P::page_size(); + let progress = self.current_progress(p, aligned_buf)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { - let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 2; - aligned.fill(!P::STATE::ERASE_VALUE); - + fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { + let max_index = ((self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE) - 2; let state_flash = config.state(); + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; self.state - .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?; - if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + if state_word.iter().any(|&b| b != P::STATE::ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state - .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; + self.state.read_blocking( + state_flash, + (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, + state_word, + )?; - if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let aligned = magic; - aligned.fill(!P::STATE::ERASE_VALUE); + fn update_progress( + &mut self, + index: usize, + p: &mut P, + aligned_buf: &mut [u8], + ) -> Result<(), BootError> { + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + state_word.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; + .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; Ok(()) } @@ -259,26 +274,24 @@ impl BootLoader { from_offset: u32, to_offset: u32, p: &mut P, - magic: &mut [u8], - page: &mut [u8], + aligned_buf: &mut [u8], ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { + if self.current_progress(p, aligned_buf)? <= idx { let mut offset = from_offset; - for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { + for chunk in aligned_buf.chunks_mut(P::DFU::BLOCK_SIZE) { self.dfu.read_blocking(p.dfu(), offset, chunk)?; offset += chunk.len() as u32; } self.active - .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?; + .erase_blocking(p.active(), to_offset, to_offset + P::page_size() as u32)?; let mut offset = to_offset; - for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { + for chunk in aligned_buf.chunks(P::ACTIVE::BLOCK_SIZE) { self.active.write_blocking(p.active(), offset, chunk)?; offset += chunk.len() as u32; } - self.update_progress(idx, p, magic)?; + self.update_progress(idx, p, aligned_buf)?; } Ok(()) } @@ -289,32 +302,30 @@ impl BootLoader { from_offset: u32, to_offset: u32, p: &mut P, - magic: &mut [u8], - page: &mut [u8], + aligned_buf: &mut [u8], ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { + if self.current_progress(p, aligned_buf)? <= idx { let mut offset = from_offset; - for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { + for chunk in aligned_buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { self.active.read_blocking(p.active(), offset, chunk)?; offset += chunk.len() as u32; } self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?; + .erase_blocking(p.dfu(), to_offset as u32, to_offset + P::page_size() as u32)?; let mut offset = to_offset; - for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { + for chunk in aligned_buf.chunks(P::DFU::BLOCK_SIZE) { self.dfu.write_blocking(p.dfu(), offset, chunk)?; offset += chunk.len() as u32; } - self.update_progress(idx, p, magic)?; + self.update_progress(idx, p, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); + fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_size = P::page_size(); let page_count = self.active.len() / page_size; trace!("Page count: {}", page_count); for page_num in 0..page_count { @@ -326,20 +337,20 @@ impl BootLoader { let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; let dfu_to_offset = ((page_count - page_num) * page_size) as u32; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy DFU page to the active page let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); + fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_size = P::page_size(); let page_count = self.active.len() / page_size; for page_num in 0..page_count { let idx = page_count * 2 + page_num * 2; @@ -347,21 +358,22 @@ impl BootLoader { // Copy the bad active page to the DFU page let active_from_offset = (page_num * page_size) as u32; let dfu_to_offset = (page_num * page_size) as u32; - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy the DFU page back to the active page let active_to_offset = (page_num * page_size) as u32; let dfu_from_offset = ((page_num + 1) * page_size) as u32; - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) } - fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - self.state.read_blocking(config.state(), 0, magic)?; + fn read_state(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + self.state.read_blocking(config.state(), 0, state_word)?; - if !magic.iter().any(|&b| b != SWAP_MAGIC) { + if !state_word.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) } else { Ok(State::Boot) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index d53c613a..896498c0 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -77,12 +77,8 @@ mod tests { let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; - assert_eq!( - State::Boot, - bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap() - ); + assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap()); } #[test] @@ -110,12 +106,11 @@ mod tests { } block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); @@ -132,7 +127,7 @@ mod tests { assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); @@ -150,7 +145,7 @@ mod tests { assert_eq!( State::Boot, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); } @@ -184,17 +179,12 @@ mod tests { block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot( - &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), - &mut magic, - &mut page - ) + .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page) .unwrap() ); @@ -237,14 +227,12 @@ mod tests { block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader .prepare_boot( &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), - &mut magic, &mut page ) .unwrap() From 25577e0eafd8a3d4ffaa4b8f17cb55399fd58038 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:09:30 +0200 Subject: [PATCH 066/112] Assert active and dfu have same erase size and copy in smaller chunks The copy from active to dfu (and vice versa) is now done in smaller portions depending on aligned_buf, which now does not need to be erase_size big. --- embassy-boot/boot/src/boot_loader.rs | 66 ++++++++++++------------ embassy-boot/boot/src/large_erase.rs | 76 ++++++++++++++++++++++++++++ embassy-boot/boot/src/lib.rs | 32 ++++-------- embassy-boot/boot/src/mem_flash.rs | 1 - 4 files changed, 118 insertions(+), 57 deletions(-) create mode 100644 embassy-boot/boot/src/large_erase.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 69807559..db067da5 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -32,14 +32,13 @@ where /// Extension of the embedded-storage flash type information with block size and erase value. pub trait Flash: NorFlash { - /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase - /// size of the flash, but for external QSPI flash modules, this can be lower. - const BLOCK_SIZE: usize; /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. const ERASE_VALUE: u8 = 0xFF; } -/// Trait defining the flash handles used for active and DFU partition +/// Trait defining the flash handles used for active and DFU partition. +/// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the +/// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { /// Flash type used for the state partition. type STATE: Flash; @@ -62,12 +61,12 @@ trait FlashConfigEx { impl FlashConfigEx for T { fn page_size() -> usize { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + assert_eq!(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE); + T::ACTIVE::ERASE_SIZE } } -/// BootLoader works with any flash implementing embedded_storage and can also work with -/// different page sizes and flash write sizes. +/// BootLoader works with any flash implementing embedded_storage. pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: // All ranges are in multiples of WRITE_SIZE bytes. @@ -184,7 +183,9 @@ impl BootLoader { /// pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(aligned_buf.len(), P::page_size()); + assert_eq!(0, P::page_size() % aligned_buf.len()); + assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); @@ -277,20 +278,18 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { if self.current_progress(p, aligned_buf)? <= idx { - let mut offset = from_offset; - for chunk in aligned_buf.chunks_mut(P::DFU::BLOCK_SIZE) { - self.dfu.read_blocking(p.dfu(), offset, chunk)?; - offset += chunk.len() as u32; - } + let page_size = P::page_size() as u32; self.active - .erase_blocking(p.active(), to_offset, to_offset + P::page_size() as u32)?; + .erase_blocking(p.active(), to_offset, to_offset + page_size)?; - let mut offset = to_offset; - for chunk in aligned_buf.chunks(P::ACTIVE::BLOCK_SIZE) { - self.active.write_blocking(p.active(), offset, chunk)?; - offset += chunk.len() as u32; + for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { + self.dfu + .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; + self.active + .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; } + self.update_progress(idx, p, aligned_buf)?; } Ok(()) @@ -305,20 +304,18 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { if self.current_progress(p, aligned_buf)? <= idx { - let mut offset = from_offset; - for chunk in aligned_buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - self.active.read_blocking(p.active(), offset, chunk)?; - offset += chunk.len() as u32; - } + let page_size = P::page_size() as u32; self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + P::page_size() as u32)?; + .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; - let mut offset = to_offset; - for chunk in aligned_buf.chunks(P::DFU::BLOCK_SIZE) { - self.dfu.write_blocking(p.dfu(), offset, chunk)?; - offset += chunk.len() as u32; + for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { + self.active + .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; + self.dfu + .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; } + self.update_progress(idx, p, aligned_buf)?; } Ok(()) @@ -389,14 +386,14 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash +pub struct BootFlash where F: NorFlash + ReadNorFlash, { flash: F, } -impl BootFlash +impl BootFlash where F: NorFlash + ReadNorFlash, { @@ -406,22 +403,21 @@ where } } -impl Flash for BootFlash +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { - const BLOCK_SIZE: usize = BLOCK_SIZE; const ERASE_VALUE: u8 = ERASE_VALUE; } -impl ErrorType for BootFlash +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl NorFlash for BootFlash +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -437,7 +433,7 @@ where } } -impl ReadNorFlash for BootFlash +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs new file mode 100644 index 00000000..d00d4359 --- /dev/null +++ b/embassy-boot/boot/src/large_erase.rs @@ -0,0 +1,76 @@ +#![allow(unused)] + +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +use crate::Flash; + +pub struct LargeErase(pub F); + +impl LargeErase { + pub const fn new(flash: F) -> Self { + Self(flash) + } +} + +impl Flash for LargeErase { + const ERASE_VALUE: u8 = F::ERASE_VALUE; +} + +impl ErrorType for LargeErase { + type Error = F::Error; +} + +impl NorFlash for LargeErase { + const WRITE_SIZE: usize = F::ERASE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + assert!(ERASE_SIZE >= F::ERASE_SIZE); + assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); + self.0.erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.0.write(offset, bytes) + } +} + +impl ReadNorFlash for LargeErase { + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.0.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.capacity() + } +} + +impl AsyncNorFlash for LargeErase { + const WRITE_SIZE: usize = F::ERASE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + assert!(ERASE_SIZE >= F::ERASE_SIZE); + assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); + self.0.erase(from, to).await + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.0.write(offset, bytes).await + } +} + +impl AsyncReadNorFlash for LargeErase { + const READ_SIZE: usize = F::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.0.read(offset, bytes).await + } + + fn capacity(&self) -> usize { + self.0.capacity() + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 896498c0..79759124 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,6 +7,7 @@ mod fmt; mod boot_loader; mod firmware_updater; +mod large_erase; mod mem_flash; mod partition; @@ -48,6 +49,7 @@ mod tests { use futures::executor::block_on; use super::*; + use crate::large_erase::LargeErase; use crate::mem_flash::MemFlash; /* @@ -99,14 +101,10 @@ mod tests { let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); - let mut page = [0; 4096]; + let mut page = [0; 1024]; assert_eq!( State::Swap, bootloader @@ -158,7 +156,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = MemFlash::<16384, 2048, 8>::random(); + let mut dfu = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 8>::random()); let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; @@ -171,11 +169,7 @@ mod tests { let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -194,7 +188,7 @@ mod tests { // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.0.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -206,7 +200,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = MemFlash::<16384, 2048, 4>::random(); + let mut active = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 4>::random()); let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); @@ -214,16 +208,12 @@ mod tests { let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; + active.0.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -239,7 +229,7 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.0.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 828aad9d..2598bf4d 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -47,7 +47,6 @@ impl Defaul impl Flash for MemFlash { - const BLOCK_SIZE: usize = ERASE_SIZE; const ERASE_VALUE: u8 = 0xFF; } From 6c93309df490020f0ae4a515bf404dfd251b9b69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:18:41 +0200 Subject: [PATCH 067/112] Remove the Flash trait --- embassy-boot/boot/src/boot_loader.rs | 73 ++++++++++++---------------- embassy-boot/boot/src/large_erase.rs | 6 --- embassy-boot/boot/src/lib.rs | 2 +- embassy-boot/boot/src/mem_flash.rs | 8 --- 4 files changed, 32 insertions(+), 57 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index db067da5..37fff621 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -30,22 +30,18 @@ where } } -/// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash { - /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. - const ERASE_VALUE: u8 = 0xFF; -} - /// Trait defining the flash handles used for active and DFU partition. /// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the /// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { + /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. + const STATE_ERASE_VALUE: u8 = 0xFF; /// Flash type used for the state partition. - type STATE: Flash; + type STATE: NorFlash; /// Flash type used for the active partition. - type ACTIVE: Flash; + type ACTIVE: NorFlash; /// Flash type used for the dfu partition. - type DFU: Flash; + type DFU: NorFlash; /// Return flash instance used to write/read to/from active partition. fn active(&mut self) -> &mut Self::ACTIVE; @@ -208,7 +204,7 @@ impl BootLoader { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE_ERASE_VALUE); self.state .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; @@ -237,7 +233,7 @@ impl BootLoader { self.state .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; - if state_word.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } @@ -249,7 +245,7 @@ impl BootLoader { state_word, )?; - if state_word.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { return Ok(index); } } @@ -263,7 +259,7 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE_ERASE_VALUE); self.state .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; Ok(()) @@ -386,16 +382,16 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash +pub struct BootFlash where - F: NorFlash + ReadNorFlash, + F: NorFlash, { flash: F, } -impl BootFlash +impl BootFlash where - F: NorFlash + ReadNorFlash, + F: NorFlash, { /// Create a new instance of a bootable flash pub fn new(flash: F) -> Self { @@ -403,23 +399,16 @@ where } } -impl Flash for BootFlash +impl ErrorType for BootFlash where - F: NorFlash + ReadNorFlash, -{ - const ERASE_VALUE: u8 = ERASE_VALUE; -} - -impl ErrorType for BootFlash -where - F: ReadNorFlash + NorFlash, + F: NorFlash, { type Error = F::Error; } -impl NorFlash for BootFlash +impl NorFlash for BootFlash where - F: ReadNorFlash + NorFlash, + F: NorFlash, { const WRITE_SIZE: usize = F::WRITE_SIZE; const ERASE_SIZE: usize = F::ERASE_SIZE; @@ -433,9 +422,9 @@ where } } -impl ReadNorFlash for BootFlash +impl ReadNorFlash for BootFlash where - F: ReadNorFlash + NorFlash, + F: NorFlash, { const READ_SIZE: usize = F::READ_SIZE; @@ -451,14 +440,14 @@ where /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { flash: &'a mut F, } impl<'a, F> SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { /// Create a provider for a single flash. pub fn new(flash: &'a mut F) -> Self { @@ -468,7 +457,7 @@ where impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { type STATE = F; type ACTIVE = F; @@ -488,9 +477,9 @@ where /// Convenience flash provider that uses separate flash instances for each partition. pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { active: &'a mut ACTIVE, state: &'a mut STATE, @@ -499,9 +488,9 @@ where impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { /// Create a new flash provider with separate configuration for all three partitions. pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { @@ -511,9 +500,9 @@ where impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { type STATE = STATE; type ACTIVE = ACTIVE; diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs index d00d4359..b999a046 100644 --- a/embassy-boot/boot/src/large_erase.rs +++ b/embassy-boot/boot/src/large_erase.rs @@ -3,8 +3,6 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; -use crate::Flash; - pub struct LargeErase(pub F); impl LargeErase { @@ -13,10 +11,6 @@ impl LargeErase { } } -impl Flash for LargeErase { - const ERASE_VALUE: u8 = F::ERASE_VALUE; -} - impl ErrorType for LargeErase { type Error = F::Error; } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 79759124..cc812d79 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -11,7 +11,7 @@ mod large_erase; mod mem_flash; mod partition; -pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; +pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; pub use partition::Partition; diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 2598bf4d..dd85405c 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -5,8 +5,6 @@ use core::ops::{Bound, Range, RangeBounds}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; -use crate::Flash; - pub struct MemFlash { pub mem: [u8; SIZE], pub pending_write_successes: Option, @@ -44,12 +42,6 @@ impl Defaul } } -impl Flash - for MemFlash -{ - const ERASE_VALUE: u8 = 0xFF; -} - impl ErrorType for MemFlash { From 53efb029009e3cb92bb19c8ac8f521407aa4d1e2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:30:49 +0200 Subject: [PATCH 068/112] Allow different erase sizes for active and dfu --- embassy-boot/boot/src/boot_loader.rs | 6 ++- embassy-boot/boot/src/large_erase.rs | 70 ---------------------------- embassy-boot/boot/src/lib.rs | 12 ++--- 3 files changed, 9 insertions(+), 79 deletions(-) delete mode 100644 embassy-boot/boot/src/large_erase.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 37fff621..25f81009 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -56,9 +56,9 @@ trait FlashConfigEx { } impl FlashConfigEx for T { + /// Get the page size which is the "unit of operation" within the bootloader. fn page_size() -> usize { - assert_eq!(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE); - T::ACTIVE::ERASE_SIZE + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) } } @@ -182,6 +182,8 @@ impl BootLoader { assert_eq!(0, P::page_size() % aligned_buf.len()); assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); + assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs deleted file mode 100644 index b999a046..00000000 --- a/embassy-boot/boot/src/large_erase.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![allow(unused)] - -use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -pub struct LargeErase(pub F); - -impl LargeErase { - pub const fn new(flash: F) -> Self { - Self(flash) - } -} - -impl ErrorType for LargeErase { - type Error = F::Error; -} - -impl NorFlash for LargeErase { - const WRITE_SIZE: usize = F::ERASE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - assert!(ERASE_SIZE >= F::ERASE_SIZE); - assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); - self.0.erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.0.write(offset, bytes) - } -} - -impl ReadNorFlash for LargeErase { - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.0.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.capacity() - } -} - -impl AsyncNorFlash for LargeErase { - const WRITE_SIZE: usize = F::ERASE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - assert!(ERASE_SIZE >= F::ERASE_SIZE); - assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); - self.0.erase(from, to).await - } - - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.0.write(offset, bytes).await - } -} - -impl AsyncReadNorFlash for LargeErase { - const READ_SIZE: usize = F::READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.0.read(offset, bytes).await - } - - fn capacity(&self) -> usize { - self.0.capacity() - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index cc812d79..3109f2b4 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,7 +7,6 @@ mod fmt; mod boot_loader; mod firmware_updater; -mod large_erase; mod mem_flash; mod partition; @@ -49,7 +48,6 @@ mod tests { use futures::executor::block_on; use super::*; - use crate::large_erase::LargeErase; use crate::mem_flash::MemFlash; /* @@ -156,7 +154,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 8>::random()); + let mut dfu = MemFlash::<16384, 2048, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; @@ -188,7 +186,7 @@ mod tests { // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0.mem[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -200,7 +198,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 4>::random()); + let mut active = MemFlash::<16384, 2048, 4>::random(); let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); @@ -208,7 +206,7 @@ mod tests { let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0.mem[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -229,7 +227,7 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0.mem[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched From 78e6b4d26134887b50e3ff3239a20f9b3002e8a0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:43:18 +0200 Subject: [PATCH 069/112] Remove comment about equal erase size requirement --- embassy-boot/boot/src/boot_loader.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 25f81009..ccd74b23 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -31,8 +31,6 @@ where } /// Trait defining the flash handles used for active and DFU partition. -/// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the -/// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. const STATE_ERASE_VALUE: u8 = 0xFF; From e962fe794ce3c83b094e22523d0c59264983b036 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:57:28 +0200 Subject: [PATCH 070/112] Add assertions about the aligned_buf % write sizes --- embassy-boot/boot/src/boot_loader.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index ccd74b23..2412427c 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -179,10 +179,12 @@ impl BootLoader { // Ensure we have enough progress pages to store copy progress assert_eq!(0, P::page_size() % aligned_buf.len()); assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active From a77ce1088d927266a23cbc971aed6facd3b7f918 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 22:22:25 +0200 Subject: [PATCH 071/112] Align chip specific boot projects with new prepare_boot() signature --- embassy-boot/nrf/src/lib.rs | 14 ++++++-------- embassy-boot/rp/src/lib.rs | 16 +++++++--------- embassy-boot/stm32/src/lib.rs | 14 ++++++-------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index f40ae62d..d3774d30 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -11,13 +11,12 @@ use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer<4>, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { @@ -56,20 +55,19 @@ impl Default for BootLoader { } } -impl BootLoader { +impl BootLoader { /// Create a new bootloader instance using the supplied partitions for active, dfu and state. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), - magic: AlignedBuffer([0; 4]), - page: AlignedBuffer([0; PAGE_SIZE]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) { + match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { Ok(_) => self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 6df34133..c163db13 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -5,33 +5,31 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::flash::{Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl BootLoader { +impl BootLoader { /// Create a new bootloader instance using the supplied partitions for active, dfu and state. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), - magic: AlignedBuffer([0; WRITE_SIZE]), - page: AlignedBuffer([0; ERASE_SIZE]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } @@ -54,7 +52,7 @@ impl BootLoader { } } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 82f712c4..1f63fcd6 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -7,26 +7,24 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; /// A bootloader for STM32 devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl BootLoader { +impl BootLoader { /// Create a new bootloader instance using the supplied partitions for active, dfu and state. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), - magic: AlignedBuffer([0; WRITE_SIZE]), - page: AlignedBuffer([0; PAGE_SIZE]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } @@ -49,7 +47,7 @@ impl BootLoader Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { From 84bfe9b8c93ea8634ce2192fb719034b5c13e5da Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 22:44:21 +0200 Subject: [PATCH 072/112] Align examples with bootloader changes --- embassy-boot/nrf/src/lib.rs | 2 +- embassy-boot/rp/src/lib.rs | 2 +- examples/boot/bootloader/nrf/src/main.rs | 8 +++++--- examples/boot/bootloader/rp/src/main.rs | 3 +-- examples/boot/bootloader/stm32/src/main.rs | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index d3774d30..a2176f60 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -16,7 +16,7 @@ pub struct BootLoader { aligned_buf: AlignedBuffer, } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index c163db13..0031efa6 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -52,7 +52,7 @@ impl BootLoader { } } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index aca3b857..8818a23b 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -27,9 +27,11 @@ fn main() -> ! { wdt_config.run_during_sleep = true; wdt_config.run_during_debug_halt = false; - let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), - ))); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( + Nvmc::new(p.NVMC), + p.WDT, + wdt_config, + )))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index fb7f0522..8129591f 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -5,7 +5,6 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_rp::*; -use embassy_rp::flash::ERASE_SIZE; use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -24,7 +23,7 @@ fn main() -> ! { let mut bl: BootLoader = BootLoader::default(); let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); - let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); + let mut flash = BootFlash::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 4b17cd79..b8027d19 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; +use embassy_stm32::flash::{Flash, ERASE_SIZE}; #[entry] fn main() -> ! { @@ -19,9 +19,9 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader = BootLoader::default(); let flash = Flash::new(p.FLASH); - let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let mut flash = BootFlash::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } From 991b22b6a1e845dc15eca72bf9881e60f1803840 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 4 Apr 2023 19:35:25 -0500 Subject: [PATCH 073/112] stm32/pwm: add complementary pwm --- embassy-stm32/src/pwm/complementary_pwm.rs | 145 +++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 22 ++++ 2 files changed, 167 insertions(+) create mode 100644 embassy-stm32/src/pwm/complementary_pwm.rs diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs new file mode 100644 index 00000000..b8761724 --- /dev/null +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -0,0 +1,145 @@ +use core::marker::PhantomData; + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use super::*; +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +pub struct Ch1; +pub struct Ch2; +pub struct Ch3; +pub struct Ch4; + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| unsafe { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| unsafe { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); +channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); +channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); +channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); + +pub struct ComplementaryPwm<'d, T> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>, + _ch1n: Option>, + _ch2: Option>, + _ch2n: Option>, + _ch3: Option>, + _ch3n: Option>, + _ch4: Option>, + _ch4n: Option>, + freq: Hertz, + ) -> Self { + Self::new_inner(tim, freq) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + let mut this = Self { inner: tim }; + + this.inner.set_frequency(freq); + this.inner.start(); + + unsafe { + this.inner.enable_outputs(true); + + this.inner + .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + } + this + } + + pub fn enable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, true); + } + } + + pub fn disable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, false); + } + } + + pub fn set_freq(&mut self, freq: Hertz) { + self.inner.set_frequency(freq); + } + + pub fn get_max_duty(&self) -> u16 { + unsafe { self.inner.get_max_compare_value() } + } + + pub fn set_duty(&mut self, channel: Channel, duty: u16) { + assert!(duty < self.get_max_duty()); + unsafe { self.inner.set_compare_value(channel, duty) } + } + + /* + set the value of the dead-time register + */ + pub fn set_dead_time_value(&mut self, value: u8) { + unsafe { self.inner.set_dead_time_value(value) } + } +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index d3713391..6f3c1266 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,3 +1,4 @@ +pub mod complementary_pwm; pub mod simple_pwm; #[cfg(feature = "unstable-pac")] @@ -67,6 +68,10 @@ pub(crate) mod sealed { unsafe fn get_max_compare_value(&self) -> u16; } + pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + unsafe fn set_dead_time_value(&mut self, value: u8); + } + pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); @@ -82,6 +87,12 @@ pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { } + +pub trait ComplementaryCaptureCompare16bitInstance: + sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static +{ +} + pub trait CaptureCompare32bitInstance: sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static { @@ -209,6 +220,17 @@ foreach_interrupt! { impl CaptureCompare16bitInstance for crate::peripherals::$inst { } + + impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + unsafe fn set_dead_time_value(&mut self, value: u8) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); + } + } + + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + + } }; } From d8e2f82569e1182e2a3a7ebe43af64f91d1e57e0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 06:57:56 +0200 Subject: [PATCH 074/112] Let update_len be usize for now --- embassy-boot/boot/src/firmware_updater.rs | 20 +++++++++----------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index fffb9a50..48e15024 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -119,13 +119,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: u32, + _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len() as u32); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -182,10 +180,11 @@ impl FirmwareUpdater { pub async fn hash( &mut self, dfu_flash: &mut F, - update_len: u32, + update_len: usize, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { + let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read(dfu_flash, offset, chunk_buf).await?; @@ -341,13 +340,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: u32, + _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len() as u32); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -402,10 +399,11 @@ impl FirmwareUpdater { pub fn hash_blocking( &mut self, dfu_flash: &mut F, - update_len: u32, + update_len: usize, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { + let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; @@ -536,7 +534,7 @@ mod tests { block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + block_on(updater.hash::<_, Sha1>(&mut flash, update.len(), &mut chunk_buf, &mut hash)).unwrap(); assert_eq!(Sha1::digest(update).as_slice(), hash); } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 605e5253..acd90996 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -281,7 +281,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len as u32, + firmware_len, &mut aligned, )) .is_ok()); From 2deb2c624c78f4ff582441d7d00a654ac3844e33 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 08:28:31 +0200 Subject: [PATCH 075/112] Let Partition range be u32 instead of usize --- embassy-boot/boot/src/boot_loader.rs | 101 +++++++++++----------- embassy-boot/boot/src/firmware_updater.rs | 17 ++-- embassy-boot/boot/src/lib.rs | 60 ++++--------- embassy-boot/boot/src/mem_flash.rs | 17 ++++ embassy-boot/boot/src/partition.rs | 9 +- 5 files changed, 93 insertions(+), 111 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 2412427c..b959de2c 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -50,13 +50,13 @@ pub trait FlashConfig { } trait FlashConfigEx { - fn page_size() -> usize; + fn page_size() -> u32; } impl FlashConfigEx for T { /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> usize { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + fn page_size() -> u32 { + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 } } @@ -86,7 +86,7 @@ impl BootLoader { /// Return the offset of the active partition into the active flash. pub fn boot_address(&self) -> usize { - self.active.from + self.active.from as usize } /// Perform necessary boot preparations like swapping images. @@ -177,11 +177,11 @@ impl BootLoader { /// pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len()); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); + assert_eq!(0, P::page_size() % aligned_buf.len() as u32); + assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); + assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); + assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); @@ -222,30 +222,27 @@ impl BootLoader { } fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = self.active.len() / P::page_size(); + let page_count = (self.active.size() / P::page_size()) as usize; let progress = self.current_progress(p, aligned_buf)?; Ok(progress >= page_count * 2) } fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let max_index = ((self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE) - 2; + let write_size = P::STATE::WRITE_SIZE as u32; + let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; let state_flash = config.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..write_size as usize]; - self.state - .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + self.state.read_blocking(state_flash, write_size, state_word)?; if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state.read_blocking( - state_flash, - (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + self.state + .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { return Ok(index); @@ -256,26 +253,29 @@ impl BootLoader { fn update_progress( &mut self, - index: usize, + progress_index: usize, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; + self.state.write_blocking( + p.state(), + (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, + state_word, + )?; Ok(()) } fn copy_page_once_to_active( &mut self, - idx: usize, + progress_index: usize, from_offset: u32, to_offset: u32, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= idx { + if self.current_progress(p, aligned_buf)? <= progress_index { let page_size = P::page_size() as u32; self.active @@ -288,20 +288,20 @@ impl BootLoader { .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(idx, p, aligned_buf)?; + self.update_progress(progress_index, p, aligned_buf)?; } Ok(()) } fn copy_page_once_to_dfu( &mut self, - idx: usize, + progress_index: usize, from_offset: u32, to_offset: u32, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= idx { + if self.current_progress(p, aligned_buf)? <= progress_index { let page_size = P::page_size() as u32; self.dfu @@ -314,31 +314,28 @@ impl BootLoader { .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(idx, p, aligned_buf)?; + self.update_progress(progress_index, p, aligned_buf)?; } Ok(()) } fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { let page_size = P::page_size(); - let page_count = self.active.len() / page_size; - trace!("Page count: {}", page_count); + let page_count = self.active.size() / page_size; for page_num in 0..page_count { - trace!("COPY PAGE {}", page_num); - - let idx = page_num * 2; + let progress_index = (page_num * 2) as usize; // Copy active page to the 'next' DFU page. - let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; - let dfu_to_offset = ((page_count - page_num) * page_size) as u32; + let active_from_offset = (page_count - 1 - page_num) * page_size; + let dfu_to_offset = (page_count - page_num) * page_size; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy DFU page to the active page - let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; - let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + let active_to_offset = (page_count - 1 - page_num) * page_size; + let dfu_from_offset = (page_count - 1 - page_num) * page_size; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) @@ -346,19 +343,19 @@ impl BootLoader { fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { let page_size = P::page_size(); - let page_count = self.active.len() / page_size; + let page_count = self.active.size() / page_size; for page_num in 0..page_count { - let idx = page_count * 2 + page_num * 2; + let progress_index = (page_count * 2 + page_num * 2) as usize; // Copy the bad active page to the DFU page - let active_from_offset = (page_num * page_size) as u32; - let dfu_to_offset = (page_num * page_size) as u32; - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; + let active_from_offset = page_num * page_size; + let dfu_to_offset = page_num * page_size; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy the DFU page back to the active page - let active_to_offset = (page_num * page_size) as u32; - let dfu_from_offset = ((page_num + 1) * page_size) as u32; - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + let active_to_offset = page_num * page_size; + let dfu_from_offset = (page_num + 1) * page_size; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) @@ -376,11 +373,11 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { - assert_eq!(active.len() % page_size, 0); - assert_eq!(dfu.len() % page_size, 0); - assert!(dfu.len() - active.len() >= page_size); - assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size); +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { + assert_eq!(active.size() % page_size, 0); + assert_eq!(dfu.size() % page_size, 0); + assert!(dfu.size() - active.size() >= page_size); + assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); } /// A flash wrapper implementing the Flash and embedded_storage traits. diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 2b5cc72f..93d4a4c1 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -49,14 +49,14 @@ impl Default for FirmwareUpdater { let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; @@ -121,10 +121,8 @@ impl FirmwareUpdater { _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len()); + assert!(_update_len as u32 <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -330,11 +328,8 @@ impl FirmwareUpdater { _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len as u32 <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3109f2b4..8b94b6bd 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -89,13 +89,11 @@ mod tests { const DFU: Partition = Partition::new(61440, 122880); let mut flash = MemFlash::<131072, 4096, 4>::random(); - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; let mut aligned = [0; 4]; - for i in ACTIVE.from..ACTIVE.to { - flash.mem[i] = original[i - ACTIVE.from]; - } + flash.program(ACTIVE.from, &original).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -110,14 +108,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + flash.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + flash.assert_eq(DFU.from + 4096, &original); // Running again should cause a revert assert_eq!( @@ -127,14 +120,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i); - } - + flash.assert_eq(ACTIVE.from, &original); // Last page is untouched - for i in DFU.from..DFU.to - 4096 { - assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i); - } + flash.assert_eq(DFU.from, &update); // Mark as booted block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); @@ -158,12 +146,10 @@ mod tests { let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; - for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; - } + active.program(ACTIVE.from, &original).unwrap(); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -180,14 +166,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + active.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + dfu.assert_eq(DFU.from + 4096, &original); } #[test] @@ -202,12 +183,10 @@ mod tests { let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; - for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; - } + active.program(ACTIVE.from, &original).unwrap(); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -226,14 +205,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + active.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + dfu.assert_eq(DFU.from + 4096, &original); } #[test] diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index dd85405c..c62379b2 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -32,6 +32,23 @@ impl MemFla pending_write_successes: None, } } + + pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { + let offset = offset as usize; + assert!(bytes.len() % WRITE_SIZE == 0); + assert!(offset % WRITE_SIZE == 0); + assert!(offset + bytes.len() <= SIZE); + + self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); + + Ok(()) + } + + pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { + for i in 0..expectation.len() { + assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); + } + } } impl Default diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index ac6b0ed0..7529059b 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -6,20 +6,19 @@ use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { /// The offset into the flash where the partition starts. - pub from: usize, + pub from: u32, /// The offset into the flash where the partition ends. - pub to: usize, + pub to: u32, } impl Partition { /// Create a new partition with the provided range - pub const fn new(from: usize, to: usize) -> Self { + pub const fn new(from: u32, to: u32) -> Self { Self { from, to } } /// Return the size of the partition - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> usize { + pub const fn size(&self) -> u32 { self.to - self.from } From 7e5ead78fed6b9c352852f2619c523f20e7b7fb7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 08:28:46 +0200 Subject: [PATCH 076/112] Remove firmware_len --- embassy-boot/boot/src/firmware_updater.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 93d4a4c1..61c902ed 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -72,11 +72,6 @@ impl FirmwareUpdater { Self { dfu, state } } - /// Return the length of the DFU area - pub fn firmware_len(&self) -> usize { - self.dfu.len() - } - /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order From 05b2b2fb5f0f8d52689057c22dd2fa026d6cc796 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 09:56:40 +0200 Subject: [PATCH 077/112] Align platform specific bootloaders --- embassy-boot/nrf/src/lib.rs | 12 ++++++------ embassy-boot/rp/src/lib.rs | 12 ++++++------ embassy-boot/stm32/src/lib.rs | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index a2176f60..d46ed9f3 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -30,20 +30,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 0031efa6..c3cb2229 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -66,20 +66,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 1f63fcd6..94404697 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -61,20 +61,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; From 95b31cf2db647b8d1779cc3f7439d5c7df98f379 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:27:13 +0200 Subject: [PATCH 078/112] Remove Drop on Flash and FlashLayout and propage lifetime to region types This allows the user to "split" the FlashRegions struct into each region --- embassy-stm32/build.rs | 11 ++++---- embassy-stm32/src/flash/common.rs | 20 +++------------ embassy-stm32/src/flash/f4.rs | 42 ++++++++++++++++--------------- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9df5a58d..61aceed9 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -148,7 +148,8 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { - pub struct #region_type(pub &'static crate::flash::FlashRegion); + #[cfg(flash)] + pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,); }); } @@ -159,11 +160,11 @@ fn main() { let field_name = format_ident!("{}", region_name.to_lowercase()); let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { - pub #field_name: #field_type + pub #field_name: #field_type<'d> }; let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(&#region_name) + #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}) }; (field, (init, region_name)) @@ -174,15 +175,13 @@ fn main() { flash_regions.extend(quote! { #[cfg(flash)] pub struct FlashLayout<'d> { - _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } #[cfg(flash)] impl<'d> FlashLayout<'d> { - pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(mut p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { - _inner: p, #(#inits),* } } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c48b2f2e..8235d6f0 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -38,18 +38,6 @@ impl<'d> Flash<'d> { } } -impl Drop for Flash<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); @@ -177,7 +165,7 @@ impl FlashRegion { foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name { + impl crate::_generated::flash_regions::$type_name<'_> { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { blocking_read(self.0.base, self.0.size, offset, bytes) } @@ -191,11 +179,11 @@ foreach_flash_region! { } } - impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -207,7 +195,7 @@ foreach_flash_region! { } } - impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2f5b417c..2ce9df69 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -45,34 +45,36 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub type AltBank1Region1 = Bank1Region1; - pub type AltBank1Region2 = Bank1Region2; - pub struct AltBank1Region3(&'static FlashRegion); - pub struct AltBank2Region1(&'static FlashRegion); - pub struct AltBank2Region2(&'static FlashRegion); - pub struct AltBank2Region3(&'static FlashRegion); + pub type AltBank1Region1<'d> = Bank1Region1<'d>; + pub type AltBank1Region2<'d> = Bank1Region2<'d>; + pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltFlashLayout<'d> { - _inner: PeripheralRef<'d, FLASH>, - pub bank1_region1: AltBank1Region1, - pub bank1_region2: AltBank1Region2, - pub bank1_region3: AltBank1Region3, - pub bank2_region1: AltBank2Region1, - pub bank2_region2: AltBank2Region2, - pub bank2_region3: AltBank2Region3, + pub bank1_region1: AltBank1Region1<'d>, + pub bank1_region2: AltBank1Region2<'d>, + pub bank1_region3: AltBank1Region3<'d>, + pub bank2_region1: AltBank2Region1<'d>, + pub bank2_region2: AltBank2Region2<'d>, + pub bank2_region3: AltBank2Region3<'d>, } impl<'d> Flash<'d> { pub fn into_alt_regions(self) -> AltFlashLayout<'d> { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + + // SAFETY: We never expose the cloned peripheral references, and their instance is not public. + // Also, all flash region operations are protected with a cs. + let mut p = self.release(); AltFlashLayout { - _inner: self.release(), - bank1_region1: Bank1Region1(&BANK1_REGION1), - bank1_region2: Bank1Region2(&BANK1_REGION2), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3), + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), } } } From 57d3d4d58148fefbd6db4770918b52f31ded0124 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:29:45 +0200 Subject: [PATCH 079/112] Align stm32 bootloader example --- examples/boot/bootloader/stm32/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index b8027d19..49c21920 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE}; +use embassy_stm32::flash::Flash; #[entry] fn main() -> ! { @@ -19,9 +19,10 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader<2048> = BootLoader::default(); let flash = Flash::new(p.FLASH); - let mut flash = BootFlash::new(flash); + let layout = flash.into_regions(); + let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } From 2a49e11cb0ffd3e0d9a0cc94444f293de523b47f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:55:31 +0200 Subject: [PATCH 080/112] Align flash examples --- examples/stm32f3/src/bin/flash.rs | 2 +- examples/stm32f4/src/bin/flash.rs | 13 +++++++------ examples/stm32f7/src/bin/flash.rs | 4 ++-- examples/stm32h7/src/bin/flash.rs | 4 ++-- examples/stm32l0/src/bin/flash.rs | 2 +- examples/stm32l1/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index baa7484d..e40ad4fc 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 7ea068a4..bd3a7c95 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -5,7 +5,6 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -13,6 +12,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); + // Once can also call `into_regions()` to get access to NorFlash implementations + // for each of the unique characteristics. let mut f = Flash::new(p.FLASH); // Sector 5 @@ -30,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(offset, offset + size)); + unwrap!(f.blocking_erase(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.blocking_write( offset, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, @@ -52,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 4a7bca1f..aabfe855 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x8_0000; + const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000. // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index ee86bdbf..7ee9838c 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x08_0000; + const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000 // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index ffe4fb10..33742502 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 476ed51a..38feb0d7 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 2a888062..e6bc2865 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; From 8290236ed64435453a9c028c95246a86371bd4ce Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 2 Apr 2023 14:11:31 -0500 Subject: [PATCH 081/112] executor: Replace unsound critical sections with atomics --- embassy-executor/src/raw/mod.rs | 28 +++++++++++++-------------- embassy-executor/src/raw/run_queue.rs | 18 +++++++++++------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f6c66da5..bd0cff26 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -22,7 +22,6 @@ use core::ptr::NonNull; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, Ordering}; -use critical_section::CriticalSection; #[cfg(feature = "integrated-timers")] use embassy_time::driver::{self, AlarmHandle}; #[cfg(feature = "integrated-timers")] @@ -373,11 +372,11 @@ impl SyncExecutor { /// - `task` must be set up to run in this executor. /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] - unsafe fn enqueue(&self, cs: CriticalSection, task: TaskRef) { + unsafe fn enqueue(&self, task: TaskRef) { #[cfg(feature = "rtos-trace")] trace::task_ready_begin(task.as_ptr() as u32); - if self.run_queue.enqueue(cs, task) { + if self.run_queue.enqueue(task) { self.pender.pend(); } } @@ -394,9 +393,7 @@ impl SyncExecutor { #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); - critical_section::with(|cs| { - self.enqueue(cs, task); - }) + self.enqueue(task); } /// # Safety @@ -552,24 +549,25 @@ impl Executor { /// /// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. pub fn wake_task(task: TaskRef) { - critical_section::with(|cs| { - let header = task.header(); - let state = header.state.load(Ordering::Relaxed); + let header = task.header(); + let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { // If already scheduled, or if not started, if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { - return; + None + } else { + // Mark it as scheduled + Some(state | STATE_RUN_QUEUED) } + }); - // Mark it as scheduled - header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); - + if res.is_ok() { // We have just marked the task as scheduled, so enqueue it. unsafe { let executor = header.executor.get().unwrap_unchecked(); - executor.enqueue(cs, task); + executor.enqueue(task); } - }) + } } #[cfg(feature = "integrated-timers")] diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 36215753..a88174a0 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -2,7 +2,6 @@ use core::ptr; use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; -use critical_section::CriticalSection; use super::{TaskHeader, TaskRef}; @@ -46,11 +45,18 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: TaskRef) -> bool { - let prev = self.head.load(Ordering::Relaxed); - task.header().run_queue_item.next.store(prev, Ordering::Relaxed); - self.head.store(task.as_ptr() as _, Ordering::Relaxed); - prev.is_null() + pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool { + let mut was_empty = false; + + self.head + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { + was_empty = prev.is_null(); + task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + Some(task.as_ptr() as *mut _) + }) + .ok(); + + was_empty } /// Empty the queue, then call `on_task` for each task that was in the queue. From 5d9ae3dbdbfe8ba6e1008cd2eadc09743cfc6284 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:08:03 -0400 Subject: [PATCH 082/112] Add implementation of STM32 v1 ADC --- embassy-stm32/src/adc/v1.rs | 277 ++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 8b137891..923a1d97 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1 +1,278 @@ +use core::marker::PhantomData; +use embassy_hal_common::into_ref; +use embedded_hal_02::blocking::delay::DelayUs; + +use crate::adc::{AdcPin, Instance}; +use crate::{pac, Peripheral}; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const VREF_INT: u32 = 1230; + +fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); + }); +} + +pub enum Resolution { + TwelveBit, + TenBit, + EightBit, + SixBit, +} + +impl Default for Resolution { + fn default() -> Self { + Self::TwelveBit + } +} + +impl Resolution { + fn res(&self) -> pac::adc::vals::Res { + match self { + Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, + Resolution::TenBit => pac::adc::vals::Res::TENBIT, + Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, + Resolution::SixBit => pac::adc::vals::Res::SIXBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + Resolution::SixBit => (1 << 6) - 1, + } + } +} + +pub struct Vbat; +impl AdcPin for Vbat {} +impl super::sealed::AdcPin for Vbat { + fn channel(&self) -> u8 { + 18 + } +} + +pub struct Vref; +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 17 + } +} + +pub struct Temperature; +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +mod sample_time { + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5 = 0b000, + + /// 7.5 ADC clock cycles + Cycles7_5 = 0b001, + + /// 13.5 ADC clock cycles + Cycles13_5 = 0b010, + + /// 28.5 ADC clock cycles + Cycles28_5 = 0b011, + + /// 41.5 ADC clock cycles + Cycles41_5 = 0b100, + + /// 55.5 ADC clock cycles + Cycles55_5 = 0b101, + + /// 71.5 ADC clock cycles + Cycles71_5 = 0b110, + + /// 239.5 ADC clock cycles + Cycles239_5 = 0b111, + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { + match self { + SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, + SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, + SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, + SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, + SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, + SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, + SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, + SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::Cycles1_5 + } + } +} + +pub use sample_time::SampleTime; + +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + vref_mv: u32, + resolution: Resolution, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(_peri); + enable(); + + // Delay 1μs when using HSI14 as the ADC clock. + // + // Table 57. ADC characteristics + // tstab = 14 * 1/fadc + delay.delay_us(1); + + let s = Self { + sample_time: Default::default(), + vref_mv: VDDA_CALIB_MV, + resolution: Resolution::default(), + phantom: PhantomData, + }; + s.calibrate(); + s + } + + pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.20 Vbat monitoring characteristics + // ts_vbat ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vbaten(true)); + } + Vbat + } + + pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { + // Table 28. Embedded internal reference voltage + // tstart = 10μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vrefen(true)); + } + delay.delay_us(10); + Vref + } + + pub fn enable_temperature(&self, delay: &mut impl DelayUs) -> Temperature { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.19 Temperature sensor characteristics + // tstart ≤ 10μs + // ts_temp ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_tsen(true)); + } + delay.delay_us(10); + Temperature + } + + fn calibrate(&self) { + unsafe { + // A.7.1 ADC calibration code example + if T::regs().cr().read().aden() { + T::regs().cr().modify(|reg| reg.set_addis(true)); + } + while T::regs().cr().read().aden() { + // spin + } + T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); + T::regs().cr().modify(|reg| reg.set_adcal(true)); + while T::regs().cr().read().adcal() { + // spin + } + } + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + pub fn set_vref_mv(&mut self, vref_mv: u32) { + self.vref_mv = vref_mv; + } + + pub fn set_resolution(&mut self, resolution: Resolution) { + self.resolution = resolution; + } + + pub fn to_millivolts(&self, sample: u16) -> u16 { + ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + } + + fn convert(&mut self) -> u16 { + unsafe { + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + + T::regs().dr().read().0 as u16 + } + } + + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + unsafe { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + Self::set_channel_sample_time(pin.channel(), self.sample_time); + T::regs() + .chselr() + .write(|reg| reg.set_chselx(pin.channel() as usize, true)); + + let value = self.convert(); + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value + } + } + + unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + } +} From 7e9e628eb9e63598a7d15ecae21af5a94ee93be4 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:10:29 -0400 Subject: [PATCH 083/112] Add ADC example for STM32F0 --- examples/stm32f0/src/bin/adc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/stm32f0/src/bin/adc.rs diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs new file mode 100644 index 00000000..b69bc3c3 --- /dev/null +++ b/examples/stm32f0/src/bin/adc.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC, &mut Delay); + let mut pin = p.PA1; + + loop { + let v = adc.read(&mut pin); + info!("--> {} - {} mV", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; + } +} From a0b60966106f96e2915d429319a347e239eb1a5f Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 13:03:42 -0400 Subject: [PATCH 084/112] Put ADC input pin into analog mode --- embassy-stm32/src/adc/v1.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 923a1d97..224d1717 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -236,7 +236,10 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin + crate::gpio::sealed::Pin, + { unsafe { // A.7.2 ADC enable sequence code example if T::regs().isr().read().adrdy() { @@ -252,6 +255,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); Self::set_channel_sample_time(pin.channel(), self.sample_time); + pin.set_as_analog(); T::regs() .chselr() .write(|reg| reg.set_chselx(pin.channel() as usize, true)); From 511a95124690be253dc9b789abeae1bdea3aa83a Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:18:37 -0400 Subject: [PATCH 085/112] Differentiate between `read` and `read_internal` for STM32F0 ADC The internal channels (vbat, vref, and temperature) are not real pins and do not have the `set_as_analog` method. They must be read using the `read_internal` method. --- embassy-stm32/src/adc/v1.rs | 120 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 224d1717..f787f72a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -48,25 +48,33 @@ impl Resolution { } } +pub trait InternalChannel: sealed::InternalChannel {} + +mod sealed { + pub trait InternalChannel { + fn channel(&self) -> u8; + } +} + pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl InternalChannel for Vref {} +impl sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -219,64 +227,64 @@ impl<'d, T: Instance> Adc<'d, T> { ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } - fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eoc(true); - reg.set_eosmp(true); - }); - - // A.7.5 Single conversion sequence code example - Software trigger - T::regs().cr().modify(|reg| reg.set_adstart(true)); - while !T::regs().isr().read().eoc() { - // spin - } - - T::regs().dr().read().0 as u16 - } - } - pub fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin + crate::gpio::sealed::Pin, { + let channel = pin.channel(); unsafe { - // A.7.2 ADC enable sequence code example - if T::regs().isr().read().adrdy() { - T::regs().isr().modify(|reg| reg.set_adrdy(true)); - } - T::regs().cr().modify(|reg| reg.set_aden(true)); - while !T::regs().isr().read().adrdy() { - // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration - // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the - // ADEN bit until the ADRDY flag goes high. - T::regs().cr().modify(|reg| reg.set_aden(true)); - } - - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); - Self::set_channel_sample_time(pin.channel(), self.sample_time); pin.set_as_analog(); - T::regs() - .chselr() - .write(|reg| reg.set_chselx(pin.channel() as usize, true)); - - let value = self.convert(); - - // A.7.3 ADC disable code example - T::regs().cr().modify(|reg| reg.set_adstp(true)); - while T::regs().cr().read().adstp() { - // spin - } - T::regs().cr().modify(|reg| reg.set_addis(true)); - while T::regs().cr().read().aden() { - // spin - } - - value + self.read_channel(channel) } } - unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + let channel = channel.channel(); + unsafe { + self.read_channel(channel) + } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs() + .chselr() + .write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + let value = T::regs().dr().read().0 as u16; + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value } } From 28b8ac4b62d952918ddfe143cf6925e1402fc2ce Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:23:31 -0400 Subject: [PATCH 086/112] Update STM32F0 ADC example to use `read_internal` --- examples/stm32f0/src/bin/adc.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index b69bc3c3..6205596f 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,11 +14,14 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut adc = Adc::new(p.ADC, &mut Delay); + adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; + let mut vref = adc.enable_temperature(&mut Delay); loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + let r = adc.read_internal(&mut vref); + info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); Timer::after(Duration::from_millis(100)).await; } } From f5881054297713f6224d5d81f407f8a6cf591ef4 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 14:31:32 -0500 Subject: [PATCH 087/112] wip --- embassy-stm32/src/adc/mod.rs | 11 +-- embassy-stm32/src/adc/resolution.rs | 8 +- embassy-stm32/src/adc/sample_time.rs | 2 +- embassy-stm32/src/adc/v1.rs | 129 +++------------------------ 4 files changed, 20 insertions(+), 130 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ec49dace..56ecd63c 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -7,21 +7,18 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] mod resolution; -#[cfg(not(adc_v1))] mod sample_time; #[allow(unused)] pub use _version::*; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] pub use resolution::Resolution; -#[cfg(not(adc_v1))] pub use sample_time::SampleTime; use crate::peripherals; -#[cfg(not(adc_v1))] pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, @@ -44,9 +41,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} -#[cfg(any(adc_f1, adc_v2, adc_v4))] +#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 62b52a46..67fb9b8c 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,4 @@ -#[cfg(any(adc_v2, adc_v3, adc_g0))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, @@ -19,7 +19,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] { Self::TwelveBit } @@ -40,7 +40,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +56,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index bc5fb1d6..0faa1e3c 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -25,7 +25,7 @@ macro_rules! impl_sample_time { }; } -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v1))] impl_sample_time!( "1.5", Cycles1_5, diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index f787f72a..0fdb9556 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,10 +1,8 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; -use crate::{pac, Peripheral}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -15,39 +13,6 @@ fn enable() { }); } -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> pac::adc::vals::Res { - match self { - Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => pac::adc::vals::Res::TENBIT, - Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -80,68 +45,9 @@ impl sealed::InternalChannel for Temperature { } } -mod sample_time { - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b001, - - /// 13.5 ADC clock cycles - Cycles13_5 = 0b010, - - /// 28.5 ADC clock cycles - Cycles28_5 = 0b011, - - /// 41.5 ADC clock cycles - Cycles41_5 = 0b100, - - /// 55.5 ADC clock cycles - Cycles55_5 = 0b101, - - /// 71.5 ADC clock cycles - Cycles71_5 = 0b110, - - /// 239.5 ADC clock cycles - Cycles239_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, - SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, - SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, - SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, - SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, - SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, - SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } - } -} - -pub use sample_time::SampleTime; - -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - vref_mv: u32, - resolution: Resolution, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); enable(); // Delay 1μs when using HSI14 as the ADC clock. @@ -151,10 +57,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us(1); let s = Self { + adc, sample_time: Default::default(), - vref_mv: VDDA_CALIB_MV, - resolution: Resolution::default(), - phantom: PhantomData, }; s.calibrate(); s @@ -215,16 +119,10 @@ impl<'d, T: Instance> Adc<'d, T> { self.sample_time = sample_time; } - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; - } - - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + unsafe { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -240,9 +138,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { let channel = channel.channel(); - unsafe { - self.read_channel(channel) - } + unsafe { self.read_channel(channel) } } unsafe fn read_channel(&mut self, channel: u8) -> u16 { @@ -258,17 +154,14 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|reg| reg.set_aden(true)); } - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); T::regs().isr().modify(|reg| { reg.set_eoc(true); reg.set_eosmp(true); }); // A.7.5 Single conversion sequence code example - Software trigger - T::regs() - .chselr() - .write(|reg| reg.set_chselx(channel as usize, true)); - T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); T::regs().cr().modify(|reg| reg.set_adstart(true)); while !T::regs().isr().read().eoc() { // spin From efd9e18321c258a188fa675804d59346a4c11cc2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:12:27 -0500 Subject: [PATCH 088/112] Fix example --- examples/stm32f0/src/bin/adc.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 6205596f..c639299d 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -16,12 +16,21 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC, &mut Delay); adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; - let mut vref = adc.enable_temperature(&mut Delay); + + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read_internal(&mut vrefint); + let convert_to_millivolts = |sample| { + // FIXME: use proper datasheet and value + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1200; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; loop { let v = adc.read(&mut pin); - let r = adc.read_internal(&mut vref); - info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } From 37d8f2e51256403cc4839c3b45e5c2253e3a09f1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:28:42 -0500 Subject: [PATCH 089/112] Properly enable and reset adc --- embassy-stm32/src/adc/v1.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 0fdb9556..a699d8f7 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -7,12 +7,6 @@ use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -fn enable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); - }); -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -48,7 +42,8 @@ impl sealed::InternalChannel for Temperature { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(adc); - enable(); + T::enable(); + T::reset(); // Delay 1μs when using HSI14 as the ADC clock. // From 20e7b5e2965f718aa1a0ece6009356ba50d2d780 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:11:21 -0500 Subject: [PATCH 090/112] InternalChannel --- embassy-stm32/src/adc/v1.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index a699d8f7..5f0f8c14 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,39 +1,32 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::Peripheral; +use crate::peripherals::ADC1; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -pub trait InternalChannel: sealed::InternalChannel {} - -mod sealed { - pub trait InternalChannel { - fn channel(&self) -> u8; - } -} - pub struct Vbat; -impl InternalChannel for Vbat {} -impl sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } From 7c53ebd5766fc75dbb5ddd1953b5bff13f1cd853 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:28:28 -0500 Subject: [PATCH 091/112] Fix example reference voltage --- examples/stm32f0/src/bin/adc.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index c639299d..8ed9f98f 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -20,10 +20,9 @@ async fn main(_spawner: Spawner) { let mut vrefint = adc.enable_vref(&mut Delay); let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { - // FIXME: use proper datasheet and value - // From http://www.st.com/resource/en/datasheet/CD00161566.pdf - // 5.3.4 Embedded reference voltage - const VREFINT_MV: u32 = 1200; // mV + // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf + // 6.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1230; // mV (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; From 92e96bd601e8e663114b1efccc4a1e8d309332d9 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:38:06 -0500 Subject: [PATCH 092/112] Fix typo --- embassy-stm32/src/adc/v1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 5f0f8c14..17114732 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,8 +2,8 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::Peripheral; use crate::peripherals::ADC1; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -18,7 +18,7 @@ impl super::sealed::InternalChannel for Vbat { pub struct Vref; impl InternalChannel for Vref {} -impl super::sealed::::InternalChannel for Vref { +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } From 0ef419bee4afc5a984cab3d5f16e1f1382fa6bbf Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:52:32 -0500 Subject: [PATCH 093/112] Change ADC1 to ADC --- embassy-stm32/src/adc/v1.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 17114732..82a8c3ef 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,31 +2,31 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::peripherals::ADC1; +use crate::peripherals::ADC; use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; pub struct Vbat; -impl InternalChannel for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl super::sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } From 76772683191be15d32604ec5dd46fb5eb3949de8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 17:50:23 -0500 Subject: [PATCH 094/112] stm32/pwm: cleanup and fix complementary pwm --- embassy-stm32/src/pwm/complementary_pwm.rs | 47 ++++++---------------- embassy-stm32/src/pwm/mod.rs | 18 +++++++++ 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index b8761724..e4de1fb7 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,7 +1,9 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; +use stm32_metapac::timer::vals::Ckd; +use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; @@ -9,39 +11,13 @@ use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; -pub struct Ch1; -pub struct Ch2; -pub struct Ch3; -pub struct Ch4; - -pub struct PwmPin<'d, Perip, Channel> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, -} - pub struct ComplementaryPwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, phantom: PhantomData<(Perip, Channel)>, } -macro_rules! channel_impl { +macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { - into_ref!(pin); - critical_section::with(|_| unsafe { - pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); - #[cfg(gpio_v2)] - pin.set_speed(crate::gpio::Speed::VeryHigh); - }); - PwmPin { - _pin: pin.map_into(), - phantom: PhantomData, - } - } - } - impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); @@ -60,10 +36,10 @@ macro_rules! channel_impl { }; } -channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); -channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); -channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); -channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); +complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); +complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); +complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); +complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, @@ -114,11 +90,13 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { pub fn enable(&mut self, channel: Channel) { unsafe { self.inner.enable_channel(channel, true); + self.inner.enable_complementary_channel(channel, true); } } pub fn disable(&mut self, channel: Channel) { unsafe { + self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } } @@ -136,9 +114,10 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { unsafe { self.inner.set_compare_value(channel, duty) } } - /* - set the value of the dead-time register - */ + pub fn set_dead_time_clock_division(&mut self, value: Ckd) { + unsafe { self.inner.set_dead_time_clock_division(value) } + } + pub fn set_dead_time_value(&mut self, value: u8) { unsafe { self.inner.set_dead_time_value(value) } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 6f3c1266..0bef0708 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,6 +1,8 @@ pub mod complementary_pwm; pub mod simple_pwm; +use stm32_metapac::timer::vals::Ckd; + #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -69,7 +71,11 @@ pub(crate) mod sealed { } pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + unsafe fn set_dead_time_clock_division(&mut self, value: Ckd); + unsafe fn set_dead_time_value(&mut self, value: u8); + + unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); } pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { @@ -222,10 +228,22 @@ foreach_interrupt! { } impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + } + unsafe fn set_dead_time_value(&mut self, value: u8) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); } + + unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.raw(), enable)); + } } impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { From 9f1dac3f5daf61a2d56679d00e018e5e3557c7e5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 18:07:07 -0500 Subject: [PATCH 095/112] stm32/pwm: add complementary pwm example --- embassy-stm32/src/pwm/complementary_pwm.rs | 2 +- examples/stm32f4/src/bin/pwm_complementary.rs | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f4/src/bin/pwm_complementary.rs diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index e4de1fb7..13edfbaa 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use stm32_metapac::timer::vals::Ckd; +pub use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; use super::*; diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs new file mode 100644 index 00000000..795c38e4 --- /dev/null +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -0,0 +1,77 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::pwm::Channel; +use embassy_stm32::time::khz; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let ch1 = PwmPin::new_ch1(p.PE9); + let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); + let mut pwm = ComplementaryPwm::new( + p.TIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + khz(10), + ); + + /* + Dead-time = T_clk * T_dts * T_dtg + + T_dts: + This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the + dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters + (ETR, TIx), + 00: tDTS=tCK_INT + 01: tDTS=2*tCK_INT + 10: tDTS=4*tCK_INT + + T_dtg: + This bit-field defines the duration of the dead-time inserted between the complementary + outputs. DT correspond to this duration. + DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS. + DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS. + DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS. + DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS. + Example if TDTS=125ns (8MHz), dead-time possible values are: + 0 to 15875 ns by 125 ns steps, + 16 us to 31750 ns by 250 ns steps, + 32 us to 63us by 1 us steps, + 64 us to 126 us by 2 us steps + */ + pwm.set_dead_time_clock_division(Ckd::DIV1); + pwm.set_dead_time_value(0); + + let max = pwm.get_max_duty(); + pwm.enable(Channel::Ch1); + + info!("PWM initialized"); + info!("PWM max duty {}", max); + + loop { + pwm.set_duty(Channel::Ch1, 0); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 4); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 2); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max - 1); + Timer::after(Duration::from_millis(300)).await; + } +} From 31ef783ac1add908c4c4506d3ce4e5f6ad9bea56 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 18:18:05 -0500 Subject: [PATCH 096/112] stm32/pwm: fix unused import --- examples/stm32f4/src/bin/pwm_complementary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 795c38e4..6e17f3fd 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::pwm::simple_pwm::PwmPin; use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; use embassy_time::{Duration, Timer}; From 9f28d8097704f51fb7d7dcc8d459ce86aaf07eff Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 18:50:53 +0200 Subject: [PATCH 097/112] stm32/usb: add support for 32bit usbram. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/usb/usb.rs | 131 +++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 48 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 504caacb..da1dc24b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "3" +stm32-metapac = "4" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "3", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "4", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 0355c5f1..e6ee3954 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -12,22 +12,29 @@ use embassy_usb_driver as driver; use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; -use pac::common::{Reg, RW}; -use pac::usb::vals::{EpType, Stat}; use super::{DmPin, DpPin, Instance}; use crate::gpio::sealed::AFType; use crate::interrupt::InterruptExt; use crate::pac::usb::regs; +use crate::pac::usb::vals::{EpType, Stat}; +use crate::pac::USBRAM; use crate::rcc::sealed::RccPeripheral; -use crate::{pac, Peripheral}; +use crate::Peripheral; const EP_COUNT: usize = 8; -#[cfg(any(usb_v1_x1, usb_v1_x2))] -const EP_MEMORY_SIZE: usize = 512; -#[cfg(not(any(usb_v1_x1, usb_v1_x2)))] -const EP_MEMORY_SIZE: usize = 1024; +#[cfg(any(usbram_16x1_512, usbram_16x2_512))] +const USBRAM_SIZE: usize = 512; +#[cfg(usbram_16x2_1024)] +const USBRAM_SIZE: usize = 1024; +#[cfg(usbram_32_2048)] +const USBRAM_SIZE: usize = 2048; + +#[cfg(not(usbram_32_2048))] +const USBRAM_ALIGN: usize = 2; +#[cfg(usbram_32_2048)] +const USBRAM_ALIGN: usize = 4; const NEW_AW: AtomicWaker = AtomicWaker::new(); static BUS_WAKER: AtomicWaker = NEW_AW; @@ -57,25 +64,60 @@ fn invariant(mut r: regs::Epr) -> regs::Epr { r } +fn align_len_up(len: u16) -> u16 { + ((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16 +} + // Returns (actual_len, len_bits) fn calc_out_len(len: u16) -> (u16, u16) { match len { - 2..=62 => ((len + 1) / 2 * 2, ((len + 1) / 2) << 10), - 63..=480 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), + // NOTE: this could be 2..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity. + 2..=60 => (align_len_up(len), align_len_up(len) / 2 << 10), + 61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), _ => panic!("invalid OUT length {}", len), } } -fn ep_in_addr(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 0) + +#[cfg(not(usbram_32_2048))] +mod btable { + use super::*; + + pub(super) unsafe fn write_in(index: usize, addr: u16) { + USBRAM.mem(index * 4 + 0).write_value(addr); + } + + pub(super) unsafe fn write_in_len(index: usize, _addr: u16, len: u16) { + USBRAM.mem(index * 4 + 1).write_value(len); + } + + pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + USBRAM.mem(index * 4 + 2).write_value(addr); + USBRAM.mem(index * 4 + 3).write_value(max_len_bits); + } + + pub(super) unsafe fn read_out_len(index: usize) -> u16 { + USBRAM.mem(index * 4 + 3).read() + } } -fn ep_in_len(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 1) -} -fn ep_out_addr(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 2) -} -fn ep_out_len(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 3) +#[cfg(usbram_32_2048)] +mod btable { + use super::*; + + pub(super) unsafe fn write_in(_index: usize, _addr: u16) {} + + pub(super) unsafe fn write_in_len(index: usize, addr: u16, len: u16) { + USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); + } + + pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + USBRAM + .mem(index * 2 + 1) + .write_value((addr as u32) | ((max_len_bits as u32) << 16)); + } + + pub(super) unsafe fn read_out_len(index: usize) -> u16 { + (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 + } } struct EndpointBuffer { @@ -87,23 +129,25 @@ struct EndpointBuffer { impl EndpointBuffer { fn read(&mut self, buf: &mut [u8]) { assert!(buf.len() <= self.len as usize); - for i in 0..((buf.len() + 1) / 2) { - let val = unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).read() }; - buf[i * 2] = val as u8; - if i * 2 + 1 < buf.len() { - buf[i * 2 + 1] = (val >> 8) as u8; - } + for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN { + let val = unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read() }; + let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); + buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]); } } fn write(&mut self, buf: &[u8]) { assert!(buf.len() <= self.len as usize); - for i in 0..((buf.len() + 1) / 2) { - let mut val = buf[i * 2] as u16; - if i * 2 + 1 < buf.len() { - val |= (buf[i * 2 + 1] as u16) << 8; - } - unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).write_value(val) }; + for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN { + let mut val = [0u8; USBRAM_ALIGN]; + let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); + val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]); + + #[cfg(not(usbram_32_2048))] + let val = u16::from_le_bytes(val); + #[cfg(usbram_32_2048)] + let val = u32::from_le_bytes(val); + unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val) }; } } } @@ -139,8 +183,7 @@ impl<'d, T: Instance> Driver<'d, T> { #[cfg(stm32l5)] unsafe { crate::peripherals::PWR::enable(); - - pac::PWR.cr2().modify(|w| w.set_usv(true)); + crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); } unsafe { @@ -256,8 +299,9 @@ impl<'d, T: Instance> Driver<'d, T> { } fn alloc_ep_mem(&mut self, len: u16) -> u16 { + assert!(len as usize % USBRAM_ALIGN == 0); let addr = self.ep_mem_free; - if addr + len > EP_MEMORY_SIZE as _ { + if addr + len > USBRAM_SIZE as _ { panic!("Endpoint memory full"); } self.ep_mem_free += len; @@ -306,10 +350,7 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); trace!(" len_bits = {:04x}", len_bits); - unsafe { - ep_out_addr::(index).write_value(addr); - ep_out_len::(index).write_value(len_bits); - } + unsafe { btable::write_out::(index, addr, len_bits) } EndpointBuffer { addr, @@ -321,13 +362,11 @@ impl<'d, T: Instance> Driver<'d, T> { assert!(!ep.used_in); ep.used_in = true; - let len = (max_packet_size + 1) / 2 * 2; + let len = align_len_up(max_packet_size); let addr = self.alloc_ep_mem(len); - unsafe { - ep_in_addr::(index).write_value(addr); - // ep_in_len is written when actually TXing packets. - } + // ep_in_len is written when actually TXing packets. + unsafe { btable::write_in::(index, addr) } EndpointBuffer { addr, @@ -398,7 +437,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { w.set_ctrm(true); }); - #[cfg(usb_v3)] + #[cfg(any(usb_v3, usb_v4))] regs.bcdr().write(|w| w.set_dppu(true)) } @@ -633,12 +672,12 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { fn write_data(&mut self, buf: &[u8]) { let index = self.info.addr.index(); self.buf.write(buf); - unsafe { ep_in_len::(index).write_value(buf.len() as _) }; + unsafe { btable::write_in_len::(index, self.buf.addr, buf.len() as _) } } fn read_data(&mut self, buf: &mut [u8]) -> Result { let index = self.info.addr.index(); - let rx_len = unsafe { ep_out_len::(index).read() as usize } & 0x3FF; + let rx_len = unsafe { btable::read_out_len::(index) as usize } & 0x3FF; trace!("READ DONE, rx_len = {}", rx_len); if rx_len > buf.len() { return Err(EndpointError::BufferOverflow); From 611d0238290c9f7b3b23d05ec07adf5b48ea3479 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 18:53:51 +0200 Subject: [PATCH 098/112] stm32: add H5 support. --- ci.sh | 3 + embassy-stm32/Cargo.toml | 73 +++ embassy-stm32/build.rs | 7 +- embassy-stm32/src/eth/v2/mod.rs | 25 +- embassy-stm32/src/exti.rs | 18 +- embassy-stm32/src/rcc/h5.rs | 606 ++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 13 +- embassy-stm32/src/time.rs | 46 +- embassy-stm32/src/usb/usb.rs | 5 + examples/stm32h5/.cargo/config.toml | 8 + examples/stm32h5/Cargo.toml | 71 +++ examples/stm32h5/build.rs | 5 + examples/stm32h5/memory.x | 5 + examples/stm32h5/src/bin/blinky.rs | 27 ++ examples/stm32h5/src/bin/button_exti.rs | 27 ++ examples/stm32h5/src/bin/eth.rs | 133 ++++++ examples/stm32h5/src/bin/i2c.rs | 44 ++ examples/stm32h5/src/bin/rng.rs | 20 + examples/stm32h5/src/bin/usart.rs | 43 ++ examples/stm32h5/src/bin/usart_dma.rs | 46 ++ examples/stm32h5/src/bin/usart_split.rs | 58 +++ examples/stm32h5/src/bin/usb_serial.rs | 128 +++++ 22 files changed, 1390 insertions(+), 21 deletions(-) create mode 100644 embassy-stm32/src/rcc/h5.rs create mode 100644 examples/stm32h5/.cargo/config.toml create mode 100644 examples/stm32h5/Cargo.toml create mode 100644 examples/stm32h5/build.rs create mode 100644 examples/stm32h5/memory.x create mode 100644 examples/stm32h5/src/bin/blinky.rs create mode 100644 examples/stm32h5/src/bin/button_exti.rs create mode 100644 examples/stm32h5/src/bin/eth.rs create mode 100644 examples/stm32h5/src/bin/i2c.rs create mode 100644 examples/stm32h5/src/bin/rng.rs create mode 100644 examples/stm32h5/src/bin/usart.rs create mode 100644 examples/stm32h5/src/bin/usart_dma.rs create mode 100644 examples/stm32h5/src/bin/usart_split.rs create mode 100644 examples/stm32h5/src/bin/usb_serial.rs diff --git a/ci.sh b/ci.sh index b9dddad3..82b72ae3 100755 --- a/ci.sh +++ b/ci.sh @@ -66,6 +66,8 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ @@ -87,6 +89,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ + --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index da1dc24b..3fd1e4b4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -834,6 +834,37 @@ stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] +stm32h503cb = [ "stm32-metapac/stm32h503cb" ] +stm32h503eb = [ "stm32-metapac/stm32h503eb" ] +stm32h503kb = [ "stm32-metapac/stm32h503kb" ] +stm32h503rb = [ "stm32-metapac/stm32h503rb" ] +stm32h562ag = [ "stm32-metapac/stm32h562ag" ] +stm32h562ai = [ "stm32-metapac/stm32h562ai" ] +stm32h562ig = [ "stm32-metapac/stm32h562ig" ] +stm32h562ii = [ "stm32-metapac/stm32h562ii" ] +stm32h562rg = [ "stm32-metapac/stm32h562rg" ] +stm32h562ri = [ "stm32-metapac/stm32h562ri" ] +stm32h562vg = [ "stm32-metapac/stm32h562vg" ] +stm32h562vi = [ "stm32-metapac/stm32h562vi" ] +stm32h562zg = [ "stm32-metapac/stm32h562zg" ] +stm32h562zi = [ "stm32-metapac/stm32h562zi" ] +stm32h563ag = [ "stm32-metapac/stm32h563ag" ] +stm32h563ai = [ "stm32-metapac/stm32h563ai" ] +stm32h563ig = [ "stm32-metapac/stm32h563ig" ] +stm32h563ii = [ "stm32-metapac/stm32h563ii" ] +stm32h563mi = [ "stm32-metapac/stm32h563mi" ] +stm32h563rg = [ "stm32-metapac/stm32h563rg" ] +stm32h563ri = [ "stm32-metapac/stm32h563ri" ] +stm32h563vg = [ "stm32-metapac/stm32h563vg" ] +stm32h563vi = [ "stm32-metapac/stm32h563vi" ] +stm32h563zg = [ "stm32-metapac/stm32h563zg" ] +stm32h563zi = [ "stm32-metapac/stm32h563zi" ] +stm32h573ai = [ "stm32-metapac/stm32h573ai" ] +stm32h573ii = [ "stm32-metapac/stm32h573ii" ] +stm32h573mi = [ "stm32-metapac/stm32h573mi" ] +stm32h573ri = [ "stm32-metapac/stm32h573ri" ] +stm32h573vi = [ "stm32-metapac/stm32h573vi" ] +stm32h573zi = [ "stm32-metapac/stm32h573zi" ] stm32h723ve = [ "stm32-metapac/stm32h723ve" ] stm32h723vg = [ "stm32-metapac/stm32h723vg" ] stm32h723ze = [ "stm32-metapac/stm32h723ze" ] @@ -1316,6 +1347,22 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] stm32l562re = [ "stm32-metapac/stm32l562re" ] stm32l562ve = [ "stm32-metapac/stm32l562ve" ] stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u535cb = [ "stm32-metapac/stm32u535cb" ] +stm32u535cc = [ "stm32-metapac/stm32u535cc" ] +stm32u535ce = [ "stm32-metapac/stm32u535ce" ] +stm32u535je = [ "stm32-metapac/stm32u535je" ] +stm32u535nc = [ "stm32-metapac/stm32u535nc" ] +stm32u535ne = [ "stm32-metapac/stm32u535ne" ] +stm32u535rb = [ "stm32-metapac/stm32u535rb" ] +stm32u535rc = [ "stm32-metapac/stm32u535rc" ] +stm32u535re = [ "stm32-metapac/stm32u535re" ] +stm32u535vc = [ "stm32-metapac/stm32u535vc" ] +stm32u535ve = [ "stm32-metapac/stm32u535ve" ] +stm32u545ce = [ "stm32-metapac/stm32u545ce" ] +stm32u545je = [ "stm32-metapac/stm32u545je" ] +stm32u545ne = [ "stm32-metapac/stm32u545ne" ] +stm32u545re = [ "stm32-metapac/stm32u545re" ] +stm32u545ve = [ "stm32-metapac/stm32u545ve" ] stm32u575ag = [ "stm32-metapac/stm32u575ag" ] stm32u575ai = [ "stm32-metapac/stm32u575ai" ] stm32u575cg = [ "stm32-metapac/stm32u575cg" ] @@ -1337,6 +1384,32 @@ stm32u585qi = [ "stm32-metapac/stm32u585qi" ] stm32u585ri = [ "stm32-metapac/stm32u585ri" ] stm32u585vi = [ "stm32-metapac/stm32u585vi" ] stm32u585zi = [ "stm32-metapac/stm32u585zi" ] +stm32u595ai = [ "stm32-metapac/stm32u595ai" ] +stm32u595aj = [ "stm32-metapac/stm32u595aj" ] +stm32u595qi = [ "stm32-metapac/stm32u595qi" ] +stm32u595qj = [ "stm32-metapac/stm32u595qj" ] +stm32u595ri = [ "stm32-metapac/stm32u595ri" ] +stm32u595rj = [ "stm32-metapac/stm32u595rj" ] +stm32u595vi = [ "stm32-metapac/stm32u595vi" ] +stm32u595vj = [ "stm32-metapac/stm32u595vj" ] +stm32u595zi = [ "stm32-metapac/stm32u595zi" ] +stm32u595zj = [ "stm32-metapac/stm32u595zj" ] +stm32u599bj = [ "stm32-metapac/stm32u599bj" ] +stm32u599ni = [ "stm32-metapac/stm32u599ni" ] +stm32u599nj = [ "stm32-metapac/stm32u599nj" ] +stm32u599vi = [ "stm32-metapac/stm32u599vi" ] +stm32u599vj = [ "stm32-metapac/stm32u599vj" ] +stm32u599zi = [ "stm32-metapac/stm32u599zi" ] +stm32u599zj = [ "stm32-metapac/stm32u599zj" ] +stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] +stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] +stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] +stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] +stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] +stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] +stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] +stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 61aceed9..b01e8ba4 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -50,7 +50,7 @@ fn main() { // We *shouldn't* have singletons for these, but the HAL currently requires // singletons, for using with RccPeripheral to enable/disable clocks to them. "rcc" => { - if r.version.starts_with("h7") || r.version.starts_with("f4") { + if r.version.starts_with("h5") || r.version.starts_with("h7") || r.version.starts_with("f4") { singletons.push("MCO1".to_string()); singletons.push("MCO2".to_string()); } @@ -539,7 +539,10 @@ fn main() { // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now - if regs.version.starts_with("h7") || regs.version.starts_with("f4") { + if regs.version.starts_with("h5") + || regs.version.starts_with("h7") + || regs.version.starts_with("f4") + { peri = format_ident!("{}", pin.signal.replace("_", "")); } else { continue; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index fcb4a296..d49b1f76 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -9,7 +9,7 @@ pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; -use crate::pac::{ETH, RCC, SYSCFG}; +use crate::pac::ETH; use crate::Peripheral; const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet @@ -60,16 +60,33 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { unsafe { // Enable the necessary Clocks // NOTE(unsafe) We have exclusive access to the registers + #[cfg(not(rcc_h5))] critical_section::with(|_| { - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { + crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + crate::pac::RCC.ahb1enr().modify(|w| { w.set_eth1macen(true); w.set_eth1txen(true); w.set_eth1rxen(true); }); // RMII - SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + }); + + #[cfg(rcc_h5)] + critical_section::with(|_| { + crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true)); + + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + + // RMII + crate::pac::SBS + .pmcr() + .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4)); }); config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index e1ce09a4..10109e56 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] +#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] +#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio { } pub unsafe fn on_irq() { - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // Mask all the channels that fired. @@ -53,9 +53,9 @@ pub unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -213,9 +213,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); @@ -364,7 +364,7 @@ pub(crate) unsafe fn init() { foreach_exti_irq!(enable_irq); - #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1)))] + #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1, exti_h5, exti_h50)))] ::enable(); #[cfg(stm32f1)] ::enable(); diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs new file mode 100644 index 00000000..17fbc605 --- /dev/null +++ b/embassy-stm32/src/rcc/h5.rs @@ -0,0 +1,606 @@ +use core::marker::PhantomData; + +use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; + +use crate::pac::pwr::vals::Vos; +use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; +use crate::pac::{FLASH, PWR, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; +use crate::{peripherals, Peripheral}; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(64_000_000); + +/// CSI speed +pub const CSI_FREQ: Hertz = Hertz(4_000_000); + +/// HSI48 speed +pub const HSI48_FREQ: Hertz = Hertz(48_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +const VCO_MIN: u32 = 150_000_000; +const VCO_MAX: u32 = 420_000_000; +const VCO_WIDE_MIN: u32 = 128_000_000; +const VCO_WIDE_MAX: u32 = 560_000_000; + +/// Voltage Scale +/// +/// Represents the voltage range feeding the CPU core. The maximum core +/// clock frequency depends on this value. +#[derive(Copy, Clone, PartialEq)] +pub enum VoltageScale { + /// VOS 0 range VCORE 1.30V - 1.40V + Scale0, + /// VOS 1 range VCORE 1.15V - 1.26V + Scale1, + /// VOS 2 range VCORE 1.05V - 1.15V + Scale2, + /// VOS 3 range VCORE 0.95V - 1.05V + Scale3, +} + +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) + BypassAnalog, + /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) + BypassDigital, +} + +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +pub enum Hsi { + /// 64Mhz + Mhz64, + /// 32Mhz (divided by 2) + Mhz32, + /// 16Mhz (divided by 4) + Mhz16, + /// 8Mhz (divided by 8) + Mhz8, +} + +pub enum Sysclk { + /// HSI selected as sysclk + HSI, + /// HSE selected as sysclk + HSE, + /// CSI selected as sysclk + CSI, + /// PLL1_P selected as sysclk + Pll1P, +} + +pub enum PllSource { + Hsi, + Csi, + Hse, +} + +pub struct Pll { + /// Source clock selection. + pub source: PllSource, + + /// PLL pre-divider (DIVM). Must be between 1 and 63. + pub prediv: u8, + + /// PLL multiplication factor. Must be between 4 and 512. + pub mul: u16, + + /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. + /// On PLL1, it must be even (in particular, it cannot be 1.) + pub divp: Option, + /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. + pub divr: Option, +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div64, + Div128, + Div256, + Div512, +} + +impl AHBPrescaler { + fn div(&self, clk: Hertz) -> Hertz { + match self { + Self::NotDivided => clk, + Self::Div2 => clk / 2u32, + Self::Div4 => clk / 4u32, + Self::Div8 => clk / 8u32, + Self::Div16 => clk / 16u32, + Self::Div64 => clk / 64u32, + Self::Div128 => clk / 128u32, + Self::Div256 => clk / 256u32, + Self::Div512 => clk / 512u32, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl APBPrescaler { + fn div(&self, clk: Hertz) -> Hertz { + match self { + Self::NotDivided => clk, + Self::Div2 => clk / 2u32, + Self::Div4 => clk / 4u32, + Self::Div8 => clk / 8u32, + Self::Div16 => clk / 16u32, + } + } + + fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz { + match (tim, self) { + // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a + // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 + (TimerPrescaler::DefaultX2, Self::NotDivided) => clk, + (TimerPrescaler::DefaultX2, Self::Div2) => clk, + (TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32, + (TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32, + (TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32, + // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2 + // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2 + // this makes NO SENSE and is different than in the H7. Mistake in the RM?? + (TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32, + (TimerPrescaler::DefaultX4, Self::Div2) => clk, + (TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32, + (TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32, + (TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum TimerPrescaler { + DefaultX2, + DefaultX4, +} + +impl From for Timpre { + fn from(value: TimerPrescaler) -> Self { + match value { + TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, + TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, + } + } +} + +impl From for Ppre { + fn from(val: APBPrescaler) -> Ppre { + match val { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl From for Hpre { + fn from(val: AHBPrescaler) -> Hpre { + match val { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +/// Configuration of the core clocks +#[non_exhaustive] +pub struct Config { + pub hsi: Option, + pub hse: Option, + pub csi: bool, + pub hsi48: bool, + pub sys: Sysclk, + + pub pll1: Option, + pub pll2: Option, + #[cfg(rcc_h5)] + pub pll3: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + pub apb3_pre: APBPrescaler, + pub timer_prescaler: TimerPrescaler, + + pub voltage_scale: VoltageScale, +} + +impl Default for Config { + fn default() -> Self { + Self { + hsi: Some(Hsi::Mhz64), + hse: None, + csi: false, + hsi48: false, + sys: Sysclk::HSI, + pll1: None, + pll2: None, + #[cfg(rcc_h5)] + pll3: None, + + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + apb3_pre: APBPrescaler::NotDivided, + timer_prescaler: TimerPrescaler::DefaultX2, + + voltage_scale: VoltageScale::Scale3, + } + } +} + +pub(crate) mod sealed { + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +macro_rules! impl_peri { + ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { + impl sealed::McoInstance for peripherals::$peri { + type Source = $source; + + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { + RCC.cfgr().modify(|w| { + w.$set_source(source); + w.$set_prescaler(prescaler); + }); + } + } + + impl McoInstance for peripherals::$peri {} + }; +} + +impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); +impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + _source: T::Source, + ) -> Self { + todo!(); + } +} + +pub(crate) unsafe fn init(config: Config) { + let (vos, max_clk) = match config.voltage_scale { + VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)), + VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)), + VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)), + VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)), + }; + + // Configure voltage scale. + PWR.voscr().modify(|w| w.set_vos(vos)); + while !PWR.vossr().read().vosrdy() {} + + // Configure HSI + let hsi = match config.hsi { + None => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + Some(hsi) => { + let (freq, hsidiv) = match hsi { + Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), + Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), + Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), + Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), + }; + RCC.cr().modify(|w| { + w.set_hsidiv(hsidiv); + w.set_hsion(true); + }); + while !RCC.cr().read().hsirdy() {} + Some(freq) + } + }; + + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + let (byp, ext) = match hse.mode { + HseMode::Oscillator => (false, Hseext::ANALOG), + HseMode::BypassAnalog => (true, Hseext::ANALOG), + HseMode::BypassDigital => (true, Hseext::DIGITAL), + }; + + RCC.cr().modify(|w| { + w.set_hsebyp(byp); + w.set_hseext(ext); + }); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + // Configure HSI48. + RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); + let _hsi48 = match config.hsi48 { + false => None, + true => { + while !RCC.cr().read().hsi48rdy() {} + Some(CSI_FREQ) + } + }; + + // Configure CSI. + RCC.cr().modify(|w| w.set_csion(config.csi)); + let csi = match config.csi { + false => None, + true => { + while !RCC.cr().read().csirdy() {} + Some(CSI_FREQ) + } + }; + + // Configure PLLs. + let pll_input = PllInput { csi, hse, hsi }; + let pll1 = init_pll(0, config.pll1, &pll_input); + let _pll2 = init_pll(1, config.pll2, &pll_input); + #[cfg(rcc_h5)] + let _pll3 = init_pll(2, config.pll3, &pll_input); + + // Configure sysclk + let (sys, sw) = match config.sys { + Sysclk::HSI => (unwrap!(hsi), Sw::HSI), + Sysclk::HSE => (unwrap!(hse), Sw::HSE), + Sysclk::CSI => (unwrap!(csi), Sw::CSI), + Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), + }; + assert!(sys <= max_clk); + + let hclk = config.ahb_pre.div(sys); + + let apb1 = config.apb1_pre.div(hclk); + let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); + let apb2 = config.apb2_pre.div(hclk); + let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); + let apb3 = config.apb3_pre.div(hclk); + + flash_setup(hclk, config.voltage_scale); + + // Set hpre + let hpre = config.ahb_pre.into(); + RCC.cfgr2().modify(|w| w.set_hpre(hpre)); + while RCC.cfgr2().read().hpre() != hpre {} + + // set ppre + RCC.cfgr2().modify(|w| { + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + w.set_ppre3(config.apb3_pre.into()); + }); + + RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); + + RCC.cfgr().modify(|w| w.set_sw(sw)); + while RCC.cfgr().read().sws() != sw {} + + set_freqs(Clocks { + sys, + ahb1: hclk, + ahb2: hclk, + ahb3: hclk, + ahb4: hclk, + apb1, + apb2, + apb3, + apb1_tim, + apb2_tim, + adc: None, + }); +} + +struct PllInput { + hsi: Option, + hse: Option, + csi: Option, +} + +struct PllOutput { + p: Option, + #[allow(dead_code)] + q: Option, + #[allow(dead_code)] + r: Option, +} + +unsafe fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { + let Some(config) = config else { + // Stop PLL + RCC.cr().modify(|w| w.set_pllon(num, false)); + while RCC.cr().read().pllrdy(num) {} + + // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" + RCC.pllcfgr(num).write(|w| { + w.set_divm(0); + }); + + return PllOutput{ + p: None, + q: None, + r: None, + } + }; + + assert!(1 <= config.prediv && config.prediv <= 63); + assert!(4 <= config.mul && config.mul <= 512); + + let (in_clk, src) = match config.source { + PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), + PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), + PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), + }; + + let ref_clk = in_clk / config.prediv as u32; + + let ref_range = match ref_clk.0 { + ..=1_999_999 => Pllrge::RANGE1, + ..=3_999_999 => Pllrge::RANGE2, + ..=7_999_999 => Pllrge::RANGE4, + ..=16_000_000 => Pllrge::RANGE8, + x => panic!("pll ref_clk out of range: {} mhz", x), + }; + + // The smaller range (150 to 420 MHz) must + // be chosen when the reference clock frequency is lower than 2 MHz. + let wide_allowed = ref_range != Pllrge::RANGE1; + + let vco_clk = ref_clk * config.mul; + let vco_range = match vco_clk.0 { + VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO, + VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO, + x => panic!("pll vco_clk out of range: {} mhz", x), + }; + + let p = config.divp.map(|div| { + assert!(1 <= div && div <= 128); + if num == 0 { + // on PLL1, DIVP must be even. + assert!(div % 2 == 0); + } + + vco_clk / div + }); + let q = config.divq.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + let r = config.divr.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + + RCC.pllcfgr(num).write(|w| { + w.set_pllsrc(src); + w.set_divm(config.prediv); + w.set_pllvcosel(vco_range); + w.set_pllrge(ref_range); + w.set_pllfracen(false); + w.set_pllpen(p.is_some()); + w.set_pllqen(q.is_some()); + w.set_pllren(r.is_some()); + }); + RCC.plldivr(num).write(|w| { + w.set_plln(config.mul - 1); + w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); + w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); + w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); + }); + + RCC.cr().modify(|w| w.set_pllon(num, true)); + while !RCC.cr().read().pllrdy(num) {} + + PllOutput { p, q, r } +} + +fn flash_setup(clk: Hertz, vos: VoltageScale) { + // RM0481 Rev 1, table 37 + // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 + // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz + // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz + // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz + // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz + // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz + // 5 2 170 to 200 MHz 210 to 250 MHz + + // See RM0433 Rev 7 Table 17. FLASH recommended number of wait + // states and programming delay + let (latency, wrhighfreq) = match (vos, clk.0) { + (VoltageScale::Scale0, ..=42_000_000) => (0, 0), + (VoltageScale::Scale0, ..=84_000_000) => (1, 0), + (VoltageScale::Scale0, ..=126_000_000) => (2, 1), + (VoltageScale::Scale0, ..=168_000_000) => (3, 1), + (VoltageScale::Scale0, ..=210_000_000) => (4, 2), + (VoltageScale::Scale0, ..=250_000_000) => (5, 2), + + (VoltageScale::Scale1, ..=34_000_000) => (0, 0), + (VoltageScale::Scale1, ..=68_000_000) => (1, 0), + (VoltageScale::Scale1, ..=102_000_000) => (2, 1), + (VoltageScale::Scale1, ..=136_000_000) => (3, 1), + (VoltageScale::Scale1, ..=170_000_000) => (4, 2), + (VoltageScale::Scale1, ..=200_000_000) => (5, 2), + + (VoltageScale::Scale2, ..=30_000_000) => (0, 0), + (VoltageScale::Scale2, ..=60_000_000) => (1, 0), + (VoltageScale::Scale2, ..=90_000_000) => (2, 1), + (VoltageScale::Scale2, ..=120_000_000) => (3, 1), + (VoltageScale::Scale2, ..=150_000_000) => (4, 2), + + (VoltageScale::Scale3, ..=20_000_000) => (0, 0), + (VoltageScale::Scale3, ..=40_000_000) => (1, 0), + (VoltageScale::Scale3, ..=60_000_000) => (2, 1), + (VoltageScale::Scale3, ..=80_000_000) => (3, 1), + (VoltageScale::Scale3, ..=100_000_000) => (4, 2), + + _ => unreachable!(), + }; + + defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); + + // NOTE(unsafe) Atomic write + unsafe { + FLASH.acr().write(|w| { + w.set_wrhighfreq(wrhighfreq); + w.set_latency(latency); + }); + while FLASH.acr().read().latency() != latency {} + } +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d4bd3d6b..d6a31f17 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -21,6 +21,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")] #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] +#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; @@ -36,7 +37,7 @@ pub struct Clocks { pub apb2: Hertz, #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, - #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] + #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] pub apb3: Hertz, #[cfg(any(rcc_h7, rcc_h7ab))] pub apb4: Hertz, @@ -44,14 +45,16 @@ pub struct Clocks { // AHB pub ahb1: Hertz, #[cfg(any( - rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5, rcc_wle + rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, + rcc_wl5, rcc_wle ))] pub ahb2: Hertz, #[cfg(any( - rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, rcc_wle + rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, + rcc_wle ))] pub ahb3: Hertz, - #[cfg(any(rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub ahb4: Hertz, #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] @@ -60,7 +63,7 @@ pub struct Clocks { #[cfg(stm32f1)] pub adc: Hertz, - #[cfg(any(rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, } diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 975517a4..f08abe33 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -1,7 +1,9 @@ //! Time units +use core::ops::{Div, Mul}; + /// Hertz -#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Hertz(pub u32); @@ -33,3 +35,45 @@ pub fn khz(kilohertz: u32) -> Hertz { pub fn mhz(megahertz: u32) -> Hertz { Hertz::mhz(megahertz) } + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u32) -> Self::Output { + Hertz(self.0 * rhs) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u32) -> Self::Output { + Hertz(self.0 / rhs) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u16) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u16) -> Self::Output { + self / (rhs as u32) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u8) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u8) -> Self::Output { + self / (rhs as u32) + } +} diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e6ee3954..ad68eaba 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -186,6 +186,11 @@ impl<'d, T: Instance> Driver<'d, T> { crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); } + #[cfg(pwr_h5)] + unsafe { + crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)) + } + unsafe { ::enable(); ::reset(); diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml new file mode 100644 index 00000000..c8b864b6 --- /dev/null +++ b/examples/stm32h5/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs-cli run --chip STM32H563ZITx' + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml new file mode 100644 index 00000000..70702863 --- /dev/null +++ b/examples/stm32h5/Cargo.toml @@ -0,0 +1,71 @@ +[package] +edition = "2021" +name = "embassy-stm32h7-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } +embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-nal-async = "0.4.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } +rand_core = "0.6.3" +critical-section = "1.1" +micromath = "2.0.0" +stm32-fmc = "0.2.4" +embedded-storage = "0.3.0" +static_cell = "1.0" + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h5/build.rs b/examples/stm32h5/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32h5/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x new file mode 100644 index 00000000..45606150 --- /dev/null +++ b/examples/stm32h5/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 + RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 +} diff --git a/examples/stm32h5/src/bin/blinky.rs b/examples/stm32h5/src/bin/blinky.rs new file mode 100644 index 00000000..f9bf90d2 --- /dev/null +++ b/examples/stm32h5/src/bin/blinky.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PB0, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs new file mode 100644 index 00000000..dfe587d4 --- /dev/null +++ b/examples/stm32h5/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let button = Input::new(p.PC13, Pull::Down); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + + loop { + button.wait_for_rising_edge().await; + info!("Pressed!"); + button.wait_for_falling_edge().await; + info!("Released!"); + } +} diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs new file mode 100644 index 00000000..6d650da9 --- /dev/null +++ b/examples/stm32h5/src/bin/eth.rs @@ -0,0 +1,133 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write; +use rand_core::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.hsi = None; + config.rcc.hsi48 = true; // needed for rng + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::BypassDigital, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::Hse, + prediv: 2, + mul: 125, + divp: Some(2), + divq: Some(2), + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::NotDivided; + config.rcc.apb1_pre = APBPrescaler::NotDivided; + config.rcc.apb2_pre = APBPrescaler::NotDivided; + config.rcc.apb3_pre = APBPrescaler::NotDivided; + config.rcc.sys = Sysclk::Pll1P; + config.rcc.voltage_scale = VoltageScale::Scale0; + let p = embassy_stm32::init(config); + info!("Hello World!"); + + // Generate random seed. + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + rng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + let eth_int = interrupt::take!(ETH); + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + let device = Ethernet::new( + singleton!(PacketQueue::<4, 4>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB15, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); + + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 1024]; + let mut tx_buffer = [0; 1024]; + + loop { + let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(3)).await; + continue; + } + info!("connected!"); + loop { + let r = socket.write_all(b"Hello\n").await; + if let Err(e) = r { + info!("write error: {:?}", e); + continue; + } + Timer::after(Duration::from_secs(1)).await; + } + } +} diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs new file mode 100644 index 00000000..6cbf58bb --- /dev/null +++ b/examples/stm32h5/src/bin/i2c.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.GPDMA1_CH4, + p.GPDMA1_CH5, + Hertz(100_000), + Default::default(), + ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs new file mode 100644 index 00000000..af9be0b6 --- /dev/null +++ b/examples/stm32h5/src/bin/rng.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rng::Rng; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut rng = Rng::new(p.RNG); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); +} diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs new file mode 100644 index 00000000..405f18ec --- /dev/null +++ b/examples/stm32h5/src/bin/usart.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::Executor; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn main_task() { + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.init(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task())); + }) +} diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs new file mode 100644 index 00000000..43d791aa --- /dev/null +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::fmt::Write; + +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::Executor; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use heapless::String; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn main_task() { + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, NoDma, config); + + for n in 0u32.. { + let mut s: String<128> = String::new(); + core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap(); + + usart.write(s.as_bytes()).await.ok(); + + info!("wrote DMA"); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.init(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task())); + }) +} diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs new file mode 100644 index 00000000..16a49958 --- /dev/null +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; +use embassy_stm32::usart::{Config, Uart, UartRx}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, p.GPDMA1_CH1, config); + unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); + + let (mut tx, rx) = usart.split(); + + unwrap!(spawner.spawn(reader(rx))); + + loop { + let buf = CHANNEL.recv().await; + info!("writing..."); + unwrap!(tx.write(&buf).await); + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + CHANNEL.send(buf).await; + } +} diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs new file mode 100644 index 00000000..6af269c1 --- /dev/null +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -0,0 +1,128 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; +use embassy_stm32::time::Hertz; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{interrupt, pac, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.rcc.hsi = None; + config.rcc.hsi48 = true; // needed for usb + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::BypassDigital, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::Hse, + prediv: 2, + mul: 125, + divp: Some(2), // 250mhz + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::Div2; + config.rcc.apb1_pre = APBPrescaler::Div4; + config.rcc.apb2_pre = APBPrescaler::Div2; + config.rcc.apb3_pre = APBPrescaler::Div4; + config.rcc.sys = Sysclk::Pll1P; + config.rcc.voltage_scale = VoltageScale::Scale0; + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + unsafe { + pac::RCC.ccipr4().write(|w| { + w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); + }); + } + + // Create the driver, from the HAL. + let irq = interrupt::take!(USB_DRD_FS); + let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatiblity. + // 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 builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // 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; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_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 e2516bba09fbb99da95939656e172156ad1924fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 22:39:27 +0200 Subject: [PATCH 099/112] executor: fix doc features. --- embassy-executor/Cargo.toml | 19 ++++++++----------- rust-toolchain.toml | 1 + 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index bb8a46c8..29e1bd47 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -14,21 +14,18 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt"] +features = ["nightly", "defmt", "pender-callback"] flavors = [ - { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, - { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, - { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, - { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, - { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, - { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, - { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, + { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, + { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, + { name = "cortex-m", target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] }, + { name = "riscv32", target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"] }, ] [package.metadata.docs.rs] -features = ["std", "nightly", "defmt"] +default-target = "thumbv7em-none-eabi" +targets = ["thumbv7em-none-eabi"] +features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] [features] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 22abacde..9785cd9e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,5 +9,6 @@ targets = [ "thumbv6m-none-eabi", "thumbv7em-none-eabihf", "thumbv8m.main-none-eabihf", + "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", ] From be37eee13dbd7833e0d74ea57d31d3e5c58cd47f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 22:25:24 +0200 Subject: [PATCH 100/112] Update embedded-hal crates. --- embassy-embedded-hal/Cargo.toml | 4 +- embassy-embedded-hal/src/adapter.rs | 27 ++- embassy-embedded-hal/src/lib.rs | 2 +- .../src/shared_bus/asynch/i2c.rs | 42 ++--- .../src/shared_bus/asynch/spi.rs | 175 +++++++++++++++--- .../src/shared_bus/blocking/i2c.rs | 56 ------ .../src/shared_bus/blocking/spi.rs | 157 +++++++++++++--- embassy-lora/Cargo.toml | 4 +- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/twim.rs | 49 ++--- embassy-rp/Cargo.toml | 6 +- embassy-rp/src/i2c.rs | 107 +++-------- embassy-rp/src/spi.rs | 1 + embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/i2c/timeout.rs | 75 +++----- embassy-stm32/src/i2c/v1.rs | 73 +++----- embassy-stm32/src/i2c/v2.rs | 158 +++++++--------- embassy-time/Cargo.toml | 4 +- embassy-time/src/delay.rs | 20 +- examples/rp/.cargo/config.toml | 2 +- examples/rp/Cargo.toml | 5 +- examples/rp/src/bin/spi_display.rs | 158 ++++------------ examples/stm32f1/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 4 +- examples/stm32h7/Cargo.toml | 4 +- examples/stm32l4/Cargo.toml | 4 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 4 +- 28 files changed, 543 insertions(+), 614 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 45eb0d43..c509d6ee 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -19,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index a49f8df4..ee919bd8 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -36,27 +36,22 @@ where E: embedded_hal_1::i2c::Error + 'static, T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Self::Error> { - self.wrapped.read(address, buffer) + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, read) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Self::Error> { - self.wrapped.write(address, bytes) + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, write) } - async fn write_read<'a>( - &'a mut self, + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.write_read(address, write, read) + } + + async fn transaction( + &mut self, address: u8, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Result<(), Self::Error> { - self.wrapped.write_read(address, bytes, buffer) - } - - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { let _ = address; let _ = operations; diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 8da04222..a23fbdc4 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr( feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections, try_blocks) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index c5e1fd41..82955404 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -54,35 +54,35 @@ where M: RawMutex + 'static, BUS: i2c::I2c + 'static, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + bus.read(address, read).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + bus.write(address, write).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], + write: &[u8], + read: &mut [u8], ) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.write_read(address, wr_buffer, rd_buffer) + bus.write_read(address, write, read) .await .map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + operations: &mut [embedded_hal_async::i2c::Operation<'_>], ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; @@ -121,25 +121,25 @@ where M: RawMutex + 'static, BUS: i2c::I2c + SetConfig + 'static, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; bus.set_config(&self.config); bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; bus.set_config(&self.config); bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], + wr_buffer: &[u8], + rd_buffer: &mut [u8], ) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; bus.set_config(&self.config); @@ -149,11 +149,7 @@ where Ok(()) } - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Result<(), I2cDeviceError> { + async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> { let _ = address; let _ = operations; todo!() diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index d2571665..b5549a6c 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -25,12 +25,11 @@ //! let spi_dev2 = SpiDevice::new(spi_bus, cs_pin2); //! let display2 = ST7735::new(spi_dev2, dc2, rst2, Default::default(), 160, 128); //! ``` -use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi::ErrorType; +use embedded_hal_1::spi::Operation; use embedded_hal_async::spi; use crate::shared_bus::SpiDeviceError; @@ -57,33 +56,92 @@ where type Error = SpiDeviceError; } -unsafe impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> where - M: RawMutex + 'static, - BUS: spi::SpiBusFlush + 'static, + M: RawMutex, + BUS: spi::SpiBusRead, CS: OutputPin, { - type Bus = BUS; - - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { + async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { let mut bus = self.bus.lock().await; self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.read(buf).await?; + } + }; // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush().await; let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + } +} + +impl spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBusWrite, + CS: OutputPin, +{ + async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.write(buf).await?; + } + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + } +} + +impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBus, + CS: OutputPin, +{ + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for op in operations { + match op { + Operation::Read(buf) => bus.read(buf).await?, + Operation::Write(buf) => bus.write(buf).await?, + Operation::Transfer(read, write) => bus.transfer(read, write).await?, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + } + } + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) } } @@ -114,33 +172,94 @@ where type Error = SpiDeviceError; } -unsafe impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> where - M: RawMutex + 'static, - BUS: spi::SpiBusFlush + SetConfig + 'static, + M: RawMutex, + BUS: spi::SpiBusWrite + SetConfig, CS: OutputPin, { - type Bus = BUS; - - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { + async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { let mut bus = self.bus.lock().await; bus.set_config(&self.config); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.write(buf).await?; + } + }; // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush().await; let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + } +} + +impl spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBusRead + SetConfig, + CS: OutputPin, +{ + async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.read(buf).await?; + } + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + } +} + +impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBus + SetConfig, + CS: OutputPin, +{ + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for op in operations { + match op { + Operation::Read(buf) => bus.read(buf).await?, + Operation::Write(buf) => bus.write(buf).await?, + Operation::Transfer(read, write) => bus.transfer(read, write).await?, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + } + } + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) } } diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index 892000b2..1fe520e6 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -72,34 +72,6 @@ where let _ = operations; todo!() } - - fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - todo!() - } - - fn write_iter_read>( - &mut self, - addr: u8, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - let _ = buffer; - todo!() - } - - fn transaction_iter<'a, O: IntoIterator>>( - &mut self, - address: u8, - operations: O, - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() - } } impl<'a, M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS> @@ -204,32 +176,4 @@ where let _ = operations; todo!() } - - fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - todo!() - } - - fn write_iter_read>( - &mut self, - addr: u8, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - let _ = buffer; - todo!() - } - - fn transaction_iter<'a, O: IntoIterator>>( - &mut self, - address: u8, - operations: O, - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() - } } diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 4a08dc36..7982ffb6 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -23,8 +23,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi; -use embedded_hal_1::spi::SpiBusFlush; +use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite}; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -50,30 +49,85 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBusFlush, + BUS: SpiBusRead, CS: OutputPin, { - type Bus = BUS; - - fn transaction(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result) -> Result { + fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut bus); + let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) }) } } @@ -89,11 +143,11 @@ where self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = bus.transfer(words); + let op_res = bus.transfer(words); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) }) } } @@ -110,11 +164,11 @@ where self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = bus.write(words); + let op_res = bus.write(words); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) }) } } @@ -146,30 +200,85 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBusFlush + SetConfig, + BUS: SpiBusRead + SetConfig, CS: OutputPin, { - type Bus = BUS; - - fn transaction(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result) -> Result { + fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); bus.set_config(&self.config); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut bus); + let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBusWrite + SetConfig, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBus + SetConfig, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + Ok(op_res) }) } } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index cbe78e59..c9174ea8 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -31,8 +31,8 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 4e62ca89..4a4e7c9f 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -87,8 +87,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ef4c929a..9ae56960 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -846,20 +846,6 @@ mod eh1 { self.blocking_write(address, buffer) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_write_read(address, wr_buffer, rd_buffer) } @@ -871,13 +857,6 @@ mod eh1 { ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } @@ -885,28 +864,22 @@ mod eh1 { mod eha { use super::*; impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Error> { - self.read(address, buffer).await + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Error> { - self.write(address, bytes).await + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.write(address, write).await + } + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address, write, read).await } - async fn write_read<'a>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], - ) -> Result<(), Error> { - self.write_read(address, wr_buffer, rd_buffer).await - } - - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Result<(), Error> { + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; todo!() diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 209c665b..cb9c7be7 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -65,9 +65,9 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" pio-proc = {version= "0.2", optional = true} diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index e48e16d8..40e85c66 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -490,14 +490,14 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } } - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { + fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if read.is_empty() { return Err(Error::InvalidReadBufferLength); } let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { + let lastindex = read.len() - 1; + for (i, byte) in read.iter_mut().enumerate() { let first = i == 0; let last = i == lastindex; @@ -524,15 +524,15 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { Ok(()) } - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { + fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> { + if write.is_empty() { return Err(Error::InvalidWriteBufferLength); } let p = T::regs(); - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; + for (i, byte) in write.iter().enumerate() { + let last = i == write.len() - 1; // NOTE(unsafe) We have &mut self unsafe { @@ -572,21 +572,21 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { // Blocking public API // ========================= - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) + self.read_blocking_internal(read, true, true) // Automatic Stop } - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) + self.write_blocking_internal(write, true) } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) + self.write_blocking_internal(write, false)?; + self.read_blocking_internal(read, true, true) // Automatic Stop } } @@ -644,48 +644,22 @@ mod eh1 { } impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - let mut peekable = bytes.into_iter().peekable(); - Self::setup(address.into())?; - - while let Some(tx) = peekable.next() { - self.write_blocking_internal(&[tx], peekable.peek().is_none())?; - } - Ok(()) + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - let peekable = bytes.into_iter().peekable(); - Self::setup(address.into())?; - - for tx in peekable { - self.write_blocking_internal(&[tx], false)? - } - self.read_blocking_internal(buffer, true, true) - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'a>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { Self::setup(address.into())?; for i in 0..operations.len() { @@ -697,22 +671,6 @@ mod eh1 { } Ok(()) } - - fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - Self::setup(address.into())?; - let mut peekable = operations.into_iter().peekable(); - while let Some(operation) = peekable.next() { - let last = peekable.peek().is_none(); - match operation { - embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, - embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, - } - } - Ok(()) - } } } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] @@ -727,36 +685,29 @@ mod nightly { A: AddressMode + Into + 'static, T: Instance + 'd, { - async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.read_async_internal(read, false, true).await } - async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error> { + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.write_async_internal(write.iter().copied(), true).await } - async fn write_read<'a>( - &'a mut self, - address: A, - write: &'a [u8], - read: &'a mut [u8], - ) -> Result<(), Self::Error> { + + async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.write_async_internal(write.iter().cloned(), false).await?; self.read_async_internal(read, false, true).await } - async fn transaction<'a, 'b>( - &'a mut self, - address: A, - operations: &'a mut [Operation<'b>], - ) -> Result<(), Self::Error> { + + async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { let addr: u16 = address.into(); let mut iterator = operations.iter_mut(); diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index ebd621ec..742a35d4 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -19,6 +19,7 @@ pub enum Error { } #[non_exhaustive] +#[derive(Clone)] pub struct Config { pub frequency: u32, pub phase: Phase, diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3fd1e4b4..6710ff2d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,9 +44,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 4fca1ca2..939e2750 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -28,64 +28,64 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { } /// Blocking read with a custom timeout - pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { - self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) + pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout)) } /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, buffer, self.timeout) + pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, read, self.timeout) } /// Blocking write with a custom timeout - pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { - self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) + pub fn blocking_write_timeout(&mut self, addr: u8, write: &[u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_write_timeout(addr, write, timeout_fn(timeout)) } /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(addr, bytes, self.timeout) + pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, write, self.timeout) } /// Blocking write-read with a custom timeout pub fn blocking_write_read_timeout( &mut self, addr: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], timeout: Duration, ) -> Result<(), Error> { self.i2c - .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) + .blocking_write_read_timeout(addr, write, read, timeout_fn(timeout)) } /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) + pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, write, read, self.timeout) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(addr, buffer) + fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, read) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(addr, bytes) + fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, write) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(addr, bytes, buffer) + fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, write, read) } } @@ -98,45 +98,24 @@ mod eh1 { } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f140e2b0..4b47f0eb 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -307,18 +307,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, buffer, || Ok(())) + pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, read, || Ok(())) } pub fn blocking_write_timeout( &mut self, addr: u8, - bytes: &[u8], + write: &[u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { unsafe { - self.write_bytes(addr, bytes, &check_timeout)?; + self.write_bytes(addr, write, &check_timeout)?; // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); // Wait for STOP condition to transmit. @@ -331,49 +331,49 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(addr, bytes, || Ok(())) + pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, write, || Ok(())) } pub fn blocking_write_read_timeout( &mut self, addr: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; - self.blocking_read_timeout(addr, buffer, &check_timeout)?; + unsafe { self.write_bytes(addr, write, &check_timeout)? }; + self.blocking_read_timeout(addr, read, &check_timeout)?; Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) + pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, write, read, || Ok(())) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { type Error = Error; - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(addr, buffer) + fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, read) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { type Error = Error; - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(addr, bytes) + fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, write) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { type Error = Error; - fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(addr, bytes, buffer) + fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, write, read) } } @@ -402,46 +402,25 @@ mod eh1 { } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 06ff07b2..28663fb3 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -345,12 +345,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn read_internal( &mut self, address: u8, - buffer: &mut [u8], + read: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - let completed_chunks = buffer.len() / 255; - let total_chunks = if completed_chunks * 255 == buffer.len() { + let completed_chunks = read.len() / 255; + let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks } else { completed_chunks + 1 @@ -360,7 +360,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { unsafe { Self::master_read( address, - buffer.len().min(255), + read.len().min(255), Stop::Automatic, last_chunk_idx != 0, restart, @@ -368,7 +368,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { )?; } - for (number, chunk) in buffer.chunks_mut(255).enumerate() { + for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { @@ -391,12 +391,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn write_internal( &mut self, address: u8, - bytes: &[u8], + write: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - let completed_chunks = bytes.len() / 255; - let total_chunks = if completed_chunks * 255 == bytes.len() { + let completed_chunks = write.len() / 255; + let total_chunks = if completed_chunks * 255 == write.len() { completed_chunks } else { completed_chunks + 1 @@ -410,14 +410,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { unsafe { Self::master_write( address, - bytes.len().min(255), + write.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout, )?; } - for (number, chunk) in bytes.chunks(255).enumerate() { + for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { @@ -448,7 +448,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { async fn write_dma_internal( &mut self, address: u8, - bytes: &[u8], + write: &[u8], first_slice: bool, last_slice: bool, check_timeout: impl Fn() -> Result<(), Error>, @@ -456,7 +456,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where TXDMA: crate::i2c::TxDma, { - let total_len = bytes.len(); + let total_len = write.len(); let completed_chunks = total_len / 255; let total_chunks = if completed_chunks * 255 == total_len { completed_chunks @@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.tx_dma; let request = ch.request(); - crate::dma::write(ch, request, bytes, dst) + crate::dma::write(ch, request, write, dst) }; let state = T::state(); @@ -641,25 +641,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, true, || Ok(())) + if write.is_empty() { + self.write_internal(address, write, true, || Ok(())) } else { - self.write_dma_internal(address, bytes, true, true, || Ok(())).await + self.write_dma_internal(address, write, true, true, || Ok(())).await } } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - if bytes.is_empty() { + if write.is_empty() { return Err(Error::ZeroLengthTransfer); } - let mut iter = bytes.iter(); + let mut iter = write.iter(); let mut first = true; let mut current = iter.next(); @@ -685,21 +685,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, false, || Ok(()))?; + if write.is_empty() { + self.write_internal(address, write, false, || Ok(()))?; } else { - self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; + self.write_dma_internal(address, write, true, true, || Ok(())).await?; } - if buffer.is_empty() { - self.read_internal(address, buffer, true, || Ok(()))?; + if read.is_empty() { + self.read_internal(address, read, true, || Ok(()))?; } else { - self.read_dma_internal(address, buffer, true, || Ok(())).await?; + self.read_dma_internal(address, read, true, || Ok(())).await?; } Ok(()) @@ -711,57 +711,57 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn blocking_read_timeout( &mut self, address: u8, - buffer: &mut [u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.read_internal(address, buffer, false, &check_timeout) + self.read_internal(address, read, false, &check_timeout) // Automatic Stop } - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(address, buffer, || Ok(())) + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(address, read, || Ok(())) } pub fn blocking_write_timeout( &mut self, address: u8, - bytes: &[u8], + write: &[u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.write_internal(address, bytes, true, &check_timeout) + self.write_internal(address, write, true, &check_timeout) } - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(address, bytes, || Ok(())) + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(address, write, || Ok(())) } pub fn blocking_write_read_timeout( &mut self, address: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.write_internal(address, bytes, false, &check_timeout)?; - self.read_internal(address, buffer, true, &check_timeout) + self.write_internal(address, write, false, &check_timeout)?; + self.read_internal(address, read, true, &check_timeout) // Automatic Stop } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(address, write, read, || Ok(())) } pub fn blocking_write_vectored_timeout( &mut self, address: u8, - bytes: &[&[u8]], + write: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - if bytes.is_empty() { + if write.is_empty() { return Err(Error::ZeroLengthTransfer); } - let first_length = bytes[0].len(); - let last_slice_index = bytes.len() - 1; + let first_length = write[0].len(); + let last_slice_index = write.len() - 1; // NOTE(unsafe) We have &mut self unsafe { @@ -774,7 +774,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { )?; } - for (idx, slice) in bytes.iter().enumerate() { + for (idx, slice) in write.iter().enumerate() { let slice_len = slice.len(); let completed_chunks = slice_len / 255; let total_chunks = if completed_chunks * 255 == slice_len { @@ -828,8 +828,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { - self.blocking_write_vectored_timeout(address, bytes, || Ok(())) + pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + self.blocking_write_vectored_timeout(address, write, || Ok(())) } } @@ -847,16 +847,16 @@ mod eh02 { impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { type Error = Error; - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, bytes) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { type Error = Error; - fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, bytes, buffer) + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } } } @@ -1010,46 +1010,25 @@ mod eh1 { } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } @@ -1059,27 +1038,22 @@ mod eha { use super::*; impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { - async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } - async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> { + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.write(address, write).await } - async fn write_read<'a>( - &'a mut self, - address: u8, - write: &'a [u8], - read: &'a mut [u8], - ) -> Result<(), Self::Error> { + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.write_read(address, write, read).await } - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { let _ = address; let _ = operations; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 5b14814a..38d31f1c 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-sync = { version = "0.1", path = "../embassy-sync" } diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index 0ca176ab..cf191872 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -19,14 +19,12 @@ mod eh1 { use super::*; impl embedded_hal_1::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_micros(us as u64))) + fn delay_us(&mut self, us: u32) { + block_for(Duration::from_micros(us as u64)) } - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_millis(ms as u64))) + fn delay_ms(&mut self, ms: u32) { + block_for(Duration::from_millis(ms as u64)) } } } @@ -37,14 +35,12 @@ mod eha { use crate::Timer; impl embedded_hal_async::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - async fn delay_us(&mut self, micros: u32) -> Result<(), Self::Error> { - Ok(Timer::after(Duration::from_micros(micros as _)).await) + async fn delay_us(&mut self, micros: u32) { + Timer::after(Duration::from_micros(micros as _)).await } - async fn delay_ms(&mut self, millis: u32) -> Result<(), Self::Error> { - Ok(Timer::after(Duration::from_millis(millis as _)).await) + async fn delay_ms(&mut self, millis: u32) { + Timer::after(Duration::from_millis(millis as _)).await } } } diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index d1c8c1c5..2ee6fcb0 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip RP2040" +runner = "probe-rs-cli run --chip RP2040" [build] target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index aea61eec..63d0ac82 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } @@ -30,8 +31,8 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = "0.2.0-alpha.0" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = "0.2.0-alpha.1" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 778cad3f..85a19ce0 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -5,10 +5,13 @@ use core::cell::RefCell; use defmt::*; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::{Blocking, Spi}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -21,10 +24,9 @@ use st7789::{Orientation, ST7789}; use {defmt_rtt as _, panic_probe as _}; use crate::my_display_interface::SPIDeviceInterface; -use crate::shared_spi::SpiDeviceWithCs; use crate::touch::Touch; -//const DISPLAY_FREQ: u32 = 64_000_000; +const DISPLAY_FREQ: u32 = 64_000_000; const TOUCH_FREQ: u32 = 200_000; #[embassy_executor::main] @@ -43,16 +45,20 @@ async fn main(_spawner: Spawner) { //let touch_irq = p.PIN_17; // create SPI - let mut config = spi::Config::default(); - config.frequency = TOUCH_FREQ; // use the lowest freq - config.phase = spi::Phase::CaptureOnSecondTransition; - config.polarity = spi::Polarity::IdleHigh; + let mut display_config = spi::Config::default(); + display_config.frequency = DISPLAY_FREQ; + display_config.phase = spi::Phase::CaptureOnSecondTransition; + display_config.polarity = spi::Polarity::IdleHigh; + let mut touch_config = spi::Config::default(); + touch_config.frequency = TOUCH_FREQ; + touch_config.phase = spi::Phase::CaptureOnSecondTransition; + touch_config.polarity = spi::Polarity::IdleHigh; - let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); - let spi_bus = RefCell::new(spi); + let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); + let spi_bus: Mutex = Mutex::new(RefCell::new(spi)); - let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); - let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); + let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); + let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); let mut touch = Touch::new(touch_spi); @@ -104,85 +110,9 @@ async fn main(_spawner: Spawner) { } } -mod shared_spi { - use core::cell::RefCell; - use core::fmt::Debug; - - use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi; - use embedded_hal_1::spi::SpiDevice; - - #[derive(Copy, Clone, Eq, PartialEq, Debug)] - pub enum SpiDeviceWithCsError { - #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus - Spi(BUS), - Cs(CS), - } - - impl spi::Error for SpiDeviceWithCsError - where - BUS: spi::Error + Debug, - CS: Debug, - { - fn kind(&self) -> spi::ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => spi::ErrorKind::Other, - } - } - } - - pub struct SpiDeviceWithCs<'a, BUS, CS> { - bus: &'a RefCell, - cs: CS, - } - - impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> { - pub fn new(bus: &'a RefCell, cs: CS) -> Self { - Self { bus, cs } - } - } - - impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS> - where - BUS: spi::ErrorType, - CS: OutputPin, - { - type Error = SpiDeviceWithCsError; - } - - impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> - where - BUS: spi::SpiBusFlush, - CS: OutputPin, - { - type Bus = BUS; - - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result, - ) -> Result { - let mut bus = self.bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?; - - let f_res = f(&mut bus); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?; - flush_res.map_err(SpiDeviceWithCsError::Spi)?; - cs_res.map_err(SpiDeviceWithCsError::Cs)?; - - Ok(f_res) - } - } -} - /// Driver for the XPT2046 resistive touchscreen sensor mod touch { - use embedded_hal_1::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::{Operation, SpiDevice}; struct Calibration { x1: i32, @@ -209,7 +139,6 @@ mod touch { impl Touch where SPI: SpiDevice, - SPI::Bus: SpiBus, { pub fn new(spi: SPI) -> Self { Self { spi } @@ -219,13 +148,12 @@ mod touch { let mut x = [0; 2]; let mut y = [0; 2]; self.spi - .transaction(|bus| { - bus.write(&[0x90])?; - bus.read(&mut x)?; - bus.write(&[0xd0])?; - bus.read(&mut y)?; - Ok(()) - }) + .transaction(&mut [ + Operation::Write(&[0x90]), + Operation::Read(&mut x), + Operation::Write(&[0xd0]), + Operation::Read(&mut y), + ]) .unwrap(); let x = (u16::from_be_bytes(x) >> 3) as i32; @@ -247,7 +175,7 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::{SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::SpiDeviceWrite; /// SPI display interface. /// @@ -259,8 +187,7 @@ mod my_display_interface { impl SPIDeviceInterface where - SPI: SpiDevice, - SPI::Bus: SpiBusWrite, + SPI: SpiDeviceWrite, DC: OutputPin, { /// Create new SPI interface for communciation with a display driver @@ -271,42 +198,27 @@ mod my_display_interface { impl WriteOnlyDataCommand for SPIDeviceInterface where - SPI: SpiDevice, - SPI::Bus: SpiBusWrite, + SPI: SpiDeviceWrite, DC: OutputPin, { fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - let r = self.spi.transaction(|bus| { - // 1 = data, 0 = command - if let Err(_) = self.dc.set_low() { - return Ok(Err(DisplayError::DCError)); - } + // 1 = data, 0 = command + self.dc.set_low().map_err(|_| DisplayError::DCError)?; - // Send words over SPI - send_u8(bus, cmds)?; - - Ok(Ok(())) - }); - r.map_err(|_| DisplayError::BusWriteError)? + send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; + Ok(()) } fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - let r = self.spi.transaction(|bus| { - // 1 = data, 0 = command - if let Err(_) = self.dc.set_high() { - return Ok(Err(DisplayError::DCError)); - } + // 1 = data, 0 = command + self.dc.set_high().map_err(|_| DisplayError::DCError)?; - // Send words over SPI - send_u8(bus, buf)?; - - Ok(Ok(())) - }); - r.map_err(|_| DisplayError::BusWriteError)? + send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; + Ok(()) } } - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { + fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { match words { DataFormat::U8(slice) => spi.write(slice), DataFormat::U16(slice) => { diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 387af783..99f37cdd 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 70702863..f240c389 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d0d6a949..154f5a98 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7c254eba..fa39df6d 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,8 +18,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index eb447be3..463a370f 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -17,8 +17,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 17b64079..6070a5a8 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -25,8 +25,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From dee1d51ad3089b03c67aaa75c7985cf95ce90eec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:20:59 +0200 Subject: [PATCH 101/112] stm32: remove subghz feature. It's available only on WL. if you're using a WL, you want subghz for sure. --- embassy-lora/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 1 - embassy-stm32/src/lib.rs | 5 ++--- examples/stm32wl/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index c9174ea8..784cc228 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -19,7 +19,7 @@ flavors = [ [features] sx126x = [] sx127x = [] -stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] +stm32wl = ["dep:embassy-stm32"] time = [] defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6710ff2d..36ee5801 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,6 @@ stm32-metapac = { version = "4", default-features = false, features = ["metadata default = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] -subghz = [] exti = [] # Enables additional driver features that depend on embassy-time diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3f2d078f..d4d7155b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -53,6 +53,8 @@ pub mod rng; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(stm32wl)] +pub mod subghz; #[cfg(usart)] pub mod usart; #[cfg(all(usb, feature = "time"))] @@ -62,9 +64,6 @@ pub mod usb_otg; #[cfg(iwdg)] pub mod wdg; -#[cfg(feature = "subghz")] -pub mod subghz; - // This must go last, so that it sees all the impl_foo! macros defined earlier. pub(crate) mod _generated { #![allow(dead_code)] diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 9fc7e0f4..0d2194ea 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } From 8469a2409c9ded4bb66040475f365c60b261b51f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:21:38 +0200 Subject: [PATCH 102/112] stm32/otg: add U5 support. --- embassy-stm32/src/usb_otg/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 84fef78c..193e0df0 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -89,6 +89,9 @@ foreach_interrupt!( } else if #[cfg(stm32h7)] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; + } else if #[cfg(stm32u5)] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 6; } else { compile_error!("USB_OTG_FS peripheral is not supported by this chip."); } @@ -137,6 +140,9 @@ foreach_interrupt!( ))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; + } else if #[cfg(stm32u5)] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; } else { compile_error!("USB_OTG_HS peripheral is not supported by this chip."); } From f38899728c8bc9c81c4ac02d8177ec6420c07f02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:22:10 +0200 Subject: [PATCH 103/112] stm32: add h5 flavor. --- embassy-stm32/Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 36ee5801..4ab30622 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,10 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -# TODO: sdmmc -# TODO: net -# TODO: subghz -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -22,6 +19,7 @@ flavors = [ { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, From 1fbb8f0b321a15fd6b473c466d41fac40302ec3c Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Sun, 9 Apr 2023 09:15:57 +0100 Subject: [PATCH 104/112] feat(rp): add `Wait` impl to `OutputOpenDrain` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A while ago `OutputOpenDrain` was made to implement `InputPin`, something that allowed drivers for various one-wire protocols to be written, but it's been lacking a `Wait` implementation — something that's needed to write async versions of these drivers. This commit also adds `get_level()` to `OutputOpenDrain`, since `is_high()` and `is_low()` were already implemented, but `get_level()` itself was missing. --- embassy-rp/src/gpio.rs | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index fb45ef7c..98e18286 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -437,6 +437,37 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { pub fn is_low(&self) -> bool { self.pin.is_low() } + + /// Returns current pin level + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + #[inline] + pub async fn wait_for_high(&mut self) { + self.pin.wait_for_high().await; + } + + #[inline] + pub async fn wait_for_low(&mut self) { + self.pin.wait_for_low().await; + } + + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + self.pin.wait_for_rising_edge().await; + } + + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + self.pin.wait_for_falling_edge().await; + } + + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await; + } } /// GPIO flexible pin. @@ -1117,4 +1148,32 @@ mod eh1 { Ok(()) } } + + #[cfg(feature = "nightly")] + impl<'d, T: Pin> embedded_hal_async::digital::Wait for OutputOpenDrain<'d, T> { + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } + } } From dee8c71a2d57f3015fde55da28758a35fcbf7ca6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 9 Apr 2023 22:40:09 +0200 Subject: [PATCH 105/112] lora: fix embassy docs build. --- embassy-lora/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 784cc228..604358c5 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,9 +9,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, - { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, + { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, + { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] }, + { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, ] [lib] From 44b7fe45e279f713a49f92e786f358af6c68e157 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:11:07 +0200 Subject: [PATCH 106/112] stm32/gpdma: fix race condition when resetting channel when done. --- embassy-stm32/src/dma/gpdma.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 442fee48..6f26fd19 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -190,6 +190,10 @@ mod low_level_api { fence(Ordering::SeqCst); let ch = dma.ch(channel_number as _); + + // Reset ch + ch.cr().write(|w| w.set_reset(true)); + ch.llr().write(|_| {}); // no linked list ch.tr1().write(|w| { w.set_sdw(data_size.into()); @@ -252,7 +256,7 @@ mod low_level_api { /// Gets the running status of the channel pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { let ch = dma.ch(ch as _); - !ch.sr().read().idlef() + !ch.sr().read().tcf() } /// Gets the total remaining transfers for the channel @@ -291,7 +295,10 @@ mod low_level_api { } if sr.suspf() || sr.tcf() { - ch.cr().write(|w| w.set_reset(true)); + // disable all xxIEs to prevent the irq from firing again. + ch.cr().write(|_| {}); + + // Wake the future. It'll look at tcf and see it's set. STATE.channels[state_index].waker.wake(); } } From 4ef8e008e8e052e5e2f84b36733284b19d246794 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:11:23 +0200 Subject: [PATCH 107/112] stm32/spi: add v4/v5 support (for H5). --- embassy-stm32/src/spi/mod.rs | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1f170887..481ea4ab 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -258,7 +258,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); T::REGS.cfg2().modify(|w| { @@ -317,7 +317,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.cfg2().modify(|w| { w.set_cpha(cpha); @@ -330,7 +330,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn get_current_config(&self) -> Config { #[cfg(any(spi_v1, spi_f1, spi_v2))] let cfg = unsafe { T::REGS.cr1().read() }; - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let cfg = unsafe { T::REGS.cfg2().read() }; let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow @@ -383,7 +383,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.cr1().modify(|w| { w.set_csusp(true); @@ -429,7 +429,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -459,7 +459,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(T::REGS); set_rxdmaen(T::REGS, true); @@ -481,7 +481,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -514,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(T::REGS); set_rxdmaen(T::REGS, true); @@ -534,7 +534,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -619,9 +619,9 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { } } -#[cfg(not(any(spi_v3, spi_v4)))] +#[cfg(not(any(spi_v3, spi_v4, spi_v5)))] use vals::Br; -#[cfg(any(spi_v3, spi_v4))] +#[cfg(any(spi_v3, spi_v4, spi_v5))] use vals::Mbr as Br; fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { @@ -647,17 +647,17 @@ trait RegsExt { impl RegsExt for Regs { fn tx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] let dr = self.dr(); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.txdr(); dr.ptr() as *mut W } fn rx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] let dr = self.dr(); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.rxdr(); dr.ptr() as *mut W } @@ -667,22 +667,22 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { if sr.ovr() { return Err(Error::Overrun); } - #[cfg(not(any(spi_f1, spi_v3, spi_v4)))] + #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] if sr.fre() { return Err(Error::Framing); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.tifre() { return Err(Error::Framing); } if sr.modf() { return Err(Error::ModeFault); } - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.crcerr() { return Err(Error::Crc); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.crce() { return Err(Error::Crc); } @@ -696,11 +696,11 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.txe() { return Ok(()); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.txp() { return Ok(()); } @@ -713,11 +713,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.rxne() { return Ok(()); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.rxp() { return Ok(()); } @@ -726,11 +726,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { fn flush_rx_fifo(regs: Regs) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().rxne() { let _ = regs.dr().read(); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] while regs.sr().read().rxp() { let _ = regs.rxdr().read(); } @@ -739,11 +739,11 @@ fn flush_rx_fifo(regs: Regs) { fn set_txdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_txdmaen(val); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_txdmaen(val); }); @@ -752,11 +752,11 @@ fn set_txdmaen(regs: Regs, val: bool) { fn set_rxdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_rxdmaen(val); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_rxdmaen(val); }); @@ -768,9 +768,9 @@ fn finish_dma(regs: Regs) { #[cfg(spi_v2)] while regs.sr().read().ftlvl() > 0 {} - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] while !regs.sr().read().txc() {} - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().bsy() {} // Disable the spi peripheral @@ -780,12 +780,12 @@ fn finish_dma(regs: Regs) { // The peripheral automatically disables the DMA stream on completion without error, // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); @@ -799,7 +799,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { unsafe { ptr::write_volatile(regs.tx_ptr(), tx_word); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cr1().modify(|reg| reg.set_cstart(true)); } @@ -970,7 +970,7 @@ pub(crate) mod sealed { } } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] pub fn dsize(&self) -> u8 { match self { WordSize::EightBit => 0b0111, @@ -978,7 +978,7 @@ pub(crate) mod sealed { } } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] pub fn _frxth(&self) -> vals::Fthlv { match self { WordSize::EightBit => vals::Fthlv::ONEFRAME, From dbfd28130f95a6f8df81823253ec71c1ae6d36e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:12:47 +0200 Subject: [PATCH 108/112] stm32/test: add h5 hil tests. --- ci.sh | 1 + tests/stm32/Cargo.toml | 1 + tests/stm32/src/bin/gpio.rs | 2 ++ tests/stm32/src/bin/spi.rs | 19 +++++++++++-------- tests/stm32/src/bin/spi_dma.rs | 18 ++++++++++-------- tests/stm32/src/bin/usart.rs | 2 ++ tests/stm32/src/bin/usart_dma.rs | 9 +++++++++ 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ci.sh b/ci.sh index 82b72ae3..47bf5d66 100755 --- a/ci.sh +++ b/ci.sh @@ -119,6 +119,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/nucleo-stm32h563zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 6070a5a8..bd181f23 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -11,6 +11,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo +stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 18fd85d4..6a36df8c 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -30,6 +30,8 @@ async fn main(_spawner: Spawner) { let (mut a, mut b) = (p.PB6, p.PB7); #[cfg(feature = "stm32u585ai")] let (mut a, mut b) = (p.PD9, p.PD8); + #[cfg(feature = "stm32h563zi")] + let (mut a, mut b) = (p.PB6, p.PB7); // Test initial output { diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 1c5dc87c..bf8098b1 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -17,22 +17,25 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); #[cfg(feature = "stm32f103c8")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32f429zi")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32h755zi")] - let (sck, mosi, miso) = (p.PA5, p.PB5, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PB5, p.PA6); #[cfg(feature = "stm32g491re")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32g071rb")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32wb55rg")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32u585ai")] - let (sck, mosi, miso) = (p.PE13, p.PE15, p.PE14); + let (spi, sck, mosi, miso) = (p.SPI1, p.PE13, p.PE15, p.PE14); + #[cfg(feature = "stm32h563zi")] + let (spi, sck, mosi, miso) = (p.SPI4, p.PE12, p.PE14, p.PE13); + info!("asdfa;"); let mut spi = Spi::new( - p.SPI1, + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index cb2152e0..b3dad813 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -16,22 +16,24 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); #[cfg(feature = "stm32f103c8")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); #[cfg(feature = "stm32h755zi")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32g491re")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32wb55rg")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32u585ai")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); + #[cfg(feature = "stm32h563zi")] + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI4, p.PE12, p.PE14, p.PE13, p.GPDMA1_CH0, p.GPDMA1_CH1); let mut spi = Spi::new( - p.SPI1, + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index af55867f..52409567 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -32,6 +32,8 @@ async fn main(_spawner: Spawner) { let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); + #[cfg(feature = "stm32h563zi")] + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index d12605a9..3f70791c 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -62,6 +62,15 @@ async fn main(_spawner: Spawner) { p.GPDMA1_CH0, p.GPDMA1_CH1, ); + #[cfg(feature = "stm32h563zi")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PB6, + p.PB7, + p.LPUART1, + interrupt::take!(LPUART1), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); From 6760258ec39c629b911a417d0a554bc6167c5b5b Mon Sep 17 00:00:00 2001 From: Glenn Dirkx Date: Mon, 10 Apr 2023 16:20:47 +0200 Subject: [PATCH 109/112] fix I2C controller problems after NACK --- embassy-stm32/src/i2c/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 28663fb3..7218f770 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -262,7 +262,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if T::regs().isr().read().txis() { T::regs().txdr().write(|w| w.set_txdata(0)); } - if T::regs().isr().read().txe() { + if !T::regs().isr().read().txe() { T::regs().isr().modify(|w| w.set_txe(true)) } } From cae683cd41261312e8a8da70c862832f0acb847c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 21:12:48 +0200 Subject: [PATCH 110/112] stm32: update pac. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4ab30622..a8ebacd2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "4" +stm32-metapac = "5" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -73,7 +73,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "4", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "5", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] From d3ce64254aecb24feded2408119855daf7380e80 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 11 Apr 2023 07:46:05 +0200 Subject: [PATCH 111/112] Let update_len be u32 --- embassy-boot/boot/src/firmware_updater.rs | 16 +++++++--------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 6aedec00..a2f822f4 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -114,11 +114,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len as u32 <= self.dfu.size()); + assert!(_update_len <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -175,11 +175,10 @@ impl FirmwareUpdater { pub async fn hash( &mut self, dfu_flash: &mut F, - update_len: usize, + update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read(dfu_flash, offset, chunk_buf).await?; @@ -335,11 +334,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len as u32 <= self.dfu.size()); + assert!(_update_len <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -394,11 +393,10 @@ impl FirmwareUpdater { pub fn hash_blocking( &mut self, dfu_flash: &mut F, - update_len: usize, + update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; @@ -529,7 +527,7 @@ mod tests { block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len(), &mut chunk_buf, &mut hash)).unwrap(); + block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); assert_eq!(Sha1::digest(update).as_slice(), hash); } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index ef9333d3..87457b17 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -255,7 +255,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len, + firmware_len as u32, &mut aligned, )) .is_ok()); From ab6179fb07f2655f96c0fd58aeaf09268fe60ef3 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Tue, 11 Apr 2023 12:03:08 +0200 Subject: [PATCH 112/112] Fix duplicate package name --- examples/stm32h5/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index f240c389..b77d376c 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-stm32h7-examples" +name = "embassy-stm32h5-examples" version = "0.1.0" license = "MIT OR Apache-2.0"