#![no_std]
#![no_main]
#![macro_use]
#[cfg(all(not(test), feature = "panic-handler"))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))]
compile_error!("Panic handler can only be compiled for arm and riscv32");
unsafe {
#[cfg(target_arch = "arm")]
core::arch::asm!("udf #0");
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
core::arch::asm!("UNIMP");
core::hint::unreachable_unchecked();
}
}
pub const FUNCTION_ERASE: u32 = 1;
pub const FUNCTION_PROGRAM: u32 = 2;
pub const FUNCTION_VERIFY: u32 = 3;
pub const FUNCTION_BLANKCHECK: u32 = 4;
pub type ErrorCode = core::num::NonZeroU32;
pub trait FlashAlgorithm: Sized + 'static {
fn new(address: u32, clock: u32, function: Function) -> Result<Self, ErrorCode>;
#[cfg(feature = "erase-chip")]
fn erase_all(&mut self) -> Result<(), ErrorCode>;
fn erase_sector(&mut self, address: u32) -> Result<(), ErrorCode>;
fn program_page(&mut self, address: u32, data: &[u8]) -> Result<(), ErrorCode>;
#[cfg(feature = "verify")]
fn verify(&mut self, address: u32, size: u32, data: Option<&[u8]>) -> Result<(), u32>;
#[cfg(feature = "read-flash")]
fn read_flash(&mut self, address: u32, data: &mut [u8]) -> Result<(), ErrorCode>;
#[cfg(feature = "blank-check")]
fn blank_check(&mut self, address: u32, size: u32, pattern: u8) -> Result<(), ErrorCode>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Function {
Erase = 1,
Program = 2,
Verify = 3,
BlankCheck = 4,
}
#[macro_export]
macro_rules! algorithm {
($type:ty, {
device_name: $device_name:expr,
device_type: $device_type:expr,
flash_address: $flash_address:expr,
flash_size: $flash_size:expr,
page_size: $page_size:expr,
empty_value: $empty_value:expr,
program_time_out: $program_time_out:expr,
erase_time_out: $erase_time_out:expr,
sectors: [$({
size: $size:expr,
address: $address:expr,
}),+]
}) => {
static mut _IS_INIT: bool = false;
static mut _ALGO_INSTANCE: core::mem::MaybeUninit<$type> = core::mem::MaybeUninit::uninit();
core::arch::global_asm!(".section .PrgData, \"aw\"");
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn Init(addr: u32, clock: u32, function: u32) -> u32 {
unsafe {
if _IS_INIT {
UnInit();
}
_IS_INIT = true;
}
let function = match function {
1 => $crate::Function::Erase,
2 => $crate::Function::Program,
3 => $crate::Function::Verify,
4 => $crate::Function::BlankCheck,
_ => core::panic!("This branch can only be reached if the host library sent an unknown function code.")
};
match <$type as $crate::FlashAlgorithm>::new(addr, clock, function) {
Ok(inst) => {
unsafe { _ALGO_INSTANCE.as_mut_ptr().write(inst) };
unsafe { _IS_INIT = true };
0
}
Err(e) => e.get(),
}
}
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn UnInit() -> u32 {
unsafe {
if !_IS_INIT {
return 1;
}
_ALGO_INSTANCE.as_mut_ptr().drop_in_place();
_IS_INIT = false;
}
0
}
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn EraseSector(addr: u32) -> u32 {
let this = unsafe {
if !unsafe { _IS_INIT } {
return 1;
}
&mut *_ALGO_INSTANCE.as_mut_ptr()
};
match <$type as $crate::FlashAlgorithm>::erase_sector(this, addr) {
Ok(()) => 0,
Err(e) => e.get(),
}
}
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn ProgramPage(addr: u32, size: u32, data: *const u8) -> u32 {
let (this, data_slice) = unsafe {
if !_IS_INIT {
return 1;
}
let this = &mut *_ALGO_INSTANCE.as_mut_ptr();
let data_slice: &[u8] = core::slice::from_raw_parts(data, size as usize);
(this, data_slice)
};
match <$type as $crate::FlashAlgorithm>::program_page(this, addr, data_slice) {
Ok(()) => 0,
Err(e) => e.get(),
}
}
$crate::erase_chip!($type);
$crate::read_flash!($type);
$crate::verify!($type);
$crate::blank_check!($type);
#[allow(non_upper_case_globals)]
#[no_mangle]
#[used]
#[link_section = "DeviceData"]
pub static FlashDevice: FlashDeviceDescription = FlashDeviceDescription {
vers: 0x1u16.to_le(),
dev_name: $crate::arrayify_string($device_name),
dev_type: ($device_type as u16).to_le(),
dev_addr: ($flash_address as u32).to_le(),
device_size: ($flash_size as u32).to_le(),
page_size: ($page_size as u32).to_le(),
_reserved: 0,
empty: ($empty_value as u8).to_le(),
program_time_out: ($program_time_out as u32).to_le(),
erase_time_out: ($erase_time_out as u32).to_le(),
flash_sectors: [
$(
FlashSector {
size: ($size as u32).to_le(),
address: ($address as u32).to_le(),
}
),+,
FlashSector {
size: 0xffff_ffffu32.to_le(),
address: 0xffff_ffffu32.to_le(),
}
],
};
#[repr(C)]
pub struct FlashDeviceDescription {
vers: u16,
dev_name: [u8; 128],
dev_type: u16,
dev_addr: u32,
device_size: u32,
page_size: u32,
_reserved: u32,
empty: u8,
program_time_out: u32,
erase_time_out: u32,
flash_sectors: [FlashSector; $crate::count!($($size)*) + 1],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct FlashSector {
size: u32,
address: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum DeviceType {
Unknown = 0,
Onchip = 1,
Ext8Bit = 2,
Ext16Bit = 3,
Ext32Bit = 4,
ExtSpi = 5,
}
};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "erase-chip"))]
macro_rules! erase_chip {
($type:ty) => {};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "erase-chip")]
macro_rules! erase_chip {
($type:ty) => {
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn EraseChip() -> u32 {
let this = unsafe {
if !_IS_INIT {
return 1;
}
&mut *_ALGO_INSTANCE.as_mut_ptr()
};
match <$type as $crate::FlashAlgorithm>::erase_all(this) {
Ok(()) => 0,
Err(e) => e.get(),
}
}
};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "read-flash"))]
macro_rules! read_flash {
($type:ty) => {};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "read-flash")]
macro_rules! read_flash {
($type:ty) => {
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn ReadFlash(addr: u32, size: u32, data: *mut u8) -> u32 {
let (this, data_slice) = unsafe {
if !_IS_INIT {
return 1;
}
let this = &mut *_ALGO_INSTANCE.as_mut_ptr();
let data_slice: &mut [u8] = core::slice::from_raw_parts_mut(data, size as usize);
(this, data_slice)
};
match <$type as $crate::FlashAlgorithm>::read_flash(this, addr, data_slice) {
Ok(()) => 0,
Err(e) => e.get(),
}
}
};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "verify"))]
macro_rules! verify {
($type:ty) => {};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "verify")]
macro_rules! verify {
($type:ty) => {
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn Verify(addr: u32, size: u32, data: *const u8) -> u32 {
let this = unsafe {
if !_IS_INIT {
return 1;
}
&mut *_ALGO_INSTANCE.as_mut_ptr()
};
if data.is_null() {
match <$type as $crate::FlashAlgorithm>::verify(this, addr, size, None) {
Ok(()) => addr.wrapping_add(size),
Err(e) => e,
}
} else {
let data_slice: &[u8] = unsafe { core::slice::from_raw_parts(data, size as usize) };
match <$type as $crate::FlashAlgorithm>::verify(this, addr, size, Some(data_slice)) {
Ok(()) => addr.wrapping_add(size),
Err(e) => e,
}
}
}
};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "blank-check"))]
macro_rules! blank_check {
($type:ty) => {};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "blank-check")]
macro_rules! blank_check {
($type:ty) => {
#[no_mangle]
#[link_section = ".entry"]
pub unsafe extern "C" fn BlankCheck(addr: u32, size: u32, pattern: u8) -> u32 {
let this = unsafe {
if !_IS_INIT {
return 1;
}
&mut *_ALGO_INSTANCE.as_mut_ptr()
};
match <$type as $crate::FlashAlgorithm>::blank_check(this, addr, size, pattern) {
Ok(()) => 0,
Err(e) => e.get(),
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
pub const fn arrayify_string<const N: usize>(msg: &'static str) -> [u8; N] {
let mut arr = [0u8; N];
let mut idx = 0;
let msg_bytes = msg.as_bytes();
while (idx < msg_bytes.len()) && (idx < N) {
arr[idx] = msg_bytes[idx];
idx += 1;
}
arr
}