diff --git a/Cargo.toml b/Cargo.toml index 735f340..8352dcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] cortex-m = "0.7.0" +flash-algorithm = "0.4.0" # this lets you use `cargo fix`! [[bin]] diff --git a/link.x b/link.x index f2f2ee4..3918a45 100644 --- a/link.x +++ b/link.x @@ -35,6 +35,30 @@ SECTIONS { . = ALIGN(4); } + /* Section for data, specified by flashloader standard. */ + PrgData : { + /* + * We're explicitly putting a single object here (PRGDATA_Start in main.c) as this is required by some tools. + * It is not used by this algorithm + */ + KEEP(*(PrgData)) + + . = ALIGN(4); + } + + /* Description of the flash algorithm */ + DevDscr . : { + /* The device data content is only for external tools, + * and usually not referenced by the code. + * All rules have exceptions: device data is used by this flash algo. + * + * The KEEP statement ensures it's not removed by accident. + */ + KEEP(*(DeviceData)) + + . = ALIGN(4); + } + /DISCARD/ : { /* Unused exception related info that only wastes space */ *(.ARM.exidx); diff --git a/src/algo.rs b/src/algo.rs deleted file mode 100644 index 19d8b4e..0000000 --- a/src/algo.rs +++ /dev/null @@ -1,124 +0,0 @@ -#![macro_use] - -use core::arch::asm; -use core::num::NonZeroU32; - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { - asm!("udf #0"); - core::hint::unreachable_unchecked(); - } -} - -pub type ErrorCode = NonZeroU32; - -pub trait FlashAlgo: Sized + 'static { - /// Initialize the flash algorithm. - fn new(address: u32, clock: u32, function: u32) -> Result; - - /// Erase entire chip. May only be called after init() with FUNCTION_ERASE - fn erase_all(&mut self) -> Result<(), ErrorCode>; - - /// Erase sector. May only be called after init() with FUNCTION_ERASE - fn erase_sector(&mut self, addr: u32) -> Result<(), ErrorCode>; - - /// Program bytes. May only be called after init() with FUNCTION_PROGRAM - fn program_page(&mut self, addr: u32, size: u32, data: *const u8) -> Result<(), ErrorCode>; -} - -#[macro_export] -macro_rules! algo { - ($type:ty) => { - static mut _IS_INIT: bool = false; - static mut _ALGO_INSTANCE: MaybeUninit<$type> = MaybeUninit::uninit(); - - /// Initialise the Flash Algorithm - /// - /// # Safety - /// - /// Will disable execution from Flash. Ensure you are running from SRAM - /// and do not call any flash-based based functions. - #[no_mangle] - #[link_section = ".entry"] - pub unsafe extern "C" fn Init(addr: u32, clock: u32, function: u32) -> u32 { - if _IS_INIT { - UnInit(); - } - match <$type as FlashAlgo>::new(addr, clock, function) { - Ok(inst) => { - _ALGO_INSTANCE.as_mut_ptr().write(inst); - _IS_INIT = true; - 0 - } - Err(e) => e.get(), - } - } - /// Uninitialise the Flash Algorithm - #[no_mangle] - #[link_section = ".entry"] - pub extern "C" fn UnInit() -> u32 { - unsafe { - if !_IS_INIT { - return 1; - } - _ALGO_INSTANCE.as_mut_ptr().drop_in_place(); - _IS_INIT = false; - } - 0 - } - /// Erase the flash chip. - /// - /// # Safety - /// - /// Will erase the flash chip. Ensure you really want to erase the - /// flash chip. - #[no_mangle] - #[link_section = ".entry"] - pub unsafe extern "C" fn EraseChip() -> u32 { - if !_IS_INIT { - return 1; - } - let this = &mut *_ALGO_INSTANCE.as_mut_ptr(); - match <$type as FlashAlgo>::erase_all(this) { - Ok(()) => 0, - Err(e) => e.get(), - } - } - /// Erase the a sector on the flash chip. - /// - /// # Safety - /// - /// Will erase the given sector. Pass a valid sector address. - #[no_mangle] - #[link_section = ".entry"] - pub unsafe extern "C" fn EraseSector(addr: u32) -> u32 { - if !_IS_INIT { - return 1; - } - let this = &mut *_ALGO_INSTANCE.as_mut_ptr(); - match <$type as FlashAlgo>::erase_sector(this, addr) { - Ok(()) => 0, - Err(e) => e.get(), - } - } - /// Write to a page on the flash chip. - /// - /// # Safety - /// - /// Will program the given page. Pass a valid page address, and a - /// valid pointer to at least `size` bytes of data. - #[no_mangle] - #[link_section = ".entry"] - pub unsafe extern "C" fn ProgramPage(addr: u32, size: u32, data: *const u8) -> u32 { - if !_IS_INIT { - return 1; - } - let this = &mut *_ALGO_INSTANCE.as_mut_ptr(); - match <$type as FlashAlgo>::program_page(this, addr, size, data) { - Ok(()) => 0, - Err(e) => e.get(), - } - } - }; -} diff --git a/src/main.rs b/src/main.rs index d181a73..50446f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,7 @@ #![no_std] #![no_main] -mod algo; - -use core::mem::MaybeUninit; - -use self::algo::*; +use flash_algorithm::*; fn find_func(tag: [u8; 2]) -> Option { let tag = u16::from_le_bytes(tag) as u32; @@ -53,15 +49,23 @@ struct RP2040Algo { funcs: ROMFuncs, } -algo!(RP2040Algo); +algorithm!(RP2040Algo, { + flash_address: 0x1000_0000, + flash_size: 0x0100_0000, + page_size: 0x100, + empty_value: 0xFF, + sectors: [{ + size: 0x1000, + address: 0x10000000, + }] +}); const BLOCK_SIZE: u32 = 65536; const SECTOR_SIZE: u32 = 4096; const BLOCK_ERASE_CMD: u8 = 0xd8; -const FLASH_BASE: u32 = 0x1000_0000; -impl FlashAlgo for RP2040Algo { - fn new(_address: u32, _clock: u32, _function: u32) -> Result { +impl FlashAlgorithm for RP2040Algo { + fn new(_address: u32, _clock: u32, _function: Function) -> Result { let Some(funcs) = ROMFuncs::load() else { return Err(ErrorCode::new(1).unwrap()); }; @@ -76,12 +80,21 @@ impl FlashAlgo for RP2040Algo { } fn erase_sector(&mut self, addr: u32) -> Result<(), ErrorCode> { - (self.funcs.flash_range_erase)(addr - FLASH_BASE, SECTOR_SIZE, BLOCK_SIZE, BLOCK_ERASE_CMD); + (self.funcs.flash_range_erase)( + addr - FlashDevice.dev_addr, + SECTOR_SIZE, + BLOCK_SIZE, + BLOCK_ERASE_CMD, + ); Ok(()) } - fn program_page(&mut self, addr: u32, size: u32, data: *const u8) -> Result<(), ErrorCode> { - (self.funcs.flash_range_program)(addr - FLASH_BASE, data, size); + fn program_page(&mut self, addr: u32, data: &[u8]) -> Result<(), ErrorCode> { + (self.funcs.flash_range_program)( + addr - FlashDevice.dev_addr, + data.as_ptr(), + data.len() as u32, + ); Ok(()) } } @@ -92,3 +105,13 @@ impl Drop for RP2040Algo { (self.funcs.flash_enter_cmd_xip)(); } } + +/// Some tools (eg Segger's debugger) require the PrgData section to exist in the target binary +/// +/// They scan the flashloader binary for this symbol to determine the section location +/// If they cannot find it, the tool exits. This variable serves no other purpose +#[allow(non_upper_case_globals)] +#[no_mangle] +#[used] +#[link_section = "PrgData"] +pub static mut PRGDATA_Start: usize = 0;