---------------------------------------------------------------------------
-- Copyright (c) 2018, TTTech Computertechnik AG
-- Project        : Protocol Awair HW tester
---------------------------------------------------------------------------
-- File           : tp_error_state_000.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : April 22nd, 2019
---------------------------------------------------------------------------
-- Description    : This test procedure has been created to check basic 
-- 
--      
---------------------------------------------------------------------------

library ieee, std, sim, hw_tester;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use std.textio.all;
   -- Testbench Utils
   use sim.p_report_utils.all;
   use sim.p_generic_bfm.all;
   -- DUT 
   use hw_tester.p_commands.all;
   use hw_tester.p_functions.all;
   use hw_tester.p_parameters_and_constants.all;
   -- Testbench BFMs
   use sim.p_avalon_mem_master_bfm.all;


architecture tp_error_state_000 of test_ent is
   -- test procedure constants
   constant C_TEST_TIMEOUT             : time               := 10 ms;
   constant C_TEST_VERBOSITY           : tVerbosity         := INFO;
   constant C_LOG_NAME                 : string             := "tp_error_state_000";
   constant C_WORD_LENGTH              : natural            := 16;
   constant C_MAX_CMD_LENGTH           : natural            := 16;

   -------------------------------------------------------------------------------------------
   -- types for this test procedure
   subtype  tWordNo    is natural range 0 to C_MAX_CMD_LENGTH - 1;
   subtype  tWord      is std_logic_vector(C_WORD_LENGTH-1 downto 0);
   type     tWordArray is array (natural range <>) of tWord;

   signal   test_finished              : boolean            := false;

   -------------------------------------------------------------------------------------------
   -- Convert status register number to text 
   -------------------------------------------------------------------------------------------
   function status_mon_reg_string( reg_no : natural ) return string is
   begin 
      case reg_no is
         when C_FATAL_ERROR => return "FATAL_ERROR";             
         when C_CORRECT_CRC_PCK_COUNTER => return "CORRECT_CRC_PCK_COUNTER"; 
         when C_SMD_I_ERR_COUNT => return "SMD_I_ERR_COUNT";         
         when C_SMD_C_ERR_COUNT => return "SMD_C_ERR_COUNT";         
         when C_CRC_PACKET_ERR_COUNTER => return "CRC_PACKET_ERR_COUNTER";  
         when C_FRAG_NO_ERR_COUNTER => return "FRAG_NO_ERR_COUNTER";     
         when C_PREAMBLE_LEN_ERR_COUNTER => return "PREAMBLE_LEN_ERR_COUNTER";
         when C_ERROR_FRAME_COUNTER_1 => return "ERROR_FRAME_COUNTER_1";   
         when C_CORRECT_FRAME_COUNTER_1 => return "CORRECT_FRAME_COUNTER_1"; 
         when C_ERROR_FRAME_COUNTER_2 => return "ERROR_FRAME_COUNTER_2";   
         when C_CORRECT_FRAME_COUNTER_2 => return "CORRECT_FRAME_COUNTER_2";
         when others => return "Registr address is not defined.";
      end case;
   end function;

   -------------------------------------------------------------------------------------------
   -- Send n commands to the module
   -------------------------------------------------------------------------------------------
   procedure send_n_commands(
      signal   hHostBfm        : inout tBfmHandle;
      constant commands        : tWordArray(tWordNo);
      constant write_addr      : natural := 0;
      constant cmd_byts        : natural := 15 
   ) is
      variable send_data       : tDataArray(tAddr);
      variable first_byte      : std_logic_vector(C_WORD_LENGTH/2-1 downto 0);
      variable second_byte     : std_logic_vector(C_WORD_LENGTH/2-1 downto 0);
      variable address         : natural       := 0;
      variable reduced_burstcount : natural := 0; 
   begin
      address                  := write_addr;

      -- add commands word into data array 
      for i in 0 to commands'length-1 loop
         first_byte            := commands(i)(7 downto 0);
         second_byte           := commands(i)(15 downto 8);
         send_data(address)    := second_byte;
         address               := address + 1;
         send_data(address)    := first_byte;
         address               := address + 1;

      end loop;

      avalon_mem_master_set_burst(handle      => hHostBfm,
                                  waddr       => write_addr,
                                  burstcount  => cmd_byts,
                                  data        => send_data);

   end procedure;

