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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 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 [`ConfigurationBlock`]. See the `ConfigurationBlock` 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 [`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 [`ConfigurationBlock::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: ConfigurationBlock = /// 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::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 ConfigurationBlock { 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 ConfigurationBlock { /// Create a new configuration block that uses `lookup_table` as the /// FlexSPI LUT pub const fn new(lookup_table: LookupTable) -> Self { ConfigurationBlock { 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::<ConfigurationBlock>() == 448) as usize];