1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
//! A library for generating data structures required to boot iMXRT systems. Intended //! for generating Rust code in build scripts. //! //! # 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 //! 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. //! //! 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. //! //! # Usage //! //! Add `imxrt-boot-gen` to your build dependencies, and select your processor with a feature flag: //! //! ```toml //! [build-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 = 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, //! ]); //! //! // 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, //! ]); //! // 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. mod flexspi_lut; pub mod serial_flash;