-
Notifications
You must be signed in to change notification settings - Fork 0
FLC Rework #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FLC Rework #81
Changes from all commits
b3a0dce
e38bf9b
85c8d1e
d7cee28
2f144ad
055329c
88fa9b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -111,6 +111,52 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Busy loop until FLC is ready. | ||||||||
| /// | ||||||||
| /// This MUST be called BEFORE any FLC operation EXCEPT clearing interrupts. | ||||||||
| fn wait_until_ready(&self) { | ||||||||
| while !self.flc.ctrl().read().pend().bit_is_clear() {} | ||||||||
| } | ||||||||
|
|
||||||||
| /// Clear any stale errors in the FLC Interrupt Register | ||||||||
| /// | ||||||||
| /// This can be called without waiting for the FLC to be ready. | ||||||||
| fn clear_interrupts(&self) { | ||||||||
| self.flc.intr().modify(|_, w| w.af().clear_bit()); | ||||||||
| } | ||||||||
|
|
||||||||
| /// Prepares the FLC for a write operation. | ||||||||
| /// | ||||||||
| /// Procedure: | ||||||||
| /// - Wait until FLC ready | ||||||||
| /// - Disable icc0 | ||||||||
| /// - Clear FLC interrupts | ||||||||
| /// - Set clock divisor | ||||||||
| /// - Unlock write protection | ||||||||
| /// - EXECUTE OPERATION (CLOSURE) | ||||||||
| /// - Wait until FLC ready | ||||||||
| /// - Lock write protection | ||||||||
| /// - Flush ICC | ||||||||
| /// - Enable icc0 | ||||||||
| fn write_guard<F: Fn()>(&self, sys_clk: &SystemClock, operation: F) -> Result<(), FlashErr> { | ||||||||
| // Pre-write | ||||||||
| self.wait_until_ready(); | ||||||||
| self.disable_icc0(); | ||||||||
| self.clear_interrupts(); | ||||||||
| self.set_clock_divisor(sys_clk)?; | ||||||||
| self.unlock_write_protection(); | ||||||||
|
|
||||||||
| operation(); | ||||||||
|
|
||||||||
| // Post-write | ||||||||
| self.wait_until_ready(); | ||||||||
| self.lock_write_protection(); | ||||||||
| self.flush_icc()?; | ||||||||
| self.enable_icc0(); | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Flushes the flash line buffer and arm instruction cache. | ||||||||
| /// | ||||||||
| /// This MUST be called after any write/erase flash controller operations. | ||||||||
|
|
@@ -123,6 +169,7 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| let mut empty_buffer = [0; 4]; | ||||||||
| self.read_bytes(ptr, &mut empty_buffer)?; | ||||||||
| self.read_bytes(ptr + FLASH_PAGE_SIZE, &mut empty_buffer)?; | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -175,7 +222,7 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| } | ||||||||
|
|
||||||||
| for byte in word_chunk.into_remainder() { | ||||||||
| // SAFETY: | ||||||||
| // SAFETY:CLOSURE | ||||||||
| // * src is valid for reads. Because read range is checked at the | ||||||||
| // beginning of function. | ||||||||
| // | ||||||||
|
|
@@ -195,7 +242,6 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
| /// | ||||||||
| /// # Safety | ||||||||
| /// | ||||||||
| /// Writes must not corrupt potentially executable instructions of the program. | ||||||||
|
|
@@ -214,10 +260,6 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| ) -> Result<(), FlashErr> { | ||||||||
| self.check_address_bounds(address..(address + data.len() as u32))?; | ||||||||
|
|
||||||||
| self.set_clock_divisor(sys_clk)?; | ||||||||
|
|
||||||||
| self.disable_icc0(); | ||||||||
|
|
||||||||
| // Check alignment | ||||||||
| let mut physical_addr = address; | ||||||||
| let bytes_unaligned = if (address & 0xF) > 0 { | ||||||||
|
|
@@ -229,7 +271,7 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| // Write unaligned data | ||||||||
| if bytes_unaligned > 0 { | ||||||||
| let unaligned_data = &data[0..core::cmp::min(bytes_unaligned, data.len())]; | ||||||||
| self.write_lt_128_unaligned(physical_addr, unaligned_data)?; | ||||||||
| self.write_lt_128_unaligned(physical_addr, unaligned_data, sys_clk)?; | ||||||||
| physical_addr += unaligned_data.len() as u32; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -244,25 +286,37 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| let mut buffer_128_bits: [u32; 4] = [0; 4]; | ||||||||
| word.enumerate() | ||||||||
| .for_each(|(idx, chunk)| buffer_128_bits[idx] = chunk); | ||||||||
| self.write128(physical_addr, &buffer_128_bits)?; | ||||||||
| self.write128(physical_addr, &buffer_128_bits, sys_clk)?; | ||||||||
| physical_addr += 16; | ||||||||
| } | ||||||||
|
|
||||||||
| // remainder from chunks | ||||||||
| if !chunk_8.remainder().is_empty() { | ||||||||
| self.write_lt_128_unaligned(physical_addr, chunk_8.remainder())?; | ||||||||
| self.write_lt_128_unaligned(physical_addr, chunk_8.remainder(), sys_clk)?; | ||||||||
| } | ||||||||
|
|
||||||||
| self.flush_icc()?; | ||||||||
|
|
||||||||
| self.enable_icc0(); | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Writes less than 128 bits (16 bytes) of data to flash. Data needs to fit | ||||||||
| /// within one flash word (16 bytes). | ||||||||
| fn write_lt_128_unaligned(&self, address: u32, data: &[u8]) -> Result<(), FlashErr> { | ||||||||
| /// Writes less than 128 bits (16 bytes) of data to flash. | ||||||||
| /// Data needs to fit within one flash word (16 bytes). | ||||||||
| /// | ||||||||
| /// # Safety | ||||||||
| /// | ||||||||
| /// Writes must not corrupt potentially executable instructions of the program. | ||||||||
| /// Callers must ensure that the following condition is met: | ||||||||
| /// * If `address` points to a portion of the program's instructions, `data` must | ||||||||
| /// contain valid instructions that does not introduce undefined behavior. | ||||||||
| /// | ||||||||
| /// It is very difficult to define what would cause undefined behavior when | ||||||||
| /// modifying program instructions. This would almost certainly result | ||||||||
| /// in unwanted and likely undefined behavior. Do so at your own risk. | ||||||||
| unsafe fn write_lt_128_unaligned( | ||||||||
MelonShooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| &self, | ||||||||
| address: u32, | ||||||||
| data: &[u8], | ||||||||
| sys_clk: &SystemClock, | ||||||||
| ) -> Result<(), FlashErr> { | ||||||||
| // Get byte idx within 128-bit word | ||||||||
| let byte_idx = (address & 0xF) as usize; | ||||||||
|
|
||||||||
|
|
@@ -284,34 +338,45 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| new_data[idx] = u32::from_le_bytes(word_chunk.try_into().unwrap()) | ||||||||
| }); | ||||||||
|
|
||||||||
| self.write128(aligned_addr, &new_data) | ||||||||
| self.write128(aligned_addr, &new_data, sys_clk) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Writes 128 bits (16 bytes) of data to flash. | ||||||||
| // make sure to disable ICC with ICC_Disable(); before Running this function | ||||||||
| fn write128(&self, address: u32, data: &[u32; 4]) -> Result<(), FlashErr> { | ||||||||
MelonShooter marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| /// Address must be 128-bit aligned. | ||||||||
| /// | ||||||||
| /// # Safety | ||||||||
| /// | ||||||||
| /// Writes must not corrupt potentially executable instructions of the program. | ||||||||
| /// Callers must ensure that the following condition is met: | ||||||||
| /// * If `address` points to a portion of the program's instructions, `data` must | ||||||||
| /// contain valid instructions that does not introduce undefined behavior. | ||||||||
| /// | ||||||||
| /// It is very difficult to define what would cause undefined behavior when | ||||||||
| /// modifying program instructions. This would almost certainly result | ||||||||
| /// in unwanted and likely undefined behavior. Do so at your own risk. | ||||||||
| unsafe fn write128( | ||||||||
| &self, | ||||||||
| address: u32, | ||||||||
| data: &[u32; 4], | ||||||||
| sys_clk: &SystemClock, | ||||||||
| ) -> Result<(), FlashErr> { | ||||||||
| // Check if adddress is 128-bit aligned | ||||||||
| if address & 0xF > 0 { | ||||||||
| return Err(FlashErr::AddressNotAligned128); | ||||||||
| } | ||||||||
|
|
||||||||
| self.unlock_write_protection(); | ||||||||
|
|
||||||||
| // Clear stale errors | ||||||||
| self.flc.intr().modify(|_, w| w.af().clear_bit()); | ||||||||
|
|
||||||||
| while !self.flc.ctrl().read().pend().bit_is_clear() {} | ||||||||
|
|
||||||||
| self.flc.addr().modify(|_, w| w.addr().variant(address)); | ||||||||
| self.flc.data(0).modify(|_, w| w.data().variant(data[0])); | ||||||||
| self.flc.data(1).modify(|_, w| w.data().variant(data[1])); | ||||||||
| self.flc.data(2).modify(|_, w| w.data().variant(data[2])); | ||||||||
| self.flc.data(3).modify(|_, w| w.data().variant(data[3])); | ||||||||
| self.write_guard(sys_clk, || { | ||||||||
| self.flc.addr().modify(|_, w| w.addr().variant(address)); | ||||||||
| self.flc.data(0).modify(|_, w| w.data().variant(data[0])); | ||||||||
| self.flc.data(1).modify(|_, w| w.data().variant(data[1])); | ||||||||
| self.flc.data(2).modify(|_, w| w.data().variant(data[2])); | ||||||||
| self.flc.data(3).modify(|_, w| w.data().variant(data[3])); | ||||||||
|
|
||||||||
| self.flc.ctrl().modify(|_, w| w.wr().set_bit()); | ||||||||
| while !self.flc.ctrl().read().wr().is_complete() {} | ||||||||
| self.flc.ctrl().modify(|_, w| w.wr().set_bit()); | ||||||||
|
|
||||||||
| self.lock_write_protection(); | ||||||||
| // Wait until write completes | ||||||||
| while !self.flc.ctrl().read().wr().is_complete() {} | ||||||||
| })?; | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
@@ -327,27 +392,12 @@ impl<'gcr, 'icc> FlashController<'gcr, 'icc> { | |||||||
| pub unsafe fn page_erase(&self, address: u32, sys_clk: &SystemClock) -> Result<(), FlashErr> { | ||||||||
| self.check_address_bounds(address..address)?; | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this have size 0?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well the address is the address of the page, check_address_bounds does a contains check on the given range. Essentially this is just checking that address is within the bounds of flash. Unless I am mistaken about what range syntax is doing here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. max78000-hal/hal/src/peripherals/flash_controller.rs Lines 71 to 73 in f0072de
You're totally right here - we are just treating a range (although i think the checking code is subtly wrong - it's treating the range as inclusive when it should be
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specifically this is a false positive where |
||||||||
|
|
||||||||
| if self.set_clock_divisor(sys_clk).is_err() { | ||||||||
| return Err(FlashErr::FlcClkErr); | ||||||||
| } | ||||||||
| self.write_guard(sys_clk, || { | ||||||||
| self.flc.addr().modify(|_, w| w.addr().variant(address)); | ||||||||
|
|
||||||||
| self.unlock_write_protection(); | ||||||||
|
|
||||||||
| // Clear stale errors | ||||||||
| self.flc.intr().modify(|_, w| w.af().clear_bit()); | ||||||||
|
|
||||||||
| while !self.flc.ctrl().read().pend().bit_is_clear() {} | ||||||||
|
|
||||||||
| self.flc.addr().modify(|_, w| w.addr().variant(address)); | ||||||||
|
|
||||||||
| self.flc.ctrl().modify(|_, w| w.erase_code().erase_page()); | ||||||||
| self.flc.ctrl().modify(|_, w| w.pge().set_bit()); | ||||||||
|
|
||||||||
| while !self.flc.ctrl().read().pend().bit_is_clear() {} | ||||||||
|
|
||||||||
| self.lock_write_protection(); | ||||||||
|
|
||||||||
| self.flush_icc()?; | ||||||||
| self.flc.ctrl().modify(|_, w| w.erase_code().erase_page()); | ||||||||
| self.flc.ctrl().modify(|_, w| w.pge().set_bit()); | ||||||||
| })?; | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| } | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| //! Flash controller tests | ||
|
|
||
| use core::fmt::Write; | ||
| use cortex_m_semihosting::hio; | ||
| use max78000_hal::peripherals::flash_controller::{FlashController, FlashErr}; | ||
| use max78000_hal::peripherals::oscillator::{ | ||
| Ibro, IbroDivider, IbroFrequency, Iso, IsoDivider, IsoFrequency, Oscillator, SystemClock, | ||
|
|
@@ -14,8 +13,8 @@ use max78000_hal::peripherals::PeripheralHandle; | |
| /// [`flash_write_full_outbounds`], | ||
| /// [`flash_write_partially_outbound_beginning`], | ||
| /// [`flash_write_full_partially_outbound_end`]. | ||
| pub fn run_flc_tests( | ||
| stdout: &mut hio::HostStream, | ||
| pub fn run_flc_tests<T: Write>( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to add a test that does two writes in a row, irrespective of output, to prevent this issue from showing up again. |
||
| stdout: &mut T, | ||
| flash_controller: PeripheralHandle<'_, FlashController<'_, '_>>, | ||
| mut sys_clk: PeripheralHandle<'_, SystemClock<'_, '_>>, | ||
| ) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.