diff --git a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd index bcae51be4d3e714c1a7816ef2c3d98520a50dc24..6b1bec8ba6a243392899fb1f3c7515676f3f4c09 100644 --- a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd +++ b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd @@ -44,7 +44,7 @@ USE tech_ddr_lib.tech_ddr_pkg.ALL; ENTITY tb_io_ddr IS GENERIC ( - g_sim_model : BOOLEAN := FALSE; + g_sim_model : BOOLEAN := TRUE; --FALSE; g_technology : NATURAL := c_tech_select_default; g_tech_ddr3 : t_c_tech_ddr := c_tech_ddr3_4g_800m_master; g_tech_ddr4 : t_c_tech_ddr := c_tech_ddr4_4g_1600m; diff --git a/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd index 8d7a99493202fa8f8e45169ed30de1643344f21a..b50423baf2dae7d994cbb663cfb26d0adeb94d96 100644 --- a/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd +++ b/libraries/io/ddr/tb/vhdl/tb_tb_io_ddr.vhd @@ -39,7 +39,6 @@ END tb_tb_io_ddr; ARCHITECTURE tb OF tb_tb_io_ddr IS - CONSTANT c_sim_model : BOOLEAN := FALSE; CONSTANT c_technology : NATURAL := c_tech_select_default; CONSTANT c_tech_ddr3 : t_c_tech_ddr := c_tech_ddr3_4g_800m_master; CONSTANT c_tech_ddr4 : t_c_tech_ddr := c_tech_ddr4_4g_1600m; @@ -66,11 +65,9 @@ BEGIN -- g_nof_repeat : NATURAL := 1; -- number of stimuli repeats with write flush after each repeat -- g_wr_flush_mode : STRING := "SYN" -- "VAL", "SOP", "SYN" - gen_sim_model: IF c_sim_model=TRUE GENERATE - u_sim_model : ENTITY work.tb_io_ddr GENERIC MAP ( TRUE, c_technology, c_tech_ddr3, c_tech_ddr4, FALSE, FALSE, 5 ns, 4, 2500, 2, 1, 1, 1, "VAL") PORT MAP (tb_end_vec(0)); - END GENERATE; + u_sim_model : ENTITY work.tb_io_ddr GENERIC MAP ( TRUE, c_technology, c_tech_ddr3, c_tech_ddr4, FALSE, FALSE, 5 ns, 4, 2500, 2, 1, 1, 1, "VAL") PORT MAP (tb_end_vec(0)); - gen_ddr3 : IF c_sim_model=FALSE AND c_tech_ddr.name="DDR3" GENERATE + gen_ddr3 : IF c_tech_ddr.name="DDR3" GENERATE u_default : ENTITY work.tb_io_ddr GENERIC MAP (FALSE, c_technology, c_tech_ddr3, c_tech_ddr4, FALSE, FALSE, 5 ns, 4, 2500, 2, 1, 1, 1, "VAL") PORT MAP (tb_end_vec(0)); u_fill_wrfifo_on_next_valid : ENTITY work.tb_io_ddr GENERIC MAP (FALSE, c_technology, c_tech_ddr3, c_tech_ddr4, FALSE, FALSE, 5 ns, 1, 1000, 2, 1, 4, 2, "VAL") PORT MAP (tb_end_vec(1)); @@ -91,7 +88,7 @@ BEGIN END GENERATE; -- Distinghuis between tests for DDR3 and DDR4, because the Quartus 14.1 ip_arria10 DDR4 model simulates about 40x slower than the Quartus 11.1 ip_stratixiv DDR3 uniphy model. - gen_ddr4 : IF c_sim_model=FALSE AND c_tech_ddr.name="DDR4" GENERATE + gen_ddr4 : IF c_tech_ddr.name="DDR4" GENERATE u_default : ENTITY work.tb_io_ddr GENERIC MAP (FALSE, c_technology, c_tech_ddr3, c_tech_ddr4, FALSE, FALSE, 5 ns, 4, 2500, 2, 1, 1, 1, "VAL") PORT MAP (tb_end_vec(0)); END GENERATE; diff --git a/libraries/technology/ddr/sim_ddr.vhd b/libraries/technology/ddr/sim_ddr.vhd index d6fc241af788eb86de9455fa33a7190d12d4aafe..6bc51b873fdd70ea514203df18730cea8620c81f 100644 --- a/libraries/technology/ddr/sim_ddr.vhd +++ b/libraries/technology/ddr/sim_ddr.vhd @@ -20,14 +20,49 @@ -- -------------------------------------------------------------------------------- +-- Author: Eric Kooistra, 19 june 2017 -- Purpose: Functional simulation model of both the DDR driver and the DDR memory. -- Description: --- The component also supports different types of DDR, so DDR3 and DDR4. --- Remark: --- . It is assumed that the user only performs burst reads/writes! --- . 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. +-- 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; @@ -67,20 +102,22 @@ ARCHITECTURE str OF sim_ddr IS -- DDR memory TYPE t_mem_arr IS ARRAY(0 TO c_nof_addr-1) OF STD_LOGIC_VECTOR(c_dat_w-1 DOWNTO 0); - SIGNAL sim_clk : STD_LOGIC; - SIGNAL sim_rst : STD_LOGIC; - - SIGNAL address : NATURAL; - SIGNAL burst_size : NATURAL; - SIGNAL burst_cnt : NATURAL; - SIGNAL waitrequest_n : STD_LOGIC := '1'; - SIGNAL wr_bursting : BOOLEAN := FALSE; - SIGNAL rd_bursting : BOOLEAN := FALSE; - - SIGNAL pending_wr : BOOLEAN := FALSE; - SIGNAL pending_rd : BOOLEAN := FALSE; - SIGNAL pending_address : NATURAL; - SIGNAL pending_burst_size : NATURAL; + 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; + + SIGNAL mem_state : t_mem_state; BEGIN @@ -93,117 +130,95 @@ BEGIN ctlr_miso.done <= '0' , '1' AFTER 1 ns; ctlr_miso.cal_ok <= '0' , '1' AFTER 1 ns; ctlr_miso.cal_fail <= '0'; - - ctlr_miso.waitrequest_n <= waitrequest_n; 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_address : NATURAL; - VARIABLE v_burst_size : NATURAL; - VARIABLE v_burst_cnt : NATURAL := 0; - VARIABLE v_wr_bursting : BOOLEAN := FALSE; - VARIABLE v_rd_bursting : BOOLEAN := FALSE; - VARIABLE v_waitrequest_n : STD_LOGIC := '1'; - + 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 - -- Get state - --v_address := address; - --v_burst_size := burst_size; - --v_burst_cnt := burst_cnt; - --v_waitrequest_n := waitrequest_n; - --v_wr_bursting := wr_bursting; - --v_rd_bursting := rd_bursting; - -- 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; + 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 - pending_wr <= TRUE; - pending_address <= TO_UINT(ctlr_mosi.address); - pending_burst_size <= TO_UINT(ctlr_mosi.burstsize); + 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_waitrequest_n := '0'; - v_address := TO_UINT(ctlr_mosi.address); - v_burst_size := TO_UINT(ctlr_mosi.burstsize); - v_burst_cnt := 0; + 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 - pending_rd <= TRUE; - pending_address <= TO_UINT(ctlr_mosi.address); - pending_burst_size <= TO_UINT(ctlr_mosi.burstsize); + 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 pending_wr=TRUE AND v_rd_bursting=FALSE THEN - pending_wr <= FALSE; + 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 := pending_address; - v_burst_size := pending_burst_size; - v_burst_cnt := 0; + 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 pending_rd=TRUE AND v_rd_bursting=FALSE THEN - pending_rd <= FALSE; + 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 := pending_address; - v_burst_size := pending_burst_size; - v_burst_cnt := 0; - v_waitrequest_n := '0'; + 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; + 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 <= '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; + 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; - v_waitrequest_n := '1'; + IF v.burst_cnt = v.burst_size THEN + v.wr_bursting := FALSE; + v.rd_bursting := FALSE; + ctlr_miso.waitrequest_n <= '1'; END IF; - - -- Show state - --address <= v_address; - --burst_size <= v_burst_size; - --burst_cnt <= v_burst_cnt; - --wr_bursting <= v_wr_bursting; - --rd_bursting <= v_rd_bursting; - - waitrequest_n <= v_waitrequest_n; + + -- Show DDR memory state in wave window + mem_state <= v; END IF; END PROCESS; - + END str;