diff --git a/src/regs.rs b/src/regs.rs index 46835086..39732b9b 100644 --- a/src/regs.rs +++ b/src/regs.rs @@ -35,9 +35,11 @@ macro_rules! register_common { } pub mod $mod_name { + #[derive(Clone)] pub struct Read { pub inner: $inner, } + #[derive(Clone)] pub struct Write { pub inner: $inner, } diff --git a/src/zynq/ddr/mod.rs b/src/zynq/ddr/mod.rs index 16635565..c579983c 100644 --- a/src/zynq/ddr/mod.rs +++ b/src/zynq/ddr/mod.rs @@ -2,7 +2,7 @@ use crate::regs::{RegisterR, RegisterW, RegisterRW}; use super::slcr; use super::clocks::CpuClocks; -/// Micron MT41J256M8HX-15E: 667 MHz +/// Micron MT41J256M8HX-15E: 667 MHz DDR3 const DDR_FREQ: u32 = 666_666_666; const DCI_FREQ: u32 = 10_000_000; @@ -14,11 +14,14 @@ impl DdrRam { let clocks = CpuClocks::get(); Self::clock_setup(&clocks); Self::calibrate_iob_impedance(&clocks); + Self::configure_iob(); let ram = DdrRam {}; ram } + /// Zynq-7000 AP SoC Technical Reference Manual: + /// 10.6.1 DDR Clock Initialization fn clock_setup(clocks: &CpuClocks) { let ddr3x_clk_divisor = ((clocks.ddr - 1) / DDR_FREQ + 1).min(255) as u8; let ddr2x_clk_divisor = 3 * ddr3x_clk_divisor / 2; @@ -38,6 +41,8 @@ impl DdrRam { }); } + /// Zynq-7000 AP SoC Technical Reference Manual: + /// 10.6.2 DDR IOB Impedance Calibration fn calibrate_iob_impedance(clocks: &CpuClocks) { let divisor0 = (clocks.ddr / DCI_FREQ) .max(1).min(63) as u8; @@ -80,4 +85,49 @@ impl DdrRam { while ! slcr.ddriob_dci_status.read().done() {} }); } + + /// Zynq-7000 AP SoC Technical Reference Manual: + /// 10.6.3 DDR IOB Configuration + fn configure_iob() { + slcr::RegisterBlock::unlocked(|slcr| { + let addr_config = slcr::DdriobConfig::zeroed() + .output_en(slcr::DdriobOutputEn::Obuf); + slcr.ddriob_addr0.write(addr_config.clone()); + slcr.ddriob_addr1.write(addr_config); + let data_config = slcr::DdriobConfig::zeroed() + .inp_type(slcr::DdriobInputType::VrefDifferential) + .term_en(true) + .dci_type(slcr::DdriobDciType::Termination) + .output_en(slcr::DdriobOutputEn::Obuf); + slcr.ddriob_data0.write(data_config.clone()); + slcr.ddriob_data1.write(data_config); + let diff_config = slcr::DdriobConfig::zeroed() + .inp_type(slcr::DdriobInputType::Differential) + .term_en(true) + .dci_type(slcr::DdriobDciType::Termination) + .output_en(slcr::DdriobOutputEn::Obuf); + slcr.ddriob_diff0.write(diff_config.clone()); + slcr.ddriob_diff1.write(diff_config); + slcr.ddriob_clock.write( + slcr::DdriobConfig::zeroed() + .output_en(slcr::DdriobOutputEn::Obuf) + ); + + unsafe { + // Not documented in Technical Reference Manual + slcr.ddriob_drive_slew_addr.write(0x0018C61C); + slcr.ddriob_drive_slew_data.write(0x00F9861C); + slcr.ddriob_drive_slew_diff.write(0x00F9861C); + slcr.ddriob_drive_slew_clock.write(0x00F9861C); + } + + // Enable internal V[REF] + slcr.ddriob_ddr_ctrl.modify(|_, w| w + .vref_ext_en_lower(false) + .vref_ext_en_upper(false) + .vref_sel(slcr::DdriobVrefSel::Vref0_75V) + .vref_int_en(true) + ); + }); + } } diff --git a/src/zynq/slcr.rs b/src/zynq/slcr.rs index 5795e28d..edbe6704 100644 --- a/src/zynq/slcr.rs +++ b/src/zynq/slcr.rs @@ -19,6 +19,45 @@ pub enum ArmPllSource { IoPll = 0b11, } +#[repr(u8)] +pub enum DdriobInputType { + Off = 0b00, + /// For SSTL, HSTL + VrefDifferential = 0b01, + Differential = 0b10, + Lvcmos = 0b11, +} + +#[repr(u8)] +pub enum DdriobDciType { + /// DDR2/3L Addr and Clock + Disabled = 0b00, + /// LPDDR2 + Drive = 0b01, + /// DDR2/3/3L Data and Diff + Termination = 0b11, +} + +#[repr(u8)] +pub enum DdriobOutputEn { + Ibuf = 0b00, + Obuf = 0b11, +} + + +#[repr(u8)] +pub enum DdriobVrefSel { + /// For LPDDR2 with 1.2V IO + Vref0_6V, + /// For DDR3L with 1.35V IO + Vref0_675V, + /// For DDR3 with 1.5V IO + Vref0_75V, + /// For DDR2 with 1.8V IO + Vref0_9V, +} + + #[repr(C)] pub struct RegisterBlock { pub scl: RW, @@ -190,17 +229,18 @@ pub struct RegisterBlock { pub gpiob_cfg_hstl: RW, pub gpiob_drvr_bias_ctrl: RW, reserved21: [u32; 9], - pub ddriob_addr1: RW, - pub ddriob_data0: RW, - pub ddriob_data1: RW, - pub ddriob_diff0: RW, - pub ddriob_diff1: RW, - pub ddriob_clock: RW, - pub w_addr: RW, - pub w_data: RW, - pub w_diff: RW, - pub w_clock: RW, - pub ddriob_ddr_ctrl: RW, + pub ddriob_addr0: DdriobConfig, + pub ddriob_addr1: DdriobConfig, + pub ddriob_data0: DdriobConfig, + pub ddriob_data1: DdriobConfig, + pub ddriob_diff0: DdriobConfig, + pub ddriob_diff1: DdriobConfig, + pub ddriob_clock: DdriobConfig, + pub ddriob_drive_slew_addr: RW, + pub ddriob_drive_slew_data: RW, + pub ddriob_drive_slew_diff: RW, + pub ddriob_drive_slew_clock: RW, + pub ddriob_ddr_ctrl: DdriobDdrCtrl, pub ddriob_dci_ctrl: DdriobDciCtrl, pub ddriob_dci_status: DdriobDciStatus, } @@ -463,6 +503,23 @@ mio_pin_register!(mio_pin_53, MioPin53); register!(gpiob_ctrl, GpiobCtrl, RW, u32); register_bit!(gpiob_ctrl, vref_en, 0); +register!(ddriob_config, DdriobConfig, RW, u32); +register_bits_typed!(ddriob_config, inp_type, u8, DdriobInputType, 1, 2); +register_bit!(ddriob_config, dci_update_b, 3); +register_bit!(ddriob_config, term_en, 4); +register_bits_typed!(ddriob_config, dci_type, u8, DdriobDciType, 5, 6); +register_bit!(ddriob_config, ibuf_disable_mode, 7); +register_bit!(ddriob_config, term_disable_mode, 8); +register_bits_typed!(ddriob_config, output_en, u8, DdriobOutputEn, 9, 10); +register_bit!(ddriob_config, pullup_en, 11); + +register!(ddriob_ddr_ctrl, DdriobDdrCtrl, RW, u32); +register_bit!(ddriob_ddr_ctrl, vref_int_en, 1); +register_bits_typed!(ddriob_ddr_ctrl, vref_sel, u8, DdriobVrefSel, 1, 4); +register_bit!(ddriob_ddr_ctrl, vref_ext_en_lower, 5); +register_bit!(ddriob_ddr_ctrl, vref_ext_en_upper, 6); +register_bit!(ddriob_ddr_ctrl, refio_en, 9); + register!(ddriob_dci_ctrl, DdriobDciCtrl, RW, u32); register_bit!(ddriob_dci_ctrl, reset, 0); register_bit!(ddriob_dci_ctrl, enable, 0);