1use alloc::vec::Vec;
9use core::ops::Range;
10use crate::error::{Error, Result};
11
12pub const DEFAULT_PAGE_SIZE: usize = 4096;
14
15pub const DEFAULT_NUM_PAGES: usize = 16;
17
18pub const PROGRAM_PAGE_SIZE: usize = 256;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct PageId(pub usize);
24
25impl PageId {
26 pub fn to_offset(&self, page_size: usize) -> usize {
28 self.0 * page_size
29 }
30
31 pub fn is_valid(&self, num_pages: usize) -> bool {
33 self.0 < num_pages
34 }
35}
36
37#[derive(Clone, Debug)]
39pub struct FlashConfig {
40 pub page_size: usize,
42 pub num_pages: usize,
44 pub program_size: usize,
46 pub max_erase_cycles: u32,
48}
49
50impl Default for FlashConfig {
51 fn default() -> Self {
52 Self {
53 page_size: DEFAULT_PAGE_SIZE,
54 num_pages: DEFAULT_NUM_PAGES,
55 program_size: PROGRAM_PAGE_SIZE,
56 max_erase_cycles: 100_000,
57 }
58 }
59}
60
61#[derive(Debug)]
63pub struct Flash {
64 config: FlashConfig,
65 data: Vec<u8>,
66 erase_counts: Vec<u32>,
67 erased: Vec<bool>, }
69
70impl Flash {
71 pub fn new() -> Self {
73 Self::with_config(FlashConfig::default())
74 }
75
76 pub fn with_config(config: FlashConfig) -> Self {
78 let num_pages = config.num_pages;
79 let size = config.page_size * num_pages;
80 Self {
81 config,
82 data: vec![0xFF; size],
83 erase_counts: vec![0; num_pages],
84 erased: vec![true; num_pages],
85 }
86 }
87
88 pub fn capacity(&self) -> usize {
90 self.config.page_size * self.config.num_pages
91 }
92
93 pub fn page_size(&self) -> usize {
95 self.config.page_size
96 }
97
98 pub fn erase_page(&mut self, page: PageId) -> Result<()> {
103 if !page.is_valid(self.config.num_pages) {
104 return Err(Error::flash(format!(
105 "page {} out of range (0-{})",
106 page.0, self.config.num_pages - 1
107 )));
108 }
109
110 if self.erase_counts[page.0] >= self.config.max_erase_cycles {
111 return Err(Error::flash(format!(
112 "page {} exceeded max erase cycles ({})",
113 page.0, self.config.max_erase_cycles
114 )));
115 }
116
117 let start = page.to_offset(self.config.page_size);
118 self.data[start..start + self.config.page_size].fill(0xFF);
119 self.erase_counts[page.0] += 1;
120 self.erased[page.0] = true;
121
122 Ok(())
123 }
124
125 pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<()> {
130 if offset.saturating_add(data.len()) > self.capacity() {
131 return Err(Error::flash(format!(
132 "write out of bounds: offset={}, len={}, capacity={}",
133 offset, data.len(), self.capacity()
134 )));
135 }
136
137 for (i, &byte) in data.iter().enumerate() {
139 let current = self.data[offset + i];
140 if byte & !current != 0 {
141 let page = (offset + i) / self.config.page_size;
142 return Err(Error::flash(format!(
143 "write violation at offset {}: attempting 0->1 transition (page {} not erased?)",
144 offset + i, page
145 )));
146 }
147 }
148
149 self.data[offset..offset + data.len()].copy_from_slice(data);
150
151 let start_page = offset / self.config.page_size;
153 let end_page = (offset + data.len()) / self.config.page_size;
154 for p in start_page..=end_page {
155 if p < self.config.num_pages {
156 self.erased[p] = false;
157 }
158 }
159
160 Ok(())
161 }
162
163 pub fn read(&self, offset: usize, len: usize) -> Result<&[u8]> {
165 if offset.saturating_add(len) > self.capacity() {
166 return Err(Error::flash("read out of bounds"));
167 }
168 Ok(&self.data[offset..offset + len])
169 }
170
171 pub fn get_erase_count(&self, page: PageId) -> Option<u32> {
173 self.erase_counts.get(page.0).copied()
174 }
175
176 pub fn is_erased(&self, page: PageId) -> bool {
178 self.erased.get(page.0).copied().unwrap_or(false)
179 }
180
181 pub fn find_wear_leveled_pages(&self, count: usize) -> Vec<PageId> {
183 let mut indexed: Vec<_> = self.erase_counts.iter()
184 .enumerate()
185 .map(|(i, &count)| (count, i))
186 .collect();
187 indexed.sort();
188 indexed.iter().take(count).map(|(_, i)| PageId(*i)).collect()
189 }
190
191 pub fn total_erase_cycles(&self) -> u64 {
193 self.erase_counts.iter().map(|&c| c as u64).sum()
194 }
195
196 #[cfg(feature = "std")]
198 pub fn dump(&self, start: usize, len: usize) -> String {
199 use std::format;
200 let end = (start + len).min(self.capacity());
201 let mut result = String::new();
202 for chunk in self.data[start..end].chunks(16) {
203 for byte in chunk {
204 result.push_str(&format!("{:02x} ", byte));
205 }
206 result.push('\n');
207 }
208 result
209 }
210}
211
212impl Default for Flash {
213 fn default() -> Self {
214 Self::new()
215 }
216}