use core::fmt;
pub(crate) const INSTRUCTION_SIZE: usize = 2;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Instr([u8; INSTRUCTION_SIZE]);
impl Instr {
pub const fn new(opcode: Opcode, pads: Pads, operand: u8) -> Self {
Instr([operand, (opcode.0 << 2) | (pads as u8)])
}
const fn stop() -> Self {
Instr::new(opcodes::STOP, Pads::One , 0)
}
const fn jump_on_cs() -> Self {
Instr::new(opcodes::JUMP_ON_CS, Pads::One , 0)
}
}
impl fmt::Debug for Instr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let raw = u16::from_le_bytes(self.0);
write!(f, "{:#02X}", raw)
}
}
pub const STOP: Instr = Instr::stop();
pub const JUMP_ON_CS: Instr = Instr::jump_on_cs();
pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Sequence(pub(crate) [Instr; INSTRUCTIONS_PER_SEQUENCE]);
pub(crate) const SEQUENCE_SIZE: usize = INSTRUCTIONS_PER_SEQUENCE * INSTRUCTION_SIZE;
impl Sequence {
pub(crate) const fn stopped() -> Self {
Sequence([STOP; INSTRUCTIONS_PER_SEQUENCE])
}
}
pub struct SequenceBuilder {
sequence: Sequence,
offset: usize,
}
impl SequenceBuilder {
pub const fn new() -> Self {
SequenceBuilder {
sequence: Sequence::stopped(),
offset: 0,
}
}
pub const fn instr(self, instr: Instr) -> Self {
let mut seq = self.sequence.0;
seq[self.offset] = instr;
SequenceBuilder {
sequence: Sequence(seq),
offset: self.offset + 1,
}
}
pub const fn build(self) -> Sequence {
self.sequence
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Opcode(u8);
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum Pads {
One = 0x00,
Two = 0x01,
Four = 0x02,
Eight = 0x03,
}
impl fmt::Display for Pads {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let pads = match *self {
Pads::One => "SINGLE",
Pads::Two => "DUAL",
Pads::Four => "QUAD",
Pads::Eight => "OCTAL",
};
write!(f, "{}", pads)
}
}
impl fmt::Debug for Pads {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#02X}", *self as u8)
}
}
pub mod opcodes {
use super::Opcode;
pub mod sdr {
use super::Opcode;
pub const CMD: Opcode = Opcode(0x01);
pub const RADDR: Opcode = Opcode(0x02);
pub const CADDR: Opcode = Opcode(0x03);
pub const MODE1: Opcode = Opcode(0x04);
pub const MODE2: Opcode = Opcode(0x05);
pub const MODE4: Opcode = Opcode(0x06);
pub const MODE8: Opcode = Opcode(0x07);
pub const WRITE: Opcode = Opcode(0x08);
pub const READ: Opcode = Opcode(0x09);
pub const LEARN: Opcode = Opcode(0x0A);
pub const DATASZ: Opcode = Opcode(0x0B);
pub const DUMMY: Opcode = Opcode(0x0C);
pub const DUMMY_RWDS: Opcode = Opcode(0x0D);
}
pub(super) const STOP: Opcode = Opcode(0x00);
pub(super) const JUMP_ON_CS: Opcode = Opcode(0x1F);
pub mod ddr {
use super::sdr;
use super::Opcode;
const fn to_ddr(opcode: Opcode) -> Opcode {
Opcode(opcode.0 + 0x20)
}
pub const CMD: Opcode = to_ddr(sdr::CMD);
pub const RADDR: Opcode = to_ddr(sdr::RADDR);
pub const CADDR: Opcode = to_ddr(sdr::CADDR);
pub const MODE1: Opcode = to_ddr(sdr::MODE1);
pub const MODE2: Opcode = to_ddr(sdr::MODE2);
pub const MODE4: Opcode = to_ddr(sdr::MODE4);
pub const MODE8: Opcode = to_ddr(sdr::MODE8);
pub const WRITE: Opcode = to_ddr(sdr::WRITE);
pub const READ: Opcode = to_ddr(sdr::READ);
pub const LEARN: Opcode = to_ddr(sdr::LEARN);
pub const DATASZ: Opcode = to_ddr(sdr::DATASZ);
pub const DUMMY: Opcode = to_ddr(sdr::DUMMY);
pub const DUMMY_RWDS: Opcode = to_ddr(sdr::DUMMY_RWDS);
}
}
impl fmt::Display for Opcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use opcodes::ddr;
use opcodes::sdr;
match *self {
sdr::CMD => write!(f, "CMD_SDR"),
sdr::RADDR => write!(f, "RADDR_SDR"),
sdr::CADDR => write!(f, "CADDR_SDR"),
sdr::MODE1 => write!(f, "MODE1_SDR"),
sdr::MODE2 => write!(f, "MODE2_SDR"),
sdr::MODE4 => write!(f, "MODE4_SDR"),
sdr::MODE8 => write!(f, "MODE8_SDR"),
sdr::WRITE => write!(f, "WRITE_SDR"),
sdr::READ => write!(f, "READ_SDR"),
sdr::LEARN => write!(f, "LEARN_SDR"),
sdr::DATASZ => write!(f, "DATASZ_SDR"),
sdr::DUMMY => write!(f, "DUMMY_SDR"),
sdr::DUMMY_RWDS => write!(f, "DUMMY_RWDS_SDR"),
ddr::CMD => write!(f, "CMD_DDR"),
ddr::RADDR => write!(f, "RADDR_DDR"),
ddr::CADDR => write!(f, "CADDR_DDR"),
ddr::MODE1 => write!(f, "MODE1_DDR"),
ddr::MODE2 => write!(f, "MODE2_DDR"),
ddr::MODE4 => write!(f, "MODE4_DDR"),
ddr::MODE8 => write!(f, "MODE8_DDR"),
ddr::WRITE => write!(f, "WRITE_DDR"),
ddr::READ => write!(f, "READ_DDR"),
ddr::LEARN => write!(f, "LEARN_DDR"),
ddr::DATASZ => write!(f, "DATASZ_DDR"),
ddr::DUMMY => write!(f, "DUMMY_DDR"),
ddr::DUMMY_RWDS => write!(f, "DUMMY_RWDS_DDR"),
opcodes::STOP => write!(f, "STOP"),
opcodes::JUMP_ON_CS => write!(f, "JUMP_ON_CS"),
unknown => write!(f, "UNKNOWN({:#02X})", unknown.0),
}
}
}
impl fmt::Debug for Opcode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#02X}", self.0)
}
}
#[cfg(test)]
mod test {
use super::opcodes::sdr::*;
use super::Instr;
use super::Pads;
use super::{Sequence, SequenceBuilder};
fn seq_to_bytes(seq: Sequence) -> Vec<u8> {
let mut buffer = vec![0; super::SEQUENCE_SIZE];
buffer
.chunks_exact_mut(2)
.zip(seq.0.iter())
.for_each(|(dst, src)| dst.copy_from_slice(&src.0));
buffer
}
#[test]
fn teensy4_read() {
const EXPECTED: [u8; super::SEQUENCE_SIZE] = [
0xEB, 0x04, 0x18, 0x0A, 0x06, 0x32, 0x04, 0x26, 0, 0, 0, 0, 0, 0, 0, 0,
];
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0xEB))
.instr(Instr::new(RADDR, Pads::Four, 0x18))
.instr(Instr::new(DUMMY, Pads::Four, 0x06))
.instr(Instr::new(READ, Pads::Four, 0x04))
.build();
assert_eq!(&seq_to_bytes(SEQUENCE), &EXPECTED);
}
#[test]
fn teensy4_read_status() {
const EXPECTED: [u8; 4] = [0x05, 0x04, 0x04, 0x24];
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x05))
.instr(Instr::new(READ, Pads::One, 0x04))
.build();
assert_eq!(&seq_to_bytes(SEQUENCE)[0..4], &EXPECTED);
}
#[test]
fn teensy4_write_enable() {
const EXPECTED: u128 = 0x0000_0406;
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x06))
.build();
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_erase_sector() {
const EXPECTED: u128 = 0x0818_0420;
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x20))
.instr(Instr::new(RADDR, Pads::One, 0x18))
.build();
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_page_program() {
const EXPECTED: u128 = 0x0000_2004_0818_0402;
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x02))
.instr(Instr::new(RADDR, Pads::One, 0x18))
.instr(Instr::new(WRITE, Pads::One, 0x04))
.build();
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_chip_erase() {
const EXPECTED: u128 = 0x0000_0460;
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x60))
.build();
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
}
#[cfg(doctest)]
struct SequenceBuilderInstructionLimit;
#[cfg(doctest)]
struct SequenceBuilderTooManyInstructions;