zynq7000_hal/ddr/
mod.rs

1//! # DDR module
2//!
3//! ## Examples
4//!
5//! - [Zedboard FSBL](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-fsbl)
6use arbitrary_int::u6;
7use zynq7000::ddrc::MmioDdrController;
8
9use crate::{
10    BootMode,
11    clocks::pll::{PllConfig, configure_ddr_pll},
12    time::Hertz,
13};
14
15pub mod ll;
16
17pub use ll::{DdrcConfigSet, DdriobConfigSet};
18
19#[derive(Debug, Clone, Copy)]
20pub struct DdrClockSetupConfig {
21    pub ps_clk: Hertz,
22    pub ddr_clk: Hertz,
23    pub ddr_3x_div: u6,
24    pub ddr_2x_div: u6,
25}
26
27impl DdrClockSetupConfig {
28    pub const fn new(ps_clk: Hertz, ddr_clk: Hertz, ddr_3x_div: u6, ddr_2x_div: u6) -> Self {
29        Self {
30            ps_clk,
31            ddr_clk,
32            ddr_3x_div,
33            ddr_2x_div,
34        }
35    }
36}
37
38/// This completely sets up the DDR module for DDR3 operation.
39///
40/// It performs the following steps accoridng to the functional programming model of the
41/// DDR memory controller, TRM p.323.
42///
43///  1. Configures the DDR PLL to the target clock frequency.
44///  2. Configures the DDR clocks based on the user-provided configuration.
45///  3. Configures and sets up the DDR I/O buffers (DDR IOB) module.
46///  4. Configures and sets up the DDR controller (DDRC) module.
47///
48/// This function consumes the DDRC register block once and thus provides a safe interface for DDR
49/// initialization.
50pub fn configure_ddr_for_ddr3(
51    mut ddrc_regs: MmioDdrController<'static>,
52    boot_mode: BootMode,
53    clk_setup_cfg: DdrClockSetupConfig,
54    ddriob_cfg: &DdriobConfigSet,
55    ddr_cfg: &DdrcConfigSet,
56) {
57    // Set the DDR PLL output frequency to an even multiple of the operating frequency,
58    // as recommended by the DDR documentation.
59    configure_ddr_pll(
60        boot_mode,
61        PllConfig::new_from_target_clock(clk_setup_cfg.ps_clk, clk_setup_cfg.ddr_clk).unwrap(),
62    );
63    // Safety: Only done once here during start-up.
64    let ddr_clks = unsafe {
65        crate::clocks::DdrClocks::new_with_2x_3x_init(
66            clk_setup_cfg.ddr_clk,
67            clk_setup_cfg.ddr_3x_div,
68            clk_setup_cfg.ddr_2x_div,
69        )
70    };
71    let dci_clk_cfg = ll::calculate_dci_divisors(&ddr_clks);
72
73    ddrc_regs.modify_ddrc_ctrl(|mut val| {
74        val.set_soft_reset(zynq7000::ddrc::regs::SoftReset::Reset);
75        val
76    });
77
78    // Safety: This is only called once during DDR initialization.
79    unsafe {
80        ll::configure_iob(ddriob_cfg);
81        // Do not wait for completion, it takes a bit of time. We can set all the DDR config registers
82        // before polling for completion.
83        ll::calibrate_iob_impedance_for_ddr3(dci_clk_cfg, false);
84    }
85    ll::configure_ddr_config(&mut ddrc_regs, ddr_cfg);
86    // Safety: This is only called once during DDR initialization, and we only modify DDR related
87    // registers.
88    let slcr = unsafe { crate::slcr::Slcr::steal() };
89    let ddriob_shared = slcr.regs().ddriob_shared();
90    // Wait for DDR IOB impedance calibration to complete first.
91    while !ddriob_shared.read_dci_status().done() {
92        cortex_ar::asm::nop();
93    }
94    log::debug!("DDR IOB impedance calib done");
95
96    // Now take the DDR out of reset.
97    ddrc_regs.modify_ddrc_ctrl(|mut val| {
98        val.set_soft_reset(zynq7000::ddrc::regs::SoftReset::Active);
99        val
100    });
101    // Wait until the DDR setup has completed.
102    while ddrc_regs.read_mode_status().operating_mode()
103        != zynq7000::ddrc::regs::OperatingMode::NormalOperation
104    {
105        // Wait for the soft reset to complete.
106        cortex_ar::asm::nop();
107    }
108}
109
110pub mod memtest {
111    #[derive(Debug, thiserror::Error)]
112    pub enum MemTestError {
113        #[error("memory address is not aligned to 4 bytes")]
114        AddrNotAligned,
115        #[error("memory test error")]
116        Memory {
117            addr: usize,
118            expected: u32,
119            found: u32,
120        },
121    }
122
123    /// # Safety
124    ///
125    /// This tests writes and reads on a memory block starting at the base address
126    /// with the size `words` times 4.
127    pub unsafe fn walking_zero_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
128        unsafe { walking_value_test(true, base_addr, words) }
129    }
130
131    /// # Safety
132    ///
133    /// This tests writes and reads on a memory block starting at the base address
134    /// with the size `words` times 4.
135    pub unsafe fn walking_one_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
136        unsafe { walking_value_test(true, base_addr, words) }
137    }
138
139    /// # Safety
140    ///
141    /// This tests writes and reads on a memory block starting at the base address
142    /// with the size `words` times 4.
143    pub unsafe fn walking_value_test(
144        walking_zero: bool,
145        base_addr: usize,
146        words: usize,
147    ) -> Result<(), MemTestError> {
148        if words == 0 {
149            return Ok(());
150        }
151        if !base_addr.is_multiple_of(4) {
152            return Err(MemTestError::AddrNotAligned);
153        }
154        let base_ptr = base_addr as *mut u32;
155
156        // For each bit position 0..31 generate pattern = 1 << bit
157        for bit in 0..32 {
158            let pattern = if walking_zero {
159                !(1u32 << bit)
160            } else {
161                1u32 << bit
162            };
163
164            // write pass
165            for i in 0..words {
166                unsafe {
167                    let p = base_ptr.add(i);
168                    core::ptr::write_volatile(p, pattern);
169                }
170            }
171
172            // read/verify pass
173            for i in 0..words {
174                let val;
175                unsafe {
176                    let p = base_ptr.add(i) as *const u32;
177                    val = core::ptr::read_volatile(p);
178                }
179                if val != pattern {
180                    return Err(MemTestError::Memory {
181                        addr: base_addr + i * 4,
182                        expected: pattern,
183                        found: val,
184                    });
185                }
186            }
187        }
188        Ok(())
189    }
190
191    /// # Safety
192    ///
193    /// This tests writes and reads on a memory block starting at the base address
194    /// with the size `words` times 4.
195    pub unsafe fn checkerboard_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
196        if words == 0 {
197            return Ok(());
198        }
199        if !base_addr.is_multiple_of(4) {
200            return Err(MemTestError::AddrNotAligned);
201        }
202
203        let base_ptr = base_addr as *mut u32;
204        let patterns = [0xAAAAAAAAu32, 0x55555555u32];
205
206        for &pattern in &patterns {
207            // Write pass
208            for i in 0..words {
209                let value = if i % 2 == 0 { pattern } else { !pattern };
210                unsafe {
211                    core::ptr::write_volatile(base_ptr.add(i), value);
212                }
213            }
214
215            // Read/verify pass
216            for i in 0..words {
217                let expected = if i % 2 == 0 { pattern } else { !pattern };
218                let val = unsafe { core::ptr::read_volatile(base_ptr.add(i)) };
219
220                if val != expected {
221                    return Err(MemTestError::Memory {
222                        addr: base_addr + i * 4,
223                        expected,
224                        found: val,
225                    });
226                }
227            }
228        }
229
230        Ok(())
231    }
232}