-- TestBench Template 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity image1_top_tb is
   end image1_top_tb;


architecture Behaviour of image1_top_tb is

   component image1_top
      generic(
                g_NUMBER_OF_CHANNELS : INTEGER := 6
             );
      port(
             rst_i        : in  STD_LOGIC;  
             clk_i        : in  STD_LOGIC;      

             pulse_i      : in  STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS -1 downto 0);
             pulse_o      : out STD_LOGIC_VECTOR(g_NUMBER_OF_CHANNELS -1 downto 0);

             sda_oen      : out STD_LOGIC;
             sda_i        : in  STD_LOGIC;
             sda_o        : out STD_LOGIC;
             scl_oen      : out STD_LOGIC;
             scl_i        : in  STD_LOGIC;
             scl_o        : out STD_LOGIC;

             GA           : in  STD_LOGIC_VECTOR (4 downto 0);
             GAP          : in  STD_LOGIC
          );
   end component;


   constant clk_i_period : TIME := 10 ns;
   constant c_NUMBER_OF_CHANNELS : INTEGER := 6;


   --Inputs
   signal rst_i         : STD_LOGIC;  
   signal clk_i         : STD_LOGIC;      
   signal pulse_i       : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS -1 downto 0);
   signal pulse_o       : STD_LOGIC_VECTOR(c_NUMBER_OF_CHANNELS -1 downto 0);
   signal sda_oen       : STD_LOGIC;
   signal sda_i         : STD_LOGIC := '0';
   signal sda_o         : STD_LOGIC;
   signal scl_oen       : STD_LOGIC;
   signal scl_i         : STD_LOGIC := '0';
   signal scl_o         : STD_LOGIC;

   -- Clock period definitions
   constant wb_clk_period   : time := 50 ns;    -- @ 20 MHz
   constant scl_i_period    : time := 2500 ns;  -- @ 400 KHz

   signal PRE_value        : UNSIGNED(15 downto 0) := X"0032";
   signal VME_crate_slot   : UNSIGNED(4  downto 0)  := "00001";
   signal i2c_addr         : STD_LOGIC_VECTOR(4 downto 0) := not(std_logic_vector(VME_crate_slot));
   signal GAP              : STD_LOGIC  := '0';

   signal wb_addr_i2c      : STD_LOGIC_VECTOR (15  downto 0);

   signal i2c_rcved_data   : STD_LOGIC_VECTOR (7 downto 0);

   constant bytes_ind_addr : STD_LOGIC_VECTOR (7 downto 6) := "10";
   constant bytes_wr_state : STD_LOGIC_VECTOR (7 downto 0) := X"06";
   constant bytes_rd_state : STD_LOGIC_VECTOR (7 downto 0) := X"04";

   constant  STA_addr      : STD_LOGIC_VECTOR(3 downto 0) := X"0";    
   constant  PRE_addr      : STD_LOGIC_VECTOR(3 downto 0) := X"1"; 
   constant  CTR0_addr     : STD_LOGIC_VECTOR(3 downto 0) := X"2";
   constant  CTR1_addr     : STD_LOGIC_VECTOR(3 downto 0) := X"3";   
   constant  DRXl_addr     : STD_LOGIC_VECTOR(3 downto 0) := X"4";
   constant  DRXh_addr     : STD_LOGIC_VECTOR(3 downto 0) := X"5";
   constant  DTX_addr      : STD_LOGIC_VECTOR(3 downto 0) := X"6";


begin

   image1_top_uut: image1_top 
   port map (
               rst_i    => rst_i,    
               clk_i    => clk_i,   
               pulse_i  => pulse_i,   
               pulse_o  => pulse_o,   
               sda_oen  => sda_oen,   
               sda_i    => sda_i,   
               sda_o    => sda_o,   
               scl_oen  => scl_oen,   
               scl_i    => scl_i,   
               scl_o    => scl_o,
               GA       => i2c_addr,
               GAP      => GAP
            );

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

   scl_i_process :process
   begin
      scl_i     <= '1';
      wait for scl_i_period/2;
      scl_i     <= '0';
      wait for scl_i_period/2;
   end process;

   -- Stimulus process
   stim_proc: process

      procedure init_cond is
      begin
         pulse_i <= (others => '0');
         sda_i   <= '1';
         scl_i   <= 'Z';
      end init_cond;


      procedure rst is
      begin
         wait for wb_clk_period*2;
         rst_i   <= '1';
         wait for wb_clk_period*2;
         rst_i   <= '0';
         wait for wb_clk_period*2;
      end rst;


      procedure start is
      begin
         sda_i  <= '1';
         wait until rising_edge(scl_i);
         wait for scl_i_period/4;
         sda_i  <= '0';
      end start;


      procedure addr_send(addr : STD_LOGIC_VECTOR(6 downto 0)) is
      begin
         for i in 6 downto 0 loop
            wait until falling_edge(scl_i);
            wait for scl_i_period/4;
            sda_i <= addr(i);
         end loop;
      end addr_send;


      procedure rd_wrn_send(rd_wrn_bit: STD_LOGIC) is
      begin
         wait until falling_edge(scl_i);
         wait for scl_i_period/4;
         sda_i <= rd_wrn_bit;
      end rd_wrn_send;


      procedure read_ACK (ERRORmsg: STRING; OKmsg: STRING) is
      begin
         wait until falling_edge(scl_i);
         wait for scl_i_period*3/4;
         assert sda_o = '0' report ERRORmsg severity ERROR;
         if (sda_o = '0') then
            report OKmsg; 
         end if;
      end read_ACK;


      procedure write_ACK(ack_bit: STD_LOGIC) is
      begin
         wait until falling_edge(scl_i);
         wait for scl_i_period/4;
         sda_i <= ack_bit;
      end write_ACK;


      procedure write_SDA(byte: STD_LOGIC_VECTOR (7 downto 0)) is
      begin
         for i in 7 downto 0 loop
            wait until falling_edge(scl_i);
            wait for scl_i_period/4;
            sda_i <= byte(i);
         end loop;
      end write_SDA;


      procedure read_SDA is
      begin
         for i in 0 to 7 loop
            i2c_rcved_data(i) <= sda_o;
            wait for scl_i_period;
         end loop;
         sda_i <= '0';
         wait for scl_i_period;
         sda_i <= '1';
      end read_SDA;

      procedure pause is 
      begin
         sda_i  <= '0';
         wait until rising_edge(scl_i);
         wait for scl_i_period/4;
         sda_i  <= '1';
         wait until falling_edge(scl_i);
         wait for scl_i_period/4;
      end pause;


      -------------------------------------------------------------------------
      -- Procedure for correctly setting up the i2c slave
      -------------------------------------------------------------------------

