use crate::Error;
pub const FLASH_BYTES: usize = 2 * 1024 * 1024;
pub const FLASH_NIBBLES: usize = 2 * FLASH_BYTES;
pub const BANK_NIBBLES: usize = 0x4_0000;
#[cfg(test)]
pub const BANKS: usize = FLASH_NIBBLES / BANK_NIBBLES;
pub const BLOCK_BYTES: usize = 64 * 1024;
pub const BLOCKS: usize = FLASH_BYTES / BLOCK_BYTES;
pub const MANUFACTURER_CODE: u8 = 0xB0;
pub const DEVICE_CODE: u8 = 0xD0;
pub mod sr {
pub const READY: u8 = 0x80;
pub const ERASE_SUSPENDED: u8 = 0x40;
pub const ERASE_ERROR: u8 = 0x20;
pub const PROGRAM_ERROR: u8 = 0x10;
pub const VPP_LOW: u8 = 0x08;
pub const PROGRAM_SUSPENDED: u8 = 0x04;
pub const PROTECTED: u8 = 0x02;
pub const STICKY: u8 = ERASE_ERROR | PROGRAM_ERROR | VPP_LOW | PROTECTED;
}
pub fn latch_banks(latch: u8) -> (usize, usize) {
(usize::from((latch >> 4) & 0x3), usize::from(latch & 0xF))
}
#[cfg(test)]
pub fn latch_from_address(addr: u32) -> u8 {
((addr >> 1) & 0x3F) as u8
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReadMode {
Array,
Identifier,
Query,
Status,
ExtendedStatus,
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum Pending {
Command,
Program,
EraseConfirm,
ChipEraseConfirm,
LockConfirm,
StsConfig,
BufferCount { block: usize },
BufferData {
block: usize,
left: usize,
data: Vec<(usize, u8)>,
},
BufferConfirm {
block: usize,
data: Vec<(usize, u8)>,
},
}
const WRITE_BUFFER_BYTES: usize = 32;
#[derive(Clone, PartialEq, Eq)]
pub struct Flash {
nibbles: Vec<u8>,
mode: ReadMode,
pending: Pending,
status: u8,
lock_bits: u32,
wp_low: bool,
write_enabled: bool,
held: Option<(usize, u8)>,
generation: Generation,
}
#[derive(Clone, Copy, Debug, Default)]
struct Generation(u64);
impl PartialEq for Generation {
fn eq(&self, _: &Self) -> bool {
true
}
}
impl Eq for Generation {}
impl std::fmt::Debug for Flash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Flash")
.field("mode", &self.mode)
.field("pending", &self.pending)
.field("status", &format_args!("{:#04X}", self.status))
.field("lock_bits", &format_args!("{:#010X}", self.lock_bits))
.field("wp_low", &self.wp_low)
.field("write_enabled", &self.write_enabled)
.finish_non_exhaustive()
}
}
impl Flash {
fn from_nibble_vec(nibbles: Vec<u8>) -> Self {
Self {
nibbles,
mode: ReadMode::Array,
pending: Pending::Command,
status: sr::READY,
lock_bits: 0,
wp_low: false,
write_enabled: false,
held: None,
generation: Generation::default(),
}
}
pub fn from_packed(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != FLASH_BYTES {
return Err(Error::RomSize {
expected: FLASH_BYTES,
actual: bytes.len(),
});
}
let nibbles = bytes.iter().flat_map(|&b| [b & 0xF, b >> 4]).collect();
Ok(Self::from_nibble_vec(nibbles))
}
pub fn from_unpacked(nibbles: &[u8]) -> Result<Self, Error> {
if nibbles.len() != FLASH_NIBBLES {
return Err(Error::RomSize {
expected: FLASH_NIBBLES,
actual: nibbles.len(),
});
}
Ok(Self::from_nibble_vec(
nibbles.iter().map(|n| n & 0xF).collect(),
))
}
#[cfg(test)]
pub fn erased() -> Self {
Self::from_nibble_vec(vec![0xF; FLASH_NIBBLES])
}
pub fn to_packed(&self) -> Vec<u8> {
self.nibbles
.as_chunks::<2>()
.0
.iter()
.map(|&[lo, hi]| lo | (hi << 4))
.collect()
}
pub fn nibbles(&self) -> &[u8] {
&self.nibbles
}
pub fn generation(&self) -> u64 {
self.generation.0
}
pub fn follow(&mut self, previous: &Flash) {
self.generation = Generation(previous.generation.0.wrapping_add(1));
}
#[cfg(test)]
pub fn bank(&self, n: usize) -> &[u8] {
let start = (n % BANKS) * BANK_NIBBLES;
&self.nibbles[start..start + BANK_NIBBLES]
}
pub fn banked_address(addr: u32, latch: u8) -> usize {
let (low, high) = latch_banks(latch);
let bank = if addr & 0x4_0000 == 0 { low } else { high };
bank * BANK_NIBBLES + (addr as usize & (BANK_NIBBLES - 1))
}
pub fn write_address(addr: u32, latch: u8) -> usize {
let (_, high) = latch_banks(latch);
high * BANK_NIBBLES + (addr as usize & (BANK_NIBBLES - 1))
}
pub fn read_banked(&self, addr: u32, latch: u8) -> u8 {
self.read(Self::banked_address(addr, latch))
}
pub fn write_banked(&mut self, addr: u32, latch: u8, nibble: u8) {
self.write(Self::write_address(addr, latch), nibble);
}
pub fn read_mode(&self) -> ReadMode {
self.mode
}
pub fn status(&self) -> u8 {
self.status
}
pub fn write_enabled(&self) -> bool {
self.write_enabled
}
pub fn set_write_enabled(&mut self, enabled: bool) {
self.write_enabled = enabled;
if !enabled {
self.held = None;
}
}
pub fn set_wp_low(&mut self, low: bool) {
self.wp_low = low;
}
pub fn lock_bits(&self) -> u32 {
self.lock_bits
}
pub fn set_lock_bits(&mut self, bits: u32) {
self.lock_bits = bits;
}
pub fn wp_low(&self) -> bool {
self.wp_low
}
pub fn restore_cui(&mut self, status: u8, mode: ReadMode) {
self.status = status;
self.mode = mode;
self.pending = Pending::Command;
self.held = None;
}
pub fn reset(&mut self) {
self.mode = ReadMode::Array;
self.pending = Pending::Command;
self.status = sr::READY;
self.held = None;
}
pub fn read(&self, addr: usize) -> u8 {
let addr = addr % FLASH_NIBBLES;
let byte = match self.mode {
ReadMode::Array => return self.nibbles[addr],
ReadMode::Status => self.status,
ReadMode::ExtendedStatus => sr::READY, ReadMode::Identifier => self.identifier_byte(addr / 2),
ReadMode::Query => self.query_byte(addr / 2),
};
if addr & 1 == 0 { byte & 0xF } else { byte >> 4 }
}
pub fn write(&mut self, addr: usize, nibble: u8) {
if !self.write_enabled {
return;
}
let addr = addr % FLASH_NIBBLES;
let byte_addr = addr / 2;
let nibble = nibble & 0xF;
if addr & 1 == 0 {
self.held = Some((byte_addr, nibble));
return;
}
if let Some((held_addr, low)) = self.held.take()
&& held_addr == byte_addr
{
self.write_byte(byte_addr, low | (nibble << 4));
}
}
pub fn write_byte(&mut self, addr: usize, value: u8) {
let addr = addr % FLASH_BYTES;
const BAD_SEQUENCE: u8 = sr::ERASE_ERROR | sr::PROGRAM_ERROR;
let pending = std::mem::replace(&mut self.pending, Pending::Command);
match pending {
Pending::Command => self.command(addr, value),
Pending::Program => {
if self.blocked(addr / BLOCK_BYTES) {
self.status |= sr::PROTECTED | sr::PROGRAM_ERROR;
} else {
self.program_byte(addr, value);
}
self.mode = ReadMode::Status;
}
Pending::EraseConfirm => {
let block = addr / BLOCK_BYTES;
if value != 0xD0 {
self.status |= BAD_SEQUENCE;
} else if self.blocked(block) {
self.status |= sr::PROTECTED | sr::ERASE_ERROR;
} else {
self.erase_block(block);
}
self.mode = ReadMode::Status;
}
Pending::ChipEraseConfirm => {
if value == 0xD0 {
for block in 0..BLOCKS {
if !self.blocked(block) {
self.erase_block(block);
}
}
} else {
self.status |= BAD_SEQUENCE;
}
self.mode = ReadMode::Status;
}
Pending::LockConfirm => {
match value {
0x01 if self.wp_low => self.status |= sr::PROTECTED | sr::PROGRAM_ERROR,
0x01 => self.lock_bits |= 1 << (addr / BLOCK_BYTES),
0xD0 if self.wp_low => self.status |= sr::PROTECTED | sr::ERASE_ERROR,
0xD0 => self.lock_bits = 0,
_ => self.status |= BAD_SEQUENCE,
}
self.mode = ReadMode::Status;
}
Pending::StsConfig => {
if value > 3 {
self.status |= BAD_SEQUENCE;
}
self.mode = ReadMode::Status;
}
Pending::BufferCount { block } => {
let count = usize::from(value) + 1;
if count > WRITE_BUFFER_BYTES {
self.status |= BAD_SEQUENCE;
self.mode = ReadMode::Status;
} else {
self.mode = ReadMode::Status;
self.pending = Pending::BufferData {
block,
left: count,
data: Vec::with_capacity(count),
};
}
}
Pending::BufferData {
block,
left,
mut data,
} => {
data.push((addr, value));
self.pending = if left > 1 {
Pending::BufferData {
block,
left: left - 1,
data,
}
} else {
Pending::BufferConfirm { block, data }
};
}
Pending::BufferConfirm { block, data } => {
let in_block = data.iter().all(|(a, _)| a / BLOCK_BYTES == block);
if value != 0xD0 || !in_block {
self.status |= BAD_SEQUENCE;
} else if self.blocked(block) {
self.status |= sr::PROTECTED | sr::PROGRAM_ERROR;
} else {
for (a, d) in data {
self.program_byte(a, d);
}
}
self.mode = ReadMode::Status;
}
}
}
fn command(&mut self, addr: usize, value: u8) {
match value {
0xFF => self.mode = ReadMode::Array,
0x90 => self.mode = ReadMode::Identifier,
0x98 => self.mode = ReadMode::Query,
0x70 => self.mode = ReadMode::Status,
0x50 => self.status &= !sr::STICKY,
0x40 | 0x10 => self.pending = Pending::Program,
0x20 => self.pending = Pending::EraseConfirm,
0x30 => self.pending = Pending::ChipEraseConfirm,
0x60 => self.pending = Pending::LockConfirm,
0xB8 => self.pending = Pending::StsConfig,
0xE8 => {
self.mode = ReadMode::ExtendedStatus;
if self.status & (sr::ERASE_ERROR | sr::PROGRAM_ERROR) == 0 {
self.pending = Pending::BufferCount {
block: addr / BLOCK_BYTES,
};
}
}
0xB0 | 0xD0 => self.mode = ReadMode::Status,
_ => {}
}
}
fn blocked(&self, block: usize) -> bool {
self.wp_low && self.lock_bits & (1 << block) != 0
}
fn program_byte(&mut self, addr: usize, value: u8) {
self.generation.0 = self.generation.0.wrapping_add(1);
let n = 2 * addr;
self.nibbles[n] &= value & 0xF;
self.nibbles[n + 1] &= value >> 4;
}
fn erase_block(&mut self, block: usize) {
self.generation.0 = self.generation.0.wrapping_add(1);
let start = 2 * block * BLOCK_BYTES;
self.nibbles[start..start + 2 * BLOCK_BYTES].fill(0xF);
}
fn block_status(&self, byte_addr: usize) -> u8 {
u8::from(self.lock_bits & (1 << (byte_addr / BLOCK_BYTES)) != 0)
}
fn identifier_byte(&self, byte_addr: usize) -> u8 {
match (byte_addr % BLOCK_BYTES) >> 1 {
0 => MANUFACTURER_CODE,
1 => DEVICE_CODE,
2 => self.block_status(byte_addr),
_ => 0,
}
}
fn query_byte(&self, byte_addr: usize) -> u8 {
let word = (byte_addr % BLOCK_BYTES) >> 1;
match word {
0..=2 => self.identifier_byte(byte_addr),
0x10..=0x3E => QUERY_TABLE[word - 0x10],
_ => 0,
}
}
}
const QUERY_TABLE: [u8; 0x2F] = [
0x51, 0x52, 0x59, 0x01, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
0x27, 0x55, 0x27, 0x55, 0x03, 0x06, 0x0A, 0x0F, 0x04, 0x04, 0x04, 0x04,
0x15, 0x02, 0x00, 0x05, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x01,
0x50, 0x52, 0x49, 0x31, 0x30, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x50, 0x50,
];
#[cfg(test)]
mod tests {
use super::*;
fn chip() -> Flash {
let mut f = Flash::erased();
f.set_write_enabled(true);
f
}
fn put(f: &mut Flash, byte: usize, value: u8) {
f.write(2 * byte, value & 0xF);
f.write(2 * byte + 1, value >> 4);
}
fn get(f: &Flash, byte: usize) -> u8 {
f.read(2 * byte) | (f.read(2 * byte + 1) << 4)
}
#[test]
fn packed_round_trip_and_sizes() {
let mut img = vec![0u8; FLASH_BYTES];
img[0] = 0x21;
img[FLASH_BYTES - 1] = 0x9A;
let f = Flash::from_packed(&img).unwrap();
assert_eq!(f.read(0), 1);
assert_eq!(f.read(1), 2);
assert_eq!(f.read(FLASH_NIBBLES - 1), 9);
assert_eq!(f.to_packed(), img);
assert_eq!(
Flash::from_packed(&[0; 16]),
Err(Error::RomSize {
expected: FLASH_BYTES,
actual: 16
})
);
let unpacked: Vec<u8> = img.iter().flat_map(|&b| [b & 0xF, b >> 4]).collect();
assert_eq!(Flash::from_unpacked(&unpacked).unwrap().to_packed(), img);
assert!(Flash::from_unpacked(&img).is_err());
}
#[test]
fn writes_need_the_gate() {
let mut f = Flash::erased();
put(&mut f, 0x10, 0x40);
put(&mut f, 0x10, 0x00);
assert_eq!(get(&f, 0x10), 0xFF);
assert_eq!(f.read_mode(), ReadMode::Array);
f.set_write_enabled(true);
f.write(0x20, 0x0);
f.set_write_enabled(false);
f.set_write_enabled(true);
f.write(0x21, 0x7);
assert_eq!(f.read_mode(), ReadMode::Array, "no #70 reached the chip");
}
#[test]
fn odd_nibble_alone_is_dropped() {
let mut f = chip();
f.write(0x21, 0x7); assert_eq!(f.read_mode(), ReadMode::Array);
f.write(0x20, 0x0);
f.write(0x23, 0x7); assert_eq!(f.read_mode(), ReadMode::Array);
}
#[test]
fn program_only_clears_bits() {
let mut f = chip();
put(&mut f, 0x1234, 0x40);
put(&mut f, 0x1234, 0x5A);
assert_eq!(f.read_mode(), ReadMode::Status);
assert_eq!(get(&f, 0x1234), sr::READY);
assert_eq!(get(&f, 0), sr::READY, "status at any address");
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, 0x1234), 0x5A);
put(&mut f, 0, 0x10);
put(&mut f, 0x1234, 0xF0);
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, 0x1234), 0x50);
assert_eq!(f.status(), sr::READY, "no error for 0 -> 1 attempts");
}
#[test]
fn generation_moves_with_programs_and_erases() {
let mut f = chip();
let g0 = f.generation();
put(&mut f, 0x100, 0xFF);
assert_eq!(f.generation(), g0);
put(&mut f, 0x100, 0x40);
put(&mut f, 0x100, 0x12);
let g1 = f.generation();
assert_ne!(g1, g0);
put(&mut f, 0x100, 0x20);
put(&mut f, 0x100, 0xD0);
assert_ne!(f.generation(), g1);
let g1 = f.generation();
let mut g = chip();
g.follow(&f);
assert!(g.generation() > g1);
let mut fresh = chip();
fresh.follow(&chip());
assert_eq!(fresh, chip());
}
#[test]
fn block_erase() {
let mut f = chip();
for addr in [
BLOCK_BYTES - 1,
BLOCK_BYTES,
2 * BLOCK_BYTES - 1,
2 * BLOCK_BYTES,
] {
put(&mut f, 0, 0x40);
put(&mut f, addr, 0x00);
}
put(&mut f, BLOCK_BYTES + 0x100, 0x20);
put(&mut f, BLOCK_BYTES + 0x200, 0xD0);
assert_eq!(f.read_mode(), ReadMode::Status);
assert_eq!(f.status(), sr::READY);
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, BLOCK_BYTES - 1), 0x00, "block 0 untouched");
assert_eq!(get(&f, BLOCK_BYTES), 0xFF);
assert_eq!(get(&f, 2 * BLOCK_BYTES - 1), 0xFF);
assert_eq!(get(&f, 2 * BLOCK_BYTES), 0x00, "block 2 untouched");
}
#[test]
fn bad_confirm_sets_both_error_bits_and_clear_status_resets() {
let mut f = chip();
put(&mut f, 0, 0x40);
put(&mut f, 0, 0x00);
put(&mut f, 0, 0x20);
put(&mut f, 0, 0xFF); assert_eq!(f.status(), sr::READY | sr::ERASE_ERROR | sr::PROGRAM_ERROR);
assert_eq!(get(&f, 0), f.status(), "status output");
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, 0), 0x00, "the erase did not happen");
put(&mut f, 0, 0x50);
assert_eq!(f.status(), sr::READY);
assert_eq!(
f.read_mode(),
ReadMode::Array,
"clear status keeps the mode"
);
}
#[test]
fn status_and_read_array_commands() {
let mut f = chip();
put(&mut f, 0x55, 0x70);
assert_eq!(f.read_mode(), ReadMode::Status);
assert_eq!(f.read(0x1001), 0x8);
assert_eq!(f.read(0x1000), 0x0);
put(&mut f, 0x55, 0xFF);
assert_eq!(get(&f, 0x55), 0xFF);
put(&mut f, 0, 0xB0);
assert_eq!(f.read_mode(), ReadMode::Status);
put(&mut f, 0, 0xFF);
put(&mut f, 0, 0xD0);
assert_eq!(f.read_mode(), ReadMode::Status);
put(&mut f, 0, 0xFF);
put(&mut f, 0, 0x42);
assert_eq!(f.read_mode(), ReadMode::Array);
}
#[test]
fn identifier_codes() {
let mut f = chip();
put(&mut f, 0, 0x90);
assert_eq!(get(&f, 0), MANUFACTURER_CODE);
assert_eq!(get(&f, 1), MANUFACTURER_CODE);
assert_eq!(get(&f, 2), DEVICE_CODE);
assert_eq!(get(&f, 3), DEVICE_CODE);
assert_eq!(get(&f, 4), 0, "block 0 unlocked");
assert_eq!(get(&f, 6), 0);
f.set_lock_bits(1 << 3);
assert_eq!(get(&f, 3 * BLOCK_BYTES + 4), 1, "block 3 locked");
assert_eq!(get(&f, 2 * BLOCK_BYTES + 4), 0);
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, 0), 0xFF);
}
#[test]
fn query_table() {
let mut f = chip();
put(&mut f, 0, 0x98);
let q: Vec<u8> = (0x20..0x26).map(|b| get(&f, b)).collect();
assert_eq!(q, b"QQRRYY");
assert_eq!(get(&f, 2 * 0x27), 0x15, "2^21 bytes");
assert_eq!(get(&f, 2 * 0x2D), 0x1F, "32 erase blocks");
assert_eq!(get(&f, 2 * 0x30), 0x01, "of 64 KB");
let pri: Vec<u8> = (0x31..0x34).map(|w| get(&f, 2 * w)).collect();
assert_eq!(pri, b"PRI");
assert_eq!(get(&f, 2 * 0x3E), 0x50);
assert_eq!(get(&f, 2 * 0x3F), 0);
assert_eq!(get(&f, 0), MANUFACTURER_CODE);
}
#[test]
fn full_chip_erase() {
let mut f = chip();
for b in [0, FLASH_BYTES / 2, FLASH_BYTES - 1] {
put(&mut f, 0, 0x40);
put(&mut f, b, 0x12);
}
put(&mut f, 0, 0x30);
put(&mut f, 0, 0xD0);
assert_eq!(f.status(), sr::READY);
assert!(f.nibbles().iter().all(|&n| n == 0xF));
}
#[test]
fn lock_bits_with_wp_low() {
let mut f = chip();
put(&mut f, 0, 0x60);
put(&mut f, BLOCK_BYTES, 0x01);
assert_eq!(f.lock_bits(), 0b10);
put(&mut f, 0, 0x40);
put(&mut f, BLOCK_BYTES, 0x00);
assert_eq!(f.status(), sr::READY);
f.set_wp_low(true);
put(&mut f, 0, 0x40);
put(&mut f, BLOCK_BYTES + 1, 0x00);
assert_eq!(f.status(), sr::READY | sr::PROTECTED | sr::PROGRAM_ERROR);
put(&mut f, 0, 0x50);
put(&mut f, 0, 0x20);
put(&mut f, BLOCK_BYTES, 0xD0);
assert_eq!(f.status(), sr::READY | sr::PROTECTED | sr::ERASE_ERROR);
put(&mut f, 0, 0x50);
put(&mut f, 0, 0x30);
put(&mut f, 0, 0xD0);
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, BLOCK_BYTES), 0x00);
assert_eq!(get(&f, BLOCK_BYTES + 1), 0xFF);
put(&mut f, 0, 0x60);
put(&mut f, 0, 0xD0);
assert_eq!(f.status(), sr::READY | sr::PROTECTED | sr::ERASE_ERROR);
assert_eq!(f.lock_bits(), 0b10);
put(&mut f, 0, 0x50);
f.set_wp_low(false);
put(&mut f, 0, 0x60);
put(&mut f, 0, 0xD0);
assert_eq!(f.lock_bits(), 0);
assert_eq!(f.status(), sr::READY);
}
#[test]
fn write_to_buffer() {
let mut f = chip();
let base = 5 * BLOCK_BYTES + 0x40;
put(&mut f, base, 0xE8);
assert_eq!(f.read_mode(), ReadMode::ExtendedStatus);
assert_eq!(get(&f, base), 0x80, "XSR.7: buffer available");
put(&mut f, base, 2); for (i, v) in [0x11u8, 0x22, 0x33].into_iter().enumerate() {
put(&mut f, base + i, v);
}
put(&mut f, base, 0xD0);
assert_eq!(f.status(), sr::READY);
put(&mut f, 0, 0xFF);
assert_eq!(
[get(&f, base), get(&f, base + 1), get(&f, base + 2)],
[0x11, 0x22, 0x33]
);
assert_eq!(get(&f, base + 3), 0xFF);
put(&mut f, base, 0xE8);
put(&mut f, base, 0);
put(&mut f, base + 8, 0x00);
put(&mut f, base, 0xFF);
assert_eq!(f.status(), sr::READY | sr::ERASE_ERROR | sr::PROGRAM_ERROR);
put(&mut f, 0, 0x50);
put(&mut f, 0, 0xFF);
assert_eq!(get(&f, base + 8), 0xFF);
put(&mut f, base, 0xE8);
put(&mut f, base, 0);
put(&mut f, 6 * BLOCK_BYTES, 0x00);
put(&mut f, base, 0xD0);
assert_eq!(f.status(), sr::READY | sr::ERASE_ERROR | sr::PROGRAM_ERROR);
put(&mut f, 0, 0x50);
put(&mut f, base, 0xE8);
put(&mut f, base, 32);
assert_eq!(f.status(), sr::READY | sr::ERASE_ERROR | sr::PROGRAM_ERROR);
}
#[test]
fn sts_config() {
let mut f = chip();
put(&mut f, 0, 0xB8);
put(&mut f, 0, 0x03);
assert_eq!(f.status(), sr::READY);
put(&mut f, 0, 0xB8);
put(&mut f, 0, 0x04);
assert_eq!(f.status(), sr::READY | sr::ERASE_ERROR | sr::PROGRAM_ERROR);
}
#[test]
fn reset_returns_to_read_array() {
let mut f = chip();
put(&mut f, 0, 0x20);
put(&mut f, 0, 0x00);
put(&mut f, 0, 0x40);
f.reset();
assert_eq!(f.read_mode(), ReadMode::Array);
assert_eq!(f.status(), sr::READY);
put(&mut f, 0, 0x00); assert_eq!(get(&f, 0), 0xFF);
}
#[test]
fn latch_bits_follow_sousa() {
for n in 0..16u32 {
assert_eq!(
latch_banks(latch_from_address(0x3F000 + 2 * n)),
(0, n as usize)
);
}
for n in 0..4u32 {
assert_eq!(
latch_banks(latch_from_address(0x3F000 + 0x20 * n)),
(n as usize, 0)
);
}
assert_eq!(latch_from_address(0x64), 0x32);
assert_eq!(latch_banks(0x32), (3, 2));
assert_eq!(latch_banks(latch_from_address(0x7F000 + 0x7E)), (3, 15));
}
#[test]
fn banked_views() {
let mut img = vec![0u8; FLASH_BYTES];
for bank in 0..BANKS {
let start = bank * BANK_NIBBLES / 2;
img[start] = bank as u8; img[start + BANK_NIBBLES / 2 - 1] = 0xA0; }
let f = Flash::from_packed(&img).unwrap();
for bank in 0..BANKS {
assert_eq!(f.bank(bank)[0], bank as u8);
assert_eq!(f.bank(bank).len(), BANK_NIBBLES);
}
let latch = latch_from_address(0x40 + 0x18); assert_eq!(latch_banks(latch), (2, 12));
assert_eq!(f.read_banked(0x00000, latch), 2);
assert_eq!(f.read_banked(0x3FFFF, latch), 0xA);
assert_eq!(f.read_banked(0x40000, latch), 12);
assert_eq!(f.read_banked(0x7FFFF, latch), 0xA);
assert_eq!(f.read_banked(0x80000, latch), 2);
assert_eq!(f.read_banked(0xC0000, latch), 12);
assert_eq!(
Flash::banked_address(0x40010, latch),
12 * BANK_NIBBLES + 0x10
);
assert_eq!(
Flash::write_address(0x40010, latch),
12 * BANK_NIBBLES + 0x10
);
assert_eq!(
Flash::write_address(0x00010, latch),
12 * BANK_NIBBLES + 0x10
);
}
#[test]
fn program_through_the_high_view() {
let mut f = Flash::erased();
f.set_write_enabled(true);
let latch = latch_from_address(0x12);
let (_, high) = latch_banks(latch);
assert_eq!(high, 9);
let cmd = |f: &mut Flash, addr: u32, v: u8| {
f.write_banked(addr, latch, v & 0xF);
f.write_banked(addr + 1, latch, v >> 4);
};
cmd(&mut f, 0x40000, 0x40);
cmd(&mut f, 0x40100, 0x3C);
cmd(&mut f, 0x40000, 0xFF);
assert_eq!(f.bank(high)[0x100], 0xC);
assert_eq!(f.bank(high)[0x101], 0x3);
assert_eq!(f.read_banked(0x40100, latch), 0xC);
}
}