Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
tb_ddrctrl.vhd 13.98 KiB

-------------------------------------------------------------------------------
--
-- Copyright 2022
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------
-- Author: Job van Wee
-- Purpose: Self checking and self-stopping tb for ddrctrl.vhd
-- Usage:
-- > run -a

LIBRARY IEEE, common_lib, technology_lib, tech_ddr_lib, dp_lib;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE IEEE.MATH_REAL.ALL;
USE technology_lib.technology_pkg.ALL;
USE tech_ddr_lib.tech_ddr_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE common_lib.common_pkg.ALL;
USE technology_lib.technology_select_pkg.ALL;


ENTITY tb_ddrctrl IS
  GENERIC (

    g_tech_ddr                : t_c_tech_ddr                                            := c_tech_ddr4_8g_1600m;                    -- type of memory
    g_nof_streams             : POSITIVE                                                := 12;                                      -- number of input streams
    g_data_w                  : NATURAL                                                 := 14;                                      -- data with of input data vectors
    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;
    g_stop_percentage         : NATURAL                                                 := 80;                                      -- percentage there needs to be already written in the ddr memory when a stop gets triggered
    g_block_size              : NATURAL                                                 := 1024                                     -- amount of samples that goes into one bsn
  );
END tb_ddrctrl;

ARCHITECTURE tb OF tb_ddrctrl IS

  CONSTANT c_sim_model          : BOOLEAN                                               := TRUE;                                    -- determens if this is a simulation

  -- Select DDR3 or DDR4 dependent on the technology and sim model
  CONSTANT c_mem_ddr            : t_c_tech_ddr                                          := func_tech_sel_ddr(g_technology, g_tech_ddr3, g_tech_ddr4);
  CONSTANT c_sim_ddr            : t_c_tech_ddr                                          := func_tech_sel_ddr(g_technology, c_tech_ddr3_sim_16k, c_tech_ddr4_sim_16k);
  CONSTANT c_tech_ddr           : t_c_tech_ddr                                          := func_tech_sel_ddr(c_sim_model, c_sim_ddr, c_mem_ddr);

  -- constants for readability
  CONSTANT  c_ctrl_data_w       : NATURAL                                               := func_tech_ddr_ctlr_data_w(c_tech_ddr);   -- 576
  CONSTANT  c_in_data_w         : NATURAL                                               := g_nof_streams*g_data_w;                -- output data with, 168

  -- constants for testbench
  CONSTANT  c_clk_freq          : NATURAL                                               := 200;                                     -- clock frequency in MHz
  CONSTANT  c_clk_period        : TIME                                                  := (10**6/c_clk_freq)*1 ps;                 -- clock priod, 5 ns
  CONSTANT  c_mm_clk_freq       : NATURAL                                               := 100;                                     -- mm clock frequency in MHz
  CONSTANT  c_mm_clk_period     : TIME                                                  := (10**6/c_mm_clk_freq)*1 ps;              -- mm clock period, 10 ns
  -- constant for checking output data
  CONSTANT  c_adr_w             : NATURAL                                               := func_tech_ddr_ctlr_address_w(c_tech_ddr);                          -- the lengt of the address vector, for simulation this is smaller, otherwise the simulation would take to long, 27
  CONSTANT  c_max_adr           : NATURAL                                               := 2**(c_adr_w)-1;                                                    -- the maximal address that is possible within the vector length of the address
  CONSTANT  c_output_stop_adr   : NATURAL                                               := (c_max_adr+1)-((((c_max_adr+1)/64)*g_stop_percentage/100)*64);
  CONSTANT  c_output_ds         : NATURAL                                               := 144;
  CONSTANT  c_bim               : NATURAL                                               := (c_max_adr*c_ctrl_data_w)/(g_block_size*g_nof_streams*g_data_w);   -- the amount of whole blocks that fit in memory.
  CONSTANT  c_adr_per_b         : NATURAL                                               := ((g_block_size*g_nof_streams*g_data_w)/c_ctrl_data_w)+1;           -- rounding error removes the amount of extra addresses.

  -- the amount of addresses used
  CONSTANT  c_nof_adr           : NATURAL                                               := (c_bim*g_block_size*g_nof_streams*g_data_w)/c_ctrl_data_w;         -- rounding error removes the amount of extra addresses.

  -- the amount of overflow after one block is written
  CONSTANT  c_of_pb             : NATURAL                                               := (g_block_size*g_nof_streams*g_data_w)-(((g_block_size*g_nof_streams*g_data_w)/c_ctrl_data_w)*c_ctrl_data_w); -- amount of overflow after one block is written to memory

  FUNCTION c_of_after_nof_adr_init RETURN NATURAL IS
    VARIABLE temp               : NATURAL                                               := 0;
  BEGIN
    FOR I IN 0 TO c_bim-1 LOOP
      IF temp+c_of_pb < c_ctrl_data_w THEN
        temp := temp+c_of_pb;
      ELSE
        temp := temp+c_of_pb-c_ctrl_data_w;
      END IF;
    END LOOP;
    RETURN temp;
  END FUNCTION c_of_after_nof_adr_init;

  -- the amount of overflow into the address: c_nof_adr     NOT YET USED DELETE AFTER L2SDP-706
  CONSTANT  c_of_after_nof_adr  : NATURAL                                               := c_of_after_nof_adr_init;

  -- function for making total data vector
  FUNCTION  c_total_vector_init RETURN STD_LOGIC_VECTOR IS
    VARIABLE temp               : STD_LOGIC_VECTOR(g_data_w*g_nof_streams*c_bim*g_block_size-1 DOWNTO 0);
    VARIABLE conv               : STD_LOGIC_VECTOR(32-1 DOWNTO 0) := (OTHERS => '0');                                                                  -- removes a warning
  BEGIN
    FOR I IN 0 TO c_bim*g_block_size-1 LOOP
      conv                                     := TO_UVEC(I, 32);
      FOR J IN 0 TO g_nof_streams-1 LOOP
        temp(g_data_w*((I*g_nof_streams)+J+1)-1 DOWNTO g_data_w*((I*g_nof_streams)+j)) := conv(g_data_w-1 DOWNTO 0);
      END LOOP;
    END LOOP;
    RETURN temp;
  END FUNCTION c_total_vector_init;

  -- constant for running the test
  CONSTANT  c_total_vector    : STD_LOGIC_VECTOR(g_data_w*g_nof_streams*c_bim*g_block_size-1 DOWNTO 0) := c_total_vector_init;                     -- vector which contains all input data vectors to make it easy to fill ctr_vector

  SIGNAL    c_total_vector_length : NATURAL := c_total_vector'length;

  CONSTANT  c_check           : NATURAL                                               := 32;
  CONSTANT  c_check_bottom    : NATURAL                                               := 5;
  CONSTANT  c_ones            : STD_LOGIC_VECTOR(c_check-c_check_bottom-1 DOWNTO 0)   := (OTHERS => '1');


  -- input signals for ddrctrl.vhd
  SIGNAL    clk               : STD_LOGIC                                             := '1';
  SIGNAL    rst               : STD_LOGIC                                             := '0';
  SIGNAL    mm_clk            : STD_LOGIC                                             := '0';
  SIGNAL    mm_rst            : STD_LOGIC                                             := '0';
  SIGNAL    in_sosi_arr       : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0)               := (OTHERS => c_dp_sosi_init);              -- input data signal for ddrctrl_pack.vhd 
  SIGNAL    stop_in           : STD_LOGIC                                             := '0';
  SIGNAL    out_sosi_arr      : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0)               := (OTHERS => c_dp_sosi_init);
  SIGNAL    out_siso          : t_dp_siso;

  -- testbench signal
  SIGNAL    tb_end            : STD_LOGIC                                             := '0';                                     -- signal to turn the testbench off

  -- signals for running test
  SIGNAL    in_data_cnt       : NATURAL                                               := 0;                                       -- signal which contains the amount of times there has been input data for ddrctrl_repack.vhd
  SIGNAL    test_running      : STD_LOGIC                                             := '0';                                     -- signal to tell wheter the testing has started
  SIGNAL    bsn_cnt           : NATURAL                                               := g_block_size-1;


  -- signals for checking the output data
  SIGNAL    output_data_cnt   : NATURAL                                               := 0;

  -- PHY
  SIGNAL    phy3_io           : t_tech_ddr3_phy_io;
  SIGNAL    phy3_ou           : t_tech_ddr3_phy_ou;
  SIGNAL    phy4_io           : t_tech_ddr4_phy_io;
  SIGNAL    phy4_ou           : t_tech_ddr4_phy_ou;


