--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:   12:31:47 06/20/2012
-- Design Name:   
-- Module Name:   /media/BACKUP/CERN/contrib/ohwr/conv-ttl-blo/hdl/spi_master_multifield/test/spi_master_core_tb.vhd
-- Project Name:  spi_master_multifield
-- Target Device:  
-- Tool versions:  
-- Description:   
-- 
-- VHDL Test Bench Created by ISE for module: spi_master_core
-- 
-- 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;
library work;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.spi_master_pkg.ALL;
 
entity spi_master_core_tb is
end spi_master_core_tb;
 
architecture behavior of spi_master_core_tb is 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    component spi_master_core
       port(
              rst_i         : in  STD_LOGIC;
              clk_i         : in  STD_LOGIC;

              inst_i        : in  STD_LOGIC_VECTOR (8*c_INST_LENGTH - 1 downto 0);
              addr_i        : in  STD_LOGIC_VECTOR (8*c_ADDR_LENGTH - 1 downto 0);
              data_i        : in  STD_LOGIC_VECTOR (8*c_DATA_LENGTH - 1 downto 0);

              SPI0_i        : in  STD_LOGIC_VECTOR (31 downto 0);
              SPI1_i        : in  STD_LOGIC_VECTOR (31 downto 0);
              SPI2_o        : out STD_LOGIC_VECTOR (15 downto 0);

              spi_mosi_o    : out STD_LOGIC;
              spi_miso_i    : in  STD_LOGIC;
              spi_clk_o     : out STD_LOGIC;
              spi_cs_n_o    : out STD_LOGIC
        );
    end component;
    

   --Inputs
   signal rst_i      : STD_LOGIC := '0';
   signal clk_i      : STD_LOGIC := '0';
   signal inst_i     : STD_LOGIC_VECTOR (7    downto 0) := (others => '0');
   signal addr_i     : STD_LOGIC_VECTOR (23   downto 0) := (others => '0');
   signal data_i     : STD_LOGIC_VECTOR (2047 downto 0) := (others => '0');
   signal inst_check : STD_LOGIC_VECTOR (7    downto 0) := (others => '0');
   signal addr_check : STD_LOGIC_VECTOR (23   downto 0) := (others => '0');
   signal data_check : STD_LOGIC_VECTOR (2047 downto 0) := (others => '0');
   signal SPI0_i     : STD_LOGIC_VECTOR (31   downto 0) := (others => '0');
   signal SPI1_i     : STD_LOGIC_VECTOR (31   downto 0) := (others => '0');
   signal SPI2_o     : STD_LOGIC_VECTOR (15   downto 0);
   signal s_SPI0     : r_SPI0; 
   signal s_SPI1     : r_SPI1; 
   signal s_SPI2     : r_SPI2; 
   signal spi_miso_i : STD_LOGIC := '0';

   --Outputs
   signal spi_mosi_o         : STD_LOGIC;
   signal spi_clk_o          : STD_LOGIC;
   signal spi_cs_n_o         : STD_LOGIC;

   signal s_spi_count        : NATURAL;
   signal s_rst_spi_analyser : STD_LOGIC;

   signal s_end_inst_flag    : STD_LOGIC;
   signal s_end_addr_flag    : STD_LOGIC;
   signal s_end_data_flag    : STD_LOGIC;


   -- Clock period definitions
   constant c_WISHBONE_PERIOD : TIME := 10 ns;

--   file file_handler    : TEXT;
--   constant log_path    : STRING := "../test/log/spi_master_core_tb.log";
--   constant sep         : CHARACTER := ht;

