#![no_std]
extern crate heapless;
use embedded_shadow::prelude::*;
use heapless::Vec;
struct FlashSectorPolicy {
sector_size: u16,
}
impl FlashSectorPolicy {
const fn new(sector_size: u16) -> Self {
Self { sector_size }
}
}
impl PersistPolicy<u16> for FlashSectorPolicy {
fn push_persist_keys_for_range<F>(&self, addr: u16, len: usize, mut push_key: F) -> bool
where
F: FnMut(u16),
{
let start_sector = addr / self.sector_size;
let end_addr = addr + len as u16;
let end_sector = end_addr.div_ceil(self.sector_size);
for sector in start_sector..end_sector {
push_key(sector * self.sector_size); }
true
}
}
struct CriticalRegisterPolicy;
impl PersistPolicy<u16> for CriticalRegisterPolicy {
fn push_persist_keys_for_range<F>(&self, addr: u16, len: usize, mut push_key: F) -> bool
where
F: FnMut(u16),
{
const CRITICAL_START: u16 = 0x00;
const CRITICAL_END: u16 = 0x20;
let end_addr = addr + len as u16;
if addr < CRITICAL_END && end_addr > CRITICAL_START {
push_key(addr);
true } else {
false }
}
}
struct BatchedFlashTrigger {
pending_sectors: Vec<u16, 16>,
write_count: usize,
batch_size: usize,
}
impl BatchedFlashTrigger {
fn new(batch_size: usize) -> Self {
Self {
pending_sectors: Vec::new(),
write_count: 0,
batch_size,
}
}
fn do_persist(&mut self) {
if !self.pending_sectors.is_empty() {
for sector in self.pending_sectors.iter() {
let _ = sector; }
self.pending_sectors.clear();
self.write_count = 0;
}
}
}
impl PersistTrigger<u16> for BatchedFlashTrigger {
fn push_key(&mut self, sector_addr: u16) {
if !self.pending_sectors.contains(§or_addr) {
let _ = self.pending_sectors.push(sector_addr);
}
self.write_count += 1;
}
fn request_persist(&mut self) {
if self.write_count >= self.batch_size {
self.do_persist();
}
}
}
struct ImmediatePersistTrigger {
last_persisted_addr: Option<u16>,
}
impl ImmediatePersistTrigger {
fn new() -> Self {
Self {
last_persisted_addr: None,
}
}
}
impl PersistTrigger<u16> for ImmediatePersistTrigger {
fn push_key(&mut self, addr: u16) {
self.last_persisted_addr = Some(addr);
}
fn request_persist(&mut self) {
if let Some(addr) = self.last_persisted_addr {
let _ = addr; self.last_persisted_addr = None;
}
}
}
pub fn main() {
example_flash_sectors();
example_critical_registers();
example_selective_persistence();
}
fn example_flash_sectors() {
let storage = ShadowStorageBuilder::new()
.total_size::<16384>() .block_size::<256>() .block_count::<64>() .default_access()
.persist_policy(FlashSectorPolicy::new(4096)) .persist_trigger(BatchedFlashTrigger::new(4)) .build();
let host = storage.host_shadow();
host.with_view(|view| {
view.write_range(0x100, &[0x01; 32]).unwrap(); view.write_range(0x200, &[0x02; 32]).unwrap(); view.write_range(0x300, &[0x03; 32]).unwrap();
view.write_range(0x1000, &[0x04; 32]).unwrap();
view.write_range(0x2000, &[0x05; 32]).unwrap(); });
}
fn example_critical_registers() {
let storage = ShadowStorageBuilder::new()
.total_size::<256>()
.block_size::<32>()
.block_count::<8>()
.default_access()
.persist_policy(CriticalRegisterPolicy)
.persist_trigger(ImmediatePersistTrigger::new())
.build();
let host = storage.host_shadow();
host.with_view(|view| {
view.write_range(0x10, &[0xFF; 4]).unwrap();
view.write_range(0x80, &[0xAA; 4]).unwrap();
view.write_range(0x00, &[0x12, 0x34]).unwrap();
});
}
fn example_selective_persistence() {
struct ConfigOnlyPolicy;
impl PersistPolicy<&'static str> for ConfigOnlyPolicy {
fn push_persist_keys_for_range<F>(&self, addr: u16, _len: usize, mut push_key: F) -> bool
where
F: FnMut(&'static str),
{
match addr {
0x000..=0x0FF => {
push_key("boot_config");
true
}
0x100..=0x1FF => {
push_key("app_config");
true
}
0x200..=0x2FF => {
push_key("user_settings");
true
}
_ => false, }
}
}
struct ConfigGroupTrigger {
pending: Vec<&'static str, 8>,
}
impl ConfigGroupTrigger {
fn new() -> Self {
Self {
pending: Vec::new(),
}
}
}
impl PersistTrigger<&'static str> for ConfigGroupTrigger {
fn push_key(&mut self, config_type: &'static str) {
if !self.pending.contains(&config_type) {
let _ = self.pending.push(config_type);
}
}
fn request_persist(&mut self) {
for config in self.pending.iter() {
match *config {
"boot_config" => {
}
"app_config" => {
}
"user_settings" => {
}
_ => {}
}
}
self.pending.clear();
}
}
let storage = ShadowStorageBuilder::new()
.total_size::<1024>()
.block_size::<64>()
.block_count::<16>()
.default_access()
.persist_policy(ConfigOnlyPolicy)
.persist_trigger(ConfigGroupTrigger::new())
.build();
let host = storage.host_shadow();
host.with_view(|view| {
view.write_range(0x050, &[0x11; 8]).unwrap();
view.write_range(0x300, &[0x22; 8]).unwrap();
view.write_range(0x150, &[0x33; 8]).unwrap();
});
}
#[cfg(test)]
mod tests {
#[test]
fn test_persist_example() {
super::main();
}
}