blob: 5b04bf6cf6503b1799fe854d52cbfc773ea4e747 [file] [log] [blame]
David Brownde7729e2017-01-09 10:41:35 -07001//! A flash simulator
2//!
3//! This module is capable of simulating the type of NOR flash commonly used in microcontrollers.
4//! These generally can be written as individual bytes, but must be erased in larger units.
5
6use std::iter::Enumerate;
7use std::slice;
8use pdump::HexDump;
9
10error_chain! {
11 errors {
12 OutOfBounds(t: String) {
13 description("Offset is out of bounds")
14 display("Offset out of bounds: {}", t)
15 }
16 Write(t: String) {
17 description("Invalid write")
18 display("Invalid write: {}", t)
19 }
20 }
21}
22
23fn ebounds<T: AsRef<str>>(message: T) -> ErrorKind {
24 ErrorKind::OutOfBounds(message.as_ref().to_owned())
25}
26
27fn ewrite<T: AsRef<str>>(message: T) -> ErrorKind {
28 ErrorKind::Write(message.as_ref().to_owned())
29}
30
31/// An emulated flash device. It is represented as a block of bytes, and a list of the sector
32/// mapings.
33#[derive(Clone)]
34pub struct Flash {
35 data: Vec<u8>,
36 sectors: Vec<usize>,
David Brown562a7a02017-01-23 11:19:03 -070037 // Alignment required for writes.
38 align: usize,
David Brownde7729e2017-01-09 10:41:35 -070039}
40
41impl Flash {
42 /// Given a sector size map, construct a flash device for that.
David Brown562a7a02017-01-23 11:19:03 -070043 pub fn new(sectors: Vec<usize>, align: usize) -> Flash {
44 // Verify that the alignment is a positive power of two.
45 assert!(align > 0);
46 assert!(align & (align - 1) == 0);
47
David Brownde7729e2017-01-09 10:41:35 -070048 let total = sectors.iter().sum();
49 Flash {
50 data: vec![0xffu8; total],
51 sectors: sectors,
David Brown562a7a02017-01-23 11:19:03 -070052 align: align,
David Brownde7729e2017-01-09 10:41:35 -070053 }
54 }
55
56 /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be
57 /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
58 /// return an error.
59 pub fn erase(&mut self, offset: usize, len: usize) -> Result<()> {
60 let (_start, slen) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?;
61 let (end, elen) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?;
62
63 if slen != 0 {
64 bail!(ebounds("offset not at start of sector"));
65 }
66 if elen != self.sectors[end] - 1 {
67 bail!(ebounds("end not at start of sector"));
68 }
69
70 for x in &mut self.data[offset .. offset + len] {
71 *x = 0xff;
72 }
73
74 Ok(())
75 }
76
77 /// Writes are fairly unconstrained, but we restrict to only allowing writes of values that
78 /// are entirely written as 0xFF.
79 pub fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> {
80 if offset + payload.len() > self.data.len() {
David Brownf253fa82017-01-23 15:43:47 -070081 panic!("Write outside of device");
David Brownde7729e2017-01-09 10:41:35 -070082 }
83
David Brown562a7a02017-01-23 11:19:03 -070084 // Verify the alignment (which must be a power of two).
85 if offset & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -070086 panic!("Misaligned write address");
David Brown562a7a02017-01-23 11:19:03 -070087 }
88
89 if payload.len() & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -070090 panic!("Write length not multiple of alignment");
David Brown562a7a02017-01-23 11:19:03 -070091 }
92
David Brownde7729e2017-01-09 10:41:35 -070093 let mut sub = &mut self.data[offset .. offset + payload.len()];
94 if sub.iter().any(|x| *x != 0xFF) {
95 bail!(ewrite("Write to non-FF location"));
96 }
97
98 sub.copy_from_slice(payload);
99 Ok(())
100 }
101
102 /// Read is simple.
103 pub fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
104 if offset + data.len() > self.data.len() {
105 bail!(ebounds("Read outside of device"));
106 }
107
108 let sub = &self.data[offset .. offset + data.len()];
109 data.copy_from_slice(sub);
110 Ok(())
111 }
112
113 // Scan the sector map, and return the base and offset within a sector for this given byte.
114 // Returns None if the value is outside of the device.
115 fn get_sector(&self, offset: usize) -> Option<(usize, usize)> {
116 let mut offset = offset;
117 for (sector, &size) in self.sectors.iter().enumerate() {
118 if offset < size {
119 return Some((sector, offset));
120 }
121 offset -= size;
122 }
123 return None;
124 }
125
126 /// An iterator over each sector in the device.
127 pub fn sector_iter(&self) -> SectorIter {
128 SectorIter {
129 iter: self.sectors.iter().enumerate(),
130 base: 0,
131 }
132 }
133
134 pub fn device_size(&self) -> usize {
135 self.data.len()
136 }
137
138 pub fn dump(&self) {
139 self.data.dump();
140 }
141}
142
143/// It is possible to iterate over the sectors in the device, each element returning this.
144#[derive(Debug)]
145pub struct Sector {
146 /// Which sector is this, starting from 0.
147 pub num: usize,
148 /// The offset, in bytes, of the start of this sector.
149 pub base: usize,
150 /// The length, in bytes, of this sector.
151 pub size: usize,
152}
153
154pub struct SectorIter<'a> {
155 iter: Enumerate<slice::Iter<'a, usize>>,
156 base: usize,
157}
158
159impl<'a> Iterator for SectorIter<'a> {
160 type Item = Sector;
161
162 fn next(&mut self) -> Option<Sector> {
163 match self.iter.next() {
164 None => None,
165 Some((num, &size)) => {
166 let base = self.base;
167 self.base += size;
168 Some(Sector {
169 num: num,
170 base: base,
171 size: size,
172 })
173 }
174 }
175 }
176}
177
178#[cfg(test)]
179mod test {
180 use super::{Flash, Error, ErrorKind, Result, Sector};
181
182 #[test]
183 fn test_flash() {
184 // NXP-style, uniform sectors.
185 let mut f1 = Flash::new(vec![4096usize; 256]);
186 test_device(&mut f1);
187
188 // STM style, non-uniform sectors
189 let mut f2 = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
190 128 * 1024, 128 * 1024, 128 * 1024]);
191 test_device(&mut f2);
192 }
193
194 fn test_device(flash: &mut Flash) {
195 let sectors: Vec<Sector> = flash.sector_iter().collect();
196
197 flash.erase(0, sectors[0].size).unwrap();
198 let flash_size = flash.device_size();
199 flash.erase(0, flash_size).unwrap();
200 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
201
202 // Verify that write and erase do something.
203 flash.write(0, &[0]).unwrap();
204 let mut buf = [0; 4];
205 flash.read(0, &mut buf).unwrap();
206 assert_eq!(buf, [0, 0xff, 0xff, 0xff]);
207
208 flash.erase(0, sectors[0].size).unwrap();
209 flash.read(0, &mut buf).unwrap();
210 assert_eq!(buf, [0xff; 4]);
211
212 // Program the first and last byte of each sector, verify that has been done, and then
213 // erase to verify the erase boundaries.
214 for sector in &sectors {
215 let byte = [(sector.num & 127) as u8];
216 flash.write(sector.base, &byte).unwrap();
217 flash.write(sector.base + sector.size - 1, &byte).unwrap();
218 }
219
220 // Verify the above
221 let mut buf = Vec::new();
222 for sector in &sectors {
223 let byte = (sector.num & 127) as u8;
224 buf.resize(sector.size, 0);
225 flash.read(sector.base, &mut buf).unwrap();
226 assert_eq!(buf.first(), Some(&byte));
227 assert_eq!(buf.last(), Some(&byte));
228 assert!(buf[1..buf.len()-1].iter().all(|&x| x == 0xff));
229 }
230 }
231
232 // Helper checks for the result type.
233 trait EChecker {
234 fn is_bounds(&self) -> bool;
235 }
236
237 impl<T> EChecker for Result<T> {
238
239 fn is_bounds(&self) -> bool {
240 match *self {
241 Err(Error(ErrorKind::OutOfBounds(_), _)) => true,
242 _ => false,
243 }
244 }
245 }
246}