|
| 1 | +//! This example shows how to use the FMC controller on the STM32F469I-DISC to communicate with an |
| 2 | +//! off-chip SDRAM memory device. |
| 3 | +#![deny(warnings)] |
| 4 | +#![no_main] |
| 5 | +#![no_std] |
| 6 | + |
| 7 | +use panic_probe as _; |
| 8 | + |
| 9 | +use stm32f469i_disc as board; |
| 10 | + |
| 11 | +use crate::board::hal::gpio::alt::fmc as alt; |
| 12 | +use crate::board::hal::{fmc::FmcExt, pac, prelude::*}; |
| 13 | +use core::{mem, slice}; |
| 14 | + |
| 15 | +use cortex_m::peripheral::Peripherals; |
| 16 | + |
| 17 | +use cortex_m_rt::entry; |
| 18 | + |
| 19 | +use rtt_target::{rprintln, rtt_init_print}; |
| 20 | + |
| 21 | +use stm32_fmc::devices::is42s32400f_6; |
| 22 | + |
| 23 | +/// Configure pins for the FMC controller |
| 24 | +macro_rules! fmc_pins { |
| 25 | + ($($alt:ident: $pin:expr,)*) => { |
| 26 | + ( |
| 27 | + $( |
| 28 | + alt::$alt::from($pin.internal_pull_up(true)) |
| 29 | + ),* |
| 30 | + ) |
| 31 | + }; |
| 32 | +} |
| 33 | + |
| 34 | +/// A psuedo-random pattern generator |
| 35 | +struct XorShift32 { |
| 36 | + seed: u32, |
| 37 | +} |
| 38 | + |
| 39 | +impl XorShift32 { |
| 40 | + fn new(seed: u32) -> Self { |
| 41 | + XorShift32 { seed } |
| 42 | + } |
| 43 | + |
| 44 | + fn next(&mut self) -> u32 { |
| 45 | + self.seed ^= self.seed << 13; |
| 46 | + self.seed ^= self.seed >> 17; |
| 47 | + self.seed ^= self.seed << 5; |
| 48 | + self.seed |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +#[entry] |
| 53 | +fn main() -> ! { |
| 54 | + if let (Some(p), Some(cp)) = (pac::Peripherals::take(), Peripherals::take()) { |
| 55 | + let rcc = p.RCC.constrain(); |
| 56 | + |
| 57 | + let clocks = rcc.cfgr.sysclk(180.MHz()).freeze(); |
| 58 | + let mut delay = cp.SYST.delay(&clocks); |
| 59 | + |
| 60 | + let gpioc = p.GPIOC.split(); |
| 61 | + let gpiod = p.GPIOD.split(); |
| 62 | + let gpioe = p.GPIOE.split(); |
| 63 | + let gpiof = p.GPIOF.split(); |
| 64 | + let gpiog = p.GPIOG.split(); |
| 65 | + let gpioh = p.GPIOH.split(); |
| 66 | + let gpioi = p.GPIOI.split(); |
| 67 | + |
| 68 | + #[rustfmt::skip] |
| 69 | + let pins = fmc_pins! { |
| 70 | + A0: gpiof.pf0, A1: gpiof.pf1, A2: gpiof.pf2, A3: gpiof.pf3, |
| 71 | + A4: gpiof.pf4, A5: gpiof.pf5, A6: gpiof.pf12, A7: gpiof.pf13, |
| 72 | + A8: gpiof.pf14, A9: gpiof.pf15, A10: gpiog.pg0, A11: gpiog.pg1, |
| 73 | + Ba0: gpiog.pg4, Ba1: gpiog.pg5, |
| 74 | + D0: gpiod.pd14, D1: gpiod.pd15, D2: gpiod.pd0, D3: gpiod.pd1, |
| 75 | + D4: gpioe.pe7, D5: gpioe.pe8, D6: gpioe.pe9, D7: gpioe.pe10, |
| 76 | + D8: gpioe.pe11, D9: gpioe.pe12, D10: gpioe.pe13, D11: gpioe.pe14, |
| 77 | + D12: gpioe.pe15, D13: gpiod.pd8, D14: gpiod.pd9, D15: gpiod.pd10, |
| 78 | + D16: gpioh.ph8, D17: gpioh.ph9, D18: gpioh.ph10, D19: gpioh.ph11, |
| 79 | + D20: gpioh.ph12, D21: gpioh.ph13, D22: gpioh.ph14, D23: gpioh.ph15, |
| 80 | + D24: gpioi.pi0, D25: gpioi.pi1, D26: gpioi.pi2, D27: gpioi.pi3, |
| 81 | + D28: gpioi.pi6, D29: gpioi.pi7, D30: gpioi.pi9, D31: gpioi.pi10, |
| 82 | + Nbl0: gpioe.pe0, Nbl1: gpioe.pe1, Nbl2: gpioi.pi4, Nbl3: gpioi.pi5, |
| 83 | + Sdcke0: gpioh.ph2, Sdclk: gpiog.pg8, |
| 84 | + Sdncas: gpiog.pg15, Sdne0: gpioh.ph3, |
| 85 | + Sdnras: gpiof.pf11, Sdnwe: gpioc.pc0, |
| 86 | + }; |
| 87 | + |
| 88 | + rtt_init_print!(); |
| 89 | + |
| 90 | + rprintln!("Initializing SDRAM...\r"); |
| 91 | + |
| 92 | + let mut sdram = p.FMC.sdram(pins, is42s32400f_6::Is42s32400f6 {}, &clocks); |
| 93 | + let len_bytes = 16 * 1024 * 1024; |
| 94 | + let len_words = len_bytes / mem::size_of::<u32>(); |
| 95 | + let ram_ptr: *mut u32 = sdram.init(&mut delay); |
| 96 | + let ram = unsafe { slice::from_raw_parts_mut(ram_ptr, len_words) }; |
| 97 | + |
| 98 | + rprintln!("Testing SDRAM...\r"); |
| 99 | + |
| 100 | + let seed: u32 = 0x8675309D; |
| 101 | + let mut pattern = XorShift32::new(seed); |
| 102 | + |
| 103 | + // write our pattern |
| 104 | + for addr in 0..len_words { |
| 105 | + let val = pattern.next(); |
| 106 | + |
| 107 | + if (addr & 0x1ffff) == 0 { |
| 108 | + rprintln!("Write: {:X} <- {:X}\r", (ram_ptr as usize) + addr, val); |
| 109 | + } |
| 110 | + |
| 111 | + ram[addr] = val; |
| 112 | + } |
| 113 | + |
| 114 | + // read back pattern |
| 115 | + pattern = XorShift32::new(seed); |
| 116 | + for addr in 0..len_words { |
| 117 | + let val = pattern.next(); |
| 118 | + |
| 119 | + if (addr & 0x1ffff) == 0 { |
| 120 | + rprintln!("Read: {:X} -> {:X}\r", (ram_ptr as usize) + addr, val); |
| 121 | + } |
| 122 | + |
| 123 | + let res: u32 = ram[addr]; |
| 124 | + if res != val { |
| 125 | + rprintln!( |
| 126 | + "Error: {:X} -> {:X} != {:X}\r", |
| 127 | + (ram_ptr as usize) + addr, |
| 128 | + val, |
| 129 | + res |
| 130 | + ); |
| 131 | + break; |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + rprintln!("Done!\r"); |
| 136 | + } |
| 137 | + loop {} |
| 138 | +} |
0 commit comments