imxrt1180evk_fcb/
lib.rs

1//! FlexSPI configuration block (FCB) for the iMXRT1180EVK.
2//!
3//! This FCB is compatible with the flash storage found on the
4//! iMXRT1180EVK.
5#![no_std]
6
7pub use nor::ConfigurationBlock;
8
9use imxrt_boot_gen::Imxrt;
10use imxrt_boot_gen::flexspi::{self, opcodes::sdr::*, *};
11use imxrt_boot_gen::flexspi::{FlashPadType, ReadSampleClockSource, SerialFlashRegion};
12use imxrt_boot_gen::serial_flash::*;
13
14const CHIP: Imxrt = Imxrt::Imxrt1180;
15
16const SEQ_READ: Sequence = SequenceBuilder::new()
17    .instr(Instr::new(CMD, Pads::One, 0xEB))
18    .instr(Instr::new(RADDR, Pads::Four, 0x18))
19    .instr(Instr::new(DUMMY, Pads::Four, 0x06))
20    .instr(Instr::new(READ, Pads::Four, 0x04))
21    .build();
22const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
23    .instr(Instr::new(CMD, Pads::One, 0x05))
24    .instr(Instr::new(READ, Pads::One, 0x04))
25    .build();
26const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new()
27    .instr(Instr::new(CMD, Pads::One, 0x06))
28    .build();
29const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
30    .instr(Instr::new(CMD, Pads::One, 0x20))
31    .instr(Instr::new(RADDR, Pads::One, 0x18))
32    .build();
33const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()
34    .instr(Instr::new(CMD, Pads::One, 0x02))
35    .instr(Instr::new(RADDR, Pads::One, 0x18))
36    .instr(Instr::new(WRITE, Pads::One, 0x04))
37    .build();
38const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new()
39    .instr(Instr::new(CMD, Pads::One, 0x60))
40    .build();
41
42const LUT: LookupTable = LookupTable::new()
43    .command(Command::Read, SEQ_READ)
44    .command(Command::ReadStatus, SEQ_READ_STATUS)
45    .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
46    .command(Command::EraseSector, SEQ_ERASE_SECTOR)
47    .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
48    .command(Command::ChipErase, SEQ_CHIP_ERASE);
49
50const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock =
51    flexspi::ConfigurationBlock::new(LUT)
52        .version(Version::new(1, 4, 0))
53        .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
54        .cs_hold_time(3)
55        .cs_setup_time(3)
56        .controller_misc_options(0x10)
57        .serial_flash_pad_type(FlashPadType::Quad)
58        .serial_clk_freq(CHIP.serial_clock_frequency(SerialClockOption::MHz133))
59        .flash_size(SerialFlashRegion::A1, 16 * 1024 * 1024);
60
61pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = {
62    let mut cb = nor::ConfigurationBlock::new(CHIP, COMMON_CONFIGURATION_BLOCK)
63        .page_size(256)
64        .sector_size(4 * 1024)
65        .ip_cmd_serial_clk_freq(Some(
66            CHIP.ip_serial_clock_frequency(SerialClockOption::MHz30),
67        ));
68    cb.extras(CHIP).unwrap().block_size(64 * 1024);
69    cb
70};
71
72#[unsafe(no_mangle)]
73#[cfg_attr(
74    all(target_arch = "arm", target_os = "none"),
75    unsafe(link_section = ".fcb")
76)]
77pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;
78
79#[cfg(test)]
80mod tests {
81    use super::SERIAL_NOR_CONFIGURATION_BLOCK;
82
83    /// Magic numbers extracted from a build of the 1180 EVK's SDK.
84    ///
85    /// The actual configuration has an instruction sequence at offset 0x100.
86    /// I dropped it when copying over the raw values, since we don't have
87    /// support for custom instruction sequences. Erase sector and erase chip
88    /// are both implemented.
89    const EXPECTED: [u32; 128] = [
90        0x46434642, 0x00040156, 0x00000000, 0x01030300, 0x00000000, 0x00000000, 0x00000000,
91        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
92        0x00000000, 0x00000000, 0x10000000, 0x01040700, 0x00000000, 0x00000000, 0x00000001,
93        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
94        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xeb04180a, 0x06320426, 0x00000000,
95        0x00000000, 0x05040424, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
96        0x00000000, 0x00000000, 0x06040000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
97        0x00000000, 0x00000000, 0x00000000, 0x20041808, 0x00000000, 0x00000000, 0x00000000,
98        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
99        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02041808, 0x04200000,
100        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x60040000,
101        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
102        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
103        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
104        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
105        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
106        0x00010000, 0x00100000, 0x01000000, 0x00000000, 0x00000100, 0x00000000, 0x00000000,
107        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
108        0x00000000, 0x00000000,
109    ];
110
111    #[test]
112    fn imxrt1180evk() {
113        let actual: [u32; 128] = unsafe { core::mem::transmute(SERIAL_NOR_CONFIGURATION_BLOCK) };
114        for (i, (a, e)) in actual.iter().zip(EXPECTED).enumerate() {
115            let offset = i * 4;
116            assert_eq!(
117                a.to_be_bytes(),
118                e.to_le_bytes(),
119                "Offset {offset:#X}\nACTUAL: {actual:?}\nEXPECTED: {EXPECTED:?}"
120            );
121        }
122    }
123}