imxrt_boot_gen/flexspi.rs
1//! FlexSPI configuration block definitions
2//!
3//! The FlexSPI module includes
4//!
5//! - instruction sequences
6//! - instruction lookup table (LUT)
7//! - the FlexSPI configuration block
8//!
9//! The `flexspi` types are used throughout the [`serial_flash`](crate::serial_flash) API, since the FlexSPI
10//! configuration block is at the start of every serial NOR / NAND configuration block.
11//!
12//! # Sequences and LUTs
13//!
14//! A [`Sequence`] is a collection of up to eight FlexSPI instructions ([`Instr`]).
15//! The FlexSPI controller sequentially executes instructions to perform reads, writes
16//! and I/O with a connected FLASH device. The FlexSPI controller finds each sequence
17//! in a [`LookupTable`].
18//!
19//! Use a [`SequenceBuilder`] to create `Sequence`s:
20//!
21//! ```
22//! use imxrt_boot_gen::flexspi::{Instr, Sequence, SequenceBuilder, Pads, opcodes::sdr::*};
23//!
24//! # const FAST_READ_QUAD_IO: u8 = 0;
25//! # const READ_STATUS_REGISTER_1: u8 = 0;
26//! const SEQ_READ: Sequence = SequenceBuilder::new()
27//! .instr(Instr::new(CMD, Pads::One, FAST_READ_QUAD_IO))
28//! .instr(Instr::new(RADDR, Pads::Four, 0x18))
29//! .instr(Instr::new(DUMMY, Pads::Four, 0x06))
30//! .instr(Instr::new(READ, Pads::Four, 0x04))
31//! .build();
32//!
33//! const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
34//! .instr(Instr::new(CMD, Pads::One, READ_STATUS_REGISTER_1))
35//! .instr(Instr::new(READ, Pads::One, 0x04))
36//! .build();
37//! ```
38//!
39//! Then, assign each sequence to a [`Command`] in a `LookupTable`:
40//!
41//! ```
42//! use imxrt_boot_gen::flexspi::{Command, LookupTable};
43//! # use imxrt_boot_gen::flexspi::{Sequence, SequenceBuilder};
44//!
45//! # const SEQ_READ: Sequence = SequenceBuilder::new().build();
46//! # const SEQ_READ_STATUS: Sequence = SequenceBuilder::new().build();
47//! const LUT: LookupTable = LookupTable::new()
48//! .command(Command::Read, SEQ_READ)
49//! .command(Command::ReadStatus, SEQ_READ_STATUS);
50//! ```
51//!
52//! # FlexSPI Configuration Block
53//!
54//! Once you've created your sequences and lookup table, use the lookup table to create
55//! a [`ConfigurationBlock`]. See the `ConfigurationBlock` documentation
56//! for more information.
57
58mod fields;
59mod lookup;
60mod sequence;
61
62use core::num::NonZeroU8;
63
64pub use fields::*;
65pub use lookup::{Command, LookupTable};
66pub use sequence::{Instr, JUMP_ON_CS, Pads, STOP, Sequence, SequenceBuilder, opcodes};
67
68/// A version identifier.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[repr(transparent)]
71pub struct Version(u32);
72
73impl Version {
74 /// Construct a version number for your FCB.
75 ///
76 /// Once constructed, pass the version to the configuration block with
77 /// [`ConfigurationBlock::version`](ConfigurationBlock::version).
78 pub const fn new(major: u8, minor: u8, bugfix: u8) -> Version {
79 Version(
80 ((b'V' as u32) << 24) | ((major as u32) << 16) | ((minor as u32) << 8) | bugfix as u32,
81 )
82 }
83}
84
85/// ASCII 'FCFB'
86const TAG: u32 = 0x4246_4346;
87/// The default FCB version used by this library.
88///
89/// Use [`Version::new`](Version::new) to compute your own version identifier.
90pub const VERSION_DEFAULT: Version = Version::new(1, 0, 0);
91#[allow(clippy::assertions_on_constants)] // Sanity check.
92const _: () = assert!(VERSION_DEFAULT.0 == 0x5601_0000);
93
94/// The recommended `csHoldTime`, `0x03`.
95///
96/// This is the default value if not set with [`ConfigurationBlock::cs_hold_time`].
97pub const RECOMMENDED_CS_HOLD_TIME: u8 = 0x03;
98/// The recommended `csSetupTime`, `0x03`.
99///
100/// This is the default value if not set with [`ConfigurationBlock::cs_setup_time`].
101pub const RECOMMENDED_CS_SETUP_TIME: u8 = 0x03;
102
103/// FlexSPI configuration block
104///
105/// The FlexSPI configuration block consists of parameters that are for specific flash
106/// devices. The configuration block includes the FlexSPI [`LookupTable`]. The configuration
107/// block is shared between serial NOR and NAND configuration blocks.
108///
109/// # Default Values
110///
111/// - `cs_hold_time` is [`RECOMMENDED_CS_HOLD_TIME`]
112/// - `cs_setup_time` is [`RECOMMENDED_CS_SETUP_TIME`]
113///
114/// All other configurable values are set to a bit pattern of 0.
115///
116/// # Examples
117///
118/// ```
119/// use imxrt_boot_gen::{Imxrt, flexspi::*};
120///
121/// const CHIP: Imxrt = // ...
122/// # Imxrt::Imxrt1010;
123///
124/// # const LUT: LookupTable = LookupTable::new();
125/// const FLEXSPI_CONFIGURATION_BLOCK: ConfigurationBlock =
126/// ConfigurationBlock::new(LUT)
127/// .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
128/// .cs_hold_time(0x01)
129/// .cs_setup_time(0x02)
130/// .column_address_width(ColumnAddressWidth::OtherDevices)
131/// .device_mode_configuration(DeviceModeConfiguration::Disabled)
132/// .configuration_command(2, ConfigurationCommand::new(1, 12), 42)
133/// .wait_time_cfg_commands(WaitTimeConfigurationCommands::new(40_000))
134/// .flash_size(SerialFlashRegion::A1, 0x0020_0000)
135/// .serial_clk_freq(CHIP.serial_clock_frequency(SerialClockOption::MHz60))
136/// .serial_flash_pad_type(FlashPadType::Quad);
137///
138#[derive(Debug, Clone, Copy)]
139#[repr(C, packed)]
140pub struct ConfigurationBlock {
141 tag: u32,
142 version: Version,
143 _reserved0: [u8; 4], // 0x008
144 read_sample_clk_src: ReadSampleClockSource,
145 cs_hold_time: u8,
146 cs_setup_time: u8,
147 column_address_width: ColumnAddressWidth,
148 device_mode_configuration: u8,
149 /// TODO: this isn't reserved on 1170.
150 /// It's "device mode type", with a default value
151 /// of "generic."
152 _reserved1: [u8; 1], // 0x011
153 wait_time_cfg_commands: WaitTimeConfigurationCommands,
154 device_mode_sequence: DeviceModeSequence,
155 device_mode_arg: u32,
156 config_cmd_enable: u8,
157 _reserved2: [u8; 3], // 0x01D
158 config_cmd_seqs: [ConfigurationCommand; 3],
159 _reserved3: [u8; 4], // 0x02C
160 cfg_cmd_args: [u32; 3],
161 _reserved4: [u8; 4], // 0x03C
162 controller_misc_options: u32,
163 pub(crate) device_type: u8,
164 serial_flash_pad_type: FlashPadType,
165 serial_clk_freq: SerialClockFrequency,
166 lut_custom_seq_enable: u8,
167 _reserved5: [u8; 8], // 0x048
168 /// A1, A2, B1, B2
169 serial_flash_sizes: [u32; 4],
170 cs_pad_setting_override: u32,
171 sclk_pad_setting_override: u32,
172 data_pad_setting_override: u32,
173 dqs_pad_setting_override: u32,
174 timeout_ms: u32,
175 command_interval: u32,
176 data_valid_time: u32,
177 busy_offset: u16,
178 busy_bit_polarity: u16,
179 lookup_table: LookupTable,
180 lut_custom_seq: [u8; 48],
181 _reserved6: [u8; 16],
182}
183
184impl ConfigurationBlock {
185 /// Create a new configuration block that uses `lookup_table` as the
186 /// FlexSPI LUT
187 pub const fn new(lookup_table: LookupTable) -> Self {
188 ConfigurationBlock {
189 tag: TAG,
190 version: VERSION_DEFAULT,
191 read_sample_clk_src: ReadSampleClockSource::InternalLoopback,
192 cs_hold_time: RECOMMENDED_CS_HOLD_TIME,
193 cs_setup_time: RECOMMENDED_CS_SETUP_TIME,
194 column_address_width: ColumnAddressWidth::OtherDevices,
195 device_mode_configuration: 0, // Disabled
196 wait_time_cfg_commands: WaitTimeConfigurationCommands::disable(),
197 device_mode_sequence: DeviceModeSequence::zeroed(),
198 device_mode_arg: 0,
199 config_cmd_enable: 0,
200 config_cmd_seqs: [ConfigurationCommand::zeroed(); 3],
201 cfg_cmd_args: [0; 3],
202 controller_misc_options: 0,
203 device_type: 0, // Invalid value; must be updated in NOR / NAND configuration block
204 serial_flash_pad_type: FlashPadType::Single,
205 serial_clk_freq: SerialClockFrequency(NonZeroU8::new(1).unwrap()), // 30MHz on all parts
206 lut_custom_seq_enable: 0,
207 serial_flash_sizes: [0; 4],
208 cs_pad_setting_override: 0,
209 sclk_pad_setting_override: 0,
210 data_pad_setting_override: 0,
211 dqs_pad_setting_override: 0,
212 timeout_ms: 0,
213 command_interval: 0,
214 data_valid_time: 0,
215 busy_offset: 0,
216 busy_bit_polarity: 0,
217 lookup_table,
218 lut_custom_seq: [0; 48],
219
220 _reserved0: [0; 4],
221 _reserved1: [0; 1],
222 _reserved2: [0; 3],
223 _reserved3: [0; 4],
224 _reserved4: [0; 4],
225 _reserved5: [0; 8],
226 _reserved6: [0; 16],
227 }
228 }
229
230 /// Override the version.
231 ///
232 /// The default value is [`VERSION_DEFAULT`].
233 pub const fn version(mut self, version: Version) -> Self {
234 self.version = version;
235 self
236 }
237
238 /// `readSampleClkSrc`, the clock source for FlexSPI
239 ///
240 /// If not set, this defaults to `ReadSampleClockSource::InternalLoopback`.
241 pub const fn read_sample_clk_src(mut self, read_sample_clk_src: ReadSampleClockSource) -> Self {
242 self.read_sample_clk_src = read_sample_clk_src;
243 self
244 }
245
246 /// Set the chip select hold time (`csHoldTime`)
247 ///
248 /// If not set, this will be `RECOMMENDED_CS_HOLD_TIME`, which is `0x03`.
249 pub const fn cs_hold_time(mut self, cs_hold_time: u8) -> Self {
250 self.cs_hold_time = cs_hold_time;
251 self
252 }
253
254 /// Set the chip select setup time (`csSetupTime`)
255 ///
256 /// If not set, this will be `RECOMMENDED_CS_SETUP_TIME`, which is `0x03`.
257 pub const fn cs_setup_time(mut self, cs_setup_time: u8) -> Self {
258 self.cs_setup_time = cs_setup_time;
259 self
260 }
261
262 /// `columnAddressWidth`, the properties of the flash memory
263 ///
264 /// If not set, this defaults to `ColumnAddressWidth::OtherDevices`
265 pub const fn column_address_width(mut self, column_address_width: ColumnAddressWidth) -> Self {
266 self.column_address_width = column_address_width;
267 self
268 }
269
270 /// Sets device configuration mode. The `DeviceModeConfiguration::Disabled` variant
271 /// will set `deviceModeCfgEnable` to "disabled". Otherwise, we will set
272 /// `deviceModeCfgEnable` to "enabled," and we use the sequence and argument
273 /// parameters in the FCB.
274 ///
275 /// If not set, this defaults to `DeviceModeConfiguration::Disabled`.
276 pub const fn device_mode_configuration(
277 mut self,
278 device_mode_configuration: DeviceModeConfiguration,
279 ) -> Self {
280 match device_mode_configuration {
281 DeviceModeConfiguration::Disabled => {
282 self.device_mode_configuration = 0;
283 }
284 DeviceModeConfiguration::Enabled {
285 device_mode_seq,
286 device_mode_arg,
287 } => {
288 self.device_mode_configuration = 1;
289 self.device_mode_sequence = device_mode_seq;
290 self.device_mode_arg = device_mode_arg;
291 }
292 }
293 self
294 }
295
296 /// Set a configuration command.
297 ///
298 /// If this is ever called, it automatically enables configuration commands.
299 /// There is no way to clear the enable flag, even if you were to override it
300 /// with zero-initialized `config_command`s.
301 ///
302 /// If your boot ROM supports it, you can use this to augment flash part setup,
303 /// such as setting read parameters.
304 ///
305 /// # Panics
306 ///
307 /// There are only three configuration commands. Panics if `index` is anything
308 /// other than 0, 1, or 2.
309 pub const fn configuration_command(
310 mut self,
311 index: usize,
312 config_command: ConfigurationCommand,
313 config_arg: u32,
314 ) -> Self {
315 self.config_cmd_enable = 1;
316 self.config_cmd_seqs[index] = config_command;
317 self.cfg_cmd_args[index] = config_arg;
318 self
319 }
320
321 /// Sets `waitTimeCfgCommands`
322 ///
323 /// If not set, this defaults to `WaitTimeConfigurationCommands::disable()`.
324 pub const fn wait_time_cfg_commands(
325 mut self,
326 wait_time_cfg_commands: WaitTimeConfigurationCommands,
327 ) -> Self {
328 self.wait_time_cfg_commands = wait_time_cfg_commands;
329 self
330 }
331
332 /// Sets the serial flash pad type, `sFlashPad`.
333 ///
334 /// If not set, this defaults to `FlashPadType::Single`.
335 pub const fn serial_flash_pad_type(mut self, serial_flash_pad_type: FlashPadType) -> Self {
336 self.serial_flash_pad_type = serial_flash_pad_type;
337 self
338 }
339
340 /// Sets the serial clock frequencey, `serialClkFreq`
341 ///
342 /// If not set, this defaults to `SerialClockFrequency::MHz30`.
343 pub const fn serial_clk_freq(mut self, serial_clk_freq: SerialClockFrequency) -> Self {
344 self.serial_clk_freq = serial_clk_freq;
345 self
346 }
347
348 /// Set a flash size for the provided flash region
349 ///
350 /// Any region that's not set will default to `0`.
351 pub const fn flash_size(mut self, flash_region: SerialFlashRegion, flash_size: u32) -> Self {
352 self.serial_flash_sizes[flash_region as usize] = flash_size;
353 self
354 }
355
356 /// Set miscellaneous controller options.
357 ///
358 /// See your chip's reference manual for more information on valid values. This method performs
359 /// no checking on the input.
360 pub const fn controller_misc_options(mut self, options: u32) -> Self {
361 self.controller_misc_options = options;
362 self
363 }
364}
365
366const _STATIC_ASSERT_SIZE: [u32; 1] =
367 [0; (core::mem::size_of::<ConfigurationBlock>() == 448) as usize];