use super::flash_properties::FlashProperties;
use super::memory::{PageInfo, RamRegion, SectorInfo};
use std::{borrow::Cow, convert::TryInto};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FlashAlgorithm {
pub name: String,
pub default: bool,
pub load_address: u32,
pub instructions: Vec<u32>,
pub pc_init: Option<u32>,
pub pc_uninit: Option<u32>,
pub pc_program_page: u32,
pub pc_erase_sector: u32,
pub pc_erase_all: Option<u32>,
pub static_base: u32,
pub begin_stack: u32,
pub begin_data: u32,
pub page_buffers: Vec<u32>,
pub flash_properties: FlashProperties,
}
impl FlashAlgorithm {
pub fn sector_info(&self, address: u32) -> Option<SectorInfo> {
if !self.flash_properties.address_range.contains(&address) {
log::trace!("Address {:08x} not contained in this flash device", address);
return None;
}
let offset_address = address - self.flash_properties.address_range.start;
let containing_sector = self
.flash_properties
.sectors
.iter()
.rfind(|s| s.address <= offset_address)?;
let sector_index = (offset_address - containing_sector.address) / containing_sector.size;
let sector_address = self.flash_properties.address_range.start
+ containing_sector.address
+ sector_index * containing_sector.size;
Some(SectorInfo {
base_address: sector_address,
size: containing_sector.size,
page_size: self.flash_properties.page_size,
})
}
pub fn page_info(&self, address: u32) -> Option<PageInfo> {
if !self.flash_properties.address_range.contains(&address) {
return None;
}
Some(PageInfo {
base_address: address - (address % self.flash_properties.page_size),
size: self.flash_properties.page_size,
})
}
pub fn is_erased(&self, data: &[u8]) -> bool {
for b in data {
if *b != self.flash_properties.erased_byte_value {
return false;
}
}
true
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RawFlashAlgorithm {
pub name: Cow<'static, str>,
pub description: Cow<'static, str>,
pub default: bool,
#[serde(deserialize_with = "deserialize")]
#[serde(serialize_with = "serialize")]
pub instructions: Cow<'static, [u8]>,
pub pc_init: Option<u32>,
pub pc_uninit: Option<u32>,
pub pc_program_page: u32,
pub pc_erase_sector: u32,
pub pc_erase_all: Option<u32>,
pub data_section_offset: u32,
pub flash_properties: FlashProperties,
}
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&base64::encode(bytes))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Cow<'static, [u8]>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Base64Visitor;
impl<'de> serde::de::Visitor<'de> for Base64Visitor {
type Value = Cow<'static, [u8]>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "base64 ASCII text")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
base64::decode(v)
.map(Cow::Owned)
.map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_str(Base64Visitor)
}
impl RawFlashAlgorithm {
const FLASH_BLOB_HEADER_SIZE: u32 = 8 * 4;
const FLASH_ALGO_STACK_SIZE: u32 = 512;
const FLASH_ALGO_STACK_DECREMENT: u32 = 64;
const FLASH_BLOB_HEADER: [u32; Self::FLASH_BLOB_HEADER_SIZE as usize / 4] = [
0xE00A_BE00,
0x062D_780D,
0x2408_4068,
0xD300_0040,
0x1E64_4058,
0x1C49_D1FA,
0x2A00_1E52,
0x0477_0D1F,
];
pub fn assemble(&self, ram_region: &RamRegion) -> FlashAlgorithm {
let mut instructions = Self::FLASH_BLOB_HEADER.to_vec();
let assembled_instructions = (&self.instructions)
.chunks(4)
.map(|bytes| u32::from_le_bytes(bytes.try_into().unwrap()));
instructions.extend(assembled_instructions);
let mut offset = 0;
let mut addr_stack = 0;
let mut addr_load = 0;
let mut addr_data = 0;
for i in 0..Self::FLASH_ALGO_STACK_SIZE / Self::FLASH_ALGO_STACK_DECREMENT {
offset = Self::FLASH_ALGO_STACK_SIZE - Self::FLASH_ALGO_STACK_DECREMENT * i;
addr_stack = ram_region.range.start + offset;
addr_load = addr_stack;
offset += instructions.len() as u32 * 4;
addr_data = ram_region.range.start + offset;
offset += self.flash_properties.page_size;
if offset <= ram_region.range.end - ram_region.range.start {
break;
}
}
let addr_data2 = ram_region.range.start + offset;
offset += self.flash_properties.page_size;
let page_buffers = if offset <= ram_region.range.end - ram_region.range.start {
vec![addr_data, addr_data2]
} else {
vec![addr_data]
};
let code_start = addr_load + Self::FLASH_BLOB_HEADER_SIZE;
let name = self.name.clone().into_owned();
FlashAlgorithm {
name,
default: self.default,
load_address: addr_load,
instructions,
pc_init: self.pc_init.map(|v| code_start + v),
pc_uninit: self.pc_uninit.map(|v| code_start + v),
pc_program_page: code_start + self.pc_program_page,
pc_erase_sector: code_start + self.pc_erase_sector,
pc_erase_all: self.pc_erase_all.map(|v| code_start + v),
static_base: code_start + self.data_section_offset,
begin_stack: addr_stack,
begin_data: page_buffers[0],
page_buffers: page_buffers.clone(),
flash_properties: self.flash_properties.clone(),
}
}
}
#[test]
fn flash_sector_single_size() {
use crate::config::SectorDescription;
let config = FlashAlgorithm {
flash_properties: FlashProperties {
sectors: Cow::Borrowed(&[SectorDescription {
size: 0x100,
address: 0x0,
}]),
address_range: 0x1000..0x1000 + 0x1000,
page_size: 0x10,
..Default::default()
},
..Default::default()
};
let expected_first = SectorInfo {
base_address: 0x1000,
page_size: 0x10,
size: 0x100,
};
assert!(config.sector_info(0x1000 - 1).is_none());
assert_eq!(expected_first, config.sector_info(0x1000).unwrap());
assert_eq!(expected_first, config.sector_info(0x10ff).unwrap());
assert_eq!(expected_first, config.sector_info(0x100b).unwrap());
assert_eq!(expected_first, config.sector_info(0x10ea).unwrap());
}
#[test]
fn flash_sector_single_size_weird_sector_size() {
use crate::config::SectorDescription;
let config = FlashAlgorithm {
flash_properties: FlashProperties {
sectors: Cow::Borrowed(&[SectorDescription {
size: 258,
address: 0x0,
}]),
address_range: 0x800_0000..0x800_0000 + 258 * 10,
page_size: 0x10,
..Default::default()
},
..Default::default()
};
let expected_first = SectorInfo {
base_address: 0x800_0000,
page_size: 0x10,
size: 258,
};
assert!(config.sector_info(0x800_0000 - 1).is_none());
assert_eq!(expected_first, config.sector_info(0x800_0000).unwrap());
assert_eq!(
expected_first,
config.sector_info(0x800_0000 + 257).unwrap()
);
assert_eq!(expected_first, config.sector_info(0x800_000b).unwrap());
assert_eq!(expected_first, config.sector_info(0x800_00e0).unwrap());
}
#[test]
fn flash_sector_multiple_sizes() {
use crate::config::SectorDescription;
let config = FlashAlgorithm {
flash_properties: FlashProperties {
sectors: Cow::Borrowed(&[
SectorDescription {
size: 0x4000,
address: 0x0,
},
SectorDescription {
size: 0x1_0000,
address: 0x1_0000,
},
SectorDescription {
size: 0x2_0000,
address: 0x2_0000,
},
]),
address_range: 0x800_0000..0x800_0000 + 0x10_0000,
page_size: 0x10,
..Default::default()
},
..Default::default()
};
let expected_a = SectorInfo {
base_address: 0x800_4000,
page_size: 0x10,
size: 0x4000,
};
let expected_b = SectorInfo {
base_address: 0x801_0000,
page_size: 0x10,
size: 0x1_0000,
};
let expected_c = SectorInfo {
base_address: 0x80A_0000,
page_size: 0x10,
size: 0x2_0000,
};
assert_eq!(expected_a, config.sector_info(0x800_4000).unwrap());
assert_eq!(expected_b, config.sector_info(0x801_0000).unwrap());
assert_eq!(expected_c, config.sector_info(0x80A_0000).unwrap());
}