--==============================================================================
-- CERN (BE-CO-HT)
-- Testbench for old repeater design
--==============================================================================
--
-- author: Theodor Stana (t.stana@cern.ch)
--
-- date of creation: 2013-02-28
--
-- version: 1.0
--
-- description: 
--
-- dependencies:
--
-- references:
-- 
--==============================================================================
-- GNU LESSER GENERAL PUBLIC LICENSE
--==============================================================================
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--==============================================================================
-- last changes:
--    2013-02-28   Theodor Stana     t.stana@cern.ch     File created
--==============================================================================
-- TODO: - 
--==============================================================================

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity tb_vme64x_i2c is
end entity tb_vme64x_i2c;


architecture behav of tb_vme64x_i2c is

  --============================================================================
  -- Type declarations
  --============================================================================
  type t_state is 
    (
      ST_IDLE, 
      ST_I2C_ADDR, ST_I2C_ADDR_WA,
      
      ST_WB_ADDR_B0, ST_WB_ADDR_B0_WA,
      ST_WB_ADDR_B1, ST_WB_ADDR_B1_WA,

      ST_OP,
      
      ST_RD_RESTART, ST_RD_RESTART_WA,
      ST_RD, ST_RD_WA,

      ST_WR, ST_WR_WA,

      ST_STO,

      ST_SUCCESS,
      ST_ERR
    );

  type t_reg is array(0 to 3) of std_logic_vector(31 downto 0);

  --============================================================================
  -- Constant declarations
  --============================================================================
  constant c_clk_per : time := 8 ns;
  constant c_reset_width : time := 31 ns;

  --============================================================================
  -- Component declarations
  --============================================================================
  -- DUT
  component vme64x_i2c is
    port
    (
    -- Clock, reset
      clk_i      : in  std_logic;
      rst_n_i    : in  std_logic;

    -- I2C lines
      sda_en_o   : out std_logic;
      sda_i      : in  std_logic;
      sda_o      : out std_logic;
      scl_en_o   : out std_logic;
      scl_i      : in  std_logic;
      scl_o      : out std_logic;

    -- I2C address and status
      i2c_addr_i : in  std_logic_vector(6 downto 0);
      i2c_done_o : out std_logic;

    -- Wishbone master signals
      wbm_stb_o  : out std_logic;
      wbm_cyc_o  : out std_logic;
      wbm_sel_o  : out std_logic_vector(3 downto 0);
      wbm_we_o   : out std_logic;
      wbm_dat_i  : in  std_logic_vector(31 downto 0);
      wbm_dat_o  : out std_logic_vector(31 downto 0);
      wbm_adr_o  : out std_logic_vector(31 downto 0);
      wbm_ack_i  : in  std_logic;
      wbm_rty_i  : in  std_logic;
      wbm_err_i  : in  std_logic
    );
  end component vme64x_i2c;

  -- I2C master
  component i2c_master_byte_ctrl is
    port 
    (
      clk    : in std_logic;
      rst    : in std_logic; -- synchronous active high reset (WISHBONE compatible)
      nReset : in std_logic;	-- asynchornous active low reset (FPGA compatible)
      ena    : in std_logic; -- core enable signal

      clk_cnt : in unsigned(15 downto 0);	-- 4x SCL

      -- input signals
      start,
      stop,
      read,
      write,
      ack_in : std_logic;
      din    : in std_logic_vector(7 downto 0);

      -- output signals
      cmd_ack  : out std_logic; -- command done
      ack_out  : out std_logic;
      i2c_busy : out std_logic; -- arbitration lost
      i2c_al   : out std_logic; -- i2c bus busy
      dout     : out std_logic_vector(7 downto 0);

      -- i2c lines
      scl_i   : in std_logic;  -- i2c clock line input
      scl_o   : out std_logic; -- i2c clock line output
      scl_oen : out std_logic; -- i2c clock line output enable, active low
      sda_i   : in std_logic;  -- i2c data line input
      sda_o   : out std_logic; -- i2c data line output
      sda_oen : out std_logic  -- i2c data line output enable, active low
    );
  end component i2c_master_byte_ctrl;

  --============================================================================
  -- Signal declarations
  --============================================================================
  -- Clock, reset signals
  signal clk, rst_n  : std_logic := '0';
  signal rst         : std_logic;
  
  -- Slave-side I2C signals
  signal scl_to_slv  : std_logic;
  signal scl_fr_slv  : std_logic;
  signal scl_en_slv  : std_logic;
  signal sda_to_slv  : std_logic;
  signal sda_fr_slv  : std_logic;
  signal sda_en_slv  : std_logic;

  -- Master-side I2C signals
  signal scl_to_mst  : std_logic;
  signal scl_fr_mst  : std_logic;
  signal scl_en_mst  : std_logic;
  signal sda_to_mst  : std_logic;
  signal sda_fr_mst  : std_logic;
  signal sda_en_mst  : std_logic;

  -- I2C address, done
  signal i2c_addr        : std_logic_vector(6 downto 0);
  signal i2c_done    : std_logic;

  -- I2C master signals
  signal mst_sta     : std_logic;
  signal mst_sto     : std_logic;
  signal mst_rd      : std_logic;
  signal mst_wr      : std_logic;
  signal mst_ack     : std_logic;
  signal mst_dat_in  : std_logic_vector(7 downto 0);
  signal mst_dat_out : std_logic_vector(7 downto 0);
  signal mst_cmd_ack : std_logic; 
  signal ack_fr_slv  : std_logic;

  -- FSM signal
  signal state       : t_state;
  signal fsm_op      : std_logic;

  -- misc signals
  signal cnt         : unsigned(2 downto 0);
  signal once        : boolean;

  signal byte_cnt    : unsigned(1 downto 0);
  signal rcvd        : std_logic_vector(31 downto 0);
  signal read4       : std_logic;
  signal send        : std_logic_vector(31 downto 0);
  signal wrote       : std_logic;
  signal slv_wb_adr  : std_logic_vector(15 downto 0);

  -- Wishbone signals
  signal wb_stb      : std_logic;
  signal wb_cyc      : std_logic;
  signal wb_sel      : std_logic_vector(3 downto 0);
  signal wb_we       : std_logic;
  signal wb_dat_m2s  : std_logic_vector(31 downto 0);
  signal wb_dat_s2m  : std_logic_vector(31 downto 0);
  signal wb_adr      : std_logic_vector(31 downto 0);
  signal wb_ack      : std_logic;

  signal reg         : t_reg;

