diff --git a/zynq7000/src/ddrc.rs b/zynq7000/src/ddrc.rs index b0c6272..ca98b5d 100644 --- a/zynq7000/src/ddrc.rs +++ b/zynq7000/src/ddrc.rs @@ -1,12 +1,154 @@ +use arbitrary_int::{u11, u12, u3, u4, u5, u6, u7}; + +pub const DDRC_BASE_ADDR: usize = 0xF800_6000; + +#[bitbybit::bitenum(u2)] +#[derive(Debug, PartialEq, Eq)] +pub enum DataBusWidth { + _32Bit = 0b00, + _16Bit = 0b01, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum SoftReset { + Reset = 0, + Active = 1, +} +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct DdrcControl { + #[bit(16, rw)] + disable_auto_refresh: bool, + #[bit(15, rw)] + disable_active_bypass: bool, + #[bit(14, rw)] + disable_read_bypass: bool, + #[bits(7..=13, rw)] + read_write_idle_gap: u7, + #[bits(4..=6, rw)] + burst8_refresh: u3, + #[bits(2..=3, rw)] + data_bus_width: Option, + #[bit(1, rw)] + power_down_enable: bool, + #[bit(0, rw)] + soft_reset: SoftReset, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct TwoRankConfig { + #[bits(14..=18, rw)] + addrmap_cs_bit0: u5, + /// tREFI - Average time between refreshes, in multiples of 32 clocks. + #[bits(0..=11, rw)] + rfc_nom_x32: u12, +} + +/// Queue control for the low priority and high priority read queues. +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct LprHprQueueControl { + #[bits(22..=25, rw)] + xact_run_length: u4, + #[bits(11..=21, rw)] + max_starve_x32: u11, + #[bits(0..=10, rw)] + min_non_critical_x32: u11, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct WriteQueueControl { + #[bits(15..=25, rw)] + max_starve_x32: u11, + #[bits(11..=14, rw)] + xact_run_length: u4, + #[bits(0..=10, rw)] + min_non_critical_x32: u11, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct DramParamReg0 { + /// Minimum time to wait after coming out of self refresh before doing anything. This must be + /// bigger than all the constraints that exist. + #[bits(14..=20, rw)] + post_selfref_gap_x32: u7, + /// tRFC(min) - Minimum time from refresh to refresh or activate in clock + /// cycles. + #[bits(6..=13, rw)] + t_rfc_min: u8, + /// tRC - Min time between activates to the same bank. + #[bits(0..=5, rw)] + t_rc: u6, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct DramParamReg1 { + #[bits(28..=31, rw)] + t_cke: u4, + #[bits(22..=26, rw)] + t_ras_min: u5, + #[bits(16..=21, rw)] + t_ras_max: u6, + #[bits(10..=15, rw)] + t_faw: u6, + #[bits(5..=9, rw)] + powerdown_to_x32: u5, + #[bits(0..=4, rw)] + wr2pre: u5, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct DramParamReg2 { + #[bits(28..=31, rw)] + t_rcd: u4, + #[bits(23..=27, rw)] + rd2pre: u5, + #[bits(20..=22, rw)] + pad_pd: u3, + #[bits(15..=19, rw)] + t_xp: u5, + #[bits(10..=14, rw)] + wr2rd: u5, + #[bits(5..=9, rw)] + rd2wr: u5, + #[bits(0..=4, rw)] + write_latency: u5, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct DramParamReg3 { + #[bit(30, rw)] + disable_pad_pd: bool, + #[bits(24..=28, rw)] + read_latency: u5, + #[bit(23, rw)] + enable_dfi_dram_clk_disable: bool, + /// 0: DDR2 or DDR3. 1: LPDDR2. + #[bit(22, rw)] + mobile: bool, + /// Must be set to 0. + #[bit(21, rw)] + sdram: bool, + #[bits(16..=20, rw)] + refresh_to_x32: u5, + #[bits(12..=15, rw)] + t_rp: u4, + #[bits(8..=11, rw)] + refresh_margin: u4, + #[bits(5..=7, rw)] + t_rrd: u3, + #[bits(2..=4, rw)] + t_ccd: u3 +} + #[derive(derive_mmio::Mmio)] #[repr(C)] pub struct DdrController { - ddrc_ctrl: u32, - two_rank_cfg: u32, - hpr_reg: u32, - lpr_reg: u32, - wr_reg: u32, - dram_param_reg0: u32, + ddrc_ctrl: DdrcControl, + two_rank_cfg: TwoRankConfig, + hpr_queue_ctrl: LprHprQueueControl, + lpr_queue_ctrl: LprHprQueueControl, + wr_reg: WriteQueueControl, + dram_param_reg0: DramParamReg0, dram_param_reg1: u32, dram_param_reg2: u32, dram_param_reg3: u32, @@ -29,8 +171,12 @@ pub struct DdrController { ctrl_reg2: u32, ctrl_reg3: u32, ctrl_reg4: u32, + _reserved0: [u32; 0x2], ctrl_reg5: u32, ctrl_reg6: u32, + + _reserved1: [u32; 0x8], + che_refresh_timer_01: u32, che_t_zq: u32, che_t_zq_short_interval_reg: u32, @@ -38,7 +184,9 @@ pub struct DdrController { reg_2c: u32, reg_2d: u32, dfi_timing: u32, + _reserved2: [u32; 0x2], che_corr_control: u32, + che_corr_ecc_log: u32, che_corr_ecc_addr: u32, che_corr_ecc_data_31_0: u32, che_corr_ecc_data_63_32: u32, @@ -48,51 +196,52 @@ pub struct DdrController { che_uncorr_ecc_data_31_0: u32, che_uncorr_ecc_data_63_32: u32, che_uncorr_ecc_data_71_64: u32, - che_ecc_stats_reg: u32, + che_ecc_stats: u32, ecc_scrub: u32, che_ecc_corr_bit_mask_31_0: u32, che_ecc_corr_bit_mask_63_32: u32, + + _reserved3: [u32; 0x5], + phy_receiver_enable: u32, - phy_config_0: u32, - phy_config_1: u32, - phy_config_2: u32, - phy_config_3: u32, - phy_init_ratio_0: u32, - phy_init_ratio_1: u32, - phy_init_ratio_2: u32, - phy_init_ratio_3: u32, - phy_rd_dqs_cfg_0: u32, - phy_rd_dqs_cfg_1: u32, - phy_rd_dqs_cfg_2: u32, - phy_rd_dqs_cfg_3: u32, - phy_wr_dqs_cfg_0: u32, - phy_wr_dqs_cfg_1: u32, - phy_wr_dqs_cfg_2: u32, - phy_wr_dqs_cfg_3: u32, - phy_we_cfg_1: u32, - phy_we_cfg_2: u32, - phy_we_cfg_3: u32, - wr_data_slave_0: u32, - wr_data_slave_1: u32, - wr_data_slave_2: u32, - wr_data_slave_3: u32, + phy_config: [u32; 4], + _reserved4: u32, + phy_init_ratio: [u32; 4], + _reserved5: u32, + phy_rd_dqs_cfg: [u32; 4], + _reserved6: u32, + phy_wr_dqs_cfg: [u32; 4], + _reserved7: u32, + phy_we_cfg_0: [u32; 4], + _reserved8: u32, + wr_data_slave: [u32; 4], + + _reserved9: u32, + reg_64: u32, reg_65: u32, + _reserved10: [u32; 3], reg69_6a0: u32, reg69_6a1: u32, + _reserved11: u32, reg69_6d2: u32, reg69_6d3: u32, reg69_710: u32, reg6e_711: u32, reg6e_712: u32, reg6e_713: u32, + _reserved12: u32, phy_dll_status_0: u32, phy_dll_status_1: u32, phy_dll_status_2: u32, phy_dll_status_3: u32, + _reserved13: u32, dll_lock_status: u32, phy_control_status: u32, phy_control_status_2: u32, + + _reserved14: [u32; 0x5], + axi_id: u32, page_mask: u32, axi_priority_wr_port_0: u32, @@ -103,6 +252,9 @@ pub struct DdrController { axi_priority_rd_port_1: u32, axi_priority_rd_port_2: u32, axi_priority_rd_port_3: u32, + + _reserved15: [u32; 0x1B], + excl_access_cfg_0: u32, excl_access_cfg_1: u32, excl_access_cfg_2: u32, @@ -114,5 +266,17 @@ pub struct DdrController { lpddr_ctrl_3: u32, } - static_assertions::const_assert_eq!(core::mem::size_of::(), 0x2B8); + +impl DdrController { + /// Create a new DDR MMIO instance for the DDR controller at address [DDRC_BASE_ADDR]. + /// + /// # Safety + /// + /// This API can be used to potentially create a driver to the same peripheral structure + /// from multiple threads. The user must ensure that concurrent accesses are safe and do not + /// interfere with each other. + pub const unsafe fn new_mmio_fixed() -> MmioDdrController<'static> { + unsafe { Self::new_mmio_at(DDRC_BASE_ADDR) } + } +}