begin

   avalon_mm_master_generator: process
      variable read_data    : tDataArray(tAddr);
      variable send_commands: tWordArray(tWordNo);
   begin
      wait for 5 us;

      -- -------------------------------------------------------------------------
      -- Initialize Avalon MM master BFM
      -- -------------------------------------------------------------------------
      avalon_mem_master_set_verbosity(hAvalonMemMasterBfm(0), C_TEST_VERBOSITY); 

      report_info(C_LOG_NAME, "Avalon memory master generator set verbosity.");

      dio_out_avst_ready             <= '1'; 

      -- send start command into object generator
      send_commands(0)            := C_GEN_CMD_START;

      -- set pattern generator
      send_commands(1)            := C_GEN_CMD_FRAME_LENGTH;
      send_commands(2)            := X"0401";
      send_commands(3)            := C_GEN_CMD_FRAME_NO;
      send_commands(4)            := X"0004";
      send_commands(5)            := C_GEN_CMD_FRAME_SEED;
      send_commands(6)            := X"00CA";
      send_commands(7)            := C_GEN_CMD_RANDOM_DATA;
      send_commands(8)            := X"0001"; -- set random generator
      send_commands(9)            := C_GEN_CMD_INIT_PATTERN_GEN;
      send_commands(10)           := X"0001"; -- set generator 1

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(0),
                      cmd_byts    => 2*11 - 1, -- word has 2 byts; one command is one word (e.g. 10 commands has 20 bytes)
                      commands    => send_commands);

      wait for 10 us;

      send_commands(1)            := C_GEN_CMD_FRAME_LENGTH;
      send_commands(2)            := X"0401";
      send_commands(3)            := C_GEN_CMD_FRAME_NO;
      send_commands(4)            := X"0006";
      send_commands(5)            := C_GEN_CMD_FRAME_SEED;
      send_commands(6)            := X"0002";
      send_commands(7)            := C_GEN_CMD_LINEAR_DATA;
      send_commands(8)            := X"0001"; -- set random generator
      send_commands(9)            := C_GEN_CMD_INIT_PATTERN_GEN;
      send_commands(10)           := X"0002"; -- set generator 2

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(0),
                      write_addr  => 1,        -- start at address 1
                      cmd_byts    => 2*11 - 1, -- word has 2 byts; one command is one word (e.g. 10 commands has 20 bytes)
                      commands    => send_commands);

      wait for 10 us;

      -- set port generator
      send_commands(0)            := C_GEN_CMD_PRIORITY_INPUT; -- set first pattern to be priority
      send_commands(1)            := X"0001";
      send_commands(2)            := C_GEN_CMD_SET_FRAG;
      send_commands(3)            := X"0001"; 
      send_commands(4)            := C_GEN_CMD_NO_OF_FARGMENT;
      send_commands(5)            := X"0003";
      send_commands(6)            := C_GEN_CMD_FRAME_WAIT_PRIORITY;
      send_commands(7)            := X"01F4"; -- wait for N clk cycles  (X"1F4" = d"500", X"3E8" = d"1000")
      send_commands(8)            := C_GEN_CMD_FRAME_WAIT_FRAG;
      send_commands(9)            := X"03E8"; -- wait for N clk cycles  (X"1F4" = d"500", X"3E8" = d"1000")

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(0),
                      cmd_byts    => 2*10 - 1,
                      commands    => send_commands);

      wait for 10 us;

      send_commands(0)            := C_GEN_CMD_INIT_PORT_GEN;
      send_commands(1)            := X"0001"; 
      send_commands(2)            := C_GEN_CMD_END;

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(0),
                      cmd_byts    => 2*3 - 1,
                      commands    => send_commands);

      wait for 1 ms;
 
      wait;
   end process;

   avalon_mm_master_monitor: process
      variable read_data    : tDataArray(tAddr);
      variable read_addr    : natural;
      variable read_burst_count: natural;
      variable send_commands: tWordArray(tWordNo);
   begin
      wait for 5 us;

      -- -------------------------------------------------------------------------
      -- Initialize Avalon MM master BFM
      -- -------------------------------------------------------------------------
      avalon_mem_master_set_verbosity(hAvalonMemMasterBfm(1), C_TEST_VERBOSITY); 

      report_info(C_LOG_NAME, "Avalon memory master monitor sets verbosity.");

      -- send start command into object monitor
      send_commands(0)            := C_MON_CMD_START;

      -- set pattern monitor
      send_commands(1)            := C_MON_CMD_FRAME_LENGTH;
      send_commands(2)            := X"0301";
      send_commands(3)            := C_MON_CMD_FRAME_NO;
      send_commands(4)            := X"0008";
      send_commands(5)            := C_MON_CMD_FRAME_SEED;
      send_commands(6)            := X"00CA";
      send_commands(7)            := C_MON_CMD_RANDOM_DATA;
      send_commands(8)            := X"0001"; -- set random monitor
      send_commands(9)            := C_MON_CMD_SCOREBOARD_EN;
      send_commands(10)           := X"0001"; -- enable scoreboard
      send_commands(11)           := C_MON_CMD_INIT_PATTERN_MON;
      send_commands(12)           := X"0001"; -- set monitor 1

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(1),
                      cmd_byts    => 2*13 - 1, -- word has 2 byts; one command is one word (e.g. 10 commands has 20 bytes)
                      commands    => send_commands);

      wait for 10 us;

      send_commands(1)            := C_MON_CMD_FRAME_LENGTH;
      send_commands(2)            := X"0405";
      send_commands(3)            := C_MON_CMD_FRAME_NO;
      send_commands(4)            := X"0009";
      send_commands(5)            := C_MON_CMD_FRAME_SEED;
      send_commands(6)            := X"0002";
      send_commands(7)            := C_MON_CMD_LINEAR_DATA;
      send_commands(8)            := X"0001"; -- set random monitor
      send_commands(9)            := C_MON_CMD_SCOREBOARD_EN;
      send_commands(10)           := X"0001"; -- enable scoreboard
      send_commands(11)           := C_MON_CMD_INIT_PATTERN_MON;
      send_commands(12)           := X"0002"; -- set monitor 2

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(1),
                      write_addr  => 1,        -- start at address 1
                      cmd_byts    => 2*13 - 1, -- word has 2 byts; one command is one word (e.g. 10 commands has 20 bytes)
                      commands    => send_commands);

      wait for 10 us;

      -- set port monitor
      send_commands(0)            := C_MON_CMD_PRIORITY_OUTPUT; -- set first pattern to be priority
      send_commands(1)            := X"0001";
      send_commands(2)            := C_MON_CMD_SET_FRAG;
      send_commands(3)            := X"0001"; 
      send_commands(4)            := C_MON_CMD_NO_OF_FARGMENT;
      send_commands(5)            := X"0003";

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(1),
                      cmd_byts    => 2*6 - 1,
                      commands    => send_commands);

      wait for 10 us;

      send_commands(0)            := C_MON_CMD_INIT_PORT_MON;
      send_commands(1)            := X"0001"; 
      send_commands(2)            := C_MON_CMD_END;

      send_n_commands(hHostBfm    => hAvalonMemMasterBfm(1),
                      cmd_byts    => 2*3 - 1,
                      commands    => send_commands);

      report_info(C_LOG_NAME, "Monitor sent all commands.");

      wait for 4 ms;

      -- -------------------------------------------------------------------------
      -- Read status monitor registers
      -- -------------------------------------------------------------------------
      read_addr                   := 1;
      read_burst_count            := 11;

      avalon_mem_master_get_burst(handle      => hAvalonMemMasterBfm(1),
                                  waddr       => read_addr,
                                  burstcount  => read_burst_count,
                                  data        => read_data);

      -- report read data;  write_addr_1 + burst_count_1
      for i in read_addr to (read_addr + read_burst_count - 1) loop
         report_info(C_LOG_NAME, "Monitor status counter, " & status_mon_reg_string(i) & ": " & to_hstring(read_data(i)));
      end loop;
      -- -------------------------------------------------------------------------

      wait for 2 ms;

      test_finished  <= true;
 
      wait;
   end process;

   -- -----------------------------------------------------------------------
   -- Test Finished / Test Timeout Process
   -- -----------------------------------------------------------------------
   test_finished_timeout_p : process
   begin
      -- Wait for test finish or timeout
      wait until test_finished for C_TEST_TIMEOUT;
      -- report result
      if test_finished then
         report_test_end;
      else
         report_info(C_LOG_NAME, "test timeout");
      end if;
      -- Global Stop Request
      tb_stop <= true;
      wait;
   end process;

end;
