-------------------------------------------------------------------------------- -- -- Copyright (C) 2015 -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> -- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. -- -------------------------------------------------------------------------------- -- Author: Eric Kooistra, 19 june 2017 -- Purpose: Functional simulation model of both the DDR driver and the DDR memory. -- Description: -- This DDR model supports different types of DDR, so DDR3 and DDR4. -- -- During a write burst the user still controls the access rate by means of -- ctrl_mosi.wr. During a read burst the memory determines the access rate. -- Therefore a new write or read burst request will not occure during a write -- burst, but they can occur during a read burst. The ctrl_mosi.address and -- ctrl_mosi.burstsize are then captured with the pending_rd and prnding wr -- state and the ctlr_miso.waitrequest_n is pulled low because the model only -- supports one pending burst request. -- -- The internal state of the DDR model is maintained in a variable of type -- t_mem_state. For debugging purposes this state is also assigned to a -- signal to be able to view it together with ctrl_mosi and ctrl_miso in the -- wave window. -- -- * About other DDR features: -- The sim_ddr only models the memory contents of the DDR, which is sufficient -- for now, because that is the main function of DDR and initially it is best -- to keep the model as simple as possible. If necessary, then possible extra -- future features could be to also model the latency caused by a periodical -- DRAM refresh cycle. -- -- * About fixed size DDR memory: -- The sim_ddr uses a fixed size simulation memory that cannot be too large, -- because it as to fit in the PC memory. Modelsim raises warning 3391 when -- the DDR memory set by c_nof_addr is to big to fit in PC RAM. Use -- 'verror 3391' to display the help. This means that the computer becomes -- too slow due to page swapping to disk. Even loading the simulation -- becomes slow. A way around this would be to use a simulation memory that -- grows dynamically for each address that is actually used (like a -- dictionary). An example of such a dynamic DDR memory model is available at: -- -- https://svn.astron.nl/UniBoard_FP7/UniBoard/trunk/Firmware/modules/MegaWizard/ddr3/testbench/ddr3_test_mem_model.vhd -- -- However this dynamic model will simulate slower the more it gets filled. -- Furthermore the simulation time of such larger blocks of data will -- become unfeasible. Instead it is better to scale the parameters of the -- application such that the fixed size sim_ddr memory is as small as -- possible. library IEEE, common_lib, technology_lib; use IEEE.std_logic_1164.all; use common_lib.common_pkg.all; use common_lib.common_mem_pkg.all; use technology_lib.technology_pkg.all; use technology_lib.technology_select_pkg.all; use work.tech_ddr_pkg.all; entity sim_ddr is generic ( g_tech_ddr : t_c_tech_ddr ); port ( -- PLL reference clock ref_clk : in std_logic; ref_rst : in std_logic; -- Controller user interface ctlr_gen_clk : out std_logic; ctlr_gen_rst : out std_logic; ctlr_gen_clk_2x : out std_logic; ctlr_gen_rst_2x : out std_logic; ctlr_mosi : in t_mem_ctlr_mosi; ctlr_miso : out t_mem_ctlr_miso ); end sim_ddr; architecture str of sim_ddr is -- DDR size and controller data width constant c_nof_addr : natural := 2**func_tech_ddr_ctlr_address_w(g_tech_ddr); -- 8192; constant c_dat_w : natural := func_tech_ddr_ctlr_data_w(g_tech_ddr); -- 256; -- DDR memory type t_mem_arr is array(0 to c_nof_addr - 1) of std_logic_vector(c_dat_w - 1 downto 0); type t_mem_state is record address : natural; burst_size : natural; burst_cnt : natural; -- = 0 wr_bursting : boolean; -- = FALSE rd_bursting : boolean; -- = FALSE pending_wr : boolean; -- = FALSE pending_rd : boolean; -- = FALSE pending_address : natural; pending_burst_size : natural; end record; signal sim_clk : std_logic; signal sim_rst : std_logic; -- Debug signals for view in Wave window signal dbg_g_tech_ddr : t_c_tech_ddr; signal dbg_c_nof_addr : natural; signal dbg_c_dat_w : natural; signal mem_state : t_mem_state; begin dbg_g_tech_ddr <= g_tech_ddr; dbg_c_nof_addr <= c_nof_addr; dbg_c_dat_w <= c_dat_w; -- Prevent delta delay issues by using a re-assigned clk both internally (sim_clk) and externally (ctrl_gen_clk) ctlr_gen_clk <= ref_clk; ctlr_gen_rst <= ref_rst; sim_clk <= ref_clk; sim_rst <= ref_rst; ctlr_miso.done <= '0' , '1' after 1 ns; ctlr_miso.cal_ok <= '0' , '1' after 1 ns; ctlr_miso.cal_fail <= '0'; p_mem_access : process(sim_clk) -- Process variables get initalized once and then they keep their state variable v_mem_arr : t_mem_arr := (others => (others => '0')); variable v : t_mem_state := (0, 0, 0, false, false, false, false, 0, 0); begin if rising_edge(sim_clk) then -- Burst begin if ctlr_mosi.burstbegin = '1' then if ctlr_mosi.wr = '1' then if v.rd_bursting = false then v.wr_bursting := true; v.address := TO_UINT(ctlr_mosi.address); v.burst_size := TO_UINT(ctlr_mosi.burstsize); v.burst_cnt := 0; else v.pending_wr := true; v.pending_address := TO_UINT(ctlr_mosi.address); v.pending_burst_size := TO_UINT(ctlr_mosi.burstsize); end if; elsif ctlr_mosi.rd = '1' then if v.rd_bursting = false then v.rd_bursting := true; v.address := TO_UINT(ctlr_mosi.address); v.burst_size := TO_UINT(ctlr_mosi.burstsize); v.burst_cnt := 0; ctlr_miso.waitrequest_n <= '0'; else v.pending_rd := true; v.pending_address := TO_UINT(ctlr_mosi.address); v.pending_burst_size := TO_UINT(ctlr_mosi.burstsize); end if; end if; end if; -- Pending write burst begin, after read burst if v.pending_wr = true and v.rd_bursting = false then v.pending_wr := false; if ctlr_mosi.wr = '1' then -- require that user has kept wr still active v.wr_bursting := true; v.address := v.pending_address; v.burst_size := v.pending_burst_size; v.burst_cnt := 0; end if; end if; -- Pending read burst begin, after read burst if v.pending_rd = true and v.rd_bursting = false then v.pending_rd := false; if ctlr_mosi.rd = '1' then -- require that user has kept rd still active v.rd_bursting := true; v.address := v.pending_address; v.burst_size := v.pending_burst_size; v.burst_cnt := 0; ctlr_miso.waitrequest_n <= '0'; end if; end if; -- Write access if v.wr_bursting = true and ctlr_mosi.wr = '1' then v_mem_arr(v.address) := ctlr_mosi.wrdata(c_dat_w - 1 downto 0); v.address := v.address + 1; v.burst_cnt := v.burst_cnt + 1; end if; -- Read access ctlr_miso.rdval <= '0'; if v.rd_bursting = true then ctlr_miso.rddata(c_dat_w - 1 downto 0) <= v_mem_arr(v.address); ctlr_miso.rdval <= '1'; v.address := v.address + 1; v.burst_cnt := v.burst_cnt + 1; end if; -- Burst size count if v.burst_cnt = v.burst_size then v.wr_bursting := false; v.rd_bursting := false; ctlr_miso.waitrequest_n <= '1'; end if; -- Show DDR memory state in wave window mem_state <= v; end if; end process; end str;