BEGIN

  -- generating clock
  clk               <= NOT clk OR tb_end AFTER c_clk_period/2;
  mm_clk            <= NOT mm_clk OR tb_end AFTER c_mm_clk_period/2;
  out_siso.ready    <= '1';
  out_siso.xon      <= '1';

  -- excecuting test
  p_test : PROCESS



  BEGIN

    -- start the test
    tb_end                <= '0';
    in_sosi_arr(0).valid  <= '0';
    WAIT UNTIL rising_edge(clk);                                                                                                  -- align to rising edge
    WAIT FOR c_clk_period*4;
    rst <= '1';
    mm_rst <= '1';
    WAIT FOR c_clk_period*1;
    rst <= '0';
    mm_rst <= '0';
    test_running <= '1';
    WAIT FOR c_clk_period*1;

    -- wr fifo has delay of 4 clockcylces after reset

    -- filling the input data vectors with the corresponding numbers
    run_multiple_times : FOR K in 0 TO 3 LOOP
      make_data : FOR J IN 0 TO c_bim*g_block_size-1 LOOP
        in_data_cnt     <= in_data_cnt+1;
        fill_in_sosi_arr : FOR I IN 0 TO g_nof_streams-1 LOOP
          IF bsn_cnt = g_block_size-1 THEN
            IF I = 0 THEN
              bsn_cnt <= 0;
            END IF;
            in_sosi_arr(I).sop <= '1';
            in_sosi_arr(I).eop <= '0';
            in_sosi_arr(I).bsn <= INCR_UVEC(in_sosi_arr(I).bsn, 1);
          ELSE
            IF I = 0 THEN
              bsn_cnt <= bsn_cnt + 1;
            END IF;
            in_sosi_arr(I).sop <= '0';
          END IF;
          IF bsn_cnt = g_block_size-2 THEN
            in_sosi_arr(I).eop <= '1';
          END IF;
          in_sosi_arr(I).data(g_data_w-1 DOWNTO 0)   <= c_total_vector(g_data_w*(I+1)+J*c_in_data_w-1 DOWNTO g_data_w*I+J*c_in_data_w);
          in_sosi_arr(I).valid <= '1';
        END LOOP;
        IF K = 0 AND J = c_bim*g_block_size-1 THEN
          stop_in <= '1';
        ELSE
          stop_in <= '0';
        END IF;
        WAIT FOR c_clk_period*1;
      END LOOP;
    END LOOP;


    in_sosi_arr(0).valid <= '0';
    test_running      <= '0';


    -- stopping the testbench
    WAIT FOR c_clk_period*g_block_size;
    tb_end <= '1';
    ASSERT FALSE                                                                                                                          REPORT "Test: OK"                                                                                                                             SEVERITY FAILURE;
  END PROCESS;

  p_checking_output_data : PROCESS  -- first do tickets L2SDP-708 and L2SDP-707 before finsishing this is worth time
  BEGIN
    WAIT UNTIL rising_edge(clk);
    IF out_sosi_arr(0).valid = '1' THEN
      FOR I IN 0 TO g_nof_streams-1 LOOP
        IF c_output_stop_adr+output_data_cnt <= c_max_adr THEN
          --ASSERT out_sosi_arr(I).data(c_in_data_w-1 DOWNTO 0) = c_total_vector(g_data_w*(I+1)+(c_output_stop_adr+output_data_cnt)*c_ctrl_data_w-1 DOWNTO g_data_w*I+(c_output_stop_adr+output_data_cnt)*c_ctrl_data_w) REPORT "wrong output data at: " & NATURAL'image(c_output_stop_adr+output_data_cnt) SEVERITY ERROR;
        ELSE
          --ASSERT out_sosi_arr(I).data(c_in_data_w-1 DOWNTO 0) = c_total_vector(g_data_w*(I+1)+(c_output_stop_adr+output_data_cnt-c_max_adr)*c_ctrl_data_w-1 DOWNTO g_data_w*I+(c_output_stop_adr+output_data_cnt-c_max_adr)*c_ctrl_data_w) REPORT "wrong output data at: " & NATURAL'image(c_output_stop_adr+output_data_cnt) SEVERITY ERROR;
        END IF;
      END LOOP;
      output_data_cnt <= output_data_cnt+1;
    END IF;
    WAIT FOR c_clk_period*1;
  END PROCESS;



  -- DUT
  u_ddrctrl : ENTITY work.ddrctrl
  GENERIC MAP (
    g_tech_ddr        => c_tech_ddr,
    g_sim_model       => c_sim_model,
    g_technology      => g_technology,
    g_nof_streams     => g_nof_streams,
    g_data_w          => g_data_w,
    g_stop_percentage => g_stop_percentage,
    g_block_size      => g_block_size
  )
  PORT MAP (
    clk               => clk,
    rst               => rst,
    mm_clk            => mm_clk,
    mm_rst            => mm_rst,
    in_sosi_arr       => in_sosi_arr,
    stop_in           => stop_in,
    out_sosi_arr      => out_sosi_arr,
    out_siso          => out_siso,

    --PHY
    phy3_io           => phy3_io,
    phy3_ou           => phy3_ou,
    phy4_io           => phy4_io,
    phy4_ou           => phy4_ou
  );

END tb;