From 690e9368600bdea8478e7e968f4daf455fed6ef4 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Wed, 17 Jun 2020 21:38:53 -0400
Subject: [PATCH 01/19] Add CHANGELOG
---
CHANGELOG.md | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e490013
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,10 @@
+# Changelog
+
+## [Unreleased] - YYYY-MM-DD
+
+## [0.1.0] - 2020-04-07
+
+First release
+
+[Unreleased]: https://github.com/imxrt-rs/imxrt-boot-gen/compare/v0.1.0...HEAD
+[0.1.0]: https://github.com/imxrt-rs/imxrt-boot-gen/releases/tag/v0.1.0
\ No newline at end of file
From e312df740f4b8a72e096c5e888c48627e81ad682 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 10:35:23 -0500
Subject: [PATCH 02/19] Update flexspi_lut tests to demonstrate const seqs
---
src/flexspi_lut.rs | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index b3b5058..5e5a790 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -340,7 +340,7 @@ mod test {
0xEB, 0x04, 0x18, 0x0A, 0x06, 0x32, 0x04, 0x26, 0, 0, 0, 0, 0, 0, 0, 0,
];
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0xEB),
Instr::new(RADDR, Pads::Four, 0x18),
Instr::new(DUMMY, Pads::Four, 0x06),
@@ -351,13 +351,13 @@ mod test {
STOP,
]);
- assert_eq!(&seq_to_bytes(seq), &EXPECTED);
+ assert_eq!(&seq_to_bytes(SEQUENCE), &EXPECTED);
}
#[test]
fn teensy4_read_status() {
const EXPECTED: [u8; 4] = [0x05, 0x04, 0x04, 0x24];
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0x05),
Instr::new(READ, Pads::One, 0x04),
STOP,
@@ -367,13 +367,13 @@ mod test {
STOP,
STOP,
]);
- assert_eq!(&seq_to_bytes(seq)[0..4], &EXPECTED);
+ assert_eq!(&seq_to_bytes(SEQUENCE)[0..4], &EXPECTED);
}
#[test]
fn teensy4_write_enable() {
const EXPECTED: u128 = 0x0000_0406;
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0x06),
STOP,
STOP,
@@ -383,13 +383,13 @@ mod test {
STOP,
STOP,
]);
- assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(seq)[..]);
+ assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_erase_sector() {
const EXPECTED: u128 = 0x0818_0420;
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0x20),
Instr::new(RADDR, Pads::One, 0x18),
STOP,
@@ -399,13 +399,13 @@ mod test {
STOP,
STOP,
]);
- assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(seq)[..]);
+ assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_page_program() {
const EXPECTED: u128 = 0x0000_2004_0818_0402;
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0x02),
Instr::new(RADDR, Pads::One, 0x18),
Instr::new(WRITE, Pads::One, 0x04),
@@ -415,13 +415,13 @@ mod test {
STOP,
STOP,
]);
- assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(seq)[..]);
+ assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
#[test]
fn teensy4_chip_erase() {
const EXPECTED: u128 = 0x0000_0460;
- let seq = Sequence([
+ const SEQUENCE: Sequence = Sequence([
Instr::new(CMD, Pads::One, 0x60),
STOP,
STOP,
@@ -431,6 +431,6 @@ mod test {
STOP,
STOP,
]);
- assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(seq)[..]);
+ assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
}
From 3d07668e3df3648445829c3f150e47e7f2717425 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 11:00:56 -0500
Subject: [PATCH 03/19] Implement SequenceBuilder API
The current Sequence interface is verbose, and we can get a better
experience, and simpler code, by using a SequenceBuilder. The commit
adds and tests SequenceBuilder, which is intended to replace the
public interface of Sequence.
---
src/flexspi_lut.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 124 insertions(+), 1 deletion(-)
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index 5e5a790..f47b59e 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -133,6 +133,61 @@ impl fmt::Display for Sequence {
}
}
+/// A compile-time [`Sequence`] builder
+///
+/// Use `SequenceBuilder` to define a FlexSPI LUT sequence. If you insert too many instructions
+/// into the sequence, you'll observe a compile-time error.
+///
+/// Any unspecified instructions are set to [`STOP`].
+///
+/// # Example
+///
+/// ```
+/// use imxrt_boot_gen::serial_flash::{
+/// Sequence,
+/// SequenceBuilder,
+/// Instr,
+/// Pads,
+/// opcodes::sdr::*,
+/// };
+///
+/// const SEQ_READ: Sequence = SequenceBuilder::new()
+/// .instr(Instr::new(CMD, Pads::One, 0xEB))
+/// .instr(Instr::new(READ, Pads::Four, 0x04))
+/// .build();
+/// ```
+pub struct SequenceBuilder {
+ sequence: Sequence,
+ offset: usize,
+}
+
+impl SequenceBuilder {
+ /// Creates a new `SequenceBuilder` than can accept up to eight instructions
+ ///
+ /// All unspecified instructions are set to [`STOP`].
+ pub const fn new() -> Self {
+ SequenceBuilder {
+ sequence: Sequence::stopped(),
+ offset: 0,
+ }
+ }
+ /// Insert `instr` as the next sequence instruction
+ ///
+ /// If you call `instr` more than 8 times, you'll observe a compile-time error.
+ 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,
+ }
+ }
+ /// Create the sequence
+ pub const fn build(self) -> Sequence {
+ self.sequence
+ }
+}
+
/// A FlexSPI opcode
///
/// Available `Opcode`s are defined in the `opcodes` module.
@@ -316,8 +371,8 @@ mod test {
use super::opcodes::sdr::*;
use super::Instr;
use super::Pads;
- use super::Sequence;
use super::STOP;
+ use super::{Sequence, SequenceBuilder};
fn seq_to_bytes(seq: Sequence) -> Vec {
let mut buffer = vec![0; super::SEQUENCE_SIZE];
@@ -354,6 +409,22 @@ mod test {
assert_eq!(&seq_to_bytes(SEQUENCE), &EXPECTED);
}
+ #[test]
+ fn teensy4_read_builder() {
+ 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];
@@ -370,6 +441,16 @@ mod test {
assert_eq!(&seq_to_bytes(SEQUENCE)[0..4], &EXPECTED);
}
+ #[test]
+ fn teensy4_read_status_builder() {
+ 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;
@@ -434,3 +515,45 @@ mod test {
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
}
+
+//
+// Keep these two tests in sync
+//
+// The first one lets you know if the second one is failing to compile
+// in the way we expect.
+//
+
+/// ```
+/// use imxrt_boot_gen::serial_flash::{*, opcodes::sdr::*};
+/// const INSTR: Instr = Instr::new(RADDR, Pads::Four, 0x18);
+/// const OUT_OF_BOUNDS: Sequence = SequenceBuilder::new()
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .build();
+/// ```
+#[cfg(doctest)]
+struct SequenceBuilderInstructionLimit;
+
+/// ```compile_fail
+/// use imxrt_boot_gen::serial_flash::{*, opcodes::sdr::*};
+/// const INSTR: Instr = Instr::new(RADDR, Pads::Four, 0x18);
+/// const OUT_OF_BOUNDS: Sequence = SequenceBuilder::new()
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR)
+/// .instr(INSTR) // <------- THIS SHOULD FAIL
+/// .build();
+/// ```
+#[cfg(doctest)]
+struct SequenceBuilderTooManyInstructions;
From 35dddb9d29cde0e3cf42e6ecf0d188e671efda2d Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 11:15:04 -0500
Subject: [PATCH 04/19] Remove Sequence API in favor of SequenceBuilder
The commit marks Sequence members as private to the crate. The commit
updates all tests and documentation to use SequenceBuilder.
---
src/flexspi_lut.rs | 123 ++++++-------------------------------
src/lib.rs | 30 +++------
src/serial_flash/lookup.rs | 15 ++---
tests/teensy4.rs | 85 ++++++++-----------------
4 files changed, 59 insertions(+), 194 deletions(-)
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index f47b59e..2c78f75 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -88,36 +88,13 @@ pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
/// A collection of FlexSPI LUT instructions
///
-/// Each `Sequence` may have up to eight instructions. Any unused instructions must
-/// be inlined to [`STOP`](constant.STOP.html). The sequences you'll require are dependent
-/// on the specific flash memory that you're interacting with.
+/// Each `Sequence` may have up to eight instructions. Use [`SequenceBuilder`] to create
+/// a `Sequence`. The sequences you'll require are dependent on the specific flash memory that
+/// you're interacting with.
///
/// `Sequence`s are used to create a [`LookupTable`](../serial_flash/lookup/struct.LookupTable.html).
-///
-/// # Example
-///
-/// ```
-/// use imxrt_boot_gen::serial_flash::{
-/// Sequence,
-/// Instr,
-/// STOP,
-/// Pads,
-/// opcodes::sdr::*,
-/// };
-///
-/// const SEQ_READ: Sequence = Sequence([
-/// Instr::new(CMD, Pads::One, 0xEB),
-/// Instr::new(READ, Pads::Four, 0x04),
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// ]);
-/// ```
#[derive(Clone, Copy)]
-pub struct Sequence(pub [Instr; INSTRUCTIONS_PER_SEQUENCE]);
+pub struct Sequence(pub(crate) [Instr; INSTRUCTIONS_PER_SEQUENCE]);
pub(crate) const SEQUENCE_SIZE: usize = INSTRUCTIONS_PER_SEQUENCE * INSTRUCTION_SIZE;
impl Sequence {
@@ -371,7 +348,6 @@ mod test {
use super::opcodes::sdr::*;
use super::Instr;
use super::Pads;
- use super::STOP;
use super::{Sequence, SequenceBuilder};
fn seq_to_bytes(seq: Sequence) -> Vec {
@@ -395,26 +371,6 @@ mod test {
0xEB, 0x04, 0x18, 0x0A, 0x06, 0x32, 0x04, 0x26, 0, 0, 0, 0, 0, 0, 0, 0,
];
- const SEQUENCE: Sequence = Sequence([
- Instr::new(CMD, Pads::One, 0xEB),
- Instr::new(RADDR, Pads::Four, 0x18),
- Instr::new(DUMMY, Pads::Four, 0x06),
- Instr::new(READ, Pads::Four, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
-
- assert_eq!(&seq_to_bytes(SEQUENCE), &EXPECTED);
- }
-
- #[test]
- fn teensy4_read_builder() {
- 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))
@@ -427,22 +383,6 @@ mod test {
#[test]
fn teensy4_read_status() {
- const EXPECTED: [u8; 4] = [0x05, 0x04, 0x04, 0x24];
- const SEQUENCE: Sequence = Sequence([
- Instr::new(CMD, Pads::One, 0x05),
- Instr::new(READ, Pads::One, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
- assert_eq!(&seq_to_bytes(SEQUENCE)[0..4], &EXPECTED);
- }
-
- #[test]
- fn teensy4_read_status_builder() {
const EXPECTED: [u8; 4] = [0x05, 0x04, 0x04, 0x24];
const SEQUENCE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, 0x05))
@@ -454,64 +394,39 @@ mod test {
#[test]
fn teensy4_write_enable() {
const EXPECTED: u128 = 0x0000_0406;
- const SEQUENCE: Sequence = Sequence([
- Instr::new(CMD, Pads::One, 0x06),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
+ 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 = Sequence([
- Instr::new(CMD, Pads::One, 0x20),
- Instr::new(RADDR, Pads::One, 0x18),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
+ 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 = Sequence([
- Instr::new(CMD, Pads::One, 0x02),
- Instr::new(RADDR, Pads::One, 0x18),
- Instr::new(WRITE, Pads::One, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
+ 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 = Sequence([
- Instr::new(CMD, Pads::One, 0x60),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
+ const SEQUENCE: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, 0x60))
+ .build();
assert_eq!(&EXPECTED.to_le_bytes(), &seq_to_bytes(SEQUENCE)[..]);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 593716d..61c0880 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -69,28 +69,18 @@
//! };
//!
//! // READ sequence
-//! const SEQ_READ: Sequence = Sequence([
-//! Instr::new(CMD, Pads::One, 0xEB),
-//! Instr::new(RADDR, Pads::Four, 0x18),
-//! Instr::new(DUMMY, Pads::Four, 0x06),
-//! Instr::new(READ, Pads::Four, 0x04),
-//! STOP,
-//! STOP,
-//! STOP,
-//! STOP,
-//! ]);
+//! const SEQ_READ: 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();
//!
//! // ERASE SECTOR sequence
-//! const SEQ_ERASE_SECTOR: Sequence = Sequence([
-//! Instr::new(CMD, Pads::One, 0x20),
-//! Instr::new(RADDR, Pads::One, 0x18),
-//! STOP,
-//! STOP,
-//! STOP,
-//! STOP,
-//! STOP,
-//! STOP,
-//! ]);
+//! const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
+//! .instr(Instr::new(CMD, Pads::One, 0x20))
+//! .instr(Instr::new(RADDR, Pads::One, 0x18))
+//! .build();
//! // Other sequences...
//!
//! // Add the sequences in the lookup table
diff --git a/src/serial_flash/lookup.rs b/src/serial_flash/lookup.rs
index 0fefe6c..99080d1 100644
--- a/src/serial_flash/lookup.rs
+++ b/src/serial_flash/lookup.rs
@@ -52,6 +52,7 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// use imxrt_boot_gen::serial_flash::{
/// LookupTable,
/// CommandSequence,
+/// SequenceBuilder,
/// Sequence, Instr,
/// opcodes::sdr::*,
/// Pads,
@@ -59,16 +60,10 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// };
///
/// let mut lookup_table = LookupTable::new();
-/// lookup_table[CommandSequence::Read] = Sequence([
-/// Instr::new(CMD, Pads::One, 0xEB),
-/// Instr::new(RADDR, Pads::Four, 0x02),
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// STOP,
-/// ]);
+/// lookup_table[CommandSequence::Read] = SequenceBuilder::new()
+/// .instr(Instr::new(CMD, Pads::One, 0xEB))
+/// .instr(Instr::new(RADDR, Pads::Four, 0x02))
+/// .build();
/// ```
pub struct LookupTable([Sequence; NUMBER_OF_SEQUENCES]);
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index 3508ee2..494324b 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -20,71 +20,36 @@ use winbond::*;
// Sequences for lookup table
//
-const SEQ_READ: Sequence = Sequence([
- Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO),
- Instr::new(RADDR, Pads::Four, 0x18),
- Instr::new(DUMMY, Pads::Four, 0x06),
- Instr::new(READ, Pads::Four, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_READ: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO))
+ .instr(Instr::new(RADDR, Pads::Four, 0x18))
+ .instr(Instr::new(DUMMY, Pads::Four, 0x06))
+ .instr(Instr::new(READ, Pads::Four, 0x04))
+ .build();
-const SEQ_READ_STATUS: Sequence = Sequence([
- Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1),
- Instr::new(READ, Pads::One, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1))
+ .instr(Instr::new(READ, Pads::One, 0x04))
+ .build();
-const SEQ_WRITE_ENABLE: Sequence = Sequence([
- Instr::new(CMD, Pads::One, WRITE_ENABLE),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, WRITE_ENABLE))
+ .build();
-const SEQ_ERASE_SECTOR: Sequence = Sequence([
- Instr::new(CMD, Pads::One, SECTOR_ERASE),
- Instr::new(RADDR, Pads::One, 0x18),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, SECTOR_ERASE))
+ .instr(Instr::new(RADDR, Pads::One, 0x18))
+ .build();
-const SEQ_PAGE_PROGRAM: Sequence = Sequence([
- Instr::new(CMD, Pads::One, PAGE_PROGRAM),
- Instr::new(RADDR, Pads::One, 0x18),
- Instr::new(WRITE, Pads::One, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, PAGE_PROGRAM))
+ .instr(Instr::new(RADDR, Pads::One, 0x18))
+ .instr(Instr::new(WRITE, Pads::One, 0x04))
+ .build();
-const SEQ_CHIP_ERASE: Sequence = Sequence([
- Instr::new(CMD, Pads::One, CHIP_ERASE),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
-]);
+const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, CHIP_ERASE))
+ .build();
#[test]
fn teensy4_fcb() {
From 068cd1efcf6ea79cd962b4f02672d9afff0e76f5 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 11:22:05 -0500
Subject: [PATCH 05/19] Update CHANGELOG to describe Sequence migration
---
CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e490013..f4833ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,38 @@
## [Unreleased] - YYYY-MM-DD
+### Added
+
+- Added `SequenceBuilder` to support `Sequence` allocation. `SequenceBuilder`
+ requires fewer lines of code to define the same FlexSPI LUT sequence, and it
+ catches errors at compile time.
+
+### Removed
+
+- Removed the `Sequence` public interface. Users should change their `Sequence`
+ definitions to use `SequenceBuilder`. The example below compares the old
+ `Sequence` API with the new `SequenceBuilder` API:
+
+ ```rust
+ // Old API:
+ const SEQ_READ: Sequence = Sequence([
+ Instr::new(CMD, Pads::One, 0xEB),
+ Instr::new(READ, Pads::Four, 0x04),
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+ ]);
+
+ // New API:
+ const SEQ_READ: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, 0xEB))
+ .instr(Instr::new(READ, Pads::Four, 0x04))
+ .build();
+ ```
+
## [0.1.0] - 2020-04-07
First release
From 1b7ab40a4cdf12f68d0078ea3dada574090caee5 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 13:57:42 -0500
Subject: [PATCH 06/19] Migrate to a constant builder approach
This commit removes the API that generates the configuration block
as an array of magic numbers. Instead, it gives users a way to define
configuration blocks at compile time. The entire interface uses const
methods and functions to achieve the compile-time interface.
TODO: fix all the docs, add more tests.
---
src/flexspi_lut.rs | 60 ++------
src/serial_flash.rs | 225 ++++++++++++++++++++++++++++-
src/serial_flash/builder.rs | 280 ------------------------------------
src/serial_flash/fcb.rs | 123 ----------------
src/serial_flash/fields.rs | 47 +-----
src/serial_flash/lookup.rs | 72 ++++------
src/serial_flash/nor.rs | 58 +++++++-
tests/teensy4.rs | 81 ++++++-----
8 files changed, 362 insertions(+), 584 deletions(-)
delete mode 100644 src/serial_flash/builder.rs
delete mode 100644 src/serial_flash/fcb.rs
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index 2c78f75..53b7bba 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -14,12 +14,8 @@ pub(crate) const INSTRUCTION_SIZE: usize = 2;
///
/// `Instr`s are used to create FlexSPI lookup table command [`Sequence`s](struct.Sequence.html).
#[derive(Clone, Copy)]
-pub struct Instr {
- /// Raw instructions
- raw: [u8; INSTRUCTION_SIZE],
- opcode: Opcode,
- pads: Pads,
-}
+#[repr(transparent)]
+pub struct Instr([u8; INSTRUCTION_SIZE]);
impl Instr {
/// Create a new FlexSPI LUT instruction
@@ -28,53 +24,21 @@ impl Instr {
/// there are pre-defined [`JUMP_ON_CS`](constant.JUMP_ON_CS.html) and [`STOP`](constant.STOP.html)
/// instructions which you should use.
pub const fn new(opcode: Opcode, pads: Pads, operand: u8) -> Self {
- Instr {
- // Little endian
- raw: [operand, (opcode.0 << 2) | (pads as u8)],
- opcode,
- pads,
- }
+ Instr([operand, (opcode.0 << 2) | (pads as u8)])
}
const fn stop() -> Self {
- Instr {
- raw: [0; INSTRUCTION_SIZE],
- opcode: opcodes::STOP,
- pads: Pads::One, // unused
- }
+ Instr::new(opcodes::STOP, Pads::One /* unused */, 0)
}
const fn jump_on_cs() -> Self {
- Instr {
- raw: [0; INSTRUCTION_SIZE],
- opcode: opcodes::JUMP_ON_CS,
- pads: Pads::One, // unused
- }
- }
-
- /// Returns the raw bytes representing this instruction
- pub fn raw(&self) -> &[u8] {
- &self.raw
- }
-}
-
-impl fmt::Display for Instr {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self.opcode {
- opcodes::STOP => write!(f, "STOP"),
- opcodes::JUMP_ON_CS => write!(f, "JUMP_ON_CS"),
- opcode => write!(
- f,
- "OPCODE={}, PADS={}, OPERAND={:#02X}",
- opcode, self.pads, self.raw[0]
- ),
- }
+ Instr::new(opcodes::JUMP_ON_CS, Pads::One /* unused */, 0)
}
}
impl fmt::Debug for Instr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let raw = u16::from_le_bytes(self.raw);
+ let raw = u16::from_le_bytes(self.0);
write!(f, "{:#02X}", raw)
}
}
@@ -93,7 +57,8 @@ pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
/// you're interacting with.
///
/// `Sequence`s are used to create a [`LookupTable`](../serial_flash/lookup/struct.LookupTable.html).
-#[derive(Clone, Copy)]
+#[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;
@@ -103,13 +68,6 @@ impl Sequence {
}
}
-impl fmt::Display for Sequence {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let instr_strs: Vec = self.0.iter().map(ToString::to_string).collect();
- write!(f, "{}", instr_strs.join(";"))
- }
-}
-
/// A compile-time [`Sequence`] builder
///
/// Use `SequenceBuilder` to define a FlexSPI LUT sequence. If you insert too many instructions
@@ -355,7 +313,7 @@ mod test {
buffer
.chunks_exact_mut(2)
.zip(seq.0.iter())
- .for_each(|(dst, src)| dst.copy_from_slice(&src.raw));
+ .for_each(|(dst, src)| dst.copy_from_slice(&src.0));
buffer
}
diff --git a/src/serial_flash.rs b/src/serial_flash.rs
index 9399f22..539d57e 100644
--- a/src/serial_flash.rs
+++ b/src/serial_flash.rs
@@ -5,13 +5,230 @@
//!
//! Note: NAND Flash boot not yet implemented
-mod builder;
-mod fcb;
mod fields;
mod lookup;
pub mod nor;
-pub use builder::*;
-pub use fcb::*;
pub use fields::*;
pub use lookup::*;
+
+/// ASCII 'FCFB'
+const TAG: u32 = 0x4246_4346;
+/// [07:00] bugfix = 0
+/// [15:08] minor
+/// [23:16] major = 1
+/// [31:24] ascii ‘V’
+const VERSION: u32 = 0x5601_0000;
+
+/// The recommended `csHoldTime`, `0x03`.
+///
+/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_hold_time`].
+pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
+/// The recommended `csSetupTime`, `0x03`.
+///
+/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_setup_time`].
+pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C, packed)]
+pub struct FlexSPIConfigurationBlock {
+ tag: u32,
+ version: u32,
+ _reserved0: [u8; 4], // 0x008
+ read_sample_clk_src: u8,
+ cs_hold_time: u8,
+ cs_setup_time: u8,
+ column_address_width: u8,
+ device_mode_configuration: u8,
+ _reserved1: [u8; 1], // 0x011
+ wait_time_cfg_commands: u16,
+ device_mode_sequence: [u8; 4],
+ device_mode_arg: u32,
+ config_cmd_enable: u8,
+ _reserved2: [u8; 3], // 0x01D
+ config_cmd_seqs: [u8; 12],
+ _reserved3: [u8; 4], // 0x02C
+ cfg_cmd_args: [u8; 12],
+ _reserved4: [u8; 4], // 0x03C
+ controller_misc_options: u32,
+ device_type: u8,
+ serial_flash_pad_type: u8,
+ serial_clk_freq: u8,
+ lut_custom_seq_enable: u8,
+ _reserved5: [u8; 8], // 0x048
+ /// A1, A2, B1, B2
+ serial_flash_sizes: [u32; 4],
+ cs_pad_setting_override: u32,
+ sclk_pad_setting_override: u32,
+ data_pad_setting_override: u32,
+ dqs_pad_setting_override: u32,
+ timeout_ms: u32,
+ command_interval: u32,
+ data_valid_time: u32,
+ busy_offset: u16,
+ busy_bit_polarity: u16,
+ lookup_table: LookupTable,
+ lut_custom_seq: [u8; 48],
+ _reserved6: [u8; 16],
+}
+
+impl FlexSPIConfigurationBlock {
+ pub const fn new(lookup_table: LookupTable) -> Self {
+ FlexSPIConfigurationBlock {
+ tag: TAG,
+ version: VERSION,
+ read_sample_clk_src: ReadSampleClockSource::InternalLoopback as u8,
+ cs_hold_time: RECOMMENDED_CS_HOLD_TIME,
+ cs_setup_time: RECOMMENDED_CS_SETUP_TIME,
+ column_address_width: ColumnAddressWidth::OtherDevices as u8,
+ device_mode_configuration: 0, // Disabled
+ wait_time_cfg_commands: 0,
+ device_mode_sequence: [0; 4],
+ device_mode_arg: 0,
+ config_cmd_enable: 0,
+ config_cmd_seqs: [0; 12],
+ cfg_cmd_args: [0; 12],
+ controller_misc_options: 0,
+ device_type: 0, // Invalid value; must be updated in NOR / NAND configuration block
+ serial_flash_pad_type: 1, // Single pad
+ serial_clk_freq: 1, // 30MHz
+ lut_custom_seq_enable: 0,
+ serial_flash_sizes: [0; 4],
+ cs_pad_setting_override: 0,
+ sclk_pad_setting_override: 0,
+ data_pad_setting_override: 0,
+ dqs_pad_setting_override: 0,
+ timeout_ms: 0,
+ command_interval: 0,
+ data_valid_time: 0,
+ busy_offset: 0,
+ busy_bit_polarity: 0,
+ lookup_table: lookup_table,
+ lut_custom_seq: [0; 48],
+
+ _reserved0: [0; 4],
+ _reserved1: [0; 1],
+ _reserved2: [0; 3],
+ _reserved3: [0; 4],
+ _reserved4: [0; 4],
+ _reserved5: [0; 8],
+ _reserved6: [0; 16],
+ }
+ }
+
+ /// `readSampleClkSrc`, the clock source for FlexSPI
+ ///
+ /// If not set, this defaults to `ReadSampleClockSource::InternalLoopback`.
+ pub const fn read_sample_clk_src(mut self, read_sample_clk_src: ReadSampleClockSource) -> Self {
+ self.read_sample_clk_src = read_sample_clk_src as u8;
+ self
+ }
+
+ /// Set the chip select hold time (`csHoldTime`)
+ ///
+ /// If not set, this will be `RECOMMENDED_CS_HOLD_TIME`, which is `0x03`.
+ pub const fn cs_hold_time(mut self, cs_hold_time: u8) -> Self {
+ self.cs_hold_time = cs_hold_time;
+ self
+ }
+
+ /// Set the chip select setup time (`csSetupTime`)
+ ///
+ /// If not set, this will be `RECOMMENDED_CS_SETUP_TIME`, which is `0x03`.
+ pub const fn cs_setup_time(mut self, cs_setup_time: u8) -> Self {
+ self.cs_setup_time = cs_setup_time;
+ self
+ }
+
+ /// `columnAddressWidth`, the properties of the flash memory
+ ///
+ /// If not set, this defaults to `ColumnAddressWidth::OtherDevices`
+ pub const fn column_address_width(mut self, column_address_width: ColumnAddressWidth) -> Self {
+ self.column_address_width = column_address_width as u8;
+ self
+ }
+
+ /// Sets device configuration mode. The `DeviceModeConfiguration::Disabled` variant
+ /// will set `deviceModeCfgEnable` to "disabled". Otherwise, we will set
+ /// `deviceModeCfgEnable` to "enabled," and we use the sequence and argument
+ /// parameters in the FCB.
+ ///
+ /// If not set, this defaults to `DeviceModeConfiguration::Disabled`.
+ pub const fn device_mode_configuration(
+ mut self,
+ device_mode_configuration: DeviceModeConfiguration,
+ ) -> Self {
+ match device_mode_configuration {
+ DeviceModeConfiguration::Disabled => {
+ self.device_mode_configuration = 0;
+ }
+ DeviceModeConfiguration::Enabled {
+ device_mode_seq,
+ device_mode_arg,
+ } => {
+ self.device_mode_configuration = 1;
+ self.device_mode_sequence = device_mode_seq.0;
+ self.device_mode_arg = device_mode_arg;
+ }
+ }
+ self
+ }
+
+ /// Sets `waitTimeCfgCommands`
+ ///
+ /// If not set, this defaults to `WaitTimeConfigurationCommands::disable()`.
+ pub const fn wait_time_cfg_commands(
+ mut self,
+ wait_time_cfg_commands: WaitTimeConfigurationCommands,
+ ) -> Self {
+ self.wait_time_cfg_commands = wait_time_cfg_commands.0;
+ self
+ }
+
+ /// Sets the serial flash pad type, `sFlashPad`.
+ ///
+ /// If not set, this defaults to `FlashPadType::Single`.
+ pub const fn serial_flash_pad_type(mut self, serial_flash_pad_type: FlashPadType) -> Self {
+ self.serial_flash_pad_type = serial_flash_pad_type as u8;
+ self
+ }
+
+ /// Sets the serial clock frequencey, `serialClkFreq`
+ ///
+ /// If not set, this defaults to `SerialClockFrequency::MHz30`.
+ pub const fn serial_clk_freq(mut self, serial_clk_freq: SerialClockFrequency) -> Self {
+ self.serial_clk_freq = serial_clk_freq as u8;
+ self
+ }
+
+ /// Set a flash size for the provided flash region
+ ///
+ /// Any region that's not set will default to `0`.
+ pub const fn flash_size(mut self, flash_region: SerialFlashRegion, flash_size: u32) -> Self {
+ self.serial_flash_sizes[flash_region as usize] = flash_size;
+ self
+ }
+}
+
+const _STATIC_ASSERT_SIZE: [u32; 1] =
+ [0; (core::mem::size_of::() == 448) as usize];
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn smoke() {
+ const _FCB: FlexSPIConfigurationBlock =
+ FlexSPIConfigurationBlock::new(super::LookupTable::new())
+ .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+ .cs_hold_time(0x01)
+ .cs_setup_time(0x02)
+ .column_address_width(ColumnAddressWidth::OtherDevices)
+ .device_mode_configuration(DeviceModeConfiguration::Disabled)
+ .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
+ .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+ .serial_clk_freq(SerialClockFrequency::MHz60)
+ .serial_flash_pad_type(FlashPadType::Quad);
+ }
+}
diff --git a/src/serial_flash/builder.rs b/src/serial_flash/builder.rs
deleted file mode 100644
index e2b8239..0000000
--- a/src/serial_flash/builder.rs
+++ /dev/null
@@ -1,280 +0,0 @@
-//! The `Builder`
-//!
-//! The builder turns fields into the FlexSPI configuration block.
-
-use super::fcb;
-use super::fields::*;
-use super::lookup::LookupTable;
-
-/// The recommended `csHoldTime`, `0x03`.
-///
-/// This is the default value if not set in [`cs_hold_time()`](struct.FCBBuilder.html#method.cs_hold_time).
-pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
-/// The recommended `csSetupTime`, `0x03`.
-///
-/// This is the default value if not set in [`cs_setup_time()`](struct.FCBBuilder.html#method.cs_setup_time).
-pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
-
-/// Builder for a FlexSPI configuration block
-/// that configures reads from serial flash.
-///
-/// See the builder methods, or any associated types, for
-/// more information. Most FCB fields will have a default value
-/// unless you override the value.
-pub struct FCBBuilder {
- read_sample_clk_src: ReadSampleClockSource,
- cs_hold_time: u8,
- cs_setup_time: u8,
- column_address_width: ColumnAddressWidth,
- device_mode_configuration: DeviceModeConfiguration,
- wait_time_cfg_commands: WaitTimeConfigurationCommands,
- device_type: DeviceType,
- serial_flash_pad_type: FlashPadType,
- serial_clk_freq: SerialClockFrequency,
- flash_size: SerialFlashSize,
- lookup_table: LookupTable,
-}
-
-impl FCBBuilder {
- /// Create a new FCB builder for the specified `device_type` using the provided `lookup_table`.
- pub fn new(device_type: DeviceType, lookup_table: LookupTable) -> Self {
- FCBBuilder {
- read_sample_clk_src: ReadSampleClockSource::InternalLoopback,
- cs_hold_time: RECOMMENDED_CS_HOLD_TIME,
- cs_setup_time: RECOMMENDED_CS_SETUP_TIME,
- column_address_width: ColumnAddressWidth::OtherDevices,
- device_mode_configuration: DeviceModeConfiguration::Disabled,
- wait_time_cfg_commands: WaitTimeConfigurationCommands::disable(),
- device_type,
- serial_flash_pad_type: FlashPadType::Single,
- serial_clk_freq: SerialClockFrequency::MHz30,
- flash_size: SerialFlashSize::new(),
- lookup_table,
- }
- }
-
- /// `readSampleClkSrc`, the clock source for FlexSPI
- ///
- /// If not set, this defaults to `ReadSampleClockSource::InternalLoopback`.
- pub fn read_sample_clk_src(&mut self, read_sample_clk_src: ReadSampleClockSource) -> &mut Self {
- self.read_sample_clk_src = read_sample_clk_src;
- self
- }
-
- /// Set the chip select hold time (`csHoldTime`)
- ///
- /// If not set, this will be `RECOMMENDED_CS_HOLD_TIME`, which is `0x03`.
- pub fn cs_hold_time(&mut self, cs_hold_time: u8) -> &mut Self {
- self.cs_hold_time = cs_hold_time;
- self
- }
-
- /// Set the chip select setup time (`csSetupTime`)
- ///
- /// If not set, this will be `RECOMMENDED_CS_SETUP_TIME`, which is `0x03`.
- pub fn cs_setup_time(&mut self, cs_setup_time: u8) -> &mut Self {
- self.cs_setup_time = cs_setup_time;
- self
- }
-
- /// `columnAddressWidth`, the properties of the flash memory
- ///
- /// If not set, this defaults to `ColumnAddressWidth::OtherDevices`
- pub fn column_address_width(&mut self, column_address_width: ColumnAddressWidth) -> &mut Self {
- self.column_address_width = column_address_width;
- self
- }
-
- /// Sets device configuration mode. The `DeviceModeConfiguration::Disabled` variant
- /// will set `deviceModeCfgEnable` to "disabled". Otherwise, we will set
- /// `deviceModeCfgEnable` to "enabled," and we use the sequence and argument
- /// parameters in the FCB.
- ///
- /// If not set, this defaults to `DeviceModeConfiguration::Disabled`.
- pub fn device_mode_configuration(
- &mut self,
- device_mode_configuration: DeviceModeConfiguration,
- ) -> &mut Self {
- self.device_mode_configuration = device_mode_configuration;
- self
- }
-
- /// Sets `waitTimeCfgCommands`
- ///
- /// If not set, this defaults to `WaitTimeConfigurationCommands::disable()`.
- pub fn wait_time_cfg_commands(
- &mut self,
- wait_time_cfg_commands: WaitTimeConfigurationCommands,
- ) -> &mut Self {
- self.wait_time_cfg_commands = wait_time_cfg_commands;
- self
- }
-
- /// Sets the serial flash pad type, `sFlashPad`.
- ///
- /// If not set, this defaults to `FlashPadType::Single`.
- pub fn serial_flash_pad_type(&mut self, serial_flash_pad_type: FlashPadType) -> &mut Self {
- self.serial_flash_pad_type = serial_flash_pad_type;
- self
- }
-
- /// Sets the serial clock frequencey, `serialClkFreq`
- ///
- /// If not set, this defaults to `SerialClockFrequency::MHz30`.
- pub fn serial_clk_freq(&mut self, serial_clk_freq: SerialClockFrequency) -> &mut Self {
- self.serial_clk_freq = serial_clk_freq;
- self
- }
-
- /// Set a flash size for the provided flash region
- ///
- /// Any region that's not set will default to `0`.
- pub fn flash_size(&mut self, flash_region: SerialFlashRegion, flash_size: u32) -> &mut Self {
- self.flash_size[flash_region] = flash_size;
- self
- }
-
- /// Turns the `Builder` into an `FCB`, or returns an error if there
- /// is something incorrect.
- ///
- /// # Panics
- ///
- /// `build` may panic if there is an error in the implementation that
- /// ends up writing a field to a reserved offset in the FCB.
- pub fn build(&self) -> Result> {
- let mut fcb = fcb::FCB::new();
-
- self.serialze_lookup(&mut fcb);
-
- fcb.field_comment(
- 0x00C,
- &(self.read_sample_clk_src as u8).to_le_bytes(),
- "readSampleClkSrc",
- );
- fcb.field_comment(0x00D, &[self.cs_hold_time], "csHoldTime");
- fcb.field_comment(0x00E, &[self.cs_setup_time], "csSetupTime");
- fcb.field_comment(
- 0x00F,
- &[self.column_address_width as u8],
- "columnAddressWidth",
- );
- fcb.field_comment(
- 0x010,
- match self.device_mode_configuration {
- DeviceModeConfiguration::Disabled => &[0],
- // TODO other fields
- DeviceModeConfiguration::Enabled { .. } => &[1],
- },
- "deviceModeCfgEnable",
- );
- fcb.field_comment(
- 0x013,
- &self.wait_time_cfg_commands.0.to_le_bytes(),
- "waitTimeCfgCommands",
- );
-
- if let DeviceModeConfiguration::Enabled {
- device_mode_seq,
- device_mode_arg,
- } = self.device_mode_configuration
- {
- fcb.field_comment(0x014, &device_mode_seq.0, "deviceModeSeq");
- fcb.field_comment(0x018, &device_mode_arg.to_le_bytes(), "deviceModeArg");
- }
-
- // TODO configCmdEnable
- // TODO configCmdSeqs
- // TODO cfgCmdArgs
- // TODO controllerMiscOption
-
- fcb.field_comment(
- 0x044,
- match self.device_type {
- DeviceType::SerialNOR(_) => &[1],
- },
- "deviceType",
- );
- fcb.field_comment(
- 0x045,
- &(self.serial_flash_pad_type as u8).to_le_bytes(),
- "sflashPadType",
- );
- fcb.field_comment(
- 0x046,
- &(self.serial_clk_freq as u8).to_le_bytes(),
- "serialClkFreq",
- );
-
- // TODO lutCustomSeqEnable
-
- // TODO after adding SerialNAND, we have to multiply all
- // the flash sizes by 2
- fcb.field_comment(
- 0x050,
- &self.flash_size[SerialFlashRegion::A1].to_le_bytes(),
- "sflashA1Size",
- );
- fcb.field_comment(
- 0x054,
- &self.flash_size[SerialFlashRegion::A2].to_le_bytes(),
- "sflashA2Size",
- );
- fcb.field_comment(
- 0x058,
- &self.flash_size[SerialFlashRegion::B1].to_le_bytes(),
- "sflashB1Size",
- );
- fcb.field_comment(
- 0x05C,
- &self.flash_size[SerialFlashRegion::B2].to_le_bytes(),
- "sflashB2Size",
- );
-
- // TODO csPadSettingOverride
- // TODO sclkPadSettingOverride
- // TODO dataPadSettingOverride
- // TODO dqsPadSettingOverride
- // TODO timeoutInMs
- // TODO commandInverval
- // TODO dataValidTime
- // TODO busyOffset
- // TODO busyBitPolarity
-
- match self.device_type {
- DeviceType::SerialNOR(norcb) => {
- fcb.field_comment(0x1C0, &norcb.page_size.to_le_bytes(), "pageSize");
- fcb.field_comment(0x1C4, &norcb.sector_size.to_le_bytes(), "sectorSize");
- fcb.field_comment(
- 0x1C8,
- &(norcb.ip_cmd_serial_clk_freq as u8).to_le_bytes(),
- "ipCmdSerialClkFreq",
- );
- }
- }
- Ok(fcb)
- }
-
- fn serialze_lookup(&self, fcb: &mut fcb::FCB) {
- const LOOKUP_TABLE_OFFSET: usize = 0x080;
- let mut offset = 0;
- for (seq_idx, (seq, cmd)) in self.lookup_table.iter().enumerate() {
- for instr in seq.0.iter() {
- let raw = instr.raw();
- if let Some(cmd) = cmd {
- fcb.field_comment(
- LOOKUP_TABLE_OFFSET + offset,
- &raw,
- format!("(LUT[{}]) {}: {}", seq_idx, cmd, instr),
- );
- } else {
- fcb.field_comment(
- LOOKUP_TABLE_OFFSET + offset,
- &raw,
- format!("(LUT[{}])", seq_idx),
- );
- }
- offset += raw.len();
- }
- }
- }
-}
diff --git a/src/serial_flash/fcb.rs b/src/serial_flash/fcb.rs
deleted file mode 100644
index 31e32e5..0000000
--- a/src/serial_flash/fcb.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-//! FlexSPI configuration block (FCB)
-
-use std::collections::HashSet;
-use std::fmt;
-
-const FCB_SIZE: usize = 512;
-
-/// Offsets that are listed as 'reserved' in the main FlexSPI
-/// FCB table. We panic if we try to access one of these.
-static OFFSETS_OF_RESERVED: &[std::ops::Range] = &[
- 0x008..0x00C,
- 0x011..0x012,
- 0x01D..0x020,
- 0x02C..0x030,
- 0x03C..0x040,
- 0x048..0x050,
- 0x1B0..0x1C0,
- 0x1CC..0x200,
-];
-
-/// The FlexSPI configuration block
-///
-/// The `FCB` contains all the information to create the FCB code.
-/// It implements `Display`, and it will display itself as a formatted,
-/// commented Rust array. The array is called `FLEXSPI_CONFIGURATION_BLOCK`.
-/// It's 512 bytes large. It has comments that describe the element offsets
-/// and usage of the value.
-pub struct FCB {
- /// The raw contents of the FCB.
- raw: [u8; FCB_SIZE],
- /// Associated comments for each byte in the FCB. These
- /// will become Rust `//` comment tags
- comments: Vec,
- /// The indices of reserved fields in the FCB.
- reserved: HashSet,
-}
-
-impl FCB {
- /// Allocates a new FCB
- ///
- /// The FCB will have the required FCB header already provided.
- pub(super) fn new() -> Self {
- let mut fcb = FCB {
- raw: [0; FCB_SIZE],
- comments: vec![String::new(); FCB_SIZE],
- reserved: OFFSETS_OF_RESERVED.iter().cloned().flatten().collect(),
- };
-
- fcb.comments[0] = String::from("Tag 'FCFB'");
- fcb.raw[0..4].copy_from_slice(&0x4246_4346u32.to_le_bytes());
-
- fcb.comments[4] = String::from("Version 'bugfix'");
- fcb.comments[5] = String::from("Version 'minor'");
- fcb.comments[6] = String::from("Version 'major");
- fcb.comments[7] = String::from("Version 'V'");
- fcb.raw[4..8].copy_from_slice(&0x5601_0000u32.to_le_bytes());
-
- for reserved_offset in fcb.reserved.iter() {
- fcb.comments[*reserved_offset] = String::from("RESERVED");
- }
-
- fcb
- }
-
- /// Insert a field of bytes into the FCB at the specified `offset`
- ///
- /// # Panics
- ///
- /// Panics if the offset is in a reserved range, or if the field
- /// will index past the bounds of the FCB.
- pub(super) fn field>(&mut self, offset: usize, bytes: &B) {
- let bytes = bytes.as_ref();
- if self.reserved.contains(&offset) {
- panic!(
- "Attempting to access reserved offset 0x{:03X} in the FCB",
- offset
- );
- } else {
- self.raw[offset..offset + bytes.len()].copy_from_slice(bytes);
- }
- }
-
- /// Insert a field of bytes into the FCB at the specified `offset`, along with a
- /// documentation comment
- ///
- /// The comment will be placed at the `offset` element. This means that, if the field
- /// is multiple bytes, the comment will appear at the first element.
- ///
- /// # Panics
- ///
- /// Panics if the offset is in a reserved range, or if the field
- /// will index past the bounds of the FCB.
- pub(super) fn field_comment, S: ToString>(
- &mut self,
- offset: usize,
- bytes: &B,
- comment: S,
- ) {
- self.field(offset, bytes);
- self.comments[offset] = comment.to_string();
- }
-
- pub fn as_bytes(&self) -> &[u8] {
- &self.raw
- }
-}
-
-impl fmt::Display for FCB {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(
- f,
- r#"
-#[link_section = ".fcb"]
-#[no_mangle]
-pub static FLEXSPI_CONFIGURATION_BLOCK: [u8; 512] = ["#,
- )?;
- for (idx, (value, comment)) in self.raw.iter().zip(self.comments.iter()).enumerate() {
- writeln!(f, " 0x{:02X}, // 0x{:03X} {}", *value, idx, comment)?;
- }
- writeln!(f, "];")?;
- Ok(())
- }
-}
diff --git a/src/serial_flash/fields.rs b/src/serial_flash/fields.rs
index 2d4366e..dd51858 100644
--- a/src/serial_flash/fields.rs
+++ b/src/serial_flash/fields.rs
@@ -2,10 +2,6 @@
//!
//! The module implements and documents the common FCB fields.
-use std::convert::TryFrom;
-use std::ops::{Index, IndexMut};
-use std::time::Duration;
-
/// `readSampleClkSrc` of the general FCB
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
@@ -73,23 +69,16 @@ impl Default for DeviceModeConfiguration {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WaitTimeConfigurationCommands(pub(crate) u16);
impl WaitTimeConfigurationCommands {
- pub fn disable() -> Self {
+ pub const fn disable() -> Self {
WaitTimeConfigurationCommands(0)
}
- /// Computes the wait time from the specified `wait_time`. The
- /// provided duration should be divisible by `100us`, since the
- /// value is a factor scaled by `100us`. Returns `None` if representing
- /// this as a factor of `100us` returns `0`, or if the factor cannot be
- /// expressed in a `u16`.
- pub fn from_duration(wait_time: Duration) -> Option {
- let us = wait_time.as_micros();
- if us < 100 {
- None
- } else {
- let factor = u16::try_from(us / 100).ok()?;
- Some(WaitTimeConfigurationCommands(factor))
- }
+ /// Computes the wait time from the specified `wait_time_us` (microseconds)
+ ///
+ /// The duration should be divisible by `100us`, since the
+ /// value is a factor scaled by `100us`
+ pub const fn new(wait_time_us: u16) -> Self {
+ WaitTimeConfigurationCommands(wait_time_us / 100)
}
}
@@ -136,25 +125,3 @@ pub enum SerialFlashRegion {
B1,
B2,
}
-
-#[derive(Debug, Clone, Copy, Default)]
-pub(crate) struct SerialFlashSize(pub(crate) [u32; 4]);
-
-impl Index for SerialFlashSize {
- type Output = u32;
- fn index(&self, region: SerialFlashRegion) -> &u32 {
- &self.0[region as usize]
- }
-}
-
-impl IndexMut for SerialFlashSize {
- fn index_mut(&mut self, region: SerialFlashRegion) -> &mut u32 {
- &mut self.0[region as usize]
- }
-}
-
-impl SerialFlashSize {
- pub(crate) fn new() -> Self {
- SerialFlashSize::default()
- }
-}
diff --git a/src/serial_flash/lookup.rs b/src/serial_flash/lookup.rs
index 99080d1..46e17d9 100644
--- a/src/serial_flash/lookup.rs
+++ b/src/serial_flash/lookup.rs
@@ -1,17 +1,15 @@
//! Lookup table
-use std::ops::{Index, IndexMut};
-
pub use crate::flexspi_lut::*;
/// The default sequence definition lookup indices
///
-/// `CommandSequence`s are looked up by the processor when it needs to
+/// `Command`s are looked up by the processor when it needs to
/// interact with the flash chip. The enumeration lets us index back into
/// the `Lookup` struct, and associate a sequence command for that action.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
-pub enum CommandSequence {
+pub enum Command {
Read = 0,
ReadStatus = 1,
WriteEnable = 3,
@@ -21,22 +19,6 @@ pub enum CommandSequence {
Dummy = 15,
}
-impl CommandSequence {
- fn command_index_name(idx: usize) -> Option<&'static str> {
- use CommandSequence::*;
- match idx {
- idx if idx == Read as usize => Some("READ"),
- idx if idx == ReadStatus as usize => Some("READ_STATUS"),
- idx if idx == WriteEnable as usize => Some("WRITE_ENABLE"),
- idx if idx == EraseSector as usize => Some("ERASE_SECTOR"),
- idx if idx == PageProgram as usize => Some("PAGE_PROGRAM"),
- idx if idx == ChipErase as usize => Some("CHIP_ERASE"),
- idx if idx == Dummy as usize => Some("DUMMY"),
- _ => None,
- }
- }
-}
-
/// Size of the lookup table in bytes
const LOOKUP_TABLE_SIZE_BYTES: usize = 256;
const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
@@ -51,7 +33,7 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// ```
/// use imxrt_boot_gen::serial_flash::{
/// LookupTable,
-/// CommandSequence,
+/// Command,
/// SequenceBuilder,
/// Sequence, Instr,
/// opcodes::sdr::*,
@@ -60,43 +42,41 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// };
///
/// let mut lookup_table = LookupTable::new();
-/// lookup_table[CommandSequence::Read] = SequenceBuilder::new()
+/// lookup_table[Command::Read] = SequenceBuilder::new()
/// .instr(Instr::new(CMD, Pads::One, 0xEB))
/// .instr(Instr::new(RADDR, Pads::Four, 0x02))
/// .build();
/// ```
+#[derive(Debug, Clone, Copy)]
+#[repr(transparent)]
pub struct LookupTable([Sequence; NUMBER_OF_SEQUENCES]);
-impl Default for LookupTable {
- fn default() -> LookupTable {
- LookupTable([Sequence::stopped(); NUMBER_OF_SEQUENCES])
- }
-}
-
impl LookupTable {
/// Create a new lookup table. All memory is set to zero.
- pub fn new() -> Self {
- Self::default()
+ pub const fn new() -> Self {
+ LookupTable([Sequence::stopped(); NUMBER_OF_SEQUENCES])
}
-
- pub(crate) fn iter(&self) -> impl Iterator- )> {
- self.0
- .iter()
- .enumerate()
- .map(|(idx, instr)| (instr, CommandSequence::command_index_name(idx)))
+ /// Assign the `sequence` to the command that is found at the `Command` index
+ pub const fn command(mut self, cmd: Command, sequence: Sequence) -> Self {
+ self.0[cmd as usize] = sequence;
+ self
}
}
-impl Index for LookupTable {
- type Output = Sequence;
-
- fn index(&self, cmd: CommandSequence) -> &Sequence {
- &self.0[cmd as usize]
- }
-}
+#[cfg(test)]
+mod test {
+ use super::{Command, LookupTable};
+ use crate::serial_flash::SequenceBuilder;
-impl IndexMut for LookupTable {
- fn index_mut(&mut self, cmd: CommandSequence) -> &mut Sequence {
- &mut self.0[cmd as usize]
+ #[test]
+ fn smoke() {
+ const _LUT: LookupTable = LookupTable::new()
+ .command(Command::Read, SequenceBuilder::new().build())
+ .command(Command::ReadStatus, SequenceBuilder::new().build())
+ .command(Command::WriteEnable, SequenceBuilder::new().build())
+ .command(Command::EraseSector, SequenceBuilder::new().build())
+ .command(Command::PageProgram, SequenceBuilder::new().build())
+ .command(Command::ChipErase, SequenceBuilder::new().build())
+ .command(Command::Dummy, SequenceBuilder::new().build());
}
}
diff --git a/src/serial_flash/nor.rs b/src/serial_flash/nor.rs
index 5df8eb8..28e95ea 100644
--- a/src/serial_flash/nor.rs
+++ b/src/serial_flash/nor.rs
@@ -1,5 +1,7 @@
//! Fields specific for NOR flash
+use super::FlexSPIConfigurationBlock;
+
/// `ipCmdSerialClkFreq` field for serial NOR-specific FCB
///
/// Chip specific value, not used by ROM
@@ -26,8 +28,58 @@ pub enum SerialClockFrequency {
/// The fields specific for defining a serial NOR FCB
#[derive(Debug, Clone, Copy)]
+#[repr(C, packed)]
pub struct ConfigurationBlock {
- pub page_size: u32,
- pub sector_size: u32,
- pub ip_cmd_serial_clk_freq: SerialClockFrequency,
+ mem_cfg: FlexSPIConfigurationBlock,
+ page_size: u32,
+ sector_size: u32,
+ ip_cmd_serial_clk_freq: u32,
+ _reserved: [u8; 52],
+}
+
+impl ConfigurationBlock {
+ pub const fn new(mut mem_cfg: FlexSPIConfigurationBlock) -> Self {
+ mem_cfg.device_type = 1;
+ ConfigurationBlock {
+ mem_cfg,
+ page_size: 0,
+ sector_size: 0,
+ ip_cmd_serial_clk_freq: 0,
+ _reserved: [0; 52],
+ }
+ }
+ pub const fn page_size(mut self, page_size: u32) -> Self {
+ self.page_size = page_size;
+ self
+ }
+ pub const fn sector_size(mut self, sector_size: u32) -> Self {
+ self.sector_size = sector_size;
+ self
+ }
+ pub const fn ip_cmd_serial_clk_freq(
+ mut self,
+ serial_clock_frequency: SerialClockFrequency,
+ ) -> Self {
+ self.ip_cmd_serial_clk_freq = serial_clock_frequency as u32;
+ self
+ }
+}
+
+const _STATIC_ASSERT_SIZE: [u32; 1] =
+ [0; (core::mem::size_of::() == 512) as usize];
+
+#[cfg(test)]
+mod test {
+ use super::{
+ super::LookupTable, ConfigurationBlock, FlexSPIConfigurationBlock, SerialClockFrequency,
+ };
+
+ #[test]
+ fn smoke() {
+ const _CFG: ConfigurationBlock =
+ ConfigurationBlock::new(FlexSPIConfigurationBlock::new(LookupTable::new()))
+ .page_size(256)
+ .sector_size(4095)
+ .ip_cmd_serial_clk_freq(SerialClockFrequency::MHz30);
+ }
}
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index 494324b..4d490db 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -51,45 +51,50 @@ const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new()
.instr(Instr::new(CMD, Pads::One, CHIP_ERASE))
.build();
+//
+// Lookup table
+//
+
+const LUT: LookupTable = LookupTable::new()
+ .command(Command::Read, SEQ_READ)
+ .command(Command::ReadStatus, SEQ_READ_STATUS)
+ .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
+ .command(Command::EraseSector, SEQ_ERASE_SECTOR)
+ .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
+ .command(Command::ChipErase, SEQ_CHIP_ERASE);
+
+//
+// Common FlexSPI configuration block
+//
+
+const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock = FlexSPIConfigurationBlock::new(LUT)
+ .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+ .cs_hold_time(0x01)
+ .cs_setup_time(0x02)
+ .column_address_width(ColumnAddressWidth::OtherDevices)
+ .device_mode_configuration(DeviceModeConfiguration::Disabled)
+ .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
+ .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+ .serial_clk_freq(SerialClockFrequency::MHz60)
+ .serial_flash_pad_type(FlashPadType::Quad);
+
+//
+// Final serial NOR configuration block
+//
+// This is what you want to place in the i.MX RT boot section
+//
+
+const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
+ nor::ConfigurationBlock::new(FLEXSPI_CONFIGURATION_BLOCK)
+ .page_size(256)
+ .sector_size(4096)
+ .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
+
#[test]
-fn teensy4_fcb() {
- let nor_cb = nor::ConfigurationBlock {
- page_size: 256,
- sector_size: 4096,
- ip_cmd_serial_clk_freq: nor::SerialClockFrequency::MHz30,
- };
-
- let lookup_table = {
- use imxrt_boot_gen::serial_flash::CommandSequence::*;
- let mut lut = LookupTable::new();
- lut[Read] = SEQ_READ;
- lut[ReadStatus] = SEQ_READ_STATUS;
- lut[WriteEnable] = SEQ_WRITE_ENABLE;
- lut[EraseSector] = SEQ_ERASE_SECTOR;
- lut[PageProgram] = SEQ_PAGE_PROGRAM;
- lut[ChipErase] = SEQ_CHIP_ERASE;
- lut
- };
-
- let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
- .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
- .cs_hold_time(0x01)
- .cs_setup_time(0x02)
- .column_address_width(ColumnAddressWidth::OtherDevices)
- .device_mode_configuration(DeviceModeConfiguration::Disabled)
- .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
- .flash_size(SerialFlashRegion::A1, 0x0020_0000)
- .serial_clk_freq(SerialClockFrequency::MHz60)
- .serial_flash_pad_type(FlashPadType::Quad)
- .build()
- .unwrap();
-
- let mut actual: [u32; 128] = [0; 128];
- for (bytes, slot) in fcb.as_bytes().chunks_exact(4).zip(actual.iter_mut()) {
- use std::convert::TryInto;
- *slot = u32::from_le_bytes(bytes.try_into().unwrap());
- }
+fn teensy4() {
+ let actual: &[u32; 128] = unsafe { core::mem::transmute(&SERIAL_NOR_CONFIGURATION_BLOCK) };
const CHUNK_TEST_SIZE: usize = 16;
+ let mut count = 0;
for (idx, (actual_chunk, expected_chunk)) in actual
.chunks(CHUNK_TEST_SIZE)
.zip(EXPECTED.chunks(CHUNK_TEST_SIZE))
@@ -101,7 +106,9 @@ fn teensy4_fcb() {
"Start index {}",
idx * CHUNK_TEST_SIZE
);
+ count += 1;
}
+ assert_eq!(count, 128 / 16);
}
// A known, working FCB for the Teensy 4.
From cc8a4be2c05f899afa8d3c8a958da575bc7a804f Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 14:16:34 -0500
Subject: [PATCH 07/19] Qualify crate as no_std
Since this crate is now compiled into the embedded system -- not just
something that runs at build time -- the crate is now no_std.
This commit was tested in the teensy4-fcb crate. It works as
expected.
---
src/flexspi_lut.rs | 2 +-
src/lib.rs | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index 53b7bba..95d9407 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -3,7 +3,7 @@
//! Derived from the iMXRT1060 Reference Manual (Rev 2),
//! section 27.5.8.
-use std::fmt;
+use core::fmt;
pub(crate) const INSTRUCTION_SIZE: usize = 2;
diff --git a/src/lib.rs b/src/lib.rs
index 61c0880..fa123f7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -130,5 +130,7 @@
//! The name is not mangled. It may be referenced in a linker script by its section,
//! `".fcb"`. Given the ABI guarantees, the FCB should be usable from both Rust and C.
+#![cfg_attr(not(test), no_std)]
+
mod flexspi_lut;
pub mod serial_flash;
From f6e07183de1de2f01e77b838cfcbd8162648a2ae Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 18:37:29 -0500
Subject: [PATCH 08/19] Update documentation and doc examples
---
src/flexspi_lut.rs | 6 +-
src/lib.rs | 130 ++++---------------------------------
src/serial_flash.rs | 120 +++++++++++++++++++++++++++-------
src/serial_flash/fields.rs | 2 +
src/serial_flash/lookup.rs | 21 +++---
src/serial_flash/nor.rs | 23 ++++++-
tests/teensy4.rs | 2 +-
7 files changed, 145 insertions(+), 159 deletions(-)
diff --git a/src/flexspi_lut.rs b/src/flexspi_lut.rs
index 95d9407..dce361e 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi_lut.rs
@@ -50,13 +50,13 @@ pub const JUMP_ON_CS: Instr = Instr::jump_on_cs();
pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
-/// A collection of FlexSPI LUT instructions
+/// A collection of FlexSPI instructions
///
/// Each `Sequence` may have up to eight instructions. Use [`SequenceBuilder`] to create
/// a `Sequence`. The sequences you'll require are dependent on the specific flash memory that
/// you're interacting with.
///
-/// `Sequence`s are used to create a [`LookupTable`](../serial_flash/lookup/struct.LookupTable.html).
+/// `Sequence`s are used to create a [`LookupTable`].
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Sequence(pub(crate) [Instr; INSTRUCTIONS_PER_SEQUENCE]);
@@ -68,7 +68,7 @@ impl Sequence {
}
}
-/// A compile-time [`Sequence`] builder
+/// A [`Sequence`] builder
///
/// Use `SequenceBuilder` to define a FlexSPI LUT sequence. If you insert too many instructions
/// into the sequence, you'll observe a compile-time error.
diff --git a/src/lib.rs b/src/lib.rs
index fa123f7..754906b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,134 +1,30 @@
-//! A library for generating data structures required to boot iMXRT systems. Intended
-//! for generating Rust code in build scripts.
+//! Generate data structures for booting iMXRT processors
//!
//! # Rationale
//!
//! iMXRT processors require certain data structures in flash in order to configure
-//! FlexSPI and / or SEMC peripherals. A FlexSPI Configuration Block (FCB) is an array that
-//! describes how the processor should initiate a boot. It's expected to be placed
+//! FlexSPI and / or SEMC peripherals. The data structurs must be placed
//! in a certain region of FLASH, with values that describe how a peripheral should
-//! interact with NAND- / NOR-based FLASH memory. The raw FCB has a lot of magic
-//! numbers, and it would be nice to have an API to generate the FCB.
+//! interact with NAND- / NOR-based FLASH memory. The data structures have a lot of magic
+//! numbers, and it would be nice to have an API to correctly generate the values.
//!
-//! The `imxrt-boot-gen` crate provides an API for generating the FCB. As of this
-//! writing, it supports only the generation of an FCB for reading NOR Flash via
-//! FlexSPI. Other configurations, such as NAND Flash and / or the SEMC interface,
-//! may be added later.
+//! The `imxrt-boot-gen` crate helps you make data structures to boot i.MX RT processors.
+//! As of this writing, the API supports
+//!
+//! - serial NOR flash
+//!
+//! Other configurations, like NAND flash and parallel SEMC, may be added in the future.
//!
//! # Usage
//!
-//! Add `imxrt-boot-gen` to your build dependencies, and select your processor with a feature flag:
+//! Add `imxrt-boot-gen` to your dependencies, and select your processor with a feature flag:
//!
//! ```toml
-//! [build-dependencies]
+//! [dependencies]
//! imxrt-boot-gen = { features = ["imxrt1062"] }
//! ```
//!
-//! The rest of this documentation will describe the API for defining a FlexSPI configuration block
-//! (FCB).
-//!
-//! Prepare a `build.rs` script. Import all types from the kind of FCB that you're generating, and
-//! create a FCB from an `FCBBuilder`.
-//!
-//! ```
-//! use imxrt_boot_gen::serial_flash::*; // Booting from serial flash
-//! # let lookup_table = LookupTable::new();
-//! let nor_cb = nor::ConfigurationBlock {
-//! page_size: 256,
-//! sector_size: 4096,
-//! ip_cmd_serial_clk_freq: nor::SerialClockFrequency::MHz30,
-//! };
-//! let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
-//! .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
-//! .cs_hold_time(0x01)
-//! .cs_setup_time(0x02)
-//! .column_address_width(ColumnAddressWidth::OtherDevices)
-//! .device_mode_configuration(DeviceModeConfiguration::Disabled)
-//! .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
-//! .flash_size(SerialFlashRegion::A1, 0x0020_0000)
-//! .serial_clk_freq(SerialClockFrequency::MHz60)
-//! .serial_flash_pad_type(FlashPadType::Quad)
-//! .build()
-//! .unwrap();
-//! ```
-//!
-//! The values in the `FCBBuilder`'s will be serialized into the FCB. See the documentation on each
-//! field to learn more about the field meaning and possible values. The fields mirror those in your
-//! iMXRT's reference manual, so you may consult those docs for more information.
-//!
-//! The `FCBBuilder` requires a lookup table of type `LookupTable`. The lookup table (LUT) is an array
-//! of FlexSPI command sequences that describe how to interact with the external flash controller. We can index
-//! a LUT by a command sequence, `CommandSequence`, to associate a `Sequence` with that command. A `Sequence`
-//! is a collection of up to eight FlexSPI instructions, `Instr`. Use the `STOP` instructions if you do not need
-//! to utilize all eight instructions.
-//!
-//! ```
-//! use imxrt_boot_gen::{
-//! serial_flash::*, // All contents from serial flash
-//! serial_flash::opcodes::sdr::*, // All SDR instruction opcodes
-//! };
-//!
-//! // READ sequence
-//! const SEQ_READ: 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();
-//!
-//! // ERASE SECTOR sequence
-//! const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
-//! .instr(Instr::new(CMD, Pads::One, 0x20))
-//! .instr(Instr::new(RADDR, Pads::One, 0x18))
-//! .build();
-//! // Other sequences...
-//!
-//! // Add the sequences in the lookup table
-//! let mut lookup_table = LookupTable::new();
-//! lookup_table[CommandSequence::Read] = SEQ_READ;
-//! lookup_table[CommandSequence::EraseSector] = SEQ_ERASE_SECTOR;
-//!
-//! # let nor_cb = nor::ConfigurationBlock {
-//! # page_size: 256,
-//! # sector_size: 4096,
-//! # ip_cmd_serial_clk_freq: nor::SerialClockFrequency::MHz30,
-//! # };
-//! let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
-//! // Other FCB fields...
-//! .build()
-//! .unwrap();
-//! ```
-//!
-//! The contents of the FlexSPI sequences and instructions will be specific to your flash memory. Consult your chip's
-//! documentation for more information. Consult the iMXRT reference manual for more information on the lookup table.
-//!
-//! Once you've initialized the builder, build the FCB. The FCB implements `Display`, and it will display itself
-//! as a Rust array with the ABI guarantees described below.
-//!
-//! ```no_run
-//! # use imxrt_boot_gen::serial_flash::*; // Booting from serial flash
-//! use std::fs::File;
-//! use std::io::Write;
-//!
-//! # let nor_cb = nor::ConfigurationBlock {
-//! # page_size: 256,
-//! # sector_size: 4096,
-//! # ip_cmd_serial_clk_freq: nor::SerialClockFrequency::MHz30,
-//! # };
-//! # let lookup_table = LookupTable::new();
-//! # let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
-//! # // Other FCB fields...
-//! # .build()
-//! # .unwrap();
-//! let mut fcb_rs = File::create("fcb.rs").unwrap();
-//! writeln!(fcb_rs, "{}", fcb);
-//! ```
-//!
-//! # ABI
-//!
-//! The output is a single, 512-byte `u8` array, called `FLEXSPI_CONFIGURATION_BLOCK`.
-//! The name is not mangled. It may be referenced in a linker script by its section,
-//! `".fcb"`. Given the ABI guarantees, the FCB should be usable from both Rust and C.
+//! See the module-level documentation for more information about the API.
#![cfg_attr(not(test), no_std)]
diff --git a/src/serial_flash.rs b/src/serial_flash.rs
index 539d57e..2a8d98d 100644
--- a/src/serial_flash.rs
+++ b/src/serial_flash.rs
@@ -1,9 +1,66 @@
-//! Serial NOR and NAND FLASH booting
+//! Serial NOR flash boot
//!
-//! The types in `serial_flash` can help you define a FCB suitable for serial
-//! NOR- / NAND-flash booting.
+//! `serial_flash` provides the types necessary to boot an i.MX RT processor
+//! from serial NOR flash. *Note: NAND Flash boot not yet implemented.*
//!
-//! Note: NAND Flash boot not yet implemented
+//! The API includes
+//!
+//! - Flexible Serial Peripheral Interface (FlexSPI) sequence and lookup table (LUT)
+//! - FlexSPI configuration block ([`FlexSPIConfigurationBlock`])
+//! - serial NOR configuration block ([`nor::ConfigurationBlock`]), which should be
+//! properly placed in memory
+//!
+//! # Sequences and LUTs
+//!
+//! A [`Sequence`] is a collection of up to eight FlexSPI instructions ([`Instr`]).
+//! The FlexSPI controller sequentially executes instructions to perform reads, writes
+//! and I/O with a connected FLASH device. The FlexSPI controller finds each sequence
+//! in a [`LookupTable`].
+//!
+//! Use a [`SequenceBuilder`] to create `Sequence`s:
+//!
+//! ```
+//! use imxrt_boot_gen::serial_flash::{Instr, Sequence, SequenceBuilder, Pads, opcodes::sdr::*};
+//!
+//! # const FAST_READ_QUAD_IO: u8 = 0;
+//! # const READ_STATUS_REGISTER_1: u8 = 0;
+//! const SEQ_READ: Sequence = SequenceBuilder::new()
+//! .instr(Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO))
+//! .instr(Instr::new(RADDR, Pads::Four, 0x18))
+//! .instr(Instr::new(DUMMY, Pads::Four, 0x06))
+//! .instr(Instr::new(READ, Pads::Four, 0x04))
+//! .build();
+//!
+//! const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
+//! .instr(Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1))
+//! .instr(Instr::new(READ, Pads::One, 0x04))
+//! .build();
+//! ```
+//!
+//! Then, assign each sequence to a [`Command`] in a `LookupTable`:
+//!
+//! ```
+//! use imxrt_boot_gen::serial_flash::{Command, LookupTable};
+//! # use imxrt_boot_gen::serial_flash::{Sequence, SequenceBuilder};
+//!
+//! # const SEQ_READ: Sequence = SequenceBuilder::new().build();
+//! # const SEQ_READ_STATUS: Sequence = SequenceBuilder::new().build();
+//! const LUT: LookupTable = LookupTable::new()
+//! .command(Command::Read, SEQ_READ)
+//! .command(Command::ReadStatus, SEQ_READ_STATUS);
+//! ```
+//!
+//! # FlexSPI Configuration Block
+//!
+//! Once you've created your sequences and lookup table, use the lookup table to create
+//! a [`FlexSPIConfigurationBlock`]. See the `FlexSPIConfigurationBlock` documentation
+//! for more information.
+//!
+//! # Serial NOR Configuration Block
+//!
+//! Finally, use the FlexSPI configuration block to create a Serial NOR configuration
+//! block. You are responsible for placing the serial NOR configuration block at the correct
+//! location in memory. See [`nor::ConfigurationBlock`] for an example.
mod fields;
mod lookup;
@@ -29,6 +86,37 @@ pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_setup_time`].
pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
+/// FlexSPI configuration block
+///
+/// The FlexSPI configuration block consists of parameters that are for specific flash
+/// devices. The configuration block includes the FlexSPI [`LookupTable`]. The configuration
+/// block is shared between serial NOR and NAND configuration blocks.
+///
+/// # Default Values
+///
+/// - `cs_hold_time` is [`RECOMMENDED_CS_HOLD_TIME`]
+/// - `cs_setup_time` is [`RECOMMENDED_CS_SETUP_TIME`]
+///
+/// All other configurable values are set to a bit pattern of 0.
+///
+/// # Examples
+///
+/// ```
+/// use imxrt_boot_gen::serial_flash::*;
+///
+/// # const LUT: LookupTable = LookupTable::new();
+/// const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock =
+/// FlexSPIConfigurationBlock::new(LUT)
+/// .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+/// .cs_hold_time(0x01)
+/// .cs_setup_time(0x02)
+/// .column_address_width(ColumnAddressWidth::OtherDevices)
+/// .device_mode_configuration(DeviceModeConfiguration::Disabled)
+/// .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
+/// .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+/// .serial_clk_freq(SerialClockFrequency::MHz60)
+/// .serial_flash_pad_type(FlashPadType::Quad);
+///
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct FlexSPIConfigurationBlock {
@@ -73,6 +161,8 @@ pub struct FlexSPIConfigurationBlock {
}
impl FlexSPIConfigurationBlock {
+ /// Create a new configuration block that uses `lookup_table` as the
+ /// FlexSPI LUT
pub const fn new(lookup_table: LookupTable) -> Self {
FlexSPIConfigurationBlock {
tag: TAG,
@@ -91,7 +181,7 @@ impl FlexSPIConfigurationBlock {
controller_misc_options: 0,
device_type: 0, // Invalid value; must be updated in NOR / NAND configuration block
serial_flash_pad_type: 1, // Single pad
- serial_clk_freq: 1, // 30MHz
+ serial_clk_freq: 0, // 30MHz
lut_custom_seq_enable: 0,
serial_flash_sizes: [0; 4],
cs_pad_setting_override: 0,
@@ -212,23 +302,3 @@ impl FlexSPIConfigurationBlock {
const _STATIC_ASSERT_SIZE: [u32; 1] =
[0; (core::mem::size_of::() == 448) as usize];
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn smoke() {
- const _FCB: FlexSPIConfigurationBlock =
- FlexSPIConfigurationBlock::new(super::LookupTable::new())
- .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
- .cs_hold_time(0x01)
- .cs_setup_time(0x02)
- .column_address_width(ColumnAddressWidth::OtherDevices)
- .device_mode_configuration(DeviceModeConfiguration::Disabled)
- .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
- .flash_size(SerialFlashRegion::A1, 0x0020_0000)
- .serial_clk_freq(SerialClockFrequency::MHz60)
- .serial_flash_pad_type(FlashPadType::Quad);
- }
-}
diff --git a/src/serial_flash/fields.rs b/src/serial_flash/fields.rs
index dd51858..506a75a 100644
--- a/src/serial_flash/fields.rs
+++ b/src/serial_flash/fields.rs
@@ -21,6 +21,7 @@ pub enum ColumnAddressWidth {
/// Sequence parameter for device mode configuration
#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
pub struct DeviceModeSequence(pub(crate) [u8; 4]);
impl DeviceModeSequence {
/// Create a new sequence parameter for device configuration
@@ -117,6 +118,7 @@ pub enum SerialClockFrequency {
MHz166 = 9,
}
+/// A FlexSPI serial flash region
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub enum SerialFlashRegion {
diff --git a/src/serial_flash/lookup.rs b/src/serial_flash/lookup.rs
index 46e17d9..b465a94 100644
--- a/src/serial_flash/lookup.rs
+++ b/src/serial_flash/lookup.rs
@@ -23,12 +23,12 @@ pub enum Command {
const LOOKUP_TABLE_SIZE_BYTES: usize = 256;
const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
-/// The lookup table, part of the general FCB memory region.
+/// A sequence lookup table, part of the general FlexSPI configuration block
///
-/// `LookupTable` is a fixed-sized byte array. We provide convenience
-/// methods for inserting command sequences into the table. The contents
-/// of sequences are based on the FLASH chip that we're talking to. Refer
-/// to your flash memory's refence manual.
+/// The contents of the sequences depend on what kind of FLASH device we're
+/// interfacing. Refer to your FLASH device manual for more information.
+///
+/// Any unspecified command is set to a sequence of STOPs.
///
/// ```
/// use imxrt_boot_gen::serial_flash::{
@@ -38,14 +38,13 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// Sequence, Instr,
/// opcodes::sdr::*,
/// Pads,
-/// STOP,
/// };
///
-/// let mut lookup_table = LookupTable::new();
-/// lookup_table[Command::Read] = SequenceBuilder::new()
-/// .instr(Instr::new(CMD, Pads::One, 0xEB))
-/// .instr(Instr::new(RADDR, Pads::Four, 0x02))
-/// .build();
+/// const LUT: LookupTable = LookupTable::new()
+/// .command(Command::Read, SequenceBuilder::new()
+/// .instr(Instr::new(CMD, Pads::One, 0xEB))
+/// .instr(Instr::new(RADDR, Pads::Four, 0x02))
+/// .build());
/// ```
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
diff --git a/src/serial_flash/nor.rs b/src/serial_flash/nor.rs
index 28e95ea..b0f42dc 100644
--- a/src/serial_flash/nor.rs
+++ b/src/serial_flash/nor.rs
@@ -1,4 +1,4 @@
-//! Fields specific for NOR flash
+//! Serial NOR configuration blocks and fields
use super::FlexSPIConfigurationBlock;
@@ -26,7 +26,26 @@ pub enum SerialClockFrequency {
MHz166 = 9,
}
-/// The fields specific for defining a serial NOR FCB
+/// A serial NOR configuration block
+///
+/// This is the memory that you'll need to properly place in memory in order to
+/// boot your i.MX RT system. Consider keeping the symbol name, and specifying
+/// a link section, so that you can more easily place the memory in your linker
+/// script.
+///
+/// ```no_run
+/// use imxrt_boot_gen::serial_flash::nor;
+/// # use imxrt_boot_gen::serial_flash::{FlexSPIConfigurationBlock, LookupTable};
+///
+/// # const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock = FlexSPIConfigurationBlock::new(LookupTable::new());
+/// #[no_mangle]
+/// #[link_section = ".serial_nor_cb"]
+/// static SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
+/// nor::ConfigurationBlock::new(FLEXSPI_CONFIGURATION_BLOCK)
+/// .page_size(256)
+/// .sector_size(4096)
+/// .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
+/// ```
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct ConfigurationBlock {
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index 4d490db..a4c0629 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -1,4 +1,4 @@
-//! FlexSPI configuration block for the Teensy 4
+//! Serial NOR configuration block for the Teensy 4
use imxrt_boot_gen::serial_flash::opcodes::sdr::*;
use imxrt_boot_gen::serial_flash::*;
From 1a3bbb2fa564ef2e14244289ce525e68897c825a Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sun, 15 Nov 2020 18:37:49 -0500
Subject: [PATCH 09/19] Address clippy warnings
---
src/serial_flash.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/serial_flash.rs b/src/serial_flash.rs
index 2a8d98d..90ba8a9 100644
--- a/src/serial_flash.rs
+++ b/src/serial_flash.rs
@@ -193,7 +193,7 @@ impl FlexSPIConfigurationBlock {
data_valid_time: 0,
busy_offset: 0,
busy_bit_polarity: 0,
- lookup_table: lookup_table,
+ lookup_table,
lut_custom_seq: [0; 48],
_reserved0: [0; 4],
From c8b1c7654477d700b226db56d20ace322209da10 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:27:08 -0500
Subject: [PATCH 10/19] Reorganize flexspi API into separate module
---
src/flexspi.rs | 298 ++++++++++++++++++++
src/{serial_flash => flexspi}/fields.rs | 24 +-
src/{serial_flash => flexspi}/lookup.rs | 8 +-
src/{flexspi_lut.rs => flexspi/sequence.rs} | 10 +-
src/lib.rs | 4 +-
src/serial_flash.rs | 297 +------------------
src/serial_flash/nor.rs | 18 +-
tests/teensy4.rs | 2 +-
8 files changed, 333 insertions(+), 328 deletions(-)
create mode 100644 src/flexspi.rs
rename src/{serial_flash => flexspi}/fields.rs (82%)
rename src/{serial_flash => flexspi}/lookup.rs (94%)
rename src/{flexspi_lut.rs => flexspi/sequence.rs} (98%)
diff --git a/src/flexspi.rs b/src/flexspi.rs
new file mode 100644
index 0000000..6f2c6ad
--- /dev/null
+++ b/src/flexspi.rs
@@ -0,0 +1,298 @@
+//! FlexSPI configuration block definitions
+//!
+//! The FlexSPI module includes
+//!
+//! - instruction sequences
+//! - instruction lookup table (LUT)
+//! - the FlexSPI configuration block
+//!
+//! The `flexspi` types are used throughout the [`serial_flash`](crate::serial_flash) API, since the FlexSPI
+//! configuration block is at the start of every serial NOR / NAND configuration block.
+//!
+//! # Sequences and LUTs
+//!
+//! A [`Sequence`] is a collection of up to eight FlexSPI instructions ([`Instr`]).
+//! The FlexSPI controller sequentially executes instructions to perform reads, writes
+//! and I/O with a connected FLASH device. The FlexSPI controller finds each sequence
+//! in a [`LookupTable`].
+//!
+//! Use a [`SequenceBuilder`] to create `Sequence`s:
+//!
+//! ```
+//! use imxrt_boot_gen::flexspi::{Instr, Sequence, SequenceBuilder, Pads, opcodes::sdr::*};
+//!
+//! # const FAST_READ_QUAD_IO: u8 = 0;
+//! # const READ_STATUS_REGISTER_1: u8 = 0;
+//! const SEQ_READ: Sequence = SequenceBuilder::new()
+//! .instr(Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO))
+//! .instr(Instr::new(RADDR, Pads::Four, 0x18))
+//! .instr(Instr::new(DUMMY, Pads::Four, 0x06))
+//! .instr(Instr::new(READ, Pads::Four, 0x04))
+//! .build();
+//!
+//! const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
+//! .instr(Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1))
+//! .instr(Instr::new(READ, Pads::One, 0x04))
+//! .build();
+//! ```
+//!
+//! Then, assign each sequence to a [`Command`] in a `LookupTable`:
+//!
+//! ```
+//! use imxrt_boot_gen::flexspi::{Command, LookupTable};
+//! # use imxrt_boot_gen::flexspi::{Sequence, SequenceBuilder};
+//!
+//! # const SEQ_READ: Sequence = SequenceBuilder::new().build();
+//! # const SEQ_READ_STATUS: Sequence = SequenceBuilder::new().build();
+//! const LUT: LookupTable = LookupTable::new()
+//! .command(Command::Read, SEQ_READ)
+//! .command(Command::ReadStatus, SEQ_READ_STATUS);
+//! ```
+//!
+//! # FlexSPI Configuration Block
+//!
+//! Once you've created your sequences and lookup table, use the lookup table to create
+//! a [`FlexSPIConfigurationBlock`]. See the `FlexSPIConfigurationBlock` documentation
+//! for more information.
+
+mod fields;
+mod lookup;
+mod sequence;
+
+pub use fields::*;
+pub use lookup::{Command, LookupTable};
+pub use sequence::{opcodes, Instr, Pads, Sequence, SequenceBuilder, JUMP_ON_CS, STOP};
+
+/// ASCII 'FCFB'
+const TAG: u32 = 0x4246_4346;
+/// [07:00] bugfix = 0
+/// [15:08] minor
+/// [23:16] major = 1
+/// [31:24] ascii ‘V’
+const VERSION: u32 = 0x5601_0000;
+
+/// The recommended `csHoldTime`, `0x03`.
+///
+/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_hold_time`].
+pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
+/// The recommended `csSetupTime`, `0x03`.
+///
+/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_setup_time`].
+pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
+
+/// FlexSPI configuration block
+///
+/// The FlexSPI configuration block consists of parameters that are for specific flash
+/// devices. The configuration block includes the FlexSPI [`LookupTable`]. The configuration
+/// block is shared between serial NOR and NAND configuration blocks.
+///
+/// # Default Values
+///
+/// - `cs_hold_time` is [`RECOMMENDED_CS_HOLD_TIME`]
+/// - `cs_setup_time` is [`RECOMMENDED_CS_SETUP_TIME`]
+///
+/// All other configurable values are set to a bit pattern of 0.
+///
+/// # Examples
+///
+/// ```
+/// use imxrt_boot_gen::flexspi::*;
+///
+/// # const LUT: LookupTable = LookupTable::new();
+/// const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock =
+/// FlexSPIConfigurationBlock::new(LUT)
+/// .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+/// .cs_hold_time(0x01)
+/// .cs_setup_time(0x02)
+/// .column_address_width(ColumnAddressWidth::OtherDevices)
+/// .device_mode_configuration(DeviceModeConfiguration::Disabled)
+/// .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
+/// .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+/// .serial_clk_freq(SerialClockFrequency::MHz60)
+/// .serial_flash_pad_type(FlashPadType::Quad);
+///
+#[derive(Debug, Clone, Copy)]
+#[repr(C, packed)]
+pub struct FlexSPIConfigurationBlock {
+ tag: u32,
+ version: u32,
+ _reserved0: [u8; 4], // 0x008
+ read_sample_clk_src: u8,
+ cs_hold_time: u8,
+ cs_setup_time: u8,
+ column_address_width: u8,
+ device_mode_configuration: u8,
+ _reserved1: [u8; 1], // 0x011
+ wait_time_cfg_commands: WaitTimeConfigurationCommands,
+ device_mode_sequence: DeviceModeSequence,
+ device_mode_arg: u32,
+ config_cmd_enable: u8,
+ _reserved2: [u8; 3], // 0x01D
+ config_cmd_seqs: [u8; 12],
+ _reserved3: [u8; 4], // 0x02C
+ cfg_cmd_args: [u8; 12],
+ _reserved4: [u8; 4], // 0x03C
+ controller_misc_options: u32,
+ pub(crate) device_type: u8,
+ serial_flash_pad_type: u8,
+ serial_clk_freq: u8,
+ lut_custom_seq_enable: u8,
+ _reserved5: [u8; 8], // 0x048
+ /// A1, A2, B1, B2
+ serial_flash_sizes: [u32; 4],
+ cs_pad_setting_override: u32,
+ sclk_pad_setting_override: u32,
+ data_pad_setting_override: u32,
+ dqs_pad_setting_override: u32,
+ timeout_ms: u32,
+ command_interval: u32,
+ data_valid_time: u32,
+ busy_offset: u16,
+ busy_bit_polarity: u16,
+ lookup_table: LookupTable,
+ lut_custom_seq: [u8; 48],
+ _reserved6: [u8; 16],
+}
+
+impl FlexSPIConfigurationBlock {
+ /// Create a new configuration block that uses `lookup_table` as the
+ /// FlexSPI LUT
+ pub const fn new(lookup_table: LookupTable) -> Self {
+ FlexSPIConfigurationBlock {
+ tag: TAG,
+ version: VERSION,
+ read_sample_clk_src: ReadSampleClockSource::InternalLoopback as u8,
+ cs_hold_time: RECOMMENDED_CS_HOLD_TIME,
+ cs_setup_time: RECOMMENDED_CS_SETUP_TIME,
+ column_address_width: ColumnAddressWidth::OtherDevices as u8,
+ device_mode_configuration: 0, // Disabled
+ wait_time_cfg_commands: WaitTimeConfigurationCommands::disable(),
+ device_mode_sequence: DeviceModeSequence::new(0, 0),
+ device_mode_arg: 0,
+ config_cmd_enable: 0,
+ config_cmd_seqs: [0; 12],
+ cfg_cmd_args: [0; 12],
+ controller_misc_options: 0,
+ device_type: 0, // Invalid value; must be updated in NOR / NAND configuration block
+ serial_flash_pad_type: 1, // Single pad
+ serial_clk_freq: 0, // 30MHz
+ lut_custom_seq_enable: 0,
+ serial_flash_sizes: [0; 4],
+ cs_pad_setting_override: 0,
+ sclk_pad_setting_override: 0,
+ data_pad_setting_override: 0,
+ dqs_pad_setting_override: 0,
+ timeout_ms: 0,
+ command_interval: 0,
+ data_valid_time: 0,
+ busy_offset: 0,
+ busy_bit_polarity: 0,
+ lookup_table,
+ lut_custom_seq: [0; 48],
+
+ _reserved0: [0; 4],
+ _reserved1: [0; 1],
+ _reserved2: [0; 3],
+ _reserved3: [0; 4],
+ _reserved4: [0; 4],
+ _reserved5: [0; 8],
+ _reserved6: [0; 16],
+ }
+ }
+
+ /// `readSampleClkSrc`, the clock source for FlexSPI
+ ///
+ /// If not set, this defaults to `ReadSampleClockSource::InternalLoopback`.
+ pub const fn read_sample_clk_src(mut self, read_sample_clk_src: ReadSampleClockSource) -> Self {
+ self.read_sample_clk_src = read_sample_clk_src as u8;
+ self
+ }
+
+ /// Set the chip select hold time (`csHoldTime`)
+ ///
+ /// If not set, this will be `RECOMMENDED_CS_HOLD_TIME`, which is `0x03`.
+ pub const fn cs_hold_time(mut self, cs_hold_time: u8) -> Self {
+ self.cs_hold_time = cs_hold_time;
+ self
+ }
+
+ /// Set the chip select setup time (`csSetupTime`)
+ ///
+ /// If not set, this will be `RECOMMENDED_CS_SETUP_TIME`, which is `0x03`.
+ pub const fn cs_setup_time(mut self, cs_setup_time: u8) -> Self {
+ self.cs_setup_time = cs_setup_time;
+ self
+ }
+
+ /// `columnAddressWidth`, the properties of the flash memory
+ ///
+ /// If not set, this defaults to `ColumnAddressWidth::OtherDevices`
+ pub const fn column_address_width(mut self, column_address_width: ColumnAddressWidth) -> Self {
+ self.column_address_width = column_address_width as u8;
+ self
+ }
+
+ /// Sets device configuration mode. The `DeviceModeConfiguration::Disabled` variant
+ /// will set `deviceModeCfgEnable` to "disabled". Otherwise, we will set
+ /// `deviceModeCfgEnable` to "enabled," and we use the sequence and argument
+ /// parameters in the FCB.
+ ///
+ /// If not set, this defaults to `DeviceModeConfiguration::Disabled`.
+ pub const fn device_mode_configuration(
+ mut self,
+ device_mode_configuration: DeviceModeConfiguration,
+ ) -> Self {
+ match device_mode_configuration {
+ DeviceModeConfiguration::Disabled => {
+ self.device_mode_configuration = 0;
+ }
+ DeviceModeConfiguration::Enabled {
+ device_mode_seq,
+ device_mode_arg,
+ } => {
+ self.device_mode_configuration = 1;
+ self.device_mode_sequence = device_mode_seq;
+ self.device_mode_arg = device_mode_arg;
+ }
+ }
+ self
+ }
+
+ /// Sets `waitTimeCfgCommands`
+ ///
+ /// If not set, this defaults to `WaitTimeConfigurationCommands::disable()`.
+ pub const fn wait_time_cfg_commands(
+ mut self,
+ wait_time_cfg_commands: WaitTimeConfigurationCommands,
+ ) -> Self {
+ self.wait_time_cfg_commands = wait_time_cfg_commands;
+ self
+ }
+
+ /// Sets the serial flash pad type, `sFlashPad`.
+ ///
+ /// If not set, this defaults to `FlashPadType::Single`.
+ pub const fn serial_flash_pad_type(mut self, serial_flash_pad_type: FlashPadType) -> Self {
+ self.serial_flash_pad_type = serial_flash_pad_type as u8;
+ self
+ }
+
+ /// Sets the serial clock frequencey, `serialClkFreq`
+ ///
+ /// If not set, this defaults to `SerialClockFrequency::MHz30`.
+ pub const fn serial_clk_freq(mut self, serial_clk_freq: SerialClockFrequency) -> Self {
+ self.serial_clk_freq = serial_clk_freq as u8;
+ self
+ }
+
+ /// Set a flash size for the provided flash region
+ ///
+ /// Any region that's not set will default to `0`.
+ pub const fn flash_size(mut self, flash_region: SerialFlashRegion, flash_size: u32) -> Self {
+ self.serial_flash_sizes[flash_region as usize] = flash_size;
+ self
+ }
+}
+
+const _STATIC_ASSERT_SIZE: [u32; 1] =
+ [0; (core::mem::size_of::() == 448) as usize];
diff --git a/src/serial_flash/fields.rs b/src/flexspi/fields.rs
similarity index 82%
rename from src/serial_flash/fields.rs
rename to src/flexspi/fields.rs
index 506a75a..28b63fc 100644
--- a/src/serial_flash/fields.rs
+++ b/src/flexspi/fields.rs
@@ -1,6 +1,4 @@
-//! FCB fields
-//!
-//! The module implements and documents the common FCB fields.
+//! FlexSPI configuration block fields
/// `readSampleClkSrc` of the general FCB
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -20,17 +18,17 @@ pub enum ColumnAddressWidth {
}
/// Sequence parameter for device mode configuration
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
+#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
#[repr(transparent)]
-pub struct DeviceModeSequence(pub(crate) [u8; 4]);
+pub struct DeviceModeSequence([u8; 4]);
impl DeviceModeSequence {
/// Create a new sequence parameter for device configuration
///
/// `starting_lut_index`: starting LUT index of Device mode configuration command
/// `number_of_luts`: number of LUT sequences for Device mode configuration command
- pub fn new(number_of_luts: u8, starting_lut_index: u8) -> Self {
+ pub const fn new(number_of_luts: u8, starting_lut_index: u8) -> Self {
DeviceModeSequence(
- ((u32::from(starting_lut_index) << 8) | u32::from(number_of_luts)).to_le_bytes(),
+ (((starting_lut_index as u32) << 8) | (number_of_luts as u32)).to_le_bytes(),
)
}
}
@@ -68,7 +66,8 @@ impl Default for DeviceModeConfiguration {
/// > for all device memory configuration commands instead of using read
/// > status to wait until these commands complete.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct WaitTimeConfigurationCommands(pub(crate) u16);
+#[repr(transparent)]
+pub struct WaitTimeConfigurationCommands(u16);
impl WaitTimeConfigurationCommands {
pub const fn disable() -> Self {
WaitTimeConfigurationCommands(0)
@@ -83,15 +82,6 @@ impl WaitTimeConfigurationCommands {
}
}
-/// Describes the `deviceType` field.
-///
-/// Only the SerialNOR is implemented; `DeviceType`
-/// may also have `SerialNAND` in the future.
-#[derive(Debug, Clone, Copy)]
-pub enum DeviceType {
- SerialNOR(super::nor::ConfigurationBlock),
-}
-
/// `sFlashPad` field
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
diff --git a/src/serial_flash/lookup.rs b/src/flexspi/lookup.rs
similarity index 94%
rename from src/serial_flash/lookup.rs
rename to src/flexspi/lookup.rs
index b465a94..1202779 100644
--- a/src/serial_flash/lookup.rs
+++ b/src/flexspi/lookup.rs
@@ -1,6 +1,6 @@
-//! Lookup table
+//! FlexSPI Lookup table
-pub use crate::flexspi_lut::*;
+use super::sequence::{Sequence, SEQUENCE_SIZE};
/// The default sequence definition lookup indices
///
@@ -31,7 +31,7 @@ const NUMBER_OF_SEQUENCES: usize = LOOKUP_TABLE_SIZE_BYTES / SEQUENCE_SIZE;
/// Any unspecified command is set to a sequence of STOPs.
///
/// ```
-/// use imxrt_boot_gen::serial_flash::{
+/// use imxrt_boot_gen::flexspi::{
/// LookupTable,
/// Command,
/// SequenceBuilder,
@@ -65,7 +65,7 @@ impl LookupTable {
#[cfg(test)]
mod test {
use super::{Command, LookupTable};
- use crate::serial_flash::SequenceBuilder;
+ use crate::flexspi::sequence::SequenceBuilder;
#[test]
fn smoke() {
diff --git a/src/flexspi_lut.rs b/src/flexspi/sequence.rs
similarity index 98%
rename from src/flexspi_lut.rs
rename to src/flexspi/sequence.rs
index dce361e..dc0e899 100644
--- a/src/flexspi_lut.rs
+++ b/src/flexspi/sequence.rs
@@ -1,4 +1,4 @@
-//! FlexSPI Lookup Table (LUT) instructions, opcodes, and sequences
+//! FlexSPI instructions, opcodes, and sequences
//!
//! Derived from the iMXRT1060 Reference Manual (Rev 2),
//! section 27.5.8.
@@ -56,7 +56,7 @@ pub(crate) const INSTRUCTIONS_PER_SEQUENCE: usize = 8;
/// a `Sequence`. The sequences you'll require are dependent on the specific flash memory that
/// you're interacting with.
///
-/// `Sequence`s are used to create a [`LookupTable`].
+/// `Sequence`s are used to create a [`LookupTable`](crate::flexspi::LookupTable).
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Sequence(pub(crate) [Instr; INSTRUCTIONS_PER_SEQUENCE]);
@@ -78,7 +78,7 @@ impl Sequence {
/// # Example
///
/// ```
-/// use imxrt_boot_gen::serial_flash::{
+/// use imxrt_boot_gen::flexspi::{
/// Sequence,
/// SequenceBuilder,
/// Instr,
@@ -397,7 +397,7 @@ mod test {
//
/// ```
-/// use imxrt_boot_gen::serial_flash::{*, opcodes::sdr::*};
+/// use imxrt_boot_gen::flexspi::{*, opcodes::sdr::*};
/// const INSTR: Instr = Instr::new(RADDR, Pads::Four, 0x18);
/// const OUT_OF_BOUNDS: Sequence = SequenceBuilder::new()
/// .instr(INSTR)
@@ -414,7 +414,7 @@ mod test {
struct SequenceBuilderInstructionLimit;
/// ```compile_fail
-/// use imxrt_boot_gen::serial_flash::{*, opcodes::sdr::*};
+/// use imxrt_boot_gen::flexspi::{*, opcodes::sdr::*};
/// const INSTR: Instr = Instr::new(RADDR, Pads::Four, 0x18);
/// const OUT_OF_BOUNDS: Sequence = SequenceBuilder::new()
/// .instr(INSTR)
diff --git a/src/lib.rs b/src/lib.rs
index 754906b..3dc3b2f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,4 @@
-//! Generate data structures for booting iMXRT processors
+//! Generate data structures for booting i.MX RT processors
//!
//! # Rationale
//!
@@ -28,5 +28,5 @@
#![cfg_attr(not(test), no_std)]
-mod flexspi_lut;
+pub mod flexspi;
pub mod serial_flash;
diff --git a/src/serial_flash.rs b/src/serial_flash.rs
index 90ba8a9..55dd9ba 100644
--- a/src/serial_flash.rs
+++ b/src/serial_flash.rs
@@ -3,302 +3,13 @@
//! `serial_flash` provides the types necessary to boot an i.MX RT processor
//! from serial NOR flash. *Note: NAND Flash boot not yet implemented.*
//!
-//! The API includes
-//!
-//! - Flexible Serial Peripheral Interface (FlexSPI) sequence and lookup table (LUT)
-//! - FlexSPI configuration block ([`FlexSPIConfigurationBlock`])
-//! - serial NOR configuration block ([`nor::ConfigurationBlock`]), which should be
-//! properly placed in memory
-//!
-//! # Sequences and LUTs
-//!
-//! A [`Sequence`] is a collection of up to eight FlexSPI instructions ([`Instr`]).
-//! The FlexSPI controller sequentially executes instructions to perform reads, writes
-//! and I/O with a connected FLASH device. The FlexSPI controller finds each sequence
-//! in a [`LookupTable`].
-//!
-//! Use a [`SequenceBuilder`] to create `Sequence`s:
-//!
-//! ```
-//! use imxrt_boot_gen::serial_flash::{Instr, Sequence, SequenceBuilder, Pads, opcodes::sdr::*};
-//!
-//! # const FAST_READ_QUAD_IO: u8 = 0;
-//! # const READ_STATUS_REGISTER_1: u8 = 0;
-//! const SEQ_READ: Sequence = SequenceBuilder::new()
-//! .instr(Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO))
-//! .instr(Instr::new(RADDR, Pads::Four, 0x18))
-//! .instr(Instr::new(DUMMY, Pads::Four, 0x06))
-//! .instr(Instr::new(READ, Pads::Four, 0x04))
-//! .build();
-//!
-//! const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
-//! .instr(Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1))
-//! .instr(Instr::new(READ, Pads::One, 0x04))
-//! .build();
-//! ```
-//!
-//! Then, assign each sequence to a [`Command`] in a `LookupTable`:
-//!
-//! ```
-//! use imxrt_boot_gen::serial_flash::{Command, LookupTable};
-//! # use imxrt_boot_gen::serial_flash::{Sequence, SequenceBuilder};
-//!
-//! # const SEQ_READ: Sequence = SequenceBuilder::new().build();
-//! # const SEQ_READ_STATUS: Sequence = SequenceBuilder::new().build();
-//! const LUT: LookupTable = LookupTable::new()
-//! .command(Command::Read, SEQ_READ)
-//! .command(Command::ReadStatus, SEQ_READ_STATUS);
-//! ```
-//!
-//! # FlexSPI Configuration Block
-//!
-//! Once you've created your sequences and lookup table, use the lookup table to create
-//! a [`FlexSPIConfigurationBlock`]. See the `FlexSPIConfigurationBlock` documentation
-//! for more information.
-//!
//! # Serial NOR Configuration Block
//!
-//! Finally, use the FlexSPI configuration block to create a Serial NOR configuration
+//! To create a serial NOR configuration block, first create a FlexSPI
+//! configuration block. See the [`flexspi`](crate::flexspi) module for more details.
+//!
+//! Use the FlexSPI configuration block to create a Serial NOR configuration
//! block. You are responsible for placing the serial NOR configuration block at the correct
//! location in memory. See [`nor::ConfigurationBlock`] for an example.
-mod fields;
-mod lookup;
pub mod nor;
-
-pub use fields::*;
-pub use lookup::*;
-
-/// ASCII 'FCFB'
-const TAG: u32 = 0x4246_4346;
-/// [07:00] bugfix = 0
-/// [15:08] minor
-/// [23:16] major = 1
-/// [31:24] ascii ‘V’
-const VERSION: u32 = 0x5601_0000;
-
-/// The recommended `csHoldTime`, `0x03`.
-///
-/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_hold_time`].
-pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
-/// The recommended `csSetupTime`, `0x03`.
-///
-/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_setup_time`].
-pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
-
-/// FlexSPI configuration block
-///
-/// The FlexSPI configuration block consists of parameters that are for specific flash
-/// devices. The configuration block includes the FlexSPI [`LookupTable`]. The configuration
-/// block is shared between serial NOR and NAND configuration blocks.
-///
-/// # Default Values
-///
-/// - `cs_hold_time` is [`RECOMMENDED_CS_HOLD_TIME`]
-/// - `cs_setup_time` is [`RECOMMENDED_CS_SETUP_TIME`]
-///
-/// All other configurable values are set to a bit pattern of 0.
-///
-/// # Examples
-///
-/// ```
-/// use imxrt_boot_gen::serial_flash::*;
-///
-/// # const LUT: LookupTable = LookupTable::new();
-/// const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock =
-/// FlexSPIConfigurationBlock::new(LUT)
-/// .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
-/// .cs_hold_time(0x01)
-/// .cs_setup_time(0x02)
-/// .column_address_width(ColumnAddressWidth::OtherDevices)
-/// .device_mode_configuration(DeviceModeConfiguration::Disabled)
-/// .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
-/// .flash_size(SerialFlashRegion::A1, 0x0020_0000)
-/// .serial_clk_freq(SerialClockFrequency::MHz60)
-/// .serial_flash_pad_type(FlashPadType::Quad);
-///
-#[derive(Debug, Clone, Copy)]
-#[repr(C, packed)]
-pub struct FlexSPIConfigurationBlock {
- tag: u32,
- version: u32,
- _reserved0: [u8; 4], // 0x008
- read_sample_clk_src: u8,
- cs_hold_time: u8,
- cs_setup_time: u8,
- column_address_width: u8,
- device_mode_configuration: u8,
- _reserved1: [u8; 1], // 0x011
- wait_time_cfg_commands: u16,
- device_mode_sequence: [u8; 4],
- device_mode_arg: u32,
- config_cmd_enable: u8,
- _reserved2: [u8; 3], // 0x01D
- config_cmd_seqs: [u8; 12],
- _reserved3: [u8; 4], // 0x02C
- cfg_cmd_args: [u8; 12],
- _reserved4: [u8; 4], // 0x03C
- controller_misc_options: u32,
- device_type: u8,
- serial_flash_pad_type: u8,
- serial_clk_freq: u8,
- lut_custom_seq_enable: u8,
- _reserved5: [u8; 8], // 0x048
- /// A1, A2, B1, B2
- serial_flash_sizes: [u32; 4],
- cs_pad_setting_override: u32,
- sclk_pad_setting_override: u32,
- data_pad_setting_override: u32,
- dqs_pad_setting_override: u32,
- timeout_ms: u32,
- command_interval: u32,
- data_valid_time: u32,
- busy_offset: u16,
- busy_bit_polarity: u16,
- lookup_table: LookupTable,
- lut_custom_seq: [u8; 48],
- _reserved6: [u8; 16],
-}
-
-impl FlexSPIConfigurationBlock {
- /// Create a new configuration block that uses `lookup_table` as the
- /// FlexSPI LUT
- pub const fn new(lookup_table: LookupTable) -> Self {
- FlexSPIConfigurationBlock {
- tag: TAG,
- version: VERSION,
- read_sample_clk_src: ReadSampleClockSource::InternalLoopback as u8,
- cs_hold_time: RECOMMENDED_CS_HOLD_TIME,
- cs_setup_time: RECOMMENDED_CS_SETUP_TIME,
- column_address_width: ColumnAddressWidth::OtherDevices as u8,
- device_mode_configuration: 0, // Disabled
- wait_time_cfg_commands: 0,
- device_mode_sequence: [0; 4],
- device_mode_arg: 0,
- config_cmd_enable: 0,
- config_cmd_seqs: [0; 12],
- cfg_cmd_args: [0; 12],
- controller_misc_options: 0,
- device_type: 0, // Invalid value; must be updated in NOR / NAND configuration block
- serial_flash_pad_type: 1, // Single pad
- serial_clk_freq: 0, // 30MHz
- lut_custom_seq_enable: 0,
- serial_flash_sizes: [0; 4],
- cs_pad_setting_override: 0,
- sclk_pad_setting_override: 0,
- data_pad_setting_override: 0,
- dqs_pad_setting_override: 0,
- timeout_ms: 0,
- command_interval: 0,
- data_valid_time: 0,
- busy_offset: 0,
- busy_bit_polarity: 0,
- lookup_table,
- lut_custom_seq: [0; 48],
-
- _reserved0: [0; 4],
- _reserved1: [0; 1],
- _reserved2: [0; 3],
- _reserved3: [0; 4],
- _reserved4: [0; 4],
- _reserved5: [0; 8],
- _reserved6: [0; 16],
- }
- }
-
- /// `readSampleClkSrc`, the clock source for FlexSPI
- ///
- /// If not set, this defaults to `ReadSampleClockSource::InternalLoopback`.
- pub const fn read_sample_clk_src(mut self, read_sample_clk_src: ReadSampleClockSource) -> Self {
- self.read_sample_clk_src = read_sample_clk_src as u8;
- self
- }
-
- /// Set the chip select hold time (`csHoldTime`)
- ///
- /// If not set, this will be `RECOMMENDED_CS_HOLD_TIME`, which is `0x03`.
- pub const fn cs_hold_time(mut self, cs_hold_time: u8) -> Self {
- self.cs_hold_time = cs_hold_time;
- self
- }
-
- /// Set the chip select setup time (`csSetupTime`)
- ///
- /// If not set, this will be `RECOMMENDED_CS_SETUP_TIME`, which is `0x03`.
- pub const fn cs_setup_time(mut self, cs_setup_time: u8) -> Self {
- self.cs_setup_time = cs_setup_time;
- self
- }
-
- /// `columnAddressWidth`, the properties of the flash memory
- ///
- /// If not set, this defaults to `ColumnAddressWidth::OtherDevices`
- pub const fn column_address_width(mut self, column_address_width: ColumnAddressWidth) -> Self {
- self.column_address_width = column_address_width as u8;
- self
- }
-
- /// Sets device configuration mode. The `DeviceModeConfiguration::Disabled` variant
- /// will set `deviceModeCfgEnable` to "disabled". Otherwise, we will set
- /// `deviceModeCfgEnable` to "enabled," and we use the sequence and argument
- /// parameters in the FCB.
- ///
- /// If not set, this defaults to `DeviceModeConfiguration::Disabled`.
- pub const fn device_mode_configuration(
- mut self,
- device_mode_configuration: DeviceModeConfiguration,
- ) -> Self {
- match device_mode_configuration {
- DeviceModeConfiguration::Disabled => {
- self.device_mode_configuration = 0;
- }
- DeviceModeConfiguration::Enabled {
- device_mode_seq,
- device_mode_arg,
- } => {
- self.device_mode_configuration = 1;
- self.device_mode_sequence = device_mode_seq.0;
- self.device_mode_arg = device_mode_arg;
- }
- }
- self
- }
-
- /// Sets `waitTimeCfgCommands`
- ///
- /// If not set, this defaults to `WaitTimeConfigurationCommands::disable()`.
- pub const fn wait_time_cfg_commands(
- mut self,
- wait_time_cfg_commands: WaitTimeConfigurationCommands,
- ) -> Self {
- self.wait_time_cfg_commands = wait_time_cfg_commands.0;
- self
- }
-
- /// Sets the serial flash pad type, `sFlashPad`.
- ///
- /// If not set, this defaults to `FlashPadType::Single`.
- pub const fn serial_flash_pad_type(mut self, serial_flash_pad_type: FlashPadType) -> Self {
- self.serial_flash_pad_type = serial_flash_pad_type as u8;
- self
- }
-
- /// Sets the serial clock frequencey, `serialClkFreq`
- ///
- /// If not set, this defaults to `SerialClockFrequency::MHz30`.
- pub const fn serial_clk_freq(mut self, serial_clk_freq: SerialClockFrequency) -> Self {
- self.serial_clk_freq = serial_clk_freq as u8;
- self
- }
-
- /// Set a flash size for the provided flash region
- ///
- /// Any region that's not set will default to `0`.
- pub const fn flash_size(mut self, flash_region: SerialFlashRegion, flash_size: u32) -> Self {
- self.serial_flash_sizes[flash_region as usize] = flash_size;
- self
- }
-}
-
-const _STATIC_ASSERT_SIZE: [u32; 1] =
- [0; (core::mem::size_of::() == 448) as usize];
diff --git a/src/serial_flash/nor.rs b/src/serial_flash/nor.rs
index b0f42dc..41881af 100644
--- a/src/serial_flash/nor.rs
+++ b/src/serial_flash/nor.rs
@@ -1,10 +1,10 @@
//! Serial NOR configuration blocks and fields
-use super::FlexSPIConfigurationBlock;
+use crate::flexspi::FlexSPIConfigurationBlock;
/// `ipCmdSerialClkFreq` field for serial NOR-specific FCB
///
-/// Chip specific value, not used by ROM
+/// Chip specific value, not used by ROM.
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum SerialClockFrequency {
@@ -33,9 +33,11 @@ pub enum SerialClockFrequency {
/// a link section, so that you can more easily place the memory in your linker
/// script.
///
+/// Unless otherwise specified, all unset fields are set to a bitpattern of zero.
+///
/// ```no_run
/// use imxrt_boot_gen::serial_flash::nor;
-/// # use imxrt_boot_gen::serial_flash::{FlexSPIConfigurationBlock, LookupTable};
+/// # use imxrt_boot_gen::flexspi::{FlexSPIConfigurationBlock, LookupTable};
///
/// # const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock = FlexSPIConfigurationBlock::new(LookupTable::new());
/// #[no_mangle]
@@ -57,6 +59,8 @@ pub struct ConfigurationBlock {
}
impl ConfigurationBlock {
+ /// Create a new serial NOR configuration block based on the FlexSPI configuration
+ /// block
pub const fn new(mut mem_cfg: FlexSPIConfigurationBlock) -> Self {
mem_cfg.device_type = 1;
ConfigurationBlock {
@@ -67,14 +71,17 @@ impl ConfigurationBlock {
_reserved: [0; 52],
}
}
+ /// Set the serial NOR page size
pub const fn page_size(mut self, page_size: u32) -> Self {
self.page_size = page_size;
self
}
+ /// Set the serial NOR sector size
pub const fn sector_size(mut self, sector_size: u32) -> Self {
self.sector_size = sector_size;
self
}
+ /// Set the serial clock frequency
pub const fn ip_cmd_serial_clk_freq(
mut self,
serial_clock_frequency: SerialClockFrequency,
@@ -89,9 +96,8 @@ const _STATIC_ASSERT_SIZE: [u32; 1] =
#[cfg(test)]
mod test {
- use super::{
- super::LookupTable, ConfigurationBlock, FlexSPIConfigurationBlock, SerialClockFrequency,
- };
+ use super::{ConfigurationBlock, FlexSPIConfigurationBlock, SerialClockFrequency};
+ use crate::flexspi::LookupTable;
#[test]
fn smoke() {
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index a4c0629..5c1937b 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -1,6 +1,6 @@
//! Serial NOR configuration block for the Teensy 4
-use imxrt_boot_gen::serial_flash::opcodes::sdr::*;
+use imxrt_boot_gen::flexspi::{opcodes::sdr::*, *};
use imxrt_boot_gen::serial_flash::*;
/// Instructions for the Winbond W25Q16JV
From f9839cb599147a505c23899daf57515c656b0b6a Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:31:42 -0500
Subject: [PATCH 11/19] Rename FlexSPI configuration block
Simplify the name, since it's now in the flexspi module.
---
src/flexspi.rs | 18 +++++++++---------
src/serial_flash/nor.rs | 14 +++++++-------
tests/teensy4.rs | 23 ++++++++++++-----------
3 files changed, 28 insertions(+), 27 deletions(-)
diff --git a/src/flexspi.rs b/src/flexspi.rs
index 6f2c6ad..df8d0c2 100644
--- a/src/flexspi.rs
+++ b/src/flexspi.rs
@@ -52,7 +52,7 @@
//! # FlexSPI Configuration Block
//!
//! Once you've created your sequences and lookup table, use the lookup table to create
-//! a [`FlexSPIConfigurationBlock`]. See the `FlexSPIConfigurationBlock` documentation
+//! a [`ConfigurationBlock`]. See the `ConfigurationBlock` documentation
//! for more information.
mod fields;
@@ -73,11 +73,11 @@ const VERSION: u32 = 0x5601_0000;
/// The recommended `csHoldTime`, `0x03`.
///
-/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_hold_time`].
+/// This is the default value if not set with [`ConfigurationBlock::cs_hold_time`].
pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
/// The recommended `csSetupTime`, `0x03`.
///
-/// This is the default value if not set with [`FlexSPIConfigurationBlock::cs_setup_time`].
+/// This is the default value if not set with [`ConfigurationBlock::cs_setup_time`].
pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
/// FlexSPI configuration block
@@ -99,8 +99,8 @@ pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
/// use imxrt_boot_gen::flexspi::*;
///
/// # const LUT: LookupTable = LookupTable::new();
-/// const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock =
-/// FlexSPIConfigurationBlock::new(LUT)
+/// const FLEXSPI_CONFIGURATION_BLOCK: ConfigurationBlock =
+/// ConfigurationBlock::new(LUT)
/// .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
/// .cs_hold_time(0x01)
/// .cs_setup_time(0x02)
@@ -113,7 +113,7 @@ pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
///
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
-pub struct FlexSPIConfigurationBlock {
+pub struct ConfigurationBlock {
tag: u32,
version: u32,
_reserved0: [u8; 4], // 0x008
@@ -154,11 +154,11 @@ pub struct FlexSPIConfigurationBlock {
_reserved6: [u8; 16],
}
-impl FlexSPIConfigurationBlock {
+impl ConfigurationBlock {
/// Create a new configuration block that uses `lookup_table` as the
/// FlexSPI LUT
pub const fn new(lookup_table: LookupTable) -> Self {
- FlexSPIConfigurationBlock {
+ ConfigurationBlock {
tag: TAG,
version: VERSION,
read_sample_clk_src: ReadSampleClockSource::InternalLoopback as u8,
@@ -295,4 +295,4 @@ impl FlexSPIConfigurationBlock {
}
const _STATIC_ASSERT_SIZE: [u32; 1] =
- [0; (core::mem::size_of::() == 448) as usize];
+ [0; (core::mem::size_of::() == 448) as usize];
diff --git a/src/serial_flash/nor.rs b/src/serial_flash/nor.rs
index 41881af..153e898 100644
--- a/src/serial_flash/nor.rs
+++ b/src/serial_flash/nor.rs
@@ -1,6 +1,6 @@
//! Serial NOR configuration blocks and fields
-use crate::flexspi::FlexSPIConfigurationBlock;
+use crate::flexspi;
/// `ipCmdSerialClkFreq` field for serial NOR-specific FCB
///
@@ -37,9 +37,9 @@ pub enum SerialClockFrequency {
///
/// ```no_run
/// use imxrt_boot_gen::serial_flash::nor;
-/// # use imxrt_boot_gen::flexspi::{FlexSPIConfigurationBlock, LookupTable};
+/// # use imxrt_boot_gen::flexspi::{self, LookupTable};
///
-/// # const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock = FlexSPIConfigurationBlock::new(LookupTable::new());
+/// # const FLEXSPI_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LookupTable::new());
/// #[no_mangle]
/// #[link_section = ".serial_nor_cb"]
/// static SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
@@ -51,7 +51,7 @@ pub enum SerialClockFrequency {
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct ConfigurationBlock {
- mem_cfg: FlexSPIConfigurationBlock,
+ mem_cfg: flexspi::ConfigurationBlock,
page_size: u32,
sector_size: u32,
ip_cmd_serial_clk_freq: u32,
@@ -61,7 +61,7 @@ pub struct ConfigurationBlock {
impl ConfigurationBlock {
/// Create a new serial NOR configuration block based on the FlexSPI configuration
/// block
- pub const fn new(mut mem_cfg: FlexSPIConfigurationBlock) -> Self {
+ pub const fn new(mut mem_cfg: flexspi::ConfigurationBlock) -> Self {
mem_cfg.device_type = 1;
ConfigurationBlock {
mem_cfg,
@@ -96,13 +96,13 @@ const _STATIC_ASSERT_SIZE: [u32; 1] =
#[cfg(test)]
mod test {
- use super::{ConfigurationBlock, FlexSPIConfigurationBlock, SerialClockFrequency};
+ use super::{flexspi, ConfigurationBlock, SerialClockFrequency};
use crate::flexspi::LookupTable;
#[test]
fn smoke() {
const _CFG: ConfigurationBlock =
- ConfigurationBlock::new(FlexSPIConfigurationBlock::new(LookupTable::new()))
+ ConfigurationBlock::new(flexspi::ConfigurationBlock::new(LookupTable::new()))
.page_size(256)
.sector_size(4095)
.ip_cmd_serial_clk_freq(SerialClockFrequency::MHz30);
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index 5c1937b..445f05e 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -1,6 +1,6 @@
//! Serial NOR configuration block for the Teensy 4
-use imxrt_boot_gen::flexspi::{opcodes::sdr::*, *};
+use imxrt_boot_gen::flexspi::{self, opcodes::sdr::*, *};
use imxrt_boot_gen::serial_flash::*;
/// Instructions for the Winbond W25Q16JV
@@ -67,16 +67,17 @@ const LUT: LookupTable = LookupTable::new()
// Common FlexSPI configuration block
//
-const FLEXSPI_CONFIGURATION_BLOCK: FlexSPIConfigurationBlock = FlexSPIConfigurationBlock::new(LUT)
- .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
- .cs_hold_time(0x01)
- .cs_setup_time(0x02)
- .column_address_width(ColumnAddressWidth::OtherDevices)
- .device_mode_configuration(DeviceModeConfiguration::Disabled)
- .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
- .flash_size(SerialFlashRegion::A1, 0x0020_0000)
- .serial_clk_freq(SerialClockFrequency::MHz60)
- .serial_flash_pad_type(FlashPadType::Quad);
+const FLEXSPI_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock =
+ flexspi::ConfigurationBlock::new(LUT)
+ .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+ .cs_hold_time(0x01)
+ .cs_setup_time(0x02)
+ .column_address_width(ColumnAddressWidth::OtherDevices)
+ .device_mode_configuration(DeviceModeConfiguration::Disabled)
+ .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
+ .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+ .serial_clk_freq(SerialClockFrequency::MHz60)
+ .serial_flash_pad_type(FlashPadType::Quad);
//
// Final serial NOR configuration block
From 9f645fe88439de1203f41c90e88fda0d8400dd91 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:37:20 -0500
Subject: [PATCH 12/19] Update library documentation
---
src/lib.rs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/lib.rs b/src/lib.rs
index 3dc3b2f..739a548 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,11 +2,11 @@
//!
//! # Rationale
//!
-//! iMXRT processors require certain data structures in flash in order to configure
+//! i.MX RT processors require certain data structures in order to configure
//! FlexSPI and / or SEMC peripherals. The data structurs must be placed
//! in a certain region of FLASH, with values that describe how a peripheral should
-//! interact with NAND- / NOR-based FLASH memory. The data structures have a lot of magic
-//! numbers, and it would be nice to have an API to correctly generate the values.
+//! interact with persistent memory. The data structures have a lot of magic
+//! numbers, and need a very particular layout in order to boot the system.
//!
//! The `imxrt-boot-gen` crate helps you make data structures to boot i.MX RT processors.
//! As of this writing, the API supports
@@ -24,6 +24,9 @@
//! imxrt-boot-gen = { features = ["imxrt1062"] }
//! ```
//!
+//! The entire API is `const`. You may define your data structures at compile
+//! time, and assign them to `static` memory in your embedded program.
+//!
//! See the module-level documentation for more information about the API.
#![cfg_attr(not(test), no_std)]
From 787eee7816c7bf51efa752a32d0d28484c0bedb4 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:43:46 -0500
Subject: [PATCH 13/19] Update features
Use processor family identifiers as feature flags.
---
.github/workflows/rust.yml | 4 ++--
Cargo.toml | 7 +++----
build.rs | 2 +-
src/flexspi/fields.rs | 2 +-
src/lib.rs | 2 +-
src/serial_flash/nor.rs | 8 ++++----
6 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 423e076..5310e73 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -6,7 +6,7 @@ jobs:
build:
strategy:
matrix:
- feature: ["imxrt1011", "imxrt1061", "imxrt1062", "imxrt1064"]
+ feature: ["imxrt1010", "imxrt1060", "imxrt1064"]
runs-on: ubuntu-latest
@@ -20,7 +20,7 @@ jobs:
clippy:
strategy:
matrix:
- feature: ["imxrt1011", "imxrt1062"] # Using a iMXRT family subset, assuming the 106x chips have the same features
+ feature: ["imxrt1010", "imxrt1060"]
runs-on: ubuntu-latest
diff --git a/Cargo.toml b/Cargo.toml
index e93ecb5..a1f3257 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,10 +22,9 @@ keywords = [
[dependencies]
[features]
-imxrt1011 = []
-imxrt1061 = []
-imxrt1062 = []
+imxrt1010 = []
+imxrt1060 = []
imxrt1064 = []
[package.metadata.docs.rs]
-features = ["imxrt1062"]
\ No newline at end of file
+features = ["imxrt1060"]
\ No newline at end of file
diff --git a/build.rs b/build.rs
index cce663d..11ca4ef 100644
--- a/build.rs
+++ b/build.rs
@@ -4,7 +4,7 @@
use std::env;
// Keep this in sync with the available features
-static SUPPORTED_FEATURES: &[&str] = &["imxrt1011", "imxrt1061", "imxrt1062", "imxrt1064"];
+static SUPPORTED_FEATURES: &[&str] = &["imxrt1010", "imxrt1060", "imxrt1064"];
fn main() {
let feature_count = SUPPORTED_FEATURES
diff --git a/src/flexspi/fields.rs b/src/flexspi/fields.rs
index 28b63fc..976bca8 100644
--- a/src/flexspi/fields.rs
+++ b/src/flexspi/fields.rs
@@ -104,7 +104,7 @@ pub enum SerialClockFrequency {
MHz100 = 6,
MHz120 = 7,
MHz133 = 8,
- #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))]
+ #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
MHz166 = 9,
}
diff --git a/src/lib.rs b/src/lib.rs
index 739a548..ea0269a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,7 +21,7 @@
//!
//! ```toml
//! [dependencies]
-//! imxrt-boot-gen = { features = ["imxrt1062"] }
+//! imxrt-boot-gen = { features = ["imxrt1060"] }
//! ```
//!
//! The entire API is `const`. You may define your data structures at compile
diff --git a/src/serial_flash/nor.rs b/src/serial_flash/nor.rs
index 153e898..72a9038 100644
--- a/src/serial_flash/nor.rs
+++ b/src/serial_flash/nor.rs
@@ -16,13 +16,13 @@ pub enum SerialClockFrequency {
MHz75 = 4,
MHz80 = 5,
MHz100 = 6,
- #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))]
+ #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
MHz120 = 7,
- #[cfg(feature = "imxrt1011")]
+ #[cfg(feature = "imxrt1010")]
MHz133 = 7,
- #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))]
+ #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
MHz133 = 8,
- #[cfg(any(feature = "imxrt1061", feature = "imxrt1062", feature = "imxrt1064"))]
+ #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
MHz166 = 9,
}
From 6134f9f52114156faa5ef06c861943a8d0ca88a0 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:54:24 -0500
Subject: [PATCH 14/19] Update README, licenses
---
LICENSE-APACHE | 2 +-
LICENSE-MIT | 40 ++++++++++----------
README.md | 99 ++++++++++++++++++--------------------------------
src/lib.rs | 36 ++++++++++++++----
4 files changed, 85 insertions(+), 92 deletions(-)
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index 16838a3..f8e5e5e 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
-Copyright 2019 Ian McIntyre
+Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
index 468cd79..7d815cd 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,23 +1,21 @@
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
+MIT License
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
+Copyright (c) 2019-2020 Ian McIntyre
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
\ No newline at end of file
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 8264821..0ac6097 100644
--- a/README.md
+++ b/README.md
@@ -1,83 +1,56 @@
# imxrt-boot-gen
-Generate iMXRT data structures that are required for booting.
+Generate i.MX RT boot-time data structures.
-## Terms
+## Rationale
-- Flex Serial Peripheral Interface (FlexSPI) Configuration Block (FCB), a data structure that describes how the processor interfaces flash devices via FlexSPI. It's an array of magic numbers placed in flash at a known location. Suitable for serial NOR and NAND flash booting.
+i.MX RT processors require certain data structures in order to configure
+FlexSPI and SEMC peripherals. The data structurs must be placed
+in a certain region of memory, with values that describe how a peripheral should
+interact with that memory. The data structures only support certain values,
+and need a particular layout in order to boot the system.
-## Rationale
+The `imxrt-boot-gen` crate helps you generate data structures to boot i.MX RT processors.
+As of this writing, the API supports
-This crate lets you define iMXRT data structures that are required to boot, like FCBs. FCBs are typically written by hand. If you're defining a FCB in C, you can use structs and macros to define the FCB layout and values. The approach, however, isn't great for catching invalid values at compile time. This crate takes a different, more radical approach, by using Rust to generate the data structures at compile time.
-
-`imxrt-boot-gen` provides an API for FCB generation. Use it in another crate's `build.rs` to define the FCB, and write it to a file:
-
-```rust
-let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
- .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
- .cs_hold_time(0x01)
- .cs_setup_time(0x02)
- .column_address_width(ColumnAddressWidth::OtherDevices)
- .device_mode_configuration(DeviceModeConfiguration::Disabled)
- // Other fields...
- .build()
- .unwrap();
-let out_dir = env::var("OUT_DIR").unwrap();
-let dest_path = Path::new(&out_dir).join("fcb.rs");
-let mut f = File::create(&dest_path).unwrap();
-writeln!(f, "{}", fcb).unwrap();
-```
+- serial NOR flash
-Then, inside your crate's `lib.rs`, include the generated file:
+Other configurations, like NAND flash and parallel SEMC, may be added in the future.
-```rust
-include!(concat!(env!("OUT_DIR"), "/fcb.rs"));
-```
+## Usage
+
+Add `imxrt-boot-gen` to your dependencies, and select your processor with a feature flag:
-Your crate now exports a FCB that resembles
-
-```rust
-#[link_section = ".fcb"]
-#[no_mangle]
-pub static FLEXSPI_CONFIGURATION_BLOCK: [u8; 512] = [
- 0x46, // 0x000 Tag 'FCFB'
- 0x43, // 0x001
- 0x46, // 0x002
- 0x42, // 0x003
- 0x00, // 0x004 Version 'bugfix'
- 0x00, // 0x005 Version 'minor'
- 0x01, // 0x006 Version 'major
- 0x56, // 0x007 Version 'V'
- // ...
-];
+```toml
+[dependencies]
+imxrt-boot-gen = { features = ["imxrt1060"] }
```
-You may now link that crate into another executable. Make sure that you place the FCB at the correct location in flash! The correct location varies by processor and boot configuration; consult your iMXRT reference manual for more information.
+The entire API is `const`. You may define your data structures at compile
+time, and assign the values to `static` memory in your embedded program.
-## ABI
+See the module-level documentation for more information about the API.
-The generated FCB has the symbol `FLEXSPI_CONFIGURATION_BLOCK`. The symbol is not mangled. The memory is an array of 512 `u8`s. It has a link section of `".fcb"`. The ABI ensures compatibility with both Rust and C. By building a C static library from your Rust crate, you can link the FCB into other C applications that target the iMXRT processor family.
+## Features
-## Supported Processors
+The crate *requires* a feature selection. Features correlate to i.MX RT processor families.
+The supported features are listed below.
-The list below note the crate's compatibility with iMXRT chips. The crate selectively enables and disables features based on your processor. Processors that are not selected have not been evaluated or implemented.
+- `"imxrt1010"`
+- `"imxrt1060"`
+- `"imxrt1064"`
-- [x] imxrt1011
-- [ ] imxrt1015
-- [ ] imxrt1021
-- [ ] imxrt1051
-- [ ] imxrt1052
-- [x] imxrt1061
-- [x] imxrt1062
-- [x] imxrt1064
+### License
-Select your processor by enabling the corresponding feature:
+Licensed under either of
-```toml
-[build-dependencies]
-imxrt-boot-gen = { features = ["imxrt1062"] }
-```
+- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) ([LICENSE-APACHE](./LICENSE-APACHE))
+- [MIT License](http://opensource.org/licenses/MIT) ([LICENSE-MIT](./LICENSE-MIT))
+
+at your option.
-## Examples
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
-See the [`teensy4-fcb` crate](https://crates.io/crates/teensy4-fcb) for an example of how to use the `imxrt-boot-gen` crate. The `teensy4-rs` project uses this crate to generate the FlexSPI firmware configuration block.
+License: MIT OR Apache-2.0
diff --git a/src/lib.rs b/src/lib.rs
index ea0269a..7eb851d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,14 +1,14 @@
-//! Generate data structures for booting i.MX RT processors
+//! Generate i.MX RT boot-time data structures.
//!
//! # Rationale
//!
//! i.MX RT processors require certain data structures in order to configure
-//! FlexSPI and / or SEMC peripherals. The data structurs must be placed
-//! in a certain region of FLASH, with values that describe how a peripheral should
-//! interact with persistent memory. The data structures have a lot of magic
-//! numbers, and need a very particular layout in order to boot the system.
+//! FlexSPI and SEMC peripherals. The data structurs must be placed
+//! in a certain region of memory, with values that describe how a peripheral should
+//! interact with that memory. The data structures only support certain values,
+//! and need a particular layout in order to boot the system.
//!
-//! The `imxrt-boot-gen` crate helps you make data structures to boot i.MX RT processors.
+//! The `imxrt-boot-gen` crate helps you generate data structures to boot i.MX RT processors.
//! As of this writing, the API supports
//!
//! - serial NOR flash
@@ -25,9 +25,31 @@
//! ```
//!
//! The entire API is `const`. You may define your data structures at compile
-//! time, and assign them to `static` memory in your embedded program.
+//! time, and assign the values to `static` memory in your embedded program.
//!
//! See the module-level documentation for more information about the API.
+//!
+//! # Features
+//!
+//! The crate *requires* a feature selection. Features correlate to i.MX RT processor families.
+//! The supported features are listed below.
+//!
+//! - `"imxrt1010"`
+//! - `"imxrt1060"`
+//! - `"imxrt1064"`
+//!
+//! ## License
+//!
+//! Licensed under either of
+//!
+//! - [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) ([LICENSE-APACHE](./LICENSE-APACHE))
+//! - [MIT License](http://opensource.org/licenses/MIT) ([LICENSE-MIT](./LICENSE-MIT))
+//!
+//! at your option.
+//!
+//! Unless you explicitly state otherwise, any contribution intentionally submitted
+//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+//! dual licensed as above, without any additional terms or conditions.
#![cfg_attr(not(test), no_std)]
From ac68ea244cc08cdbc34f3e217b86cbdbd6109009 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:55:01 -0500
Subject: [PATCH 15/19] Only run Teensy4 test with imxrt1060 feature
---
tests/teensy4.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/teensy4.rs b/tests/teensy4.rs
index 445f05e..089d971 100644
--- a/tests/teensy4.rs
+++ b/tests/teensy4.rs
@@ -1,5 +1,7 @@
//! Serial NOR configuration block for the Teensy 4
+#![cfg(feature = "imxrt1060")]
+
use imxrt_boot_gen::flexspi::{self, opcodes::sdr::*, *};
use imxrt_boot_gen::serial_flash::*;
From 11a75d73025e9270a75ef4c412a41458e8b7d47e Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 12:56:43 -0500
Subject: [PATCH 16/19] Update package description
---
Cargo.toml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index a1f3257..e640aeb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
license = "MIT OR Apache-2.0"
repository = "https://github.com/imxrt-rs/imxrt-boot-gen"
description = """
-Generate data structures for booting iMXRT processors. Usable in Rust build scripts.
+Generate data structures for booting iMXRT processors.
"""
categories = [
"embedded",
@@ -27,4 +27,4 @@ imxrt1060 = []
imxrt1064 = []
[package.metadata.docs.rs]
-features = ["imxrt1060"]
\ No newline at end of file
+features = ["imxrt1060"]
From 831be543e62b00dead3b86bd01dc97853a66b4fc Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 21 Nov 2020 13:36:30 -0500
Subject: [PATCH 17/19] Update crate version and CHANGELOG
See the CHANGELOG for a migration guide.
---
CHANGELOG.md | 156 ++++++++++++++++++++++++++++++++++++++++-----------
Cargo.toml | 2 +-
2 files changed, 124 insertions(+), 34 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4833ee..a101821 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,42 +1,132 @@
# Changelog
-## [Unreleased] - YYYY-MM-DD
-
-### Added
-
-- Added `SequenceBuilder` to support `Sequence` allocation. `SequenceBuilder`
- requires fewer lines of code to define the same FlexSPI LUT sequence, and it
- catches errors at compile time.
-
-### Removed
-
-- Removed the `Sequence` public interface. Users should change their `Sequence`
- definitions to use `SequenceBuilder`. The example below compares the old
- `Sequence` API with the new `SequenceBuilder` API:
-
- ```rust
- // Old API:
- const SEQ_READ: Sequence = Sequence([
- Instr::new(CMD, Pads::One, 0xEB),
- Instr::new(READ, Pads::Four, 0x04),
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- STOP,
- ]);
-
- // New API:
- const SEQ_READ: Sequence = SequenceBuilder::new()
- .instr(Instr::new(CMD, Pads::One, 0xEB))
- .instr(Instr::new(READ, Pads::Four, 0x04))
- .build();
- ```
+## [0.2.0] - YYYY-MM-DD
+
+**BREAKING** The 0.2 release introduces a `const` API to replace the build-time
+interface. We do not generate data structures in a build script, then write them
+out as Rust code. Instead, we generate them at compile time, using a `const` API.
+This simplifies the library while remaining type safe. The redesign lets you
+specify your own symbol names and link sections for your configuration blocks.
+
+See the updated documentation for more information on the API. The rest of this
+section describes migration tips.
+
+The 0.2 release is `no_std`. Take the `imxrt_boot_gen` symbols from your build
+script, and move them into your embedded Rust code. You will need to update
+your import paths to reference the re-organized modules.
+
+### Sequences
+
+A `SequenceBuilder` supports `Sequence` allocation. You should change your
+`Sequence` definitions to use `SequenceBuilder`. The example below compares
+the old `Sequence` API with the new `SequenceBuilder` API:
+
+```rust
+// Old API:
+use imxrt_boot_gen::serial_flash::opcodes::sdr::*;
+use imxrt_boot_gen::serial_flash::*;
+
+const SEQ_READ: Sequence = Sequence([
+ Instr::new(CMD, Pads::One, 0xEB),
+ Instr::new(READ, Pads::Four, 0x04),
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+ STOP,
+]);
+
+// New API:
+use imxrt_boot_gen::flexspi::{*, opcodes::sdr::*};
+const SEQ_READ: Sequence = SequenceBuilder::new()
+ .instr(Instr::new(CMD, Pads::One, 0xEB))
+ .instr(Instr::new(READ, Pads::Four, 0x04))
+ .build();
+```
+
+### LUT
+
+Lookup tables have a `const` API that maps a command to a sequence. The example
+below shows how you might update your lookup table definition.
+
+```rust
+// Old API:
+let lookup_table = {
+ use imxrt_boot_gen::serial_flash::CommandSequence::*;
+ let mut lut = LookupTable::new();
+ lut[Read] = SEQ_READ;
+ lut[ReadStatus] = SEQ_READ_STATUS;
+ lut[WriteEnable] = SEQ_WRITE_ENABLE;
+ lut[EraseSector] = SEQ_ERASE_SECTOR;
+ lut[PageProgram] = SEQ_PAGE_PROGRAM;
+ lut[ChipErase] = SEQ_CHIP_ERASE;
+ lut
+};
+
+// New API:
+const LUT: LookupTable = LookupTable::new()
+ .command(Command::Read, SEQ_READ)
+ .command(Command::ReadStatus, SEQ_READ_STATUS)
+ .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
+ .command(Command::EraseSector, SEQ_ERASE_SECTOR)
+ .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
+ .command(Command::ChipErase, SEQ_CHIP_ERASE);
+```
+
+### FlexSPI and serial NOR configuration blocks
+
+FlexSPI configuration blocks may be `const`. Provide the LUT as an argument
+to your `flexspi::ConfigurationBlock` constructor. Then pass your FlexSPI
+configuration block into the serial NOR configuration block.
+
+Define your serial NOR configuration block in `static` memory. Place the serial
+NOR configuration block in FLASH memory to boot your system.
+
+```rust
+// Old API:
+let fcb = FCBBuilder::new(DeviceType::SerialNOR(nor_cb), lookup_table)
+ .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+ .cs_hold_time(0x01)
+ .cs_setup_time(0x02)
+ .column_address_width(ColumnAddressWidth::OtherDevices)
+ .device_mode_configuration(DeviceModeConfiguration::Disabled)
+ .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
+ .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+ .serial_clk_freq(SerialClockFrequency::MHz60)
+ .serial_flash_pad_type(FlashPadType::Quad)
+ .build()
+ .unwrap();
+
+// New API:
+use imxrt_boot_gen::flexspi::{self, *};
+use imxrt_boot_gen::serial_flash::*;
+
+const FLEXSPI_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock =
+ flexspi::ConfigurationBlock::new(LUT)
+ .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
+ .cs_hold_time(0x01)
+ .cs_setup_time(0x02)
+ .column_address_width(ColumnAddressWidth::OtherDevices)
+ .device_mode_configuration(DeviceModeConfiguration::Disabled)
+ .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
+ .flash_size(SerialFlashRegion::A1, 0x0020_0000)
+ .serial_clk_freq(SerialClockFrequency::MHz60)
+ .serial_flash_pad_type(FlashPadType::Quad);
+
+#[no_mangle]
+#[link_section = ".serial_nor_cb"]
+static SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
+ nor::ConfigurationBlock::new(FLEXSPI_CONFIGURATION_BLOCK)
+ .page_size(256)
+ .sector_size(4096)
+ .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
+```
## [0.1.0] - 2020-04-07
First release
[Unreleased]: https://github.com/imxrt-rs/imxrt-boot-gen/compare/v0.1.0...HEAD
+[0.2.0]: https://github.com/imxrt-rs/imxrt-boot-gen/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/imxrt-rs/imxrt-boot-gen/releases/tag/v0.1.0
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index e640aeb..9477084 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "imxrt-boot-gen"
-version = "0.1.0"
+version = "0.2.0"
authors = ["Ian McIntyre "]
edition = "2018"
license = "MIT OR Apache-2.0"
From 229e57eb75781046d790737433f0d5d8487da136 Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Tue, 22 Dec 2020 15:21:35 -0500
Subject: [PATCH 18/19] Add crates.io, docs.rs badges
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 0ac6097..d4b71c1 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,14 @@
Generate i.MX RT boot-time data structures.
+[![crates-io-shield][]][crates-io]
+[![docs-rs-shield][]][docs-rs]
+
+[crates-io-shield]: https://img.shields.io/crates/v/imxrt-boot-gen
+[crates-io]: https://crates.io/crates/imxrt-boot-gen
+[docs-rs-shield]: https://docs.rs/imxrt-boot-gen/badge.svg
+[docs-rs]: https://docs.rs/imxrt-boot-gen/
+
## Rationale
i.MX RT processors require certain data structures in order to configure
From 423438e25918c7701322b717ee9b72775a9ffe0c Mon Sep 17 00:00:00 2001
From: Ian McIntyre
Date: Sat, 26 Dec 2020 16:01:57 -0500
Subject: [PATCH 19/19] Add documentation job; specify default target docs
---
.github/workflows/doc.yml | 36 ++++++++++++++++++++++++++++++++++++
Cargo.toml | 1 +
README.md | 2 ++
3 files changed, 39 insertions(+)
create mode 100644 .github/workflows/doc.yml
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
new file mode 100644
index 0000000..1380a88
--- /dev/null
+++ b/.github/workflows/doc.yml
@@ -0,0 +1,36 @@
+name: Documentation
+
+on:
+ push:
+ branches: [ master ]
+
+jobs:
+ publish-docs:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: actions-rs/toolchain@v1
+ name: Install toolchain
+ with:
+ toolchain: nightly
+ profile: minimal
+ override: true
+ target: thumbv7em-none-eabihf
+
+ - name: Generate docs
+ uses: actions-rs/cargo@v1
+ with:
+ command: rustdoc
+ args: --features imxrt1060 --target thumbv7em-none-eabihf -- --cfg docsrs
+
+ - name: Write redirect
+ run: echo "" > target/thumbv7em-none-eabihf/doc/index.html
+
+ - name: Deploy to GitHub pages
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: target/thumbv7em-none-eabihf/doc
+ publish_branch: gh-pages
diff --git a/Cargo.toml b/Cargo.toml
index 9477084..1efed63 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,3 +28,4 @@ imxrt1064 = []
[package.metadata.docs.rs]
features = ["imxrt1060"]
+default-target = "thumbv7em-none-eabihf"
diff --git a/README.md b/README.md
index d4b71c1..b92d562 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ Generate i.MX RT boot-time data structures.
[docs-rs-shield]: https://docs.rs/imxrt-boot-gen/badge.svg
[docs-rs]: https://docs.rs/imxrt-boot-gen/
+### [API docs (main branch)](https://imxrt-rs.github.io/imxrt-boot-gen/)
+
## Rationale
i.MX RT processors require certain data structures in order to configure