diff --git a/examples/flash.rs b/examples/flash.rs new file mode 100644 index 00000000..ffc72977 --- /dev/null +++ b/examples/flash.rs @@ -0,0 +1,56 @@ +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use hal::flash::FlashExt; +use hal::flash::FlashSize; +use hal::stm32; +use stm32g4xx_hal as hal; +extern crate cortex_m_rt as rt; + +#[macro_use] +mod utils; + +use utils::logger::println; + +#[entry] +fn main() -> ! { + utils::logger::init(); + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + + let mut flash = dp.FLASH.constrain(); + let is_dual_bank = flash.is_dual_bank(); + + println!("Is dual bank: {}", is_dual_bank); + + let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K); + + let mut words = [0; 1]; + flash_writer.read_exact(0x0000_0000, &mut words[..]); + println!("Bank 1 - first 4 bytes: {:#X}", words[0]); + + if is_dual_bank { + let address = 0x0004_0000; + flash_writer.read_exact(address, &mut words[..]); + println!("Bank 2 - First 4 bytes before write: {:#X}", words[0]); + + flash_writer.page_erase(address).unwrap(); + flash_writer.read_exact(address, &mut words[..]); + println!("Bank 2 - First 4 bytes after erase: {:#X}", words[0]); + + let new_value = words[0].wrapping_add(2); + println!("Bank 2 - Writing: {:#X} to first 4 bytes", new_value); + flash_writer + .write(address, &new_value.to_ne_bytes(), true) + .unwrap(); + flash_writer.read_exact(address, &mut words[..]); + println!("Bank 2 - First 4 bytes after write: {:#X}", words[0]); + } + + println!("Done!"); + + loop { + cortex_m::asm::nop() + } +} diff --git a/examples/flash_with_rtic.rs b/examples/flash_with_rtic.rs index 985726a3..f07cc28f 100644 --- a/examples/flash_with_rtic.rs +++ b/examples/flash_with_rtic.rs @@ -7,7 +7,7 @@ mod utils; -#[rtic::app(device = stm32g4xx_hal::stm32g4::stm32g474, peripherals = true)] +#[rtic::app(device = stm32g4xx_hal::stm32, peripherals = true)] mod app { use crate::utils::logger; use stm32g4xx_hal::flash::{FlashExt, FlashSize, FlashWriter, Parts}; @@ -25,18 +25,6 @@ mod app { #[local] struct Local {} - fn compare_arrays(a: &[u8], b: &[u8]) -> bool { - if a.len() != b.len() { - return false; - } - for i in 0..a.len() { - if a[i] != b[i] { - return false; - } - } - true - } - #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { crate::utils::logger::init(); @@ -45,48 +33,19 @@ mod app { let cp = cx.core; let rcc = dp.RCC.constrain(); - let mut pll_config = stm32g4xx_hal::rcc::PllConfig::default(); - - // Sysclock is based on PLL_R - pll_config.mux = stm32g4xx_hal::rcc::PLLSrc::HSI; // 16MHz - pll_config.n = stm32g4xx_hal::rcc::PllNMul::MUL_32; - pll_config.m = stm32g4xx_hal::rcc::PllMDiv::DIV_2; // f(vco) = 16MHz*32/2 = 256MHz - pll_config.r = Some(stm32g4xx_hal::rcc::PllRDiv::DIV_2); // f(sysclock) = 256MHz/2 = 128MHz - - // Note to future self: The AHB clock runs the timers, among other things. - // Please refer to the Clock Tree manual to determine if it is worth - // changing to a lower speed for battery life savings. - let mut clock_config = stm32g4xx_hal::rcc::Config::default() - .pll_cfg(pll_config) - .clock_src(stm32g4xx_hal::rcc::SysClockSrc::PLL); - - // After clock configuration, the following should be true: - // Sysclock is 128MHz - // AHB clock is 128MHz - // APB1 clock is 128MHz - // APB2 clock is 128MHz - // The ADC will ultimately be put into synchronous mode and will derive - // its clock from the AHB bus clock, with a prescalar of 2 or 4. - let pwr = dp.PWR.constrain().freeze(); - let mut rcc = rcc.freeze(clock_config, pwr); - - unsafe { - let mut flash = &(*stm32g4xx_hal::stm32::FLASH::ptr()); - flash.acr.modify(|_, w| { - w.latency().bits(0b1000) // 8 wait states - }); - } + let mut rcc = rcc.freeze(stm32g4xx_hal::rcc::Config::hsi(), pwr); + let mut delay = cp.SYST.delay(&rcc.clocks); // *** FLASH Memory *** - let one_byte = [0x12 as u8]; - let two_bytes = [0xAB, 0xCD as u8]; - let three_bytes = [0x12, 0x34, 0x56 as u8]; - let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA as u8]; - let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 as u8]; + let one_byte = [0x12]; + let two_bytes = [0xAB, 0xCD]; + let three_bytes = [0x12, 0x34, 0x56]; + let four_bytes = [0xAB, 0xCD, 0xEF, 0xBA]; + let eight_bytes = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; let sixteen_bytes = [ 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, - 0xDE, 0xF0 as u8, + 0xDE, 0xF0, ]; let mut flash = dp.FLASH.constrain(); let mut flash_writer = flash.writer::<2048>(FlashSize::Sz256K); @@ -94,209 +53,54 @@ mod app { const FLASH_SPACING: u32 = 16; // Separate flash writes by 16 bytes const FLASH_EXAMPLE_START_ADDRESS: u32 = 0x1FC00; + let address = |i| FLASH_EXAMPLE_START_ADDRESS + i as u32 * FLASH_SPACING; + logger::info!( - "Erasing 128 bytes at address {}", + "Erasing 128 bytes at address {:#X}", FLASH_EXAMPLE_START_ADDRESS ); flash_writer .erase(FLASH_EXAMPLE_START_ADDRESS, 128) .unwrap(); // Erase entire page - for i in 0..6 { - match i { - 0 => { - // This test should fail, as the data needs to be divisible by 8 and force padding is false - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &one_byte, - false, - ); - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 - ); - - // This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &one_byte, - true, - ); - assert!(result.is_ok()); - logger::info!( - "Wrote 1 byte to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - 1 => { - // This test should fail, as the data needs to be divisible by 8 and force padding is false - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &two_bytes, - false, - ); - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 - ); - - // This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &two_bytes, - true, - ); - assert!(result.is_ok()); - logger::info!( - "Wrote 2 bytes to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - 2 => { - // This test should fail, as the data needs to be divisible by 8 and force padding is false - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &three_bytes, - false, - ); - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 - ); + let datasets = [ + &one_byte[..], + &two_bytes[..], + &three_bytes[..], + &four_bytes[..], + &eight_bytes[..], + &sixteen_bytes[..], + ]; - // This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &three_bytes, - true, - ); - assert!(result.is_ok()); - logger::info!( - "Wrote 3 bytes to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - 3 => { - // This test should fail, as the data needs to be divisible by 8 and force padding is false - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &four_bytes, - false, - ); - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8 - ); + for (i, data) in datasets.into_iter().enumerate() { + let address = address(i); + let mut do_write = |force_padding| flash_writer.write(address, &data, force_padding); - // This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF - let result = flash_writer.write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &four_bytes, - true, - ); - assert!(result.is_ok()); - logger::info!( - "Wrote 4 bytes to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - 4 => { - flash_writer - .write( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - &eight_bytes, - false, - ) - .unwrap(); - logger::info!( - "Wrote 8 bytes to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - 5 => { - flash_writer - .write(FLASH_EXAMPLE_START_ADDRESS + i * 16, &sixteen_bytes, false) - .unwrap(); - logger::info!( - "Wrote 16 bytes to address {}", - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING - ); - } - _ => (), + if data.len() % 8 != 0 { + // This test should fail, as the data needs to be divisible by 8 and force padding is false + assert_eq!( + do_write(false), + Err(stm32g4xx_hal::flash::Error::ArrayMustBeDivisibleBy8) + ); } - } + // This test should pass, as the data needs to be divisible by 8 and force padding is true, so the one_byte array will be padded with 7 bytes of 0xFF + do_write(true).unwrap(); + logger::info!("Wrote {} byte(s) to address {:#X}", data.len(), address); + } logger::info!("Validating data written data by performing read and compare"); - for i in 0..6 { - match i { - 0 => { - let bytes = flash_writer - .read(FLASH_EXAMPLE_START_ADDRESS as u32, one_byte.len()) - .unwrap(); - assert!(compare_arrays(&bytes, &one_byte)); - logger::info!("Validated 1 byte data"); - } - 1 => { - let bytes = flash_writer - .read( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - two_bytes.len(), - ) - .unwrap(); - assert!(compare_arrays(&bytes, &two_bytes)); - logger::info!("Validated 2 byte data"); - } - 2 => { - let bytes = flash_writer - .read( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - three_bytes.len(), - ) - .unwrap(); - assert!(compare_arrays(&bytes, &three_bytes)); - logger::info!("Validated 3 byte data"); - } - 3 => { - let bytes = flash_writer - .read( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - four_bytes.len(), - ) - .unwrap(); - assert!(compare_arrays(&bytes, &four_bytes)); - logger::info!("Validated 4 byte data"); - } - 4 => { - let bytes = flash_writer - .read( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - eight_bytes.len(), - ) - .unwrap(); - assert!(compare_arrays(&bytes, &eight_bytes)); - logger::info!("Validated 8 byte data"); - } - 5 => { - let bytes = flash_writer - .read( - FLASH_EXAMPLE_START_ADDRESS + i * FLASH_SPACING, - sixteen_bytes.len(), - ) - .unwrap(); - assert!(compare_arrays(&bytes, &sixteen_bytes)); - logger::info!("Validated 5 byte data"); - } - _ => (), - } + for (i, data) in datasets.into_iter().enumerate() { + logger::info!("Stuff: {}", i); + let mut bytes = [0; 16]; + flash_writer.read_exact(address(i), &mut bytes[..data.len()]); + + assert_eq!(&bytes[..data.len()], *data); + logger::info!("Validated {} byte data", data.len()); } logger::info!( - "Finished flash example at address {}", + "Finished flash example at address {:#X}", FLASH_EXAMPLE_START_ADDRESS ); diff --git a/examples/utils/logger.rs b/examples/utils/logger.rs index f21f341f..2c2b4a76 100644 --- a/examples/utils/logger.rs +++ b/examples/utils/logger.rs @@ -39,8 +39,8 @@ cfg_if::cfg_if! { #[allow(unused_macros)] macro_rules! println { - ($($tt:tt)*) => { - log::info!($($tt,)*); + ($($arg:tt)+) => { + log::info!($($arg)+); }; } @@ -86,8 +86,8 @@ cfg_if::cfg_if! { #[allow(unused_macros)] macro_rules! println { - ($($tt:tt)*) => { - cortex_m_semihosting::hprintln!($($tt,)*).unwrap(); + ($($arg:tt)+) => { + cortex_m_semihosting::hprintln!($($arg)+).unwrap(); }; } diff --git a/src/flash.rs b/src/flash.rs index 26b60484..4aa58f6a 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -24,7 +24,6 @@ pub enum Error { AddressMisaligned, LengthNotMultiple2, LengthTooLong, - EraseError, ProgrammingError, WriteError, VerifyError, @@ -48,21 +47,22 @@ pub enum FlashSize { Sz1M = 1024, } impl FlashSize { - const fn kbytes(self) -> u32 { + const fn bytes(self) -> u32 { SZ_1K * self as u32 } } -pub struct FlashWriter<'a, const SECTOR_SZ_KB: u32> { +pub struct FlashWriter<'a, const SECTOR_SIZE: u32> { flash: &'a mut Parts, flash_sz: FlashSize, verify: bool, + is_dual_bank: bool, } -impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { +impl<'a, const SECTOR_SIZE: u32> FlashWriter<'a, SECTOR_SIZE> { #[allow(unused)] fn unlock_options(&mut self) -> Result<()> { // Check if flash is busy - while self.flash.sr.sr().read().bsy().bit_is_set() {} + self.wait_for_ready(); // Unlock self.unlock()?; @@ -90,7 +90,7 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { fn unlock(&mut self) -> Result<()> { // Wait for any ongoing operations - while self.flash.sr.sr().read().bsy().bit_is_set() {} + self.wait_for_ready(); // NOTE(unsafe) write Keys to the key register. This is safe because the // only side effect of these writes is to unlock the flash control @@ -113,7 +113,7 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { fn lock(&mut self) -> Result<()> { //Wait for ongoing flash operations - while self.flash.sr.sr().read().bsy().bit_is_set() {} + self.wait_for_ready(); // Set lock bit self.flash.cr.cr().modify(|_, w| w.lock().set_bit()); @@ -126,67 +126,107 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { } fn valid_address(&self, offset: u32) -> Result<()> { - if FLASH_START - .checked_add(offset) - .ok_or(Error::AddressLargerThanFlash)? - > FLASH_END - { - Err(Error::AddressLargerThanFlash) - } else if offset & 0x1 != 0 { - Err(Error::AddressMisaligned) + let offset = if offset >= 0x4_0000 && self.is_dual_bank { + offset - 0x4_0000 + } else { + offset + }; + + let bank_size = if self.is_dual_bank { + // Each bank is only half the size in dual bank mode + self.flash_sz.bytes() / 2 } else { - Ok(()) + self.flash_sz.bytes() + }; + + if offset > bank_size { + return Err(Error::AddressLargerThanFlash); } + + Ok(()) } fn valid_length(&self, offset: u32, length: usize, force_padding: bool) -> Result<()> { - if offset - .checked_add(length as u32) - .ok_or(Error::LengthTooLong)? - > self.flash_sz.kbytes() - { - return Err(Error::LengthTooLong); - } - if !force_padding && length % 8 != 0 { return Err(Error::ArrayMustBeDivisibleBy8); } - Ok(()) + let end = offset + .checked_add(length as u32) + .ok_or(Error::LengthTooLong)?; + + match self.valid_address(end) { + Err(Error::AddressLargerThanFlash) => Err(Error::LengthTooLong), + x => x, + } } /// Erase sector which contains `start_offset` pub fn page_erase(&mut self, start_offset: u32) -> Result<()> { self.valid_address(start_offset)?; + if start_offset % SECTOR_SIZE != 0 { + return Err(Error::AddressMisaligned); + } + // Unlock Flash self.unlock()?; - // Set Page Erase - self.flash.cr.cr().modify(|_, w| w.per().set_bit()); + // Wait for operation to finish + self.wait_for_ready(); + + self.check_and_clear_errors()?; - let page = start_offset / SECTOR_SZ_KB; + let page = start_offset / SECTOR_SIZE; - // Write address bits + // Write address bits and Set Page Erase // NOTE(unsafe) This sets the page address in the Address Register. // The side-effect of this write is that the page will be erased when we // set the STRT bit in the CR below. The address is validated by the // call to self.valid_address() above. + // Category 3 device + #[cfg(any( + feature = "stm32g471", + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483" + ))] + unsafe { + const PAGES_PER_BANK: u32 = 128; + let (is_bank2, page) = if self.is_dual_bank() && page >= PAGES_PER_BANK { + // page 0 in bank 2 is at the same address as page 128 would have had been, assuming the same page size + (true, page - PAGES_PER_BANK) + } else { + (false, page) + }; + + self.flash.cr.cr().modify(|_, w| { + w.bits((is_bank2 as u32) << 11) // BKER // TODO remove this once it's added to the PAC + .pnb() + .bits(page.try_into().unwrap()) + .per() + .set_bit() + }); + } + + // Not category 3 device + #[cfg(not(any( + feature = "stm32g471", + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483" + )))] unsafe { self.flash .cr .cr() - .modify(|_, w| w.pnb().bits(page.try_into().unwrap())); + .modify(|_, w| w.pnb().bits(page.try_into().unwrap()).per().set_bit()); } // Start Operation self.flash.cr.cr().modify(|_, w| w.strt().set_bit()); - // Wait for operation to finish - while self.flash.sr.sr().read().bsy().bit_is_set() {} - - // Check for errors - let sr = self.flash.sr.sr().read(); + self.wait_for_ready(); // Remove Page Erase Operation bit self.flash.cr.cr().modify(|_, w| w.per().clear_bit()); @@ -194,38 +234,36 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { // Re-lock flash self.lock()?; - if sr.wrperr().bit_is_set() { - self.flash.sr.sr().modify(|_, w| w.wrperr().set_bit()); - Err(Error::EraseError) - } else { - if self.verify { - // By subtracting 1 from the sector size and masking with - // start_offset, we make 'start' point to the beginning of the - // page. We do this because the entire page should have been - // erased, regardless of where in the page the given - // 'start_offset' was. - let size = SECTOR_SZ_KB; - let start = start_offset & !(size - 1); - for idx in (start..start + size).step_by(2) { - let write_address = (FLASH_START + idx) as *const u16; - let verify: u16 = unsafe { core::ptr::read_volatile(write_address) }; - if verify != 0xFFFF { - return Err(Error::VerifyError); - } + self.check_and_clear_errors()?; + + if self.verify { + for offset in start_offset..(SECTOR_SIZE / 4) { + let mut data = [u8::MAX; 16]; + self.read_exact(offset, &mut data[..]); + if data != [u8::MAX; 16] { + return Err(Error::VerifyError); } } - - Ok(()) } + + Ok(()) } /// Erase the Flash Sectors from `FLASH_START + start_offset` to `length` pub fn erase(&mut self, start_offset: u32, length: usize) -> Result<()> { + self.valid_address(start_offset)?; + + // Make sure `start_offset` points to the begining of the page + // so as to avoid erasing any data before it in the same page + if start_offset % SECTOR_SIZE != 0 { + return Err(Error::AddressMisaligned); + } + self.valid_length(start_offset, length, true)?; // Erase every sector touched by start_offset + length for offset in - (start_offset..start_offset + length as u32).step_by(SECTOR_SZ_KB.try_into().unwrap()) + (start_offset..start_offset + length as u32).step_by(SECTOR_SIZE.try_into().unwrap()) { self.page_erase(offset)?; } @@ -235,14 +273,13 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { } /// Retrieve a slice of data from `FLASH_START + offset` - pub fn read(&self, offset: u32, length: usize) -> Result<&[u8]> { + pub fn read(&mut self, offset: u32, length: usize) -> Result<&[u32]> { self.valid_address(offset)?; + self.valid_length(offset, length, true)?; - if offset + length as u32 > self.flash_sz.kbytes() { - return Err(Error::LengthTooLong); - } + let address = (FLASH_START + offset) as *const u32; - let address = (FLASH_START + offset) as *const _; + // WARNING: I am getting the feeling that wee need to read this using volatile u32 reads Ok( // NOTE(unsafe) read with no side effects. The data returned will @@ -250,10 +287,18 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { // reference to this FlashWriter, and any operation that would // invalidate the data returned would first require taking a mutable // reference to this FlashWriter. - unsafe { core::slice::from_raw_parts(address, length) }, + unsafe { core::slice::from_raw_parts(address, (length + 4 - 1) / 4) }, ) } + pub fn read_exact(&mut self, start_offset: u32, dst: &mut [u8]) { + let address = (FLASH_START + start_offset) as *const u32; + for (i, dst) in dst.chunks_mut(4).enumerate() { + let word = unsafe { core::ptr::read_volatile(address.add(i)) }; + dst.copy_from_slice(&word.to_ne_bytes()[..dst.len()]); + } + } + /// Write data to `FLASH_START + offset`. /// /// If `force_data_padding` is true, the incoming data will be padded with 0xFF @@ -261,76 +306,54 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { /// If `force_data_padding` is false and `data.len()` is not divisible by 8, /// the error `ArrayMustBeDivisibleBy8` will be returned. pub fn write(&mut self, offset: u32, data: &[u8], force_data_padding: bool) -> Result<()> { + self.valid_address(offset)?; self.valid_length(offset, data.len(), force_data_padding)?; + assert_eq!(offset % 8, 0); // Unlock Flash self.unlock()?; - // According to RM0440 Rev 7, "It is only possible to program double word (2 x 32-bit data)" - for idx in (0..data.len()).step_by(8) { - // Check the starting address - self.valid_address(offset + idx as u32)?; - // Check the ending address to make sure there is no overflow - self.valid_address(offset + 8 + idx as u32)?; + self.wait_for_ready(); + self.check_and_clear_errors()?; - let write_address1 = (FLASH_START + offset + idx as u32) as *mut u32; - let write_address2 = (FLASH_START + offset + 4 + idx as u32) as *mut u32; + // Set Page Programming to 1 + self.flash.cr.cr().modify(|_, w| w.pg().set_bit()); - let word1: u32; - let word2: u32; + // According to RM0440 Rev 7, "It is only possible to program double word (2 x 32-bit data)" + for (idx, data) in data.chunks(8).enumerate() { + let write_address1 = (FLASH_START + offset + 8 * idx as u32) as *mut u32; + let write_address2 = unsafe { write_address1.add(1) }; // Check if there is enough data to make 2 words, if there isn't, pad the data with 0xFF - if idx + 8 > data.len() { - let mut tmp_buffer = [255u8; 8]; - tmp_buffer[idx..data.len()].copy_from_slice(&data[(idx + idx)..(data.len() + idx)]); - let tmp_dword = u64::from_le_bytes(tmp_buffer); - word1 = tmp_dword as u32; - word2 = (tmp_dword >> 32) as u32; - } else { - word1 = (data[idx] as u32) - | (data[idx + 1] as u32) << 8 - | (data[idx + 2] as u32) << 16 - | (data[idx + 3] as u32) << 24; - - word2 = (data[idx + 4] as u32) - | (data[idx + 5] as u32) << 8 - | (data[idx + 6] as u32) << 16 - | (data[idx + 7] as u32) << 24; - } - - // Set Page Programming to 1 - self.flash.cr.cr().modify(|_, w| w.pg().set_bit()); - - while self.flash.sr.sr().read().bsy().bit_is_set() {} + let mut tmp_buffer = [0xFF; 8]; + tmp_buffer[..data.len()].copy_from_slice(data); + let word1 = u32::from_le_bytes(tmp_buffer[0..4].try_into().unwrap()); // Unwrap: We know the size is exactly 4 + let word2 = u32::from_le_bytes(tmp_buffer[4..8].try_into().unwrap()); // NOTE(unsafe) Write to FLASH area with no side effects unsafe { core::ptr::write_volatile(write_address1, word1) }; unsafe { core::ptr::write_volatile(write_address2, word2) }; // Wait for write - while self.flash.sr.sr().read().bsy().bit_is_set() {} - - // Set Page Programming to 0 - self.flash.cr.cr().modify(|_, w| w.pg().clear_bit()); - - // Check for errors - if self.flash.sr.sr().read().pgaerr().bit_is_set() { - self.flash.sr.sr().modify(|_, w| w.pgaerr().clear_bit()); - - self.lock()?; - return Err(Error::ProgrammingError); - } else if self.flash.sr.sr().read().wrperr().bit_is_set() { - self.flash.sr.sr().modify(|_, w| w.wrperr().clear_bit()); - - self.lock()?; - return Err(Error::WriteError); - } else if self.verify { - // Verify written WORD - // NOTE(unsafe) read with no side effects within FLASH area - let verify1: u32 = unsafe { core::ptr::read_volatile(write_address1) }; - let verify2: u32 = unsafe { core::ptr::read_volatile(write_address2) }; - if verify1 != word1 && verify2 != word2 { - self.lock()?; + self.wait_for_ready(); + + // Clear EOP bit(is cleared by writing 1) + if self.flash.sr.sr().read().eop().bit_is_set() { + self.flash.sr.sr().modify(|_, w| w.eop().set_bit()); + } + self.check_and_clear_errors()?; // TODO dont + } + + // Set Page Programming to 0 + self.flash.cr.cr().modify(|_, w| w.pg().clear_bit()); + + self.check_and_clear_errors()?; + + if self.verify { + for (i, data) in data.iter().enumerate() { + let mut read = [u8::MAX; 1]; + self.read_exact(offset + i as u32, &mut read[..]); + if data != &read[0] { return Err(Error::VerifyError); } } @@ -341,6 +364,37 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { Ok(()) } + /// NOTE: This will (try to) lock the flash if there is an error + fn check_and_clear_errors(&mut self) -> Result<()> { + let status = self.flash.sr.sr().read(); + debug_assert!(status.fasterr().bit_is_clear()); + debug_assert!(status.miserr().bit_is_clear()); + debug_assert!(status.operr().bit_is_clear()); + debug_assert!(status.optverr().bit_is_clear()); + debug_assert!(status.progerr().bit_is_clear()); + debug_assert!(status.rderr().bit_is_clear()); + debug_assert!(status.sizerr().bit_is_clear()); + debug_assert!(status.pgserr().bit_is_clear()); + + if status.pgaerr().bit_is_set() { + self.flash.sr.sr().modify(|_, w| w.pgaerr().clear_bit()); + self.lock()?; + return Err(Error::ProgrammingError); + } else if status.wrperr().bit_is_set() { + self.flash.sr.sr().modify(|_, w| w.wrperr().clear_bit()); + + self.lock()?; + return Err(Error::WriteError); + } + + Ok(()) + } + + fn wait_for_ready(&mut self) { + // Wait for operation to finish + while self.flash.sr.sr().read().bsy().bit_is_set() {} + } + /// Enable/disable verifying that each erase or write operation completed /// successfuly. /// @@ -354,6 +408,11 @@ impl<'a, const SECTOR_SZ_KB: u32> FlashWriter<'a, SECTOR_SZ_KB> { pub fn change_verification(&mut self, verify: bool) { self.verify = verify; } + + #[inline(always)] + pub fn is_dual_bank(&mut self) -> bool { + self.is_dual_bank + } } /// Extension trait to constrain the FLASH peripheral @@ -427,33 +486,42 @@ pub struct Parts { pub(crate) _wrp1br: WRP1BR, } impl Parts { - #[cfg(any(feature = "stm32g431", feature = "stm32g441",))] - pub fn writer(&mut self, flash_sz: FlashSize) -> FlashWriter<{ 2 * SZ_1K }> { - FlashWriter { - flash: self, - flash_sz, - verify: true, - } - } - #[cfg(any( - feature = "stm32g471", - feature = "stm32g473", - feature = "stm32g474", - feature = "stm32g483", - feature = "stm32g484", - feature = "stm32g491", - feature = "stm32g4a1", - ))] pub fn writer( &mut self, flash_sz: FlashSize, ) -> FlashWriter { + let is_dual_bank = self.is_dual_bank(); FlashWriter { + is_dual_bank, flash: self, flash_sz, verify: true, } } + + pub fn is_dual_bank(&mut self) -> bool { + // TODO: use dbank() instead of `1 << 22` once it gets added to pac + + // Category 3 device + #[cfg(any( + feature = "stm32g471", + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483" + ))] + let dbank = self._optr.optr().read().bits() & (1 << 22) != 0; + + // Not category 3 device + #[cfg(not(any( + feature = "stm32g471", + feature = "stm32g473", + feature = "stm32g474", + feature = "stm32g483" + )))] + let dbank = false; + + dbank + } } /// Opaque ACR register