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