1use core::fmt;
7
8pub(crate) const INSTRUCTION_SIZE: usize = 2;
9
10#[derive(Clone, Copy)]
17#[repr(transparent)]
18pub struct Instr([u8; INSTRUCTION_SIZE]);
19
20impl Instr {
21 pub const fn new(opcode: Opcode, pads: Pads, operand: u8) -> Self {
27 Instr([operand, (opcode.0 << 2) | (pads as u8)])
28 }
29
30 const fn stop() -> Self {
31 Instr::new(opcodes::STOP, Pads::One , 0)
32 }
33
34 const fn jump_on_cs() -> Self {
35 Instr::new(opcodes::JUMP_ON_CS, Pads::One , 0)
36 }
37}
38
39impl fmt::Debug for Instr {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 let raw = u16::from_le_bytes(self.0);
42 write!(f, "{raw:#02X}")
43 }
44}
45
46pub const STOP: Instr = Instr::stop();
48pub const JUMP_ON_CS: Instr = Instr::jump_on_cs();
50
51pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
52
53#[derive(Clone, Copy, Debug)]
61#[repr(transparent)]
62pub struct Sequence(pub(crate) [Instr; INSTRUCTIONS_PER_SEQUENCE]);
63pub(crate) const SEQUENCE_SIZE: usize = INSTRUCTIONS_PER_SEQUENCE * INSTRUCTION_SIZE;
64
65impl Sequence {
66 pub(crate) const fn stopped() -> Self {
67 Sequence([STOP; INSTRUCTIONS_PER_SEQUENCE])
68 }
69}
70
71pub struct SequenceBuilder {
95 sequence: Sequence,
96 offset: usize,
97}
98
99impl Default for SequenceBuilder {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl SequenceBuilder {
106 pub const fn new() -> Self {
110 SequenceBuilder {
111 sequence: Sequence::stopped(),
112 offset: 0,
113 }
114 }
115 pub const fn instr(self, instr: Instr) -> Self {
119 let mut seq = self.sequence.0;
120 seq[self.offset] = instr;
121 SequenceBuilder {
122 sequence: Sequence(seq),
123 offset: self.offset + 1,
124 }
125 }
126 pub const fn build(self) -> Sequence {
128 self.sequence
129 }
130}
131
132#[derive(Clone, Copy, PartialEq, Eq)]
136pub struct Opcode(u8);
137
138#[derive(Clone, Copy)]
140#[repr(u8)]
141pub enum Pads {
142 One = 0x00,
144 Two = 0x01,
146 Four = 0x02,
148 Eight = 0x03,
150}
151
152impl fmt::Display for Pads {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 let pads = match *self {
155 Pads::One => "SINGLE",
156 Pads::Two => "DUAL",
157 Pads::Four => "QUAD",
158 Pads::Eight => "OCTAL",
159 };
160 write!(f, "{pads}")
161 }
162}
163
164impl fmt::Debug for Pads {
165 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166 write!(f, "{:#02X}", *self as u8)
167 }
168}
169
170pub mod opcodes {
175 use super::Opcode;
176
177 pub mod sdr {
179 use super::Opcode;
180 pub const CMD: Opcode = Opcode(0x01);
182 pub const RADDR: Opcode = Opcode(0x02);
184 pub const CADDR: Opcode = Opcode(0x03);
186 pub const MODE1: Opcode = Opcode(0x04);
190 pub const MODE2: Opcode = Opcode(0x05);
194 pub const MODE4: Opcode = Opcode(0x06);
198 pub const MODE8: Opcode = Opcode(0x07);
202 pub const WRITE: Opcode = Opcode(0x08);
204 pub const READ: Opcode = Opcode(0x09);
208 pub const LEARN: Opcode = Opcode(0x0A);
213 pub const DATASZ: Opcode = Opcode(0x0B);
215 pub const DUMMY: Opcode = Opcode(0x0C);
220 pub const DUMMY_RWDS: Opcode = Opcode(0x0D);
222 }
223
224 pub(super) const STOP: Opcode = Opcode(0x00);
227 pub(super) const JUMP_ON_CS: Opcode = Opcode(0x1F);
232
233 pub mod ddr {
238 use super::Opcode;
239 use super::sdr;
240
241 const fn to_ddr(opcode: Opcode) -> Opcode {
243 Opcode(opcode.0 + 0x20)
244 }
245
246 pub const CMD: Opcode = to_ddr(sdr::CMD);
247 pub const RADDR: Opcode = to_ddr(sdr::RADDR);
248 pub const CADDR: Opcode = to_ddr(sdr::CADDR);
249 pub const MODE1: Opcode = to_ddr(sdr::MODE1);
250 pub const MODE2: Opcode = to_ddr(sdr::MODE2);
251 pub const MODE4: Opcode = to_ddr(sdr::MODE4);
252 pub const MODE8: Opcode = to_ddr(sdr::MODE8);
253 pub const WRITE: Opcode = to_ddr(sdr::WRITE);
254 pub const READ: Opcode = to_ddr(sdr::READ);
255 pub const LEARN: Opcode = to_ddr(sdr::LEARN);
256 pub const DATASZ: Opcode = to_ddr(sdr::DATASZ);
257 pub const DUMMY: Opcode = to_ddr(sdr::DUMMY);
258 pub const DUMMY_RWDS: Opcode = to_ddr(sdr::DUMMY_RWDS);
259 }
260}
261
262impl fmt::Display for Opcode {
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 use opcodes::ddr;
265 use opcodes::sdr;
266 match *self {
267 sdr::CMD => write!(f, "CMD_SDR"),
269 sdr::RADDR => write!(f, "RADDR_SDR"),
270 sdr::CADDR => write!(f, "CADDR_SDR"),
271 sdr::MODE1 => write!(f, "MODE1_SDR"),
272 sdr::MODE2 => write!(f, "MODE2_SDR"),
273 sdr::MODE4 => write!(f, "MODE4_SDR"),
274 sdr::MODE8 => write!(f, "MODE8_SDR"),
275 sdr::WRITE => write!(f, "WRITE_SDR"),
276 sdr::READ => write!(f, "READ_SDR"),
277 sdr::LEARN => write!(f, "LEARN_SDR"),
278 sdr::DATASZ => write!(f, "DATASZ_SDR"),
279 sdr::DUMMY => write!(f, "DUMMY_SDR"),
280 sdr::DUMMY_RWDS => write!(f, "DUMMY_RWDS_SDR"),
281 ddr::CMD => write!(f, "CMD_DDR"),
283 ddr::RADDR => write!(f, "RADDR_DDR"),
284 ddr::CADDR => write!(f, "CADDR_DDR"),
285 ddr::MODE1 => write!(f, "MODE1_DDR"),
286 ddr::MODE2 => write!(f, "MODE2_DDR"),
287 ddr::MODE4 => write!(f, "MODE4_DDR"),
288 ddr::MODE8 => write!(f, "MODE8_DDR"),
289 ddr::WRITE => write!(f, "WRITE_DDR"),
290 ddr::READ => write!(f, "READ_DDR"),
291 ddr::LEARN => write!(f, "LEARN_DDR"),
292 ddr::DATASZ => write!(f, "DATASZ_DDR"),
293 ddr::DUMMY => write!(f, "DUMMY_DDR"),
294 ddr::DUMMY_RWDS => write!(f, "DUMMY_RWDS_DDR"),
295 opcodes::STOP => write!(f, "STOP"),
297 opcodes::JUMP_ON_CS => write!(f, "JUMP_ON_CS"),
298 unknown => write!(f, "UNKNOWN({:#02X})", unknown.0),
300 }
301 }
302}
303
304impl fmt::Debug for Opcode {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 write!(f, "{:#02X}", self.0)
307 }
308}
309
310#[cfg(test)]
311mod test {
312 use super::Instr;
313 use super::Pads;
314 use super::opcodes::sdr::*;
315 use super::{Sequence, SequenceBuilder};
316
317 fn seq_to_bytes(seq: Sequence) -> Vec<u8> {
318 let mut buffer = vec![0; super::SEQUENCE_SIZE];
319 buffer
320 .chunks_exact_mut(2)
321 .zip(seq.0.iter())
322 .for_each(|(dst, src)| dst.copy_from_slice(&src.0));
323 buffer
324 }
325
326 #[test]
333 fn teensy4_read() {
334 const EXPECTED: [u8; super::SEQUENCE_SIZE] = [
335 0xEB, 0x04, 0x18, 0x0A, 0x06, 0x32, 0x04, 0x26, 0, 0, 0, 0, 0, 0, 0, 0,
336 ];
337
338 const SEQUENCE: Sequence = SequenceBuilder::new()
339 .instr(Instr::new(CMD, Pads::One, 0xEB))
340 .instr(Instr::new(RADDR, Pads::Four, 0x18))
341 .instr(Instr::new(DUMMY, Pads::Four, 0x06))
342 .instr(Instr::new(READ, Pads::Four, 0x04))
343 .build();
344
345 assert_eq!(&seq_to_bytes(SEQUENCE), &EXPECTED);
346 }
347
348 #[test]
349 fn teensy4_read_status() {
350 const EXPECTED: [u8; 4] = [0x05, 0x04, 0x04, 0x24];
351 const SEQUENCE: Sequence = SequenceBuilder::new()
352 .instr(Instr::new(CMD, Pads::One, 0x05))
353 .instr(Instr::new(READ, Pads::One, 0x04))
354 .build();
355 assert_eq!(&seq_to_bytes(SEQUENCE)[0..4], &EXPECTED);
356 }
357
358 #[test]
359 fn teensy4_write_enable() {
360 const EXPECTED: u128 = 0x0000_0406;
361 const SEQUENCE: Sequence = SequenceBuilder::new()
362 .instr(Instr::new(CMD, Pads::One, 0x06))
363 .build();
364 assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
365 }
366
367 #[test]
368 fn teensy4_erase_sector() {
369 const EXPECTED: u128 = 0x0818_0420;
370 const SEQUENCE: Sequence = SequenceBuilder::new()
371 .instr(Instr::new(CMD, Pads::One, 0x20))
372 .instr(Instr::new(RADDR, Pads::One, 0x18))
373 .build();
374 assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
375 }
376
377 #[test]
378 fn teensy4_page_program() {
379 const EXPECTED: u128 = 0x0000_2004_0818_0402;
380 const SEQUENCE: Sequence = SequenceBuilder::new()
381 .instr(Instr::new(CMD, Pads::One, 0x02))
382 .instr(Instr::new(RADDR, Pads::One, 0x18))
383 .instr(Instr::new(WRITE, Pads::One, 0x04))
384 .build();
385 assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
386 }
387
388 #[test]
389 fn teensy4_chip_erase() {
390 const EXPECTED: u128 = 0x0000_0460;
391 const SEQUENCE: Sequence = SequenceBuilder::new()
392 .instr(Instr::new(CMD, Pads::One, 0x60))
393 .build();
394 assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
395 }
396}
397
398#[cfg(doctest)]
420struct SequenceBuilderInstructionLimit;
421
422#[cfg(doctest)]
438struct SequenceBuilderTooManyInstructions;