--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:   15:48:29 02/27/2014
-- Design Name:   
-- Module Name:   C:/Dropbox/00_Praksa/PCIe_Bus_master_moj/reg_access_tb.vhd
-- Project Name:  PCIe_Bus_master_xil_vhdl
-- Target Device:  
-- Tool versions:  
-- Description:   
-- 
-- VHDL Test Bench Created by ISE for module: reg_access
-- 
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
-- Notes: 
-- This testbench has been automatically generated using types std_logic and
-- std_logic_vector for the ports of the unit under test.  Xilinx recommends
-- that these types always be used for the top-level I/O of a design in order
-- to guarantee that the testbench will bind correctly to the post-implementation 
-- simulation model.
--------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
 
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
--USE ieee.numeric_std.all;
use ieee.std_logic_arith.all;
use ieee.math_real.all;
use std.textio.all;
 
ENTITY reg_access_tb IS
END reg_access_tb;
 
ARCHITECTURE behavior OF reg_access_tb IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    COMPONENT reg_access is
	 generic(	REG_WIDTH:integer:=64
			);
port (
		clk				: in std_logic;
		rst				: in std_logic;
		
	--For RX ENGINE
		data_w			: in std_logic_vector(63 downto 0);
		addr_w			: in std_logic_vector(4 downto 0);
		we_w				: in std_logic;
		addr_error_w	: out std_logic;
		re					: in std_logic; --read enable - da vemo kdaj smo dejansko prebral - zarad clear on read flagov
		
	--For TX ENGINE
		data_r			: out std_logic_vector(63 downto 0);
		addr_r			: in 	std_logic_vector(4 downto 0);
		busy_tx			: out std_logic;
		addr_error_r	: out std_logic;
		
	--BRAM - Comp Core side
		bram_data_r		: out std_logic_vector(15 downto 0);
		bram_addr_r		: in std_logic_vector(5 downto 0);
		bram_data_w		: in std_logic_vector(15 downto 0);
		bram_addr_w		: in std_logic_vector(5 downto 0);
		we_core			: in std_logic;
		busy_r			: out std_logic;
		busy_w			: out std_logic;
		
	--registri
				--read
		work_read_base 	: out std_logic_vector(REG_WIDTH-1 downto 0);
		work_read_count 	: out std_logic_vector(REG_WIDTH-1 downto 0);
		read_pck_len 		: in std_logic_vector(9 downto 0);
		read_f				: out std_logic; --je na 1 dokler mora trajati read(dokler ne preberemo vseh paketkov)
		read_done			: in std_logic; --postavi na 1 za en clock ko se prebere en paketek
		
				--write
		work_write_base 	: out std_logic_vector(REG_WIDTH-1 downto 0);
		work_write_count 	: out std_logic_vector(REG_WIDTH-1 downto 0);
		write_pck_len 		: in std_logic_vector(9 downto 0);
		write_f				: out std_logic;
		write_done			: in std_logic;
		no_more_data		: in std_logic;
		
			--settings
		freq_measure		: out std_logic;
		adaptive				: out std_logic;
		read_start			: out std_logic;		--Ko zapišemo Source read count
		write_start			: out std_logic; 		--Ko zapišemo Destination read count
		
				--interrupt masks
		int_no_data			: out std_logic;
		int_write_finished: out std_logic;
		int_read_finished	: out std_logic
		
		
);
    END COMPONENT;
    

   --Inputs
   signal clk : std_logic := '0';
   signal rst : std_logic := '0';
   signal data_w : std_logic_vector(63 downto 0) := (others => '0');
   signal addr_w : std_logic_vector(4 downto 0) := (others => '0');
   signal we_w : std_logic := '0';
   signal re : std_logic := '0';
   signal addr_r : std_logic_vector(4 downto 0) := (others => '0');
   signal bram_addr_r : std_logic_vector(5 downto 0) := (others => '0');
   signal bram_data_w : std_logic_vector(15 downto 0) := (others => '0');
   signal bram_addr_w : std_logic_vector(5 downto 0) := (others => '0');
   signal we_core : std_logic := '0';
   signal read_pck_len : std_logic_vector(9 downto 0) := (others => '0');
   signal read_done : std_logic := '0';
   signal write_pck_len : std_logic_vector(9 downto 0) := (others => '0');
   signal write_done : std_logic := '0';
   signal no_more_data : std_logic := '0';

 	--Outputs
   signal addr_error_w : std_logic;
   signal data_r : std_logic_vector(63 downto 0);
   signal busy_tx : std_logic;
   signal addr_error_r : std_logic;
   signal bram_data_r : std_logic_vector(15 downto 0);
   signal busy_r : std_logic;
   signal busy_w : std_logic;
   signal work_read_base : std_logic_vector(63 downto 0);
   signal work_read_count : std_logic_vector(63 downto 0);
   signal read_f : std_logic;
   signal work_write_base : std_logic_vector(63 downto 0);
   signal work_write_count : std_logic_vector(63 downto 0);
   signal write_f : std_logic;
   signal freq_measure : std_logic;
   signal adaptive : std_logic;
   signal read_start : std_logic;
   signal write_start : std_logic;

   -- Clock period definitions
   constant clk_period : time := 10 ns;
	
	 file sim_in : text open read_mode is "reg_access.sim";

  signal stop : Boolean := true;
  signal timeout : Boolean := true;
  signal sync : std_logic := '1';
  signal period : integer := 20;
  constant Tu : time := 1 ns;

  signal T : time := 20 ns;
  signal T2 : time := 10 ns;


  --
  --  converts character into std_logic
  function to_std_logic(ch : character) return std_logic is
  begin
    case ch is
      when '0' => return '0';
      when '1' => return '1';
      when 'Z' => return 'Z';
      when 'X' => return 'X';
      when others => return 'U';
    end case;
    return 'U';
  end to_std_logic;


  --
  --  converts a std_logic_vector int hex string
  function hstr(slv : std_logic_vector) return string is
    type vector_ptr is access std_logic_vector;
    variable pslv : vector_ptr;
    variable i, hexlen : integer;
    variable ln : line;
    variable s : std_logic;
    variable fourbit : std_logic_vector(3 downto 0);

  begin
    hexlen := (slv'LENGTH - 1) / 4;
    i := hexlen * 4;
    pslv := new std_logic_vector(i+3 downto 0);
    ln := new string(1 to hexlen+1);
    s := slv(slv'LEFT);
    if s = '1' then s := '0'; end if;
    pslv.all(pslv.all'LEFT downto slv'LENGTH) := (others => s);
    pslv.all(slv'LENGTH-1 downto 0) := slv;
    for j in ln.all'RANGE loop
      fourbit := pslv(i+3 downto i);
      case fourbit is
        when "0000" => ln.all(j) := '0';
        when "0001" => ln.all(j) := '1';
        when "0010" => ln.all(j) := '2';
        when "0011" => ln.all(j) := '3';
        when "0100" => ln.all(j) := '4';
        when "0101" => ln.all(j) := '5';
        when "0110" => ln.all(j) := '6';
        when "0111" => ln.all(j) := '7';
        when "1000" => ln.all(j) := '8';
        when "1001" => ln.all(j) := '9';
        when "1010" => ln.all(j) := 'A';
        when "1011" => ln.all(j) := 'B';
        when "1100" => ln.all(j) := 'C';
        when "1101" => ln.all(j) := 'D';
        when "1110" => ln.all(j) := 'E';
        when "1111" => ln.all(j) := 'F';
        when "ZZZZ" => ln.all(j) := 'z';
        when "UUUU" => ln.all(j) := 'u';
        when "XXXX" => ln.all(j) := 'x';
        when others => ln.all(j) := '?';
      end case;
      i := i-4;
    end loop;
    deallocate(pslv);
    return ln.all;
  end hstr;

  procedure read_vector(variable ln : inout line;
                        variable sig : inout std_logic_vector) is
    type vector_ptr is access std_logic_vector;
    variable i : integer;
    variable ch : character;
    variable str : line;
    variable slv1, slv2 : vector_ptr;
    begin
      read(ln, i);
      if (i > 0) then
        sig := conv_std_logic_vector(i, sig'LENGTH);
        return;
      end if;
      if ln.all'LENGTH < 2 then
        sig := conv_std_logic_vector(0, sig'LENGTH);
        return;
      end if;
      if ln.all(1) /= 'x' and ln.all(1) /= 'X' then return; end if;
      read(ln, ch);
      ch := ln.all(1);
      i := ln.all'LENGTH;
      for k in ln.all'RANGE loop
        ch := ln.all(k);
        if (ch < '0' or ch > '9') and
           (ch < 'A' or ch > 'F') and
           (ch < 'a' or ch > 'f') and
	   (ch /= 'z') and (ch /= 'Z') and
	   (ch /= 'u') and (ch /= 'U') and
	   (ch /= 'X') and (ch /= 'X') then
          i := k-1;
          exit;
        end if;
      end loop;
      slv1 := new std_logic_vector(sig'LENGTH-1 downto 0);
      str := new string(1 to i);
      read(ln, str.all);
      i := i*4;
      slv2 := new std_logic_vector(i-1 downto 0);
      for j in str.all'RANGE loop
        i := i-4;
        case str.all(j) is
          when '0' => slv2.all(i+3 downto i) := "0000";
          when '1' => slv2.all(i+3 downto i) := "0001";
          when '2' => slv2.all(i+3 downto i) := "0010";
          when '3' => slv2.all(i+3 downto i) := "0011";
          when '4' => slv2.all(i+3 downto i) := "0100";
          when '5' => slv2.all(i+3 downto i) := "0101";
          when '6' => slv2.all(i+3 downto i) := "0110";
          when '7' => slv2.all(i+3 downto i) := "0111";
          when '8' => slv2.all(i+3 downto i) := "1000";
          when '9' => slv2.all(i+3 downto i) := "1001";
          when 'A' => slv2.all(i+3 downto i) := "1010";
          when 'B' => slv2.all(i+3 downto i) := "1011";
          when 'C' => slv2.all(i+3 downto i) := "1100";
          when 'D' => slv2.all(i+3 downto i) := "1101";
          when 'E' => slv2.all(i+3 downto i) := "1110";
          when 'F' => slv2.all(i+3 downto i) := "1111";
          when 'a' => slv2.all(i+3 downto i) := "1010";
          when 'b' => slv2.all(i+3 downto i) := "1011";
          when 'c' => slv2.all(i+3 downto i) := "1100";
          when 'd' => slv2.all(i+3 downto i) := "1101";
          when 'e' => slv2.all(i+3 downto i) := "1110";
          when 'f' => slv2.all(i+3 downto i) := "1111";
          when 'z' => slv2.all(i+3 downto i) := "ZZZZ";
          when 'u' => slv2.all(i+3 downto i) := "UUUU";
          when 'x' => slv2.all(i+3 downto i) := "XXXX";
          when others => null;
        end case;
      end loop;
      deallocate(str);
      i := slv1'LEFT;
      if slv2.all'LEFT < i then i := slv2.all'LEFT; end if;
      slv1.all(i downto 0) := slv2.all(i downto 0);
      if slv1.all'LEFT > i then
        if slv1.all(i) = '1' then
          slv1.all(slv1'LEFT downto i+1) := (others => '0');
        else
          slv1.all(slv1'LEFT downto i+1) := (others => slv1.all(i));
        end if;
      end if;
      sig := slv1.all;
      deallocate(slv1);
      deallocate(slv2);
      return;
    end read_vector;


  procedure readreal(variable ln : inout line; variable val : inout real) is
    variable i : integer;
    begin
      read(ln, i);
      val := 0.0;
      if ln.all'LENGTH > 0 then
        if ln.all(1) = '.' and ln.all'LENGTH > 1 then
          read(ln, val);
        end if;
      end if;
      val := val + real(i);
    end readreal;
 
BEGIN
 
	-- Instantiate the Unit Under Test (UUT)
   uut: reg_access PORT MAP (
          clk => clk,
          rst => rst,
          data_w => data_w,
          addr_w => addr_w,
          we_w => we_w,
          addr_error_w => addr_error_w,
          re => re,
          data_r => data_r,
          addr_r => addr_r,
          busy_tx => busy_tx,
          addr_error_r => addr_error_r,
          bram_data_r => bram_data_r,
          bram_addr_r => bram_addr_r,
          bram_data_w => bram_data_w,
          bram_addr_w => bram_addr_w,
          we_core => we_core,
          busy_r => busy_r,
          busy_w => busy_w,
          work_read_base => work_read_base,
          work_read_count => work_read_count,
          read_pck_len => read_pck_len,
          read_f => read_f,
          read_done => read_done,
          work_write_base => work_write_base,
          work_write_count => work_write_count,
          write_pck_len => write_pck_len,
          write_f => write_f,
          write_done => write_done,
          no_more_data => no_more_data,
          freq_measure => freq_measure,
          adaptive => adaptive,
          read_start => read_start,
          write_start => write_start
        );

--   -- Clock process definitions
--   clk_process :process
--   begin
--		clk <= '0';
--		wait for clk_period/2;
--		clk <= '1';
--		wait for clk_period/2;
--   end process;
   gen_clk: Clk <= not(Clk) after T2;
  -- gen_reset: t_nRst <= '0', '1' after 5*T2/2;
  timeout <= false after 500000 ns;

testbench: process (sync, clk)
    variable start : boolean := true;
    variable command : string(8 downto 1);
    variable ln, hex, ln2 : line;
    variable ch : character;
    variable id : integer;
    variable int1, int2 : integer;
    variable arg1, arg2, arg3, arg4 : real;
    variable nxt : integer;
    variable st : integer;
    variable Tm, Sync_delay, delay : time;
    variable PCI_Addr : std_logic_vector(4 downto 0);
    variable PCI_Data : std_logic_vector(63 downto 0);
    variable CPL_Addr : std_logic_vector(31 downto 0);
    variable AC_Sym   : std_logic_vector(7 downto 0);
    variable bar      : std_logic_vector(6 downto 0);
	variable bram_data: std_logic_vector(15 downto 0);
	variable bram_addr: std_logic_vector(5 downto 0);
  begin
    --
    -- Finishing read/write cycles
    --
	read_pck_len<=conv_std_logic_vector(10#512#, 10);
	write_pck_len<=conv_std_logic_vector(10#256#, 10);


    if not endfile(sim_in) then
      if start then
        Tm := T;
        start := false;
      end if;
      if sync = '1' then
        -- view_opb <= '1', '0' after 1 ns;    -- REMOVE
        delay := 0 fs;
        readline(sim_in, ln);
        Command := (others => ' ');
        if ln.all'LENGTH > 0 then
          for i in Command'RANGE loop
            read(ln, Command(i));
            exit when Command(i) = ' ' or ln.all'LENGTH = 0;
          end loop;
        end if;
        case Command is
          when "clock   " =>
            delay := clk'LAST_ACTIVE;
            if delay > 0 fs then
              delay := Tm - clk'LAST_EVENT;
            else
              delay := T2 - delay;
            end if;
            read(ln, nxt);
            Tm := nxt * Tu;
            if clk = '0' then
              T <= Tm;
              T2 <= Tm/2;
            else
              T <= Tm after T2;
              T2 <= Tm/2 after T2;
            end if;
          when "reset   " =>
            readreal(ln, arg1);
            readreal(ln, arg2);
            if arg1 > 0.0 then
              Rst <= '1' after arg1*Tm, '0' after arg2*Tm;
            else
              Rst <= '1', '0' after arg2*Tm;
            end if;
          when "delay   " =>
            readreal(ln, arg1);
            delay := arg1 * Tm;
          when "stop    " =>
            if ln.all'LENGTH > 0 then readreal(ln,arg1); else arg1:=0.0; end if;
            if (arg1 > 0.0) then
              stop <= false after arg1*Tm;
            else
              stop <= false;
            end if;
          when "sync    " =>
            if clk'LAST_VALUE = '0' then
              delay := Tm - clk'LAST_EVENT;
            else
              delay := T2 - clk'LAST_EVENT;
            end if;
          when "PCI_addr" =>
             read_vector(ln, PCI_Addr);
             addr_w <= PCI_Addr;
  			 when "PCI_in  " =>
            read_vector(ln, PCI_Data);
            data_w <= PCI_Data; --FIXME ? 
				delay := 10 ns;
			 when "Pout_adr" => --PCI_out_addr
            read_vector(ln, PCI_Addr);
            addr_r <= PCI_Addr;
			 when "RE      " =>
            read(ln, ch);
            re <= to_std_logic(ch);
          when "RX_SOF  " =>
            read(ln, ch);
            we_w <= to_std_logic(ch);
          when "RX_EOF  " =>
            read(ln, ch);
            we_w <= to_std_logic(ch);
		  when "RD_DONE " => --READ DONE	
            readreal(ln, arg1);
            if arg1 > 0.0 then
              read_done <= '1' , '0' after arg1*Tm;
            else
              read_done <= '1', '0' after Tm;
				end if;
				delay:= 10 ns;
		  when "WR_DONE " =>	--WRITE DONE
            readreal(ln, arg1);
            if arg1 > 0.0 then
              write_done <= '1' , '0' after arg1*Tm;
            else
              write_done <= '1', '0' after Tm;
				end if;
				delay:= 10 ns;
          when "NO_DATA " => --NO DATA
            read(ln, ch);
            no_more_data <= to_std_logic(ch);
			 when "BR_ADR_R" =>	--BRAM ADDR R
            read_vector(ln, bram_addr);
            bram_addr_r <= bram_addr;	
			 when "BR_ADR_W" =>	--BRAM ADDR W
            read_vector(ln, bram_addr);
            bram_addr_w <= bram_addr;	
			 when "BR_DATAW" =>	--BRAM DATA W
            read_vector(ln, bram_data);
            bram_data_w <= std_logic_vector(bram_data);
          when "CORE_WE " =>	--CORE WE
            read(ln, ch);
            we_core <= to_std_logic(ch);				
--          when "SRC_RDY " =>
--            read(ln, ch);
--            t_TRN_rsrc_rdy_n <= to_std_logic(ch);
	  -- this is output in this case
          -- when "DST_RDY " =>
          --   read(ln, ch);
          --   t_TRN_rdst_rdy_n <= to_std_logic(ch);
          -- when "Sym_in  " =>
          --   read_vector(ln, AC_Sym);
          --   t_AC_Sym <= AC_Sym;
--          when "M_busy  " =>
--            read(ln, ch);
--            t_MEM_busy <= to_std_logic(ch);
--          when "RQ_full " =>
--            read(ln, ch);
--            t_REQ_FIFO_Full <= to_std_logic(ch);
--          when "CP_addr " =>
--            read_vector(ln, CPL_Addr);
--            t_CPL_Addr <= CPL_Addr;
--          when "bar     " =>
--            read_vector(ln, bar);
--            t_TRN_rbar_hit_n <= not bar;
          when "read    " =>
            sync_delay := clk'LAST_EVENT;
            if clk'LAST_VALUE = '1' then
              sync_delay := T2 - sync_delay;
            elsif sync_delay > 0 fs then
              sync_delay := T2 - sync_delay;
            else
              sync_delay := 0 fs;
            end if;
	    -- t_WE ...
            -- t_OPB_RNW <= transport '1' after sync_delay + T2/10, '0' after sync_delay + 21*T2/20;
            -- t_OPB_CS <= transport '1' after sync_delay + T2/5, '0' after sync_delay + 11*T2/10;
            delay := sync_delay + Tm;

          when others => null;
        end case;
        sync <= '0';
      else
        if delay > 0 fs then
          sync <= '1' after delay;
        else
          sync <= '1';
        end if;
      end if;
    elsif sync = '1' then
      sync <= '0';

    end if;
  end process;
  
  assert stop report "Simulation completed" severity failure;
  assert timeout report "Simulation timeouted" severity failure;
END;