--==============================================================================
--  architecture begin
--==============================================================================
begin

  -- CLOCK GENERATION
  p_clk: process
  begin
    clk <= not clk;
    wait for c_clk_per/2;
  end process p_clk;

  -- RESET GENERATION
  rst <= not rst_n;
  p_rst_n: process
  begin
    rst_n <= '0';
    wait for c_reset_width;
    rst_n <= '1';
    wait;
  end process p_rst_n;

  -- DUT INSTANTIATION
  DUT: vme64x_i2c
    port map
    (
      -- Clock, reset
      clk_i      => clk,
      rst_n_i    => rst_n,

      -- I2C lines
      sda_en_o   => sda_en_slv,
      sda_i      => sda_to_slv,
      sda_o      => sda_fr_slv,
      scl_en_o   => scl_en_slv,
      scl_i      => scl_to_slv,
      scl_o      => scl_fr_slv,

      -- I2C address and status
      i2c_addr_i => "1011110",
      i2c_done_o => i2c_done,

      -- Wishbone master signals
      wbm_stb_o  => wb_stb,
      wbm_cyc_o  => wb_cyc,
      wbm_sel_o  => wb_sel,
      wbm_we_o   => wb_we,
      wbm_dat_i  => wb_dat_s2m,
      wbm_dat_o  => wb_dat_m2s,
      wbm_adr_o  => wb_adr,
      wbm_ack_i  => wb_ack,
      wbm_rty_i  => '0',
      wbm_err_i  => '0'
    );

  -- MASTER INSTANTIATION
  cmp_master: i2c_master_byte_ctrl
    port map
    (
      clk      => clk,
      rst      => rst,
      nReset   => rst_n,
      ena      => '1',

      clk_cnt  => x"00FA",

      -- input signals
      start    => mst_sta,
      stop     => mst_sto,
      read     => mst_rd,
      write    => mst_wr,
      ack_in   => mst_ack,
      din      => mst_dat_in,

      -- output signals
      cmd_ack  => mst_cmd_ack,
      ack_out  => ack_fr_slv,
      i2c_busy => open,
      i2c_al   => open,
      dout     => mst_dat_out,

      -- i2c lines
      scl_i    => scl_to_mst,
      scl_o    => scl_fr_mst,
      scl_oen  => scl_en_mst,
      sda_i    => sda_to_mst,
      sda_o    => sda_fr_mst,
      sda_oen  => sda_en_mst
    );

  -- BUS MODEL
  scl_to_mst <= scl_fr_slv when scl_en_slv = '1' else
                scl_fr_mst when scl_en_mst = '0' else
                '1';
  sda_to_mst <= sda_fr_slv when sda_en_slv = '1' else
                sda_fr_mst when sda_en_mst = '0' else
                '1';

  scl_to_slv <= scl_fr_mst when scl_en_mst = '0' else
                scl_fr_slv when scl_en_slv = '1' else
                '1';
  sda_to_slv <= sda_fr_mst when sda_en_mst = '0' else
                sda_fr_slv when sda_en_slv = '1' else
                '1';

  -- ADDRESS
  i2c_addr <= "1011110";

  -- I2C MASTER FSM
  p_mst_fsm: process (clk) is
  begin
    if rising_edge(clk) then
      if (rst_n = '0') then
        state      <= ST_IDLE;
        mst_sta    <= '0';
        mst_wr     <= '0';
        mst_sto    <= '0';
        mst_rd     <= '0';
        mst_dat_in <= (others => '0');
        mst_ack    <= '0';
        cnt        <= (others => '0');
        once       <= true;
        byte_cnt   <= (others => '0');
        rcvd       <= (others => '0');
        wrote      <= '0';
        send       <= x"11223344";
        fsm_op     <= '0'; -- '0' - SysMonWrite / '1' - SysMonRead
        slv_wb_adr <= x"0001";
      else
        case state is

          when ST_IDLE =>
            state <= ST_I2C_ADDR;

          when ST_I2C_ADDR => 
            mst_sta   <= '1';
            mst_wr    <= '1';
            mst_dat_in <= i2c_addr & '0';
            if (mst_cmd_ack = '1') then
              mst_sta <= '0';
              mst_wr  <= '0';
              state   <= ST_I2C_ADDR_WA;
            end if;

          when ST_I2C_ADDR_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              if (ack_fr_slv = '0') then
                state <= ST_WB_ADDR_B0;
              else
                state <= ST_ERR;
              end if;
            end if;

          when ST_WB_ADDR_B0 =>
            mst_wr <= '1';
            mst_dat_in <= slv_wb_adr(15 downto 8);
            if (mst_cmd_ack = '1') then
              mst_wr <= '0';
              state  <= ST_WB_ADDR_B0_WA;
            end if;

          when ST_WB_ADDR_B0_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              if (ack_fr_slv = '0') then
                state <= ST_WB_ADDR_B1;
              else
                state <= ST_ERR;
              end if;
            end if;

          when ST_WB_ADDR_B1 =>
            mst_wr <= '1';
            mst_dat_in <= slv_wb_adr(7 downto 0);
            if (mst_cmd_ack = '1') then
              mst_wr <= '0';
              state  <= ST_WB_ADDR_B1_WA;
            end if;

          when ST_WB_ADDR_B1_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              if (ack_fr_slv = '0') then
                state <= ST_OP;
              else
                state <= ST_ERR;
              end if;
            end if;

          when ST_OP =>
            if (fsm_op = '1') then
              state <= ST_RD_RESTART;
            else
              state <= ST_WR;
            end if;

          when ST_RD_RESTART =>
            mst_wr <= '1';
            mst_dat_in <= i2c_addr & '1';
            mst_sta <= '1';
            if (mst_cmd_ack = '1') then
              mst_sta <= '0';
              mst_wr  <= '0';
              state <= ST_RD_RESTART_WA;
            end if;

          when ST_RD_RESTART_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              if (ack_fr_slv = '0') then
                state <= ST_RD;
              else
                state <= ST_ERR;
              end if;
            end if;

          when ST_RD =>
            mst_rd <= '1';
            mst_ack <= '0';
            if (byte_cnt = 3) then
              mst_ack <= '1';
            end if;
            if (mst_cmd_ack = '1') then
              mst_rd <= '0';
              byte_cnt <= byte_cnt + 1;
              rcvd     <= mst_dat_out & rcvd(31 downto 8);
              mst_ack  <= '0';
              state    <= ST_RD;
              if (byte_cnt = 3) then
                state  <= ST_STO;
              end if;
            end if;

          when ST_RD_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              byte_cnt <= byte_cnt + 1;
              rcvd     <= mst_dat_out & rcvd(31 downto 8);
              mst_ack  <= '0';
              state    <= ST_RD;
              if (byte_cnt = 3) then
                state  <= ST_STO;
              end if;
            end if;

          when ST_WR =>
            mst_wr <= '1';
            mst_dat_in <= send(7 downto 0);
            if (mst_cmd_ack = '1') then
              mst_wr <= '0';
              state  <= ST_WR_WA;
            end if;

          when ST_WR_WA =>
            cnt <= cnt + 1;
            if (cnt = 7) then
              if (ack_fr_slv = '0') then
                byte_cnt <= byte_cnt + 1;
                send     <= x"00" & send(31 downto 8); 
                state    <= ST_WR;
                if (byte_cnt = 3) then
                  state  <= ST_STO;
                end if;
              else
                state <= ST_ERR;
              end if;
            end if;

          when ST_STO =>
            mst_sto <= '1';
            if (mst_cmd_ack = '1') then
              mst_sto <= '0';
              state   <= ST_I2C_ADDR;
              send    <= x"44332211";
              wrote   <= '1';
              slv_wb_adr <= x"0003";
              if (wrote = '1') then
                state  <= ST_I2C_ADDR;
                fsm_op <= '1';
                if (fsm_op = '1') then
                  read4  <= '1';
                  slv_wb_adr <= x"0001";
                  state <= ST_I2C_ADDR;
                  if (read4 = '1') then
                    state <= ST_SUCCESS;
                  end if;
                end if;
              end if;
            end if;

           when ST_SUCCESS =>
            if (once) then
              report("Success!");
              once <= false;
            end if;

          when ST_ERR =>
            if (once) then
              report("Error!");
              once <= false;
            end if;

          when others => 
            state <= ST_ERR;

        end case;
      end if;
    end if;
  end process p_mst_fsm;

  -- Wishbone slave
  p_wb_slv: process (clk) is
  begin
    if rising_edge(clk) then
      if (rst_n = '0') then
        reg <= (
               x"00000001",
               x"00000002",
               x"00000003",
               x"00000004"
             );
        wb_ack <= '0';
        wb_dat_s2m <= (others => '0');
      else
        wb_ack <= '0';
        if (wb_cyc = '1') and (wb_stb = '1') then
          wb_ack <= '1';
          wb_dat_s2m <= reg(to_integer(unsigned(wb_adr))); 
          if (wb_we = '1') then
            reg(to_integer(unsigned(wb_adr))) <= wb_dat_m2s;
          end if;
        end if;
      end if;
    end if;
  end process p_wb_slv;

  -- checkup signal
  p_check: process(read4) is
  begin
    if (read4 = '1') then
      if (rcvd = reg(to_integer(unsigned(wb_adr)))) then
        report("properly received four bytes");
      else
        report("error in WB to I2C bridge readout");
      end if;
    end if;
  end process p_check;

end architecture behav;
--==============================================================================
--  architecture end
--==============================================================================
