1use core::cmp;
4use core::convert::TryInto;
5use core::marker::PhantomData;
6
7use embedded_hal::delay::DelayNs;
8
9use crate::fmc::{AddressPinSet, FmcBank, FmcRegisters};
10use crate::FmcPeripheral;
11
12use crate::ral::{fmc, modify_reg, write_reg};
13
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct SdramConfiguration {
19 pub column_bits: u8,
21 pub row_bits: u8,
23 pub memory_data_width: u8,
25 pub internal_banks: u8,
27 pub cas_latency: u8,
29 pub write_protection: bool,
31 pub read_burst: bool,
33 pub read_pipe_delay_cycles: u8,
35}
36
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39#[derive(Clone, Copy, Debug, PartialEq)]
40pub struct SdramTiming {
41 pub startup_delay_ns: u32,
44 pub max_sd_clock_hz: u32,
46 pub refresh_period_ns: u32,
48 pub mode_register_to_active: u32,
50 pub exit_self_refresh: u32,
52 pub active_to_precharge: u32,
54 pub row_cycle: u32,
56 pub row_precharge: u32,
58 pub row_to_column: u32,
60}
61
62pub trait SdramChip {
64 const MODE_REGISTER: u16;
66
67 const CONFIG: SdramConfiguration;
69
70 const TIMING: SdramTiming;
72}
73
74#[allow(missing_debug_implementations)]
76pub struct Sdram<FMC, IC> {
77 target_bank: SdramTargetBank,
79 fmc_bank: FmcBank,
81 _chip: PhantomData<IC>,
83 fmc: FMC,
85 regs: FmcRegisters,
87}
88
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
91#[derive(Clone, Copy, Debug, PartialEq)]
92#[allow(unused)]
93enum SdramCommand {
94 NormalMode,
95 ClkEnable,
96 Pall,
97 Autorefresh(u8),
98 LoadMode(u16),
99 Selfrefresh,
100 Powerdown,
101}
102#[derive(Clone, Copy, Debug, PartialEq)]
104#[cfg_attr(feature = "defmt", derive(defmt::Format))]
105#[allow(unused)]
106pub enum SdramTargetBank {
107 Bank1,
109 Bank2,
111 Both,
113}
114impl From<u32> for SdramTargetBank {
115 fn from(n: u32) -> Self {
116 match n {
117 1 => SdramTargetBank::Bank1,
118 2 => SdramTargetBank::Bank2,
119 _ => unimplemented!(),
120 }
121 }
122}
123
124pub trait SdramPinSet {
126 const TARGET: SdramTargetBank;
128 const FMC: FmcBank;
130}
131
132#[derive(Clone, Copy, Debug)]
134#[cfg_attr(feature = "defmt", derive(defmt::Format))]
135pub struct SdramBank1;
136impl SdramPinSet for SdramBank1 {
137 const TARGET: SdramTargetBank = SdramTargetBank::Bank1;
138 const FMC: FmcBank = FmcBank::Bank5;
139}
140
141#[derive(Clone, Copy, Debug)]
143#[cfg_attr(feature = "defmt", derive(defmt::Format))]
144pub struct SdramBank2;
145impl SdramPinSet for SdramBank2 {
146 const TARGET: SdramTargetBank = SdramTargetBank::Bank2;
147 const FMC: FmcBank = FmcBank::Bank6;
148}
149
150pub trait PinsSdram<Bank: SdramPinSet, Address: AddressPinSet> {
152 const NUMBER_INTERNAL_BANKS: u8;
154}
155
156macro_rules! modify_reg_banked {
158 ( $periph:path, $instance:expr, $bank:expr, $reg1:ident, $reg2:ident, $( $field:ident : $value:expr ),+ ) => {{
159 use SdramTargetBank::*;
160
161 match $bank {
162 Bank1 => modify_reg!( $periph, $instance, $reg1, $( $field : $value ),*),
163 Bank2 => modify_reg!( $periph, $instance, $reg2, $( $field : $value ),*),
164 _ => panic!(),
165 }
166 }};
167}
168
169impl<IC: SdramChip, FMC: FmcPeripheral> Sdram<FMC, IC> {
170 pub fn new<PINS, BANK, ADDR>(fmc: FMC, _pins: PINS, _chip: IC) -> Self
183 where
184 PINS: PinsSdram<BANK, ADDR>,
185 ADDR: AddressPinSet,
186 BANK: SdramPinSet,
187 {
188 assert!(
189 ADDR::ADDRESS_PINS >= IC::CONFIG.row_bits,
190 "Not enough address pins to access all SDRAM rows"
191 );
192 assert!(
193 ADDR::ADDRESS_PINS >= IC::CONFIG.column_bits,
194 "Not enough address pins to access all SDRAM colums"
195 );
196 assert!(
197 PINS::NUMBER_INTERNAL_BANKS >= IC::CONFIG.internal_banks,
198 "Not enough bank address pins to access all internal banks"
199 );
200
201 fmc_trace!("Bank selected via pins: {}.", BANK::TARGET);
202
203 Sdram {
204 target_bank: BANK::TARGET,
205 fmc_bank: BANK::FMC,
206 _chip: PhantomData,
207 fmc,
208 regs: FmcRegisters::new::<FMC>(),
209 }
210 }
211
212 pub fn new_unchecked(
223 fmc: FMC,
224 bank: impl Into<SdramTargetBank>,
225 _chip: IC,
226 ) -> Self {
227 let target_bank = bank.into();
229 let fmc_bank = match target_bank {
230 SdramTargetBank::Bank1 => FmcBank::Bank5,
231 SdramTargetBank::Bank2 => FmcBank::Bank6,
232 _ => unimplemented!(),
233 };
234
235 Sdram {
236 target_bank,
237 fmc_bank,
238 _chip: PhantomData,
239 fmc,
240 regs: FmcRegisters::new::<FMC>(),
241 }
242 }
243
244 pub fn init<D>(&mut self, delay: &mut D) -> *mut u32
256 where
257 D: DelayNs,
258 {
259 use SdramCommand::*;
260
261 let bank = self.target_bank;
263
264 let (sd_clock_hz, divide) = {
266 let fmc_source_ck_hz = self.fmc.source_clock_hz();
267 let sd_clock_wanted = IC::TIMING.max_sd_clock_hz;
268
269 let divide: u32 = cmp::max(
271 (fmc_source_ck_hz + sd_clock_wanted - 1) / sd_clock_wanted,
272 2,
273 );
274
275 assert!(divide <= 3,
277 "Source clock too fast for required SD_CLOCK. The maximum division ratio is 3");
278
279 let sd_clock_hz = fmc_source_ck_hz / divide;
280 (sd_clock_hz, divide)
281 };
282
283 fmc_trace!(
284 "FMC clock {:?} (/{}, Max {:?})",
285 sd_clock_hz,
286 divide,
287 IC::TIMING.max_sd_clock_hz
288 );
289
290 unsafe {
291 self.fmc.enable();
293
294 self.set_features_timings(IC::CONFIG, IC::TIMING, divide);
296
297 self.fmc.memory_controller_enable();
299
300 self.send_command(ClkEnable, bank);
302
303 let startup_delay_us = (IC::TIMING.startup_delay_ns + 999) / 1000;
305 fmc_trace!("Startup delay: {} us", startup_delay_us);
306
307 delay.delay_us(startup_delay_us.try_into().unwrap());
308
309 self.send_command(Pall, bank);
311
312 self.send_command(Autorefresh(8), bank);
314
315 self.send_command(LoadMode(IC::MODE_REGISTER), bank);
317
318 let refresh_counter_top = ((IC::TIMING.refresh_period_ns as u64
321 * sd_clock_hz as u64)
322 / 1_000_000_000)
323 - 20;
324 assert!(
325 refresh_counter_top >= 41 && refresh_counter_top < (1 << 13),
326 "Impossible configuration for H7 FMC Controller"
327 );
328
329 fmc_trace!("SDRTR: count {}", refresh_counter_top);
330
331 modify_reg!(
332 fmc,
333 self.regs.global(),
334 SDRTR,
335 COUNT: refresh_counter_top as u32
336 );
337 }
338
339 #[cfg(feature = "trace-register-values")]
340 {
341 use crate::read_reg;
342 fmc_trace!(
343 "BCR1: 0x{:x}",
344 read_reg!(fmc, self.regs.global(), BCR1)
345 );
346 fmc_trace!(
347 "BTR1: 0x{:x}",
348 read_reg!(fmc, self.regs.global(), BTR1)
349 );
350 fmc_trace!(
351 "BCR2: 0x{:x}",
352 read_reg!(fmc, self.regs.global(), BCR2)
353 );
354 fmc_trace!(
355 "BTR2: 0x{:x}",
356 read_reg!(fmc, self.regs.global(), BTR2)
357 );
358 fmc_trace!(
359 "BCR3: 0x{:x}",
360 read_reg!(fmc, self.regs.global(), BCR3)
361 );
362 fmc_trace!(
363 "BTR3: 0x{:x}",
364 read_reg!(fmc, self.regs.global(), BTR3)
365 );
366 fmc_trace!(
367 "BCR4: 0x{:x}",
368 read_reg!(fmc, self.regs.global(), BCR4)
369 );
370 fmc_trace!(
371 "BTR4: 0x{:x}",
372 read_reg!(fmc, self.regs.global(), BTR4)
373 );
374 fmc_trace!(
375 "SDCR1: 0x{:x}",
376 read_reg!(fmc, self.regs.global(), SDCR1)
377 );
378 fmc_trace!(
379 "SDCR2: 0x{:x}",
380 read_reg!(fmc, self.regs.global(), SDCR2)
381 );
382 fmc_trace!(
383 "SDTR1: 0x{:x}",
384 read_reg!(fmc, self.regs.global(), SDTR1)
385 );
386 fmc_trace!(
387 "SDTR2: 0x{:x}",
388 read_reg!(fmc, self.regs.global(), SDTR2)
389 );
390 fmc_trace!(
391 "SDCMR: 0x{:x}",
392 read_reg!(fmc, self.regs.global(), SDCMR)
393 );
394 fmc_trace!(
395 "SDRTR: 0x{:x}",
396 read_reg!(fmc, self.regs.global(), SDRTR)
397 );
398 }
399
400 self.fmc_bank.ptr()
402 }
403
404 unsafe fn set_features_timings(
414 &mut self,
415 config: SdramConfiguration,
416 timing: SdramTiming,
417 sd_clock_divide: u32,
418 ) {
419 assert!(
423 config.cas_latency >= 1 && config.cas_latency <= 3,
424 "Impossible configuration for FMC Controller"
425 );
426
427 assert!(
429 config.row_bits >= 11 && config.row_bits <= 13,
430 "Impossible configuration for FMC Controller"
431 );
432
433 assert!(
435 config.column_bits >= 8 && config.column_bits <= 11,
436 "Impossible configuration for FMC Controller"
437 );
438
439 assert!(
441 config.read_pipe_delay_cycles <= 2,
442 "Impossible configuration for FMC Controller"
443 );
444
445 modify_reg!(fmc, self.regs.global(), SDCR1,
447 RPIPE: config.read_pipe_delay_cycles as u32,
448 RBURST: config.read_burst as u32,
449 SDCLK: sd_clock_divide);
450
451 modify_reg_banked!(fmc, self.regs.global(),
452 self.target_bank, SDCR1, SDCR2,
453 WP: config.write_protection as u32,
455 CAS: config.cas_latency as u32,
456 NB:
457 match config.internal_banks {
458 2 => 0,
459 4 => 1,
460 _ => {
461 panic!("Impossible configuration for FMC Controller")
462 }
463 },
464 MWID:
465 match config.memory_data_width {
466 8 => 0,
467 16 => 1,
468 32 => 2,
469 _ => {
470 panic!("Impossible configuration for FMC Controller")
471 }
472 },
473 NR: config.row_bits as u32 - 11,
474 NC: config.column_bits as u32 - 8);
475
476 let minimum_self_refresh = timing.active_to_precharge;
480
481 let write_recovery_self_refresh =
483 minimum_self_refresh - timing.row_to_column;
484 let write_recovery_row_cycle =
486 timing.row_cycle - timing.row_to_column - timing.row_precharge;
487 let write_recovery =
488 cmp::max(write_recovery_self_refresh, write_recovery_row_cycle);
489
490 modify_reg!(fmc, self.regs.global(), SDTR1,
492 TRC: timing.row_cycle - 1,
493 TRP: timing.row_precharge - 1
494 );
495
496 modify_reg_banked!(fmc, self.regs.global(),
497 self.target_bank, SDTR1, SDTR2,
498 TRCD: timing.row_to_column - 1,
500 TWR: write_recovery - 1,
501 TRAS: minimum_self_refresh - 1,
502 TXSR: timing.exit_self_refresh - 1,
503 TMRD: timing.mode_register_to_active - 1
504 );
505 }
506
507 unsafe fn send_command(
509 &mut self,
510 mode: SdramCommand,
511 target: SdramTargetBank,
512 ) {
513 use SdramCommand::*;
514 use SdramTargetBank::*;
515
516 let (cmd, number_refresh, mode_reg) = match mode {
518 NormalMode => (0x00, 1, 0),
519 ClkEnable => (0x01, 1, 0),
520 Pall => (0x02, 1, 0),
521 Autorefresh(a) => (0x03, a, 0), LoadMode(mr) => (0x04, 1, mr), Selfrefresh => (0x05, 1, 0),
524 Powerdown => (0x06, 1, 0),
525 };
526 let (b1, b2) = match target {
528 Bank1 => (1, 0),
529 Bank2 => (0, 1),
530 Both => (1, 1),
531 };
532
533 write_reg!(
535 fmc,
536 self.regs.global(),
537 SDCMR,
538 MRD: mode_reg as u32,
539 NRFS: number_refresh as u32,
540 CTB1: b1,
541 CTB2: b2,
542 MODE: cmd
543 );
544
545 #[cfg(feature = "trace-register-values")]
546 fmc_trace!(
547 "Modifying SDCMR: mrd {}, nrfs {}, ctb1 {}, ctb2 {}, mode {}",
548 mode_reg,
549 number_refresh,
550 b1,
551 b2,
552 cmd
553 );
554 }
555}