1//! HAL for FMC peripheral used to access NAND Flash
2//!
3
4use core::cmp;
5use core::marker::PhantomData;
6
7use embedded_hal::blocking::delay::DelayUs;
8
9use crate::fmc::{FmcBank, FmcRegisters};
10use crate::FmcPeripheral;
11
12use crate::ral::{fmc, modify_reg};
13
14pub mod device;
15
16/// FMC NAND Physical Interface Configuration
17///
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19#[derive(Clone, Copy, Debug, PartialEq)]
20pub struct NandConfiguration {
21 /// Data path width in bits
22 pub data_width: u8,
23 /// Number of address bits used for the column address
24 pub column_bits: u8,
25}
26
27/// FMC NAND Timing parameters
28///
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct NandTiming {
32 /// nCE setup time tCS
33 pub nce_setup_time: i32,
34 /// Data setup time tDS
35 pub data_setup_time: i32,
36 /// ALE hold time
37 pub ale_hold_time: i32,
38 /// CLE hold time
39 pub cle_hold_time: i32,
40 /// ALE to nRE delay
41 pub ale_to_nre_delay: i32,
42 /// CLE to nRE delay
43 pub cle_to_nre_delay: i32,
44 /// nRE pulse width tRP
45 pub nre_pulse_width_ns: i32,
46 /// nWE pulse width tWP
47 pub nwe_pulse_width_ns: i32,
48 /// Read cycle time tRC
49 pub read_cycle_time_ns: i32,
50 /// Write cycle time tWC
51 pub write_cycle_time_ns: i32,
52 /// nWE high to busy tWB
53 pub nwe_high_to_busy_ns: i32,
54}
55
56/// Respresents a model of NAND chip
57pub trait NandChip {
58 /// NAND controller configuration
59 const CONFIG: NandConfiguration;
60 /// Timing parameters
61 const TIMING: NandTiming;
62}
63
64/// FMC Peripheral specialized as a NAND Controller. Not yet initialized.
65#[allow(missing_debug_implementations)]
66pub struct Nand<FMC, IC> {
67 /// Parameters for the NAND IC
68 _chip: PhantomData<IC>,
69 /// FMC peripheral
70 fmc: FMC,
71 /// Register access
72 regs: FmcRegisters,
73}
74
75/// Set of pins for a NAND
76pub trait PinsNand {
77 /// Number of data bus pins
78 const N_DATA: usize;
79}
80
81impl<IC: NandChip, FMC: FmcPeripheral> Nand<FMC, IC> {
82 /// New NAND instance
83 ///
84 /// `_pins` must be a set of pins connecting to an NAND on the FMC
85 /// controller
86 ///
87 /// # Panics
88 ///
89 /// * Panics if there is a mismatch between the data lines in `PINS` and the
90 /// NAND device
91 pub fn new<PINS>(fmc: FMC, _pins: PINS, _chip: IC) -> Self
92 where
93 PINS: PinsNand,
94 {
95 assert!(
96 PINS::N_DATA == IC::CONFIG.data_width as usize,
97 "NAND Data Bus Width mismatch between IC and controller"
98 );
99
100 Nand {
101 _chip: PhantomData,
102 fmc,
103 regs: FmcRegisters::new::<FMC>(),
104 }
105 }
106
107 /// New NAND instance
108 ///
109 /// # Safety
110 ///
111 /// This method does not ensure that IO pins are configured
112 /// correctly. Misconfiguration may result in a bus lockup or stall when
113 /// attempting to initialise the NAND device.
114 ///
115 /// The pins are not checked against the requirements for the NAND
116 /// chip. Using this method it is possible to initialise a NAND device
117 /// without sufficient pins to access the whole memory
118 ///
119 pub unsafe fn new_unchecked(fmc: FMC, _chip: IC) -> Self {
120 Nand {
121 _chip: PhantomData,
122 fmc,
123 regs: FmcRegisters::new::<FMC>(),
124 }
125 }
126
127 /// Initialise NAND instance. `delay` is used to wait 1µs after enabling the
128 /// memory controller.
129 ///
130 /// Returns a [`NandDevice`](device::NandDevice) instance.
131 ///
132 /// # Panics
133 ///
134 /// * Panics if any setting in `IC::CONFIG` cannot be achieved
135 /// * Panics if the FMC Kernel Clock is too fast to achieve the timing
136 /// required by the NAND device
137 pub fn init<D>(&mut self, delay: &mut D) -> device::NandDevice
138 where
139 D: DelayUs<u8>,
140 {
141 // calculate clock period, round down
142 let fmc_source_ck_hz = self.fmc.source_clock_hz();
143 let ker_clk_period_ns = 1_000_000_000u32 / fmc_source_ck_hz;
144
145 // enable memory controller AHB register access
146 self.fmc.enable();
147
148 // device features and timing
149 self.set_features_timings(IC::CONFIG, IC::TIMING, ker_clk_period_ns);
150
151 // enable memory controller
152 self.fmc.memory_controller_enable();
153 delay.delay_us(1u8);
154
155 // NOTE(unsafe): FMC controller has been initialized and enabled for
156 // this bank
157 unsafe {
158 // Create device. NAND Flash is always on Bank 3
159 let ptr = FmcBank::Bank3.ptr() as *mut u8;
160 device::NandDevice::init(ptr, IC::CONFIG.column_bits as usize)
161 }
162 }
163
164 /// Program memory device features and timings
165 ///
166 /// Timing calculations from AN4761 Section 4.2
167 #[allow(non_snake_case)]
168 fn set_features_timings(
169 &mut self,
170 config: NandConfiguration,
171 timing: NandTiming,
172 period_ns: u32,
173 ) {
174 let period_ns = period_ns as i32;
175 let n_clock_periods = |time_ns: i32| {
176 (time_ns + period_ns - 1) / period_ns // round up
177 };
178 let t_CS = timing.nce_setup_time;
179 let t_DS = timing.data_setup_time;
180 let t_ALH = timing.ale_hold_time;
181 let t_CLH = timing.cle_hold_time;
182 let t_AR = timing.ale_to_nre_delay;
183 let t_CLR = timing.cle_to_nre_delay;
184 let t_RP = timing.nre_pulse_width_ns;
185 let t_WP = timing.nwe_pulse_width_ns;
186 let t_RC = timing.read_cycle_time_ns;
187 let t_WC = timing.write_cycle_time_ns;
188 let t_WB = timing.nwe_high_to_busy_ns;
189
190 // setup time before RE/WE assertion
191 let setup_time = cmp::max(t_CS, cmp::max(t_AR, t_CLR));
192 let set = cmp::max(n_clock_periods(setup_time - t_WP), 1) - 1;
193 assert!(set < 255, "FMC ker clock too fast"); // 255 = reserved
194
195 // RE/WE assertion time (minimum = 1)
196 let wait = cmp::max(n_clock_periods(cmp::max(t_RP, t_WP)), 2) - 1;
197 assert!(wait < 255, "FMC ker clock too fast"); // 255 = reserved
198
199 // hold time after RE/WE deassertion (minimum = 1)
200 let mut hold = cmp::max(n_clock_periods(cmp::max(t_ALH, t_CLH)), 1);
201 // satisfy total cycle time
202 let cycle_time = n_clock_periods(cmp::max(t_RC, t_WC));
203 while wait + 1 + hold + set + 1 < cycle_time {
204 hold += 1;
205 }
206 assert!(hold < 255, "FMC ker clock too fast"); // 255 = reserved
207
208 // hold time to meet t_WB timing
209 let atthold = cmp::max(n_clock_periods(t_WB), 2) - 1;
210 let atthold = cmp::max(atthold, hold);
211 assert!(atthold < 255, "FMC ker clock too fast"); // 255 = reserved
212
213 // CS assertion to data setup
214 let hiz = cmp::max(n_clock_periods(t_CS + t_WP - t_DS), 0);
215 assert!(hiz < 255, "FMC ker clock too fast"); // 255 = reserved
216
217 // ALE low to RE assert
218 let ale_to_nre = n_clock_periods(t_AR);
219 let tar = cmp::max(ale_to_nre - set - 2, 0);
220 assert!(tar < 16, "FMC ker clock too fast");
221
222 // CLE low to RE assert
223 let clr_to_nre = n_clock_periods(t_CLR);
224 let tclr = cmp::max(clr_to_nre - set - 2, 0);
225 assert!(tclr < 16, "FMC ker clock too fast");
226
227 let data_width = match config.data_width {
228 8 => 0,
229 16 => 1,
230 _ => panic!("not possible"),
231 };
232
233 // PCR
234 #[rustfmt::skip]
235 modify_reg!(fmc, self.regs.global(), PCR,
236 TAR: tar as u32,
237 TCLR: tclr as u32,
238 ECCPS: 1, // 0b1: 512 bytes
239 ECCEN: 0, // 0b0: ECC computation disabled
240 PWID: data_width,
241 PTYP: 1, // 0b1: NAND Flash
242 PWAITEN: 1 // 0b1: Wait feature enabled
243 );
244
245 // PMEM: Common memory space timing register
246 #[rustfmt::skip]
247 modify_reg!(fmc, self.regs.global(), PMEM,
248 MEMHIZ: hiz as u32,
249 MEMHOLD: hold as u32,
250 MEMWAIT: wait as u32,
251 MEMSET: set as u32);
252
253 // PATT: Attribute memory space timing register
254 #[rustfmt::skip]
255 modify_reg!(fmc, self.regs.global(), PATT,
256 ATTHIZ: hiz as u32,
257 ATTHOLD: atthold as u32,
258 ATTWAIT: wait as u32,
259 ATTSET: set as u32);
260
261 // Enable
262 #[rustfmt::skip]
263 modify_reg!(fmc, self.regs.global(), PCR,
264 PBKEN: 1);
265 }
266}
267