begin

   SPI0_i   <= f_STD_LOGIC_VECTOR(s_SPI0); 
   SPI1_i   <= f_STD_LOGIC_VECTOR(s_SPI1); 
   s_SPI2   <= f_SPI2(SPI2_o);

   -- Instantiate the Unit Under Test (UUT)
   uut: spi_master_core port map (
          rst_i      => rst_i,
          clk_i      => clk_i,
          inst_i     => inst_i,
          addr_i     => addr_i,
          data_i     => data_i,
          SPI0_i     => SPI0_i,
          SPI1_i     => SPI1_i,
          SPI2_o     => SPI2_o,
          spi_mosi_o => spi_mosi_o,
          spi_miso_i => spi_miso_i,
          spi_clk_o  => spi_clk_o,
          spi_cs_n_o => spi_cs_n_o
        );


   -- Clock process definitions
   clk_i_process :process
   begin
      clk_i <= '0';
         wait for c_WISHBONE_PERIOD/2;
      clk_i <= '1';
         wait for c_WISHBONE_PERIOD/2;
   end process;

   spi_analyser: process(spi_clk_o, s_rst_spi_analyser)
         variable v_inst_length    : NATURAL;
         variable v_addr_length    : NATURAL;
         variable v_data_length    : NATURAL;

      procedure analyze_SPI_bit is
      begin
         --! This is the equivalent to the HW crop in spi_master_core
         v_inst_length     := f_min(to_integer(s_SPI0.BINST), c_INST_LENGTH)*8;
         v_addr_length     := f_min(to_integer(s_SPI0.BADDR), c_ADDR_LENGTH)*8;
         v_data_length     := f_min(to_integer(s_SPI0.BDATA), c_DATA_LENGTH)*8;
         s_spi_count       <= s_spi_count + 1;
         s_end_inst_flag   <= '0';
         s_end_addr_flag   <= '0';
         s_end_data_flag   <= '0';
 
         if    s_SPI1.SEND_INST = '1' then
            if s_spi_count < v_inst_length then
               inst_check(v_inst_length -  1  - s_spi_count) <= spi_mosi_o;
               if s_spi_count = (v_inst_length - 1) then
                  if s_end_inst_flag = '0' then
                     s_end_inst_flag <= '1';
                  end if;
               end if;
            else
            end if;
         else
         end if;
 
         if s_SPI1.SEND_ADDR = '1' then
            if s_SPI1.SEND_INST = '1' then
               if    s_spi_count >=  v_inst_length 
                 and s_spi_count <  (v_inst_length + v_addr_length) then
                  addr_check(v_addr_length - 1 - (s_spi_count -
                             v_inst_length))     <= spi_mosi_o;
                  if s_spi_count = (v_inst_length + v_addr_length - 1) then
                     if s_end_addr_flag = '0' then
                        s_end_addr_flag <= '1';
                     end if;
                  end if;
               else
               end if;
            else
               if    s_spi_count <  v_addr_length then
                  addr_check(v_addr_length - 1 - s_spi_count) <= spi_mosi_o;
                  if s_spi_count = (v_addr_length - 1) then
                    if s_end_addr_flag = '0' then
                       s_end_addr_flag <= '1';
                    end if;
                  end if;
              else
               end if;
            end if;
         else
         end if;
 
         if s_SPI1.SEND_DATA = '1' then
            if s_SPI1.SEND_INST = '1' then
                if s_SPI1.SEND_ADDR = '1' then
                    if  s_spi_count >= (v_inst_length + v_addr_length) then
                       data_check(v_data_length - 1 - (s_spi_count - v_inst_length
                                                       - v_addr_length)) <= spi_mosi_o;
                       if s_spi_count = (v_inst_length + v_addr_length + v_data_length - 1) then
                         if s_end_data_flag = '0' then
                            s_end_data_flag <= '1';
                         end if;
                       end if;
                    else
                    end if;
                else
                   if s_spi_count >= v_inst_length then
                      data_check(v_data_length - 1 - (s_spi_count -
                                                      v_inst_length))  <= spi_mosi_o;
                      if s_spi_count = (v_inst_length + v_data_length - 1) then
                        if s_end_data_flag = '0' then
                           s_end_data_flag <= '1';
                        end if;
                      end if;
                   else
                   end if;
                end if;
            else
               if s_SPI1.SEND_ADDR = '1' then
                  if s_spi_count >= v_addr_length then
                     data_check(v_data_length - 1 - (s_spi_count -
                                                     v_addr_length))   <= spi_mosi_o;
                     if s_spi_count = (v_addr_length + v_data_length - 1) then
                       if s_end_data_flag = '0' then
                          s_end_data_flag <= '1';
                       end if;
                     end if;
                  else
                  end if;
               else
                  if s_spi_count = (v_data_length - 1) then
                     if s_end_data_flag = '0' then
                        s_end_data_flag <= '1';
                     end if;
                  end if;
                  data_check(s_spi_count)                      <= spi_mosi_o;
               end if;
            end if;
         else
         end if;
      end procedure;


   begin
      if s_rst_spi_analyser = '1' then
         s_spi_count <= 0;
         inst_check  <= (others => '0') ; 
         addr_check  <= (others => '0') ; 
         data_check  <= (others => '0') ;  
      else
         if rising_edge(spi_clk_o) then
            if (s_SPI0.CPOL xor s_SPI0.CPHA) = '0' then
               analyze_SPI_bit;
            end if;
         elsif falling_edge(spi_clk_o) then
            if (s_SPI0.CPOL xor s_SPI0.CPHA) = '1' then
               analyze_SPI_bit;
            end if;
         else
         end if;

      end if;
   end process;


   -- Stimulus process
   stim_proc: process


      procedure init_cond is
      begin
         inst_i <= (others => '0'); 
         addr_i <= (others => '0'); 
         data_i <= (others => '0'); 
         s_SPI0 <= c_SPI0_default; 
         s_SPI1 <= c_SPI1_default; 
      end procedure;

      procedure set_FIFO_contents(inst: STD_LOGIC_VECTOR(8*c_INST_LENGTH - 1 downto 0);
                                  addr: STD_LOGIC_VECTOR(8*c_ADDR_LENGTH - 1 downto 0);
                                  data: STD_LOGIC_VECTOR(8*c_DATA_LENGTH - 1 downto 0)) is
      begin
         inst_i <= inst;
         addr_i <= addr;
         data_i <= data;
            wait until rising_edge(clk_i);
         s_SPI1.PUSH_INST <= '1';
         s_SPI1.PUSH_ADDR <= '1';
         s_SPI1.PUSH_DATA <= '1';
            wait until rising_edge(clk_i);
         s_SPI1.PUSH_INST <= '0';
         s_SPI1.PUSH_ADDR <= '0';
         s_SPI1.PUSH_DATA <= '0';
      end procedure;

      procedure set_SPI_mode (cpol: STD_LOGIC;
                              cpha: STD_LOGIC ) is
      begin
         s_SPI0.CPOL   <= cpol;
         s_SPI0.CPHA   <= cpha;
      end procedure;

      procedure set_spi_lengths(inst_length: NATURAL;
                                addr_length: NATURAL;
                                data_length: NATURAL) is
      begin
         s_SPI0.BDATA  <= to_unsigned(data_length, s_SPI0.BDATA'length);
         s_SPI0.BADDR  <= to_unsigned(addr_length, s_SPI0.BADDR'length);
         s_SPI0.BINST  <= to_unsigned(inst_length, s_SPI0.BINST'length);
      end procedure;

      procedure send_SPI_and_wait(send_inst: STD_LOGIC;
                                  send_addr: STD_LOGIC;
                                  send_data: STD_LOGIC) is
      begin
         s_SPI1.SEND_INST <= '0';
         s_SPI1.SEND_ADDR <= '0';
         s_SPI1.SEND_DATA <= '0';
            wait until rising_edge(clk_i);
         if send_inst = '1' then
            s_SPI1.SEND_INST <= '1';
         end if;
         if send_addr = '1' then
            s_SPI1.SEND_ADDR <= '1';
         end if;
         if send_data = '1' then
            s_SPI1.SEND_DATA <= '1';
         end if;
         s_SPI1.SEND_OP   <= '1';
            wait until s_SPI2.SENT_OP = '1';
         s_SPI1.SEND_OP   <= '0';
         s_SPI1.SEND_INST <= '0';
         s_SPI1.SEND_ADDR <= '0';
         s_SPI1.SEND_DATA <= '0';
      end procedure;

--      procedure assert_to_file(data      : STD_LOGIC_VECTOR(31 downto 0);
--                               reference : STD_LOGIC_VECTOR(31 downto 0);
--                               test_id   : STRING;
--                               msg       : STRING) is
--         variable line_buffer : LINE;
--      begin
--         if data = reference then
--            write(line_buffer, string'(test_id&sep&"OK"&sep&msg));
--         else
--            write(line_buffer, string'(test_id&sep&"Fail"&sep&msg));
--         end if;
--         writeline(file_handler, line_buffer);
--         assert data = reference  report "Bad"&msg;
--      end procedure;

      procedure spi_command(cpol : STD_LOGIC;       cpha : STD_LOGIC;
                            inst_length: NATURAL;   addr_length: NATURAL; data_length: NATURAL;
                            send_inst  : STD_LOGIC; send_addr: STD_LOGIC; send_data: STD_LOGIC; 
                            inst       : STD_LOGIC_VECTOR(8*c_INST_LENGTH - 1 downto 0);
                            addr       : STD_LOGIC_VECTOR(8*c_ADDR_LENGTH - 1 downto 0);
                            data       : STD_LOGIC_VECTOR(8*c_DATA_LENGTH - 1 downto 0)) is
      begin
         s_rst_spi_analyser <= '1';
            wait until rising_edge(clk_i);
            wait for c_WISHBONE_PERIOD*3/2;
         s_rst_spi_analyser <= '0';
            wait until rising_edge(clk_i);
         set_SPI_mode(cpol, cpha);
         set_FIFO_contents(inst, addr, data);
         set_spi_lengths(inst_length, addr_length, data_length);
         send_SPI_and_wait(send_inst, send_addr, send_data); 
      end procedure;


   begin		

      init_cond;
         wait until rising_edge(clk_i);
         wait for c_WISHBONE_PERIOD*10;
      rst_i  <= '1';
         wait for c_WISHBONE_PERIOD*10;
      rst_i  <= '0';
         wait for c_WISHBONE_PERIOD*10;

      set_FIFO_contents(X"AB", X"123456", (others => '0'));


      inst_i <= X"AB";
      addr_i <= X"123456";
      data_i <= (others => '0');

      spi_command(  '0', '0',
                     1,    1,   1,
                   '1',  '1', '1',
                   inst_i, addr_i, data_i);
         wait for c_WISHBONE_PERIOD*10;

      spi_command(  '0', '1',
                     1,    1,   1,
                   '1',  '1', '1',
                   inst_i, addr_i, data_i);
         wait for c_WISHBONE_PERIOD*10;

      spi_command(  '1', '0',
                     1,    1,   1,
                   '1',  '1', '1',
                   inst_i, addr_i, data_i);
         wait for c_WISHBONE_PERIOD*10;

      spi_command(  '1', '1',
                     1,    1,   1,
                   '1',  '1', '1',
                   inst_i, addr_i, data_i);
         wait for c_WISHBONE_PERIOD*10;

      spi_command(  '0', '0',
                     1,    3,   256,
                   '1',  '1', '1',
                   inst_i, addr_i, data_i);
         wait for c_WISHBONE_PERIOD*10;


      wait;
   end process;

END;
