From b3de752ef3dd362fffa960ef9c643ba7d3d71f44 Mon Sep 17 00:00:00 2001 From: Erik Kooistra <kooistra@astron.nl> Date: Tue, 20 Jan 2015 14:53:33 +0000 Subject: [PATCH] Improved stimuli using generics and functions to define the write and read block accesses. --- libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd | 217 +++++++++++++++++++------ 1 file changed, 165 insertions(+), 52 deletions(-) diff --git a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd index 331542621c..61d5b04dcc 100644 --- a/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd +++ b/libraries/io/ddr/tb/vhdl/tb_io_ddr.vhd @@ -20,12 +20,9 @@ -- -------------------------------------------------------------------------------- --- This testbench tests the different type of DDR controllers: +-- This testbench tests the different type of DDR controllers. -- --- - uphy_4g_800_master --- - uphy_4g_800_slave --- --- The DUT can be selected, using the g_tech_ddr constants. +-- The DUT can be selected, using the g_technology and g_tech_ddr constants. -- -- Testbench is selftesting: -- @@ -41,53 +38,127 @@ USE common_lib.common_mem_pkg.ALL; USE common_lib.tb_common_pkg.ALL; USE common_lib.tb_common_mem_pkg.ALL; USE dp_lib.dp_stream_pkg.ALL; +USE technology_lib.technology_pkg.ALL; USE technology_lib.technology_select_pkg.ALL; USE tech_ddr_lib.tech_ddr_pkg.ALL; ENTITY tb_io_ddr IS GENERIC ( g_technology : NATURAL := c_tech_select_default; - g_tech_ddr : t_c_tech_ddr := c_tech_ddr3_4g_800m_master; - --g_tech_ddr : t_c_tech_ddr := c_tech_ddr4_4g_1600m; - g_sim : BOOLEAN := TRUE; -- when TRUE use the internal DDR memory model, else use the DDR model in this tb. - g_ctlr_ref_clk_period : TIME := 5 ns; -- 200 MHz - g_dvr_clk_period : TIME := 5 ns; -- 50 ns - g_dp_clk_period : TIME := 5000 ps; -- 200 MHz - g_dp_data_w : NATURAL := 256; -- 32 for mixed width and 256 for equal width FIFO - g_nof_repeat : NATURAL := 2; - g_wr_flush_mode : STRING := "SYN" -- "VAL", "SOP", "SYN" + 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; + g_tb_end : BOOLEAN := TRUE; -- when TRUE then tb_end ends this simulation, else a higher multi-testbench will end the simulation + g_sim : BOOLEAN := TRUE; -- when TRUE use the internal DDR memory model, else use the DDR model in this tb. + g_cross_domain_dvr_ctlr : BOOLEAN := FALSE; -- when TRUE insert clock cross domain logic + g_ctlr_ref_clk_period : TIME := 5000 ps; -- 200 MHz + g_dvr_clk_period : TIME := 5000 ps; -- 50 MHz + g_dp_clk_period : TIME := 5000 ps; -- 200 MHz + g_dp_factor : NATURAL := 4; -- 1 or power of 2, c_dp_data_w = c_ctlr_data_w / g_dp_factor + g_block_len : NATURAL := 5; -- block length for a DDR write access and read back access in number of c_ctlr_data_w words + g_nof_block : NATURAL := 1; -- number of blocks that will be written to DDR and readback from DDR + g_nof_wr_per_block : NATURAL := 6; -- number of write accesses per block + g_nof_rd_per_block : NATURAL := 5; -- number of read accesses per block + g_nof_repeat : NATURAL := 1; -- number of stimuli repeats with write flush after each repeat + g_wr_flush_mode : STRING := "SYN" -- "VAL", "SOP", "SYN" + ); + PORT ( + tb_end : OUT STD_LOGIC ); END ENTITY tb_io_ddr; ARCHITECTURE str of tb_io_ddr IS - CONSTANT c_cross_domain_dvr_ctlr : BOOLEAN := g_ctlr_ref_clk_period/=g_dvr_clk_period; + -- Select DDR3 or DDR4 dependent on the technology + CONSTANT c_tech_ddr : t_c_tech_ddr := func_tech_sel_ddr(g_technology, g_tech_ddr3, g_tech_ddr4); + + CONSTANT c_cross_domain_dvr_ctlr : BOOLEAN := g_cross_domain_dvr_ctlr OR g_ctlr_ref_clk_period/=g_dvr_clk_period; - CONSTANT c_ctlr_address_w : NATURAL := func_tech_ddr_ctlr_address_w(g_tech_ddr); - CONSTANT c_ctlr_data_w : NATURAL := func_tech_ddr_ctlr_data_w(g_tech_ddr); + CONSTANT c_ctlr_address_w : NATURAL := func_tech_ddr_ctlr_address_w(c_tech_ddr); + CONSTANT c_ctlr_data_w : NATURAL := func_tech_ddr_ctlr_data_w(c_tech_ddr); + + CONSTANT c_dp_data_w : NATURAL := c_ctlr_data_w/g_dp_factor; + + -- Typical DDR access stimuli + -- . write block of words in 1 write access and then readback in 4 block read accesses + -- . use appropriate c_len to access across a DDR address column (a_col_w=10) + CONSTANT c_nof_access_per_block : NATURAL := g_nof_wr_per_block + g_nof_rd_per_block; + CONSTANT c_nof_access : NATURAL := g_nof_block * c_nof_access_per_block; - -- DDR access stimuli - -- . write the words in two parts and the read back all these, to verify that 2 block writes can be readback in 1 block read - -- . write 1 + 6 words and then readback the 1+1+2+3 = 7 words, in between also write 0 and read back 0 words (to verify that these are indeed void) - -- . write 1900 words and read back to verify an access can span more than one DDR address column (a_col_w=10). - CONSTANT c_all : NATURAL := 3017; - CONSTANT c_part : NATURAL := c_all/2; - CONSTANT c_rest : NATURAL := c_all-c_part; - CONSTANT c_ctlr_address_lo_arr : t_nat_natural_arr := ( 0, c_part, 0, 1, 2, 1, 1, 1, 2, 3, 5, 1900, 1900); - CONSTANT c_ctlr_nof_address_arr : t_nat_natural_arr := (c_part, c_rest, c_all, 1, 6, 0, 0, 1, 1, 2, 3, 153, 153); - CONSTANT c_ctlr_wr_not_rd_arr : STD_LOGIC_VECTOR := ( '1', '1', '0', '1', '1', '1', '0', '0', '0', '0', '0', '1', '0'); - - CONSTANT c_dp_factor : NATURAL := c_ctlr_data_w/g_dp_data_w; + FUNCTION func_ctlr_address_lo_arr RETURN t_nat_natural_arr IS + CONSTANT c_wr : NATURAL := g_block_len/g_nof_wr_per_block; + CONSTANT c_rd : NATURAL := g_block_len/g_nof_rd_per_block; + VARIABLE v_arr : t_nat_natural_arr(0 TO c_nof_access-1); + BEGIN + FOR R IN 0 TO g_nof_block-1 LOOP + -- Write block in g_nof_wr_per_block accesses + FOR I IN 0 TO g_nof_wr_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+I) := R*g_block_len+I*c_wr; + END LOOP; + -- Read back block in g_nof_rd_per_block accesses + FOR I IN 0 TO g_nof_rd_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+g_nof_wr_per_block+I) := R*g_block_len+I*c_rd; + END LOOP; + END LOOP; + RETURN v_arr; + END; + + FUNCTION func_ctlr_nof_address_arr RETURN t_nat_natural_arr IS + CONSTANT c_wr : NATURAL := g_block_len/g_nof_wr_per_block; + CONSTANT c_rd : NATURAL := g_block_len/g_nof_rd_per_block; + CONSTANT c_wr_last : NATURAL := g_block_len - c_wr*(g_nof_wr_per_block-1); + CONSTANT c_rd_last : NATURAL := g_block_len - c_rd*(g_nof_rd_per_block-1); + VARIABLE v_arr : t_nat_natural_arr(0 TO c_nof_access-1); + BEGIN + FOR R IN 0 TO g_nof_block-1 LOOP + -- Write block in g_nof_wr_per_block accesses + FOR I IN 0 TO g_nof_wr_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+I) := c_wr; + END LOOP; + v_arr(R*c_nof_access_per_block+g_nof_wr_per_block-1) := c_wr_last; + -- Read back block in g_nof_rd_per_block accesses + FOR I IN 0 TO g_nof_rd_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+g_nof_wr_per_block+I) := c_rd; + END LOOP; + v_arr(R*c_nof_access_per_block+g_nof_wr_per_block+g_nof_rd_per_block-1) := c_rd_last; + END LOOP; + RETURN v_arr; + END; + + FUNCTION func_ctlr_wr_not_rd_arr RETURN STD_LOGIC_VECTOR IS + VARIABLE v_arr : STD_LOGIC_VECTOR(0 TO c_nof_access-1); + BEGIN + FOR R IN 0 TO g_nof_block-1 LOOP + -- Write block in g_nof_wr_per_block accesses + FOR I IN 0 TO g_nof_wr_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+I) := '1'; + END LOOP; + -- Read back block in g_nof_rd_per_block accesses + FOR I IN 0 TO g_nof_rd_per_block-1 LOOP + v_arr(R*c_nof_access_per_block+g_nof_wr_per_block+I) := '0'; + END LOOP; + END LOOP; + RETURN v_arr; + END; - CONSTANT c_wr_fifo_depth : NATURAL := 128; - CONSTANT c_rd_fifo_depth : NATURAL := 256; + CONSTANT c_ctlr_address_lo_arr : t_nat_natural_arr(0 TO c_nof_access-1) := func_ctlr_address_lo_arr; + CONSTANT c_ctlr_nof_address_arr : t_nat_natural_arr(0 TO c_nof_access-1) := func_ctlr_nof_address_arr; + CONSTANT c_ctlr_wr_not_rd_arr : STD_LOGIC_VECTOR(0 TO c_nof_access-1) := func_ctlr_wr_not_rd_arr; + + CONSTANT c_wr_fifo_depth : NATURAL := 256; + CONSTANT c_rd_fifo_depth : NATURAL := 256; -- Frame size for sop/eop - CONSTANT c_wr_frame_size : NATURAL := 32; + CONSTANT c_wr_frame_size : NATURAL := 32; -- Sync period - CONSTANT c_wr_sync_period : NATURAL := 512; + CONSTANT c_wr_sync_period : NATURAL := 512; - SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL dbg_c_ctlr_address_lo_arr : t_nat_natural_arr(0 TO c_nof_access-1) := c_ctlr_address_lo_arr; + SIGNAL dbg_c_ctlr_nof_address_arr : t_nat_natural_arr(0 TO c_nof_access-1) := c_ctlr_nof_address_arr; + SIGNAL dbg_c_ctlr_wr_not_rd_arr : STD_LOGIC_VECTOR(0 TO c_nof_access-1) := c_ctlr_wr_not_rd_arr; + SIGNAL dbg_c_tech_ddr : t_c_tech_ddr := c_tech_ddr; + SIGNAL dbg_c_dp_data_w : NATURAL := c_dp_data_w; + + SIGNAL i_tb_end : STD_LOGIC := '0'; SIGNAL ctlr_ref_clk : STD_LOGIC := '0'; SIGNAL ctlr_ref_rst : STD_LOGIC; SIGNAL ctlr_clk : STD_LOGIC; @@ -110,14 +181,19 @@ ARCHITECTURE str of tb_io_ddr IS SIGNAL diag_wr_src_in : t_dp_siso; SIGNAL diag_wr_src_out : t_dp_sosi; - SIGNAL wr_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(c_wr_fifo_depth * c_dp_factor)-1 DOWNTO 0); + SIGNAL wr_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(c_wr_fifo_depth * g_dp_factor)-1 DOWNTO 0); SIGNAL wr_src_out : t_dp_sosi; SIGNAL wr_val_cnt : NATURAL := 0; SIGNAL diag_rd_snk_out : t_dp_siso; SIGNAL diag_rd_snk_in : t_dp_sosi; - SIGNAL rd_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(c_rd_fifo_depth * c_dp_factor)-1 DOWNTO 0); + SIGNAL rd_fifo_usedw : STD_LOGIC_VECTOR(ceil_log2(c_rd_fifo_depth * g_dp_factor)-1 DOWNTO 0); + SIGNAL dbg_wr_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL dbg_wr_val : STD_LOGIC; + SIGNAL dbg_rd_data : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0); + SIGNAL dbg_rd_val : STD_LOGIC; + SIGNAL src_diag_en : STD_LOGIC; SIGNAL src_val_cnt : STD_LOGIC_VECTOR(31 DOWNTO 0); @@ -133,19 +209,21 @@ ARCHITECTURE str of tb_io_ddr IS SIGNAL phy_ou : t_tech_ddr_phy_ou; BEGIN - - ctlr_ref_clk <= NOT ctlr_ref_clk OR tb_end AFTER g_ctlr_ref_clk_period/2; - ctlr_ref_rst <= '1', '0' AFTER 100 ns; + + ctlr_ref_clk <= NOT ctlr_ref_clk OR i_tb_end AFTER g_ctlr_ref_clk_period/2; - dvr_clk <= NOT dvr_clk OR tb_end AFTER g_dvr_clk_period/2; + dvr_clk <= NOT dvr_clk OR i_tb_end AFTER g_dvr_clk_period/2; dvr_rst <= '1', '0' AFTER 100 ns; - dp_clk <= NOT dp_clk OR tb_end AFTER g_dp_clk_period/2; + dp_clk <= NOT dp_clk OR i_tb_end AFTER g_dp_clk_period/2; dp_rst <= '1', '0' AFTER 100 ns; + tb_end <= i_tb_end; + p_stimuli : PROCESS + VARIABLE v_diag_first_rd : BOOLEAN; BEGIN - tb_end <= '0'; + i_tb_end <= '0'; dvr_en <= '0'; dvr_wr_flush_en <= '0'; dvr_wr_not_rd <= '0'; @@ -154,17 +232,23 @@ BEGIN src_diag_en <= '0'; snk_diag_en <= '0'; expected_cnt <= 0; + ctlr_ref_rst <= '1'; + WAIT FOR 100 ns; + ctlr_ref_rst <= '0'; proc_common_wait_until_high(dvr_clk, dvr_done); -- Start diagnostics source for write and sink for verify read + proc_common_wait_some_cycles(dp_clk, 1); src_diag_en <= '1'; snk_diag_en <= '1'; + v_diag_first_rd := TRUE; -- After reset the write FIFO is flushed until the first write access is started, even when dvr_wr_flush_en='0' proc_common_wait_some_cycles(ctlr_clk, 1000); - + FOR R IN 0 TO g_nof_repeat-1 LOOP + proc_common_wait_some_cycles(dvr_clk, 1); FOR I IN c_ctlr_address_lo_arr'RANGE LOOP dvr_start_address <= TO_UVEC(c_ctlr_address_lo_arr(I) , c_ctlr_address_w); dvr_nof_data <= TO_UVEC(c_ctlr_nof_address_arr(I), c_ctlr_address_w); @@ -177,9 +261,15 @@ BEGIN -- ACCESS DONE proc_common_wait_until_lo_hi(dvr_clk, dvr_done); + + -- wait for first valid read data that starts the diagnostics + IF v_diag_first_rd=TRUE AND c_ctlr_wr_not_rd_arr(I)='0' THEN + v_diag_first_rd := FALSE; + proc_common_wait_some_cycles(dp_clk, 100); -- wait for somewhat longer than the dvr_en read latency + END IF; IF c_ctlr_wr_not_rd_arr(I)='0' THEN - expected_cnt <= expected_cnt + c_ctlr_nof_address_arr(I)*c_dp_factor; + expected_cnt <= expected_cnt + c_ctlr_nof_address_arr(I)*g_dp_factor; ASSERT snk_diag_res_val = '1' REPORT "[ERROR] DIAG_RES INVALID!" SEVERITY FAILURE; ASSERT snk_diag_res = '0' REPORT "[ERROR] NON-ZERO DIAG_RES!" SEVERITY FAILURE; @@ -192,30 +282,47 @@ BEGIN snk_diag_en <= '0'; -- Flush the wr fifo + proc_common_wait_some_cycles(dvr_clk, 1); dvr_wr_flush_en <= '1'; proc_common_wait_some_cycles(dvr_clk, 1); dvr_wr_flush_en <= '0'; -- Wait until the wr fifo has been flushed and the rd fifo has been read empty - proc_common_wait_some_cycles(ctlr_clk, 500*c_dp_factor); - ASSERT UNSIGNED(wr_fifo_usedw) < c_dp_factor REPORT "[ERROR] Write FIFO is flushed but not empty!" SEVERITY FAILURE; + proc_common_wait_some_cycles(ctlr_clk, c_tech_ddr.command_queue_depth*c_tech_ddr.maxburstsize); -- rd FIFO may still get filled some more + proc_common_wait_some_cycles(ctlr_clk, largest(TO_UINT(wr_fifo_usedw)/g_dp_factor, TO_UINT(rd_fifo_usedw))); + proc_common_wait_some_cycles(ctlr_clk, 10); -- some extra margin + + ASSERT UNSIGNED(wr_fifo_usedw) < g_dp_factor REPORT "[ERROR] Write FIFO is flushed but not empty!" SEVERITY FAILURE; ASSERT UNSIGNED(rd_fifo_usedw) = 0 REPORT "[ERROR] Read FIFO is not empty!" SEVERITY FAILURE; ASSERT UNSIGNED(snk_val_cnt) = expected_cnt REPORT "[ERROR] Unexpected number of read data!" SEVERITY FAILURE; -- Restart diagnostics source and sink + proc_common_wait_some_cycles(dp_clk, 1); src_diag_en <= '1'; snk_diag_en <= '1'; + v_diag_first_rd := TRUE; END LOOP; + -- If the test failed then it would have stopped already, so it the test has passed REPORT "[OK] Test passed." SEVERITY NOTE; - tb_end <= '1'; + -- Stop the simulation + -- . Stopping the clocks via tb_end does end the tb for the DDR3 IP, but is not sufficient to stop the tb for the DDR4 IP. + -- . Making ctlr_ref_rst <= '1'; also does not stop the tb with the DDR4 IP (apparently some loop remains running in the DDR4 model), so therefore force simulation stop + i_tb_end <= '1'; + + ctlr_ref_rst <= '1'; + IF g_tb_end=FALSE THEN + REPORT "Tb Simulation finished." SEVERITY NOTE; + ELSE + REPORT "Tb Simulation finished." SEVERITY FAILURE; + END IF; WAIT; END PROCESS; u_diagnostics: ENTITY diagnostics_lib.diagnostics GENERIC MAP ( - g_dat_w => g_dp_data_w, + g_dat_w => c_dp_data_w, g_nof_streams => 1 ) PORT MAP ( @@ -237,6 +344,11 @@ BEGIN src_val_cnt(0) => src_val_cnt ); + dbg_wr_data <= diag_wr_src_out.data(c_dp_data_w-1 DOWNTO 0); + dbg_wr_val <= diag_wr_src_out.valid; + dbg_rd_data <= diag_rd_snk_in.data(c_dp_data_w-1 DOWNTO 0); + dbg_rd_val <= diag_rd_snk_in.valid; + wr_val_cnt <= wr_val_cnt + 1 WHEN rising_edge(dp_clk) AND diag_wr_src_out.valid='1'; p_sop_eop : PROCESS (diag_wr_src_out, wr_val_cnt) @@ -274,13 +386,14 @@ BEGIN u_io_ddr: ENTITY work.io_ddr GENERIC MAP( g_technology => g_technology, - g_tech_ddr => g_tech_ddr, + g_tech_ddr => c_tech_ddr, g_sim => g_sim, -- when TRUE use internal DDR memory model g_cross_domain_dvr_ctlr => c_cross_domain_dvr_ctlr, - g_wr_data_w => g_dp_data_w, + g_cross_domain_delay_len => c_meta_delay_len, + g_wr_data_w => c_dp_data_w, g_wr_fifo_depth => c_wr_fifo_depth, -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO. g_rd_fifo_depth => c_rd_fifo_depth, -- >=16 AND >g_tech_ddr.maxburstsize, defined at DDR side of the FIFO. - g_rd_data_w => g_dp_data_w, + g_rd_data_w => c_dp_data_w, g_wr_flush_mode => g_wr_flush_mode, g_wr_flush_use_channel => FALSE, g_wr_flush_start_channel => 0, @@ -330,7 +443,7 @@ BEGIN external_ddr_memory_model : IF g_sim=FALSE GENERATE u_tech_ddr_memory_model : ENTITY tech_ddr_lib.tech_ddr_memory_model GENERIC MAP ( - g_tech_ddr => g_tech_ddr + g_tech_ddr => c_tech_ddr ) PORT MAP ( mem_in => phy_ou, -- GitLab