From 9aa3990348dd8704d3c92d5fd964089226dab6d5 Mon Sep 17 00:00:00 2001 From: gferraro Date: Wed, 13 Nov 2024 17:37:50 +0100 Subject: [PATCH] moved device config onto flash --- src/byte_slice_cursor.rs | 22 ++++++ src/core0_audio.rs | 5 +- src/core1_sub_tasks.rs | 9 ++- src/core1_task.rs | 2 +- src/device_config.rs | 33 +++++--- src/main.rs | 69 ++++++++++++++--- src/onboard_flash.rs | 163 ++++++++++++++++++++++++++++++++++++++- src/rp2040_flash.rs | 6 +- 8 files changed, 279 insertions(+), 30 deletions(-) diff --git a/src/byte_slice_cursor.rs b/src/byte_slice_cursor.rs index 6118b84..8b94ecd 100644 --- a/src/byte_slice_cursor.rs +++ b/src/byte_slice_cursor.rs @@ -71,6 +71,28 @@ impl<'a> CursorMut<'a> { let len = self.pos.min(self.inner.as_mut().len()); &mut self.inner.as_mut()[len..] } + + pub fn data(&mut self) -> &mut [u8] { + &mut self.inner.as_mut()[..self.pos] + } + + pub fn write_bytes(&mut self, bytes: &[u8]) -> fmt::Result { + // Skip over already-copied data + let remainder = &mut self.inner[self.pos..]; + // Check if there is space remaining (return error instead of panicking) + if remainder.len() < bytes.len() { + return Err(core::fmt::Error); + } + // Make the two slices the same length + let remainder = &mut remainder[..bytes.len()]; + // Copy + remainder.copy_from_slice(bytes); + + // Update offset to avoid overwriting + self.pos += bytes.len(); + + Ok(()) + } } impl<'a> ErrorType for Cursor<'a> { diff --git a/src/core0_audio.rs b/src/core0_audio.rs index 64969dd..140da49 100644 --- a/src/core0_audio.rs +++ b/src/core0_audio.rs @@ -86,8 +86,6 @@ pub fn audio_task( PullDown, >, ) -> ! { - let mut device_config = DeviceConfig::load_existing_config_from_flash().unwrap(); - watchdog.feed(); let core = unsafe { pac::CorePeripherals::steal() }; @@ -138,6 +136,9 @@ pub fn audio_task( flash_storage.take_spi(peripherals.SPI1, &mut peripherals.RESETS, clock_freq.Hz()); flash_storage.init(); + let mut device_config = + DeviceConfig::load_existing_config_from_flash(&mut flash_storage).unwrap(); + let (pio1, _, sm1, _, _) = peripherals.PIO1.split(&mut peripherals.RESETS); let mut delay = Delay::new(core.SYST, clock_freq); let mut shared_i2c = SharedI2C::new(i2c_config, unlocked_pin, &mut delay); diff --git a/src/core1_sub_tasks.rs b/src/core1_sub_tasks.rs index 111086c..b77d252 100644 --- a/src/core1_sub_tasks.rs +++ b/src/core1_sub_tasks.rs @@ -6,7 +6,7 @@ use crate::device_config::DeviceConfig; use crate::event_logger::{EventLogger, LoggerEvent, LoggerEventKind}; use crate::ext_spi_transfers::{ExtSpiTransfers, ExtTransferMessage}; use crate::onboard_flash::OnboardFlash; -use crate::rp2040_flash::write_device_config_to_rp2040_flash; +// use crate::rp2040_flash::write_device_config_to_rp2040_flash; use crate::FIRMWARE_VERSION; use byteorder::{ByteOrder, LittleEndian}; use cortex_m::delay::Delay; @@ -407,8 +407,11 @@ pub fn get_existing_device_config_or_config_from_pi_on_initial_handshake( new_config_bytes[length_used..length_used + 2400] .copy_from_slice(&new_config.motion_detection_mask.inner); - let slice_to_write = &new_config_bytes[0..length_used + 2400]; - write_device_config_to_rp2040_flash(slice_to_write); + let mut slice_to_write = &mut new_config_bytes[0..length_used + 2400]; + if let Some(spi_free) = pi_spi.disable() { + flash_storage.take_spi(spi_free, resets, clock_freq); + } + flash_storage.write_device_config(&mut slice_to_write); new_config.cursor_position += 2400; config_was_updated = true; } diff --git a/src/core1_task.rs b/src/core1_task.rs index 1a3f81f..3ad9557 100644 --- a/src/core1_task.rs +++ b/src/core1_task.rs @@ -375,7 +375,7 @@ pub fn core_1_task( let radiometry_enabled = sio.fifo.read_blocking(); info!("Core 1 got radiometry enabled: {}", radiometry_enabled == 2); let lepton_version = if radiometry_enabled == 2 { 35 } else { 3 }; - let existing_config = DeviceConfig::load_existing_config_from_flash(); + let existing_config = DeviceConfig::load_existing_config_from_flash(&mut flash_storage); if let Some(existing_config) = &existing_config { info!("Existing config {:#?}", existing_config.config()); diff --git a/src/device_config.rs b/src/device_config.rs index 4792546..c3d25f2 100644 --- a/src/device_config.rs +++ b/src/device_config.rs @@ -1,6 +1,7 @@ -use crate::byte_slice_cursor::Cursor; use crate::motion_detector::DetectionMask; -use crate::rp2040_flash::read_device_config_from_rp2040_flash; +use crate::onboard_flash::OnboardFlash; +use crate::{byte_slice_cursor::Cursor, onboard_flash}; +// use crate::rp2040_flash::read_device_config_from_rp2040_flash; use crate::sun_times::sun_times; use chrono::{Duration, NaiveDateTime, NaiveTime, Timelike}; use defmt::{info, Format, Formatter}; @@ -270,16 +271,28 @@ impl Default for DeviceConfig { } impl DeviceConfig { - pub fn load_existing_config_from_flash() -> Option { - let slice = read_device_config_from_rp2040_flash(); - let device_config = DeviceConfig::from_bytes(slice); - device_config + pub fn load_existing_config_from_flash( + flash_storage: &mut OnboardFlash, + ) -> Option { + let slice = flash_storage.read_device_config(); + if let Ok(slice) = slice { + let device_config = DeviceConfig::from_bytes(&slice); + device_config + } else { + None + } } - pub fn load_existing_inner_config_from_flash() -> Option<(DeviceConfigInner, usize)> { - let slice = read_device_config_from_rp2040_flash(); - let device_config = DeviceConfig::inner_from_bytes(slice); - device_config + pub fn load_existing_inner_config_from_flash( + flash_storage: &mut OnboardFlash, + ) -> Option<(DeviceConfigInner, usize)> { + let slice = flash_storage.read_device_config(); + if let Ok(slice) = slice { + let device_config = DeviceConfig::inner_from_bytes(&slice); + device_config + } else { + None + } } pub fn inner_from_bytes(bytes: &[u8]) -> Option<(DeviceConfigInner, usize)> { diff --git a/src/main.rs b/src/main.rs index 9c0624b..bf1b0fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -110,13 +110,55 @@ impl FrameBuffer { } } +use crate::onboard_flash::OnboardFlash; +use rp2040_hal::dma::DMAExt; + #[entry] fn main() -> ! { info!("Startup tc2-firmware {}", FIRMWARE_VERSION); // TODO: Check wake_en and sleep_en registers to make sure we're not enabling any clocks we don't need. let mut peripherals: Peripherals = Peripherals::take().unwrap(); - let mut config = DeviceConfig::load_existing_inner_config_from_flash(); - let mut is_audio: bool = config.is_some() && config.as_mut().unwrap().0.is_audio_device(); + let sio = Sio::new(peripherals.SIO); + let dma_channels = peripherals.DMA.split(&mut peripherals.RESETS); + let pins = rp2040_hal::gpio::Pins::new( + peripherals.IO_BANK0, + peripherals.PADS_BANK0, + sio.gpio_bank0, + &mut peripherals.RESETS, + ); + let core1 = Core1Pins { + pi_ping: pins.gpio5.into_pull_down_input(), + + pi_miso: pins.gpio15.into_floating_disabled(), + pi_mosi: pins.gpio12.into_floating_disabled(), + pi_cs: pins.gpio13.into_floating_disabled(), + pi_clk: pins.gpio14.into_floating_disabled(), + + fs_cs: pins.gpio9.into_push_pull_output(), + fs_miso: pins.gpio8.into_pull_down_disabled().into_pull_type(), + fs_mosi: pins.gpio11.into_pull_down_disabled().into_pull_type(), + fs_clk: pins.gpio10.into_pull_down_disabled().into_pull_type(), + }; + + // init flash + let mut flash_page_buf = [0xffu8; 4 + 2048 + 128]; + let mut flash_page_buf_2 = [0xffu8; 4 + 2048 + 128]; + let flash_page_buf = unsafe { extend_lifetime_generic_mut(&mut flash_page_buf) }; + let flash_page_buf_2 = unsafe { extend_lifetime_generic_mut(&mut flash_page_buf_2) }; + + let mut flash_storage = OnboardFlash::new( + core1.fs_cs, + core1.fs_mosi, + core1.fs_clk, + core1.fs_miso, + flash_page_buf, + flash_page_buf_2, + dma_channels.ch1, + dma_channels.ch2, + true, + None, + ); + let (clocks, rosc) = clock_utils::setup_rosc_as_system_clock( peripherals.CLOCKS, peripherals.XOSC, @@ -132,6 +174,16 @@ fn main() -> ! { clocks.system_clock.freq().to_MHz() ); + flash_storage.take_spi( + peripherals.SPI1, + &mut peripherals.RESETS, + system_clock_freq.Hz(), + ); + flash_storage.init(); + + let mut config = DeviceConfig::load_existing_inner_config_from_flash(&mut flash_storage); + let mut is_audio: bool = config.is_some() && config.as_mut().unwrap().0.is_audio_device(); + // Watchdog ticks are required to run the timer peripheral, since they're shared between both. let mut watchdog = bsp::hal::Watchdog::new(peripherals.WATCHDOG); @@ -145,14 +197,13 @@ fn main() -> ! { let core = pac::CorePeripherals::take().unwrap(); let mut delay = Delay::new(core.SYST, system_clock_freq); - let sio = Sio::new(peripherals.SIO); - let pins = rp2040_hal::gpio::Pins::new( - peripherals.IO_BANK0, - peripherals.PADS_BANK0, - sio.gpio_bank0, - &mut peripherals.RESETS, - ); + // let pins = rp2040_hal::gpio::Pins::new( + // peripherals.IO_BANK0, + // peripherals.PADS_BANK0, + // sio.gpio_bank0, + // &mut peripherals.RESETS, + // ); // Attiny + RTC comms let sda_pin = pins.gpio6.into_function::(); diff --git a/src/onboard_flash.rs b/src/onboard_flash.rs index f780476..0afe0d9 100644 --- a/src/onboard_flash.rs +++ b/src/onboard_flash.rs @@ -12,6 +12,8 @@ // in blocks of a certain size. use crate::bsp::pac::SPI1; +use crate::byte_slice_cursor::CursorMut; + use byteorder::{ByteOrder, LittleEndian}; use core::mem; use crc::{Crc, CRC_16_XMODEM}; @@ -20,6 +22,7 @@ use embedded_hal::digital::v2::OutputPin; use embedded_hal::prelude::{ _embedded_hal_blocking_spi_Transfer, _embedded_hal_blocking_spi_Write, }; +use embedded_io::Read; use fugit::{HertzU32, RateExtU32}; use rp2040_hal::dma::{bidirectional, Channel, CH1, CH2}; @@ -49,7 +52,8 @@ const FEATURE_CONFIG: u8 = 0xb0; const FEATURE_BLOCK_LOCK: u8 = 0xa0; const FEATURE_DIE_SELECT: u8 = 0xd0; -const NUM_RECORDING_BLOCKS: isize = 2048 - 5; // Leave 1 block between recordings and event logs +const CONFIG_BLOCKS: isize = 2; +const NUM_RECORDING_BLOCKS: isize = 2048 - 5 - CONFIG_BLOCKS; // Leave 1 block between recordings and event logs struct FileAllocation { offset: u32, @@ -311,7 +315,9 @@ pub struct OnboardFlash { dma_channel_2: Option>, record_to_flash: bool, pub payload_buffer: Option<&'static mut [u8; 2115]>, - pub file_start_block_index: Option, //start of currently writing file, or last written + pub file_start_block_index: Option, + pub config_block: Option, + pub audio_block: Option, //start of currently writing file, or last written } /// Each block is made up 64 pages of 2176 bytes. 139,264 bytes per block. /// Each page has a 2048 byte data storage section and a 128byte spare area for ECC codes. @@ -351,6 +357,8 @@ impl OnboardFlash { payload_buffer: payload_buffer, file_start_block_index: None, previous_file_start_block_index: None, + config_block: None, + audio_block: None, } } pub fn init(&mut self) { @@ -369,6 +377,157 @@ impl OnboardFlash { self.unlock_blocks(); } + pub fn set_config_block(&mut self) { + let mut block_i = (NUM_RECORDING_BLOCKS + CONFIG_BLOCKS) as i16; + while self.bad_blocks.contains(&block_i) { + block_i -= 1; + info!("Bad block {} for audio config", block_i) + } + if block_i <= 0 { + panic!("No good blocks found for config"); + } + self.audio_block = Some(block_i as isize); + block_i -= 1; + while self.bad_blocks.contains(&block_i) { + block_i -= 1; + info!("Bad block {} for device config", block_i) + } + if block_i <= 0 { + panic!("No good blocks found for config"); + } + self.config_block = Some(block_i as isize); + info!( + "Using {} for audio config and {} for device config", + self.audio_block, self.config_block + ); + } + pub fn write_device_config(&mut self, device_bytes: &mut [u8]) { + if self.config_block.is_none() { + panic!("Config block has not been initialized, call flash_storage.init()"); + } + let config_block = self.config_block.unwrap(); + self.erase_block(config_block); + // let mut payload = [0xffu8; 2115]; + let mut start = 0; + let mut page = 0; + let mut is_last = false; + + while !is_last { + let mut end = start + 2048; + if start + 2048 > device_bytes.len() { + end = device_bytes.len(); + } + let mut payload = [0xffu8; 2115]; + let is_last = end == device_bytes.len(); + let device_chunk = &mut device_bytes[start..end]; + self.write_config_bytes( + device_chunk, + device_chunk.len(), + is_last, + config_block, + page, + ); + page += 1; + } + } + + pub fn write_config_bytes( + &mut self, + bytes: &mut [u8], + user_bytes_length: usize, + is_last: bool, + b: isize, + p: isize, + ) -> Result<(), &str> { + self.write_enable(); + assert_eq!(self.write_enabled(), true); + // Bytes will always be a full page + metadata + command info at the start + assert_eq!(bytes.len(), 2112 + 4); // 2116 + + let address = OnboardFlash::get_address(b, p); + let plane = ((b % 2) << 4) as u8; + let crc_check = Crc::::new(&CRC_16_XMODEM); + let crc = crc_check.checksum(&bytes[4..4 + user_bytes_length]); + bytes[1] = PROGRAM_LOAD; + bytes[2] = plane; + bytes[3] = 0; + { + //Now write into the user meta section + bytes[4..][0x820..=0x83f][0] = 0; // Page is used + bytes[4..][0x820..=0x83f][1] = if is_last { 0 } else { 0xff }; // Page is last page of file? + { + let space = &mut bytes[4..][0x820..=0x83f][2..=3]; + //info!("Writing {} bytes", user_bytes_length); + LittleEndian::write_u16(space, user_bytes_length as u16); + } + // TODO Write user detected bad blocks into user-metadata section? + + bytes[4..][0x820..=0x83f][4] = b as u8; + bytes[4..][0x820..=0x83f][5] = p as u8; + { + let space = &mut bytes[4..][0x820..=0x83f][6..=7]; + LittleEndian::write_u16(space, user_bytes_length as u16); + } + { + let space = &mut bytes[4..][0x820..=0x83f][8..=9]; + LittleEndian::write_u16(space, crc); + } + { + let space = &mut bytes[4..][0x820..=0x83f][10..=11]; + LittleEndian::write_u16(space, crc); + } + } + if is_last { + warn!("Ending file at {}:{}", b, p); + } + + if self.record_to_flash { + self.spi_write(&bytes[1..]); + self.spi_write(&[PROGRAM_EXECUTE, address[0], address[1], address[2]]); + } + + // FIXME - can program failed bit get set, and then discarded, before wait for ready completes? + let status = self.wait_for_ready(); + + if status.program_failed() { + error!("Programming failed"); + } + if !status.erase_failed() { + // Relocate earlier pages on this block to the next free block + // Re-write this page + // Erase and mark the earlier block as bad. + } + return Ok(()); + } + + pub fn read_device_config(&mut self) -> Result<[u8; 2505], &str> { + if self.config_block.is_none() { + panic!("Config block has not been initialized, call flash_storage.init()"); + } + //guess this is the size of the config at the moment + let mut config_bytes = [0u8; 2400 + 105]; + + let block_i = self.config_block.unwrap(); + let mut page_i = 0; + let mut is_last = false; + let mut cursor = CursorMut::new(&mut config_bytes); + + while !is_last { + self.read_page(block_i as isize, page_i).unwrap(); + self.read_page_metadata(block_i as isize); + self.wait_for_all_ready(); + if !self.current_page.page_is_used() { + return Err("Config block empty"); + // return Err(&format!("Config block {} page {} empty", block,_i page_i)); + } + let length = self.current_page.page_bytes_used(); + cursor.write_bytes(&self.current_page.user_data()[..length]); + is_last = self.current_page.is_last_page_for_file(); + page_i += 1; + } + return Ok(config_bytes); + } + pub fn reset(&mut self) { self.spi_write(&[RESET]); self.wait_for_ready(); diff --git a/src/rp2040_flash.rs b/src/rp2040_flash.rs index e5f6327..43857fe 100644 --- a/src/rp2040_flash.rs +++ b/src/rp2040_flash.rs @@ -29,7 +29,7 @@ pub const FLASH_EVENT_LOG_SIZE: u32 = 256 * 4096; // Amount dedicated to user ev // 2460 bytes //Config is stored in first sector of the flash drive 0-4096 bytes, if it ever becomes more will need to re arrange //the rp2040 flash alarm location too -pub fn write_device_config_to_rp2040_flash(data: &[u8]) { +pub fn write_device_config_to_rp2040_flash_old(data: &[u8]) { let addr = FLASH_END - FLASH_USER_SIZE; unsafe { cortex_m::interrupt::free(|_cs| { @@ -42,13 +42,13 @@ pub fn write_device_config_to_rp2040_flash(data: &[u8]) { }); } } -pub fn read_device_config_from_rp2040_flash() -> &'static [u8] { +pub fn read_device_config_from_rp2040_flash_old() -> &'static [u8] { let addr = (FLASH_XIP_BASE + FLASH_END - FLASH_USER_SIZE) as *const u8; unsafe { slice::from_raw_parts(addr, FLASH_USER_SIZE as usize) } } // this is assuming the device config stucture has not change and is audio mode is at byte 4 -pub fn read_is_audio_from_rp2040_flash() -> bool { +pub fn read_is_audio_from_rp2040_flash_old() -> bool { let addr = (FLASH_XIP_BASE + FLASH_END - FLASH_USER_SIZE) as *const u8; let config = unsafe { slice::from_raw_parts(addr, 5usize) }; if config[0] == u8::MAX && config[1] == u8::MAX && config[2] == u8::MAX && config[3] == u8::MAX