--      procedure init_cfg_i2c_slave(PRE_data: STD_LOGIC_VECTOR(15 downto 0); CTR0_data : STD_LOGIC_VECTOR(15 downto 0) ; CTR1_data : STD_LOGIC_VECTOR(15 downto 0)) is
--      begin
--         write_CTR0(CTR0_data and X"0002");
--         wait for wb_clk_period;
--         write_PRE(PRE_data);
--         wait for wb_clk_period;
--         write_CTR0(CTR0_data and X"FF00");
--         wait for wb_clk_period;
--         write_CTR1(CTR1_data);
--         wait for wb_clk_period;
--         write_CTR0(CTR0_data); -- Enough to assert enable bit
--      end procedure;
   begin

      -- ERROR list
      ----> ACK erros
      --    01   Instruction byte
      --    02   Internal wishbone address high byte
      --    03   Internal wishbone address low byte

      rst_i   <= '0';
      init_cond;
      wait for 100ns;

      ----------------------------------------------------------------------------
      -- i2c_slave_top initial configuration
      ----------------------------------------------------------------------------

      rst;
      --init_cfg_i2c_slave(std_logic_vector(PRE_value), '0'&i2c_addr&bytes_ind_addr&"0000"&"01", bytes_wr_state&bytes_rd_state);


      --   rst;
      --   start;
      --   addr_send("0101100"); --This address is inverted
      --   addr_send("0101101"); --This address is bad address
      --   addr_send("0000000"); --This is the general address
      --   rd_wrn_send('1');
      --   pause;


      -------------------------------------------------------------------------------
      -- Performing a write according to ELMA crate needs
      -------------------------------------------------------------------------------
      -- Placing a reset produces an error in scl. It should be solved
      --   rst;


      -------------------------------------------------------------------------------
      -- Indirect i2c write
      -------------------------------------------------------------------------------   
      start;
      addr_send("1011110");  -- MSB first
      rd_wrn_send('0');
      read_ACK("ACK@01:  NACK received. Sending instruction byte (i2c addres + rd_wrn bit)", "PASS -> Instruction byte ACKED");
      wb_addr_i2c <= X"0302";
      write_SDA(wb_addr_i2c(15 downto 8)); -- mirrored MSB
      read_ACK("ACK@02:  NACK received. Sending internal wishbone address high byte", "PASS -> Internal Wishbone Address High Byte ACKED");
      write_SDA(wb_addr_i2c(7  downto 0)); -- mirrored LSB
      read_ACK("ACK@03:  NACK received. Sending internal wishbone address low byte", "PASS -> Internal Wishbone Address Low Byte ACKED");

      write_SDA("10000000"); -- inverted
      read_ACK("ACK@04:  NACK received. Sending 1st byte", "PASS -> 1st data byte");
      write_SDA("10000000"); -- inverted
      read_ACK("ACK@04:  NACK received. Sending 2nd byte", "PASS -> 2nd data byte");
      write_SDA("10000000"); -- inverted
      read_ACK("ACK@04:  NACK received. Sending 3rd byte", "PASS -> 3rd data byte");
      write_SDA("10000000"); -- inverted
      read_ACK("ACK@04:  NACK received. Sending 4th byte", "PASS -> 4th data byte");
      pause;

      wait for 100*wb_clk_period;

         -------------------------------------------------------------------------------
         -- Indirect i2c read
         -- This is divided in a write process to set the Wishbone slave and a i2c read
         -------------------------------------------------------------------------------
      start;
      addr_send("1011110"); --This address is inverted in bit order
      rd_wrn_send('0');
      read_ACK("ACK@01:  NACK received. Sending instruction byte (i2c addres + rd_wrn bit)", "PASS -> Instruction byte ACKED");
      wb_addr_i2c <= X"0302";
      write_SDA(wb_addr_i2c(15 downto 8)); -- inverted MSB
      read_ACK("ACK@02:  NACK received. Sending internal wishbone address high byte", "PASS -> Internal Wishbone Address High Byte ACKED");
      write_SDA(wb_addr_i2c(7  downto 0)); -- inverted LSB
      read_ACK("ACK@03:  NACK received. Sending internal wishbone address low byte", "PASS -> Internal Wishbone Address Low Byte ACKED");
      -- At this point we must prefetch the data to be sent
--      write_DTX(X"DDCCBBAA");
      start;
      addr_send("1011110"); --This address is inverted in bit order
      rd_wrn_send('1');
      read_ACK("ACK@01:  NACK received. Sending instruction byte (i2c addres + rd_wrn bit)", "PASS -> Instruction byte ACKED");
      read_SDA;
      read_SDA;
      read_SDA;
      read_SDA;
      pause;

      wait;
   end process;


end;
