-------------------------------------------------------------------------------
--
-- Project name: AXI4-Lite simulation helpers
--
-- (C) Copyright 2013-2017 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Marek Kvas (m.kvas@dspfpga.com)
--
-------------------------------------------------------------------------------
--
-- Package that should easy simulation of components with AXI Lite.
--
-------------------------------------------------------------------------------



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


package axi_lite_sim_pkg is

   constant ALSPKG_C_S_AXI_ADDR_WIDTH   : integer := 12;
   constant ALSPKG_C_S_AXI_DATA_WIDTH   : integer := 32;

   -- Signals going from master to slave
   type axi_lite_m2s_type is
      record
         awaddr     : std_logic_vector(ALSPKG_C_S_AXI_ADDR_WIDTH-1 downto 0);
         awprot     : std_logic_vector(2 downto 0);
         awvalid    : std_logic;
         wdata      : std_logic_vector(ALSPKG_C_S_AXI_DATA_WIDTH-1 downto 0);
         wstrb      : std_logic_vector((ALSPKG_C_S_AXI_DATA_WIDTH/8)-1 downto 0);
         wvalid     : std_logic;
         bready     : std_logic;
         araddr     : std_logic_vector(ALSPKG_C_S_AXI_ADDR_WIDTH-1 downto 0);
         arprot     : std_logic_vector(2 downto 0);
         arvalid    : std_logic;
         rready     : std_logic;         
      end record;

   -- Signals going from slave to master
   type axi_lite_s2m_type is
      record
         awready    : std_logic;         
         wready     : std_logic;
         bresp      : std_logic_vector(1 downto 0);
         bvalid     : std_logic;         
         arready    : std_logic;
         rdata      : std_logic_vector(ALSPKG_C_S_AXI_DATA_WIDTH-1 downto 0);
         rresp      : std_logic_vector(1 downto 0);
         rvalid     : std_logic;
      end record;

   -- Init master to slave record
   procedure init_m2s (signal m2s : out axi_lite_m2s_type);
   -- Init slave to master record
   procedure init_s2m (signal s2m : out axi_lite_s2m_type);

   -- Procedure simulating master write
   procedure axi_lite_sim_mwr(signal clk : in  std_logic;
                              signal m2s : out axi_lite_m2s_type;
                              signal s2m : in  axi_lite_s2m_type;
                              constant addr : in  integer;
                              constant data : in  std_logic_vector);

   -- Procedure simulating master read
   procedure axi_lite_sim_mrd(signal clk : in  std_logic;
                              signal m2s : out axi_lite_m2s_type;
                              signal s2m : in  axi_lite_s2m_type;
                              constant addr : in  integer;
                              variable data : out std_logic_vector);


end package;



package body axi_lite_sim_pkg is

   procedure init_m2s (signal m2s : out axi_lite_m2s_type) is
   begin
      m2s.awaddr      <= (others => '0');
      m2s.awprot      <= (others => '0');
      m2s.awvalid     <= '0';
      m2s.wdata       <= (others => '0');
      m2s.wstrb       <= (others => '0');
      m2s.wvalid      <= '0';
      m2s.bready      <= '0';
      m2s.araddr      <= (others => '0');
      m2s.arprot      <= (others => '0');
      m2s.arvalid     <= '0';
      m2s.rready      <= '0';
   end procedure;
   procedure init_s2m (signal s2m : out axi_lite_s2m_type) is
   begin
      s2m.awready     <= '0';
      s2m.wready      <= '0';
      s2m.bresp       <= (others => '0');
      s2m.bvalid      <= '0';
      s2m.arready     <= '0';
      s2m.rdata       <= (others => '0');
      s2m.rresp       <= (others => '0');
      s2m.rvalid      <= '0';
   end procedure;

   -- Procedure simulating master write
   procedure axi_lite_sim_mwr(signal clk : in  std_logic;
                              signal m2s : out axi_lite_m2s_type;
                              signal s2m : in  axi_lite_s2m_type;
                              constant addr : in  integer;
                              constant data : in  std_logic_vector) is
      variable aw_done, w_done : boolean;
   begin
      m2s.awaddr <= std_logic_vector(to_unsigned(addr, ALSPKG_C_S_AXI_ADDR_WIDTH));
      m2s.awvalid <= '1';
      m2s.wdata <= data;
      m2s.wstrb <= (others => '1');
      m2s.wvalid <= '1';
      m2s.bready <= '1';
      aw_done := false;
      w_done := false;

      while not (aw_done and w_done) loop
         wait until rising_edge(clk);
         if s2m.awready = '1' then
            m2s.awvalid <= '0';
            aw_done := true;
         end if;
         if s2m.wready = '1' then
            m2s.wvalid <= '0';
            w_done := true;
         end if;
      end loop;

      wait until  rising_edge(clk) and s2m.bvalid = '1';
      m2s.bready <= '0';
   end procedure;


   -- Procedure simulating master read
   procedure axi_lite_sim_mrd(signal clk : in  std_logic;
                              signal m2s : out axi_lite_m2s_type;
                              signal s2m : in  axi_lite_s2m_type;
                              constant addr : in  integer;
                              variable data : out std_logic_vector) is
      variable res   : std_logic_vector(s2m.rdata'range);
      variable arvalid : boolean;
   begin
      m2s.araddr <= std_logic_vector(to_unsigned(addr, ALSPKG_C_S_AXI_ADDR_WIDTH));
      m2s.arvalid <= '1';
      arvalid := true;
      m2s.rready <= '1';
      while s2m.rvalid = '0' or arvalid loop
         wait until rising_edge(clk);
         if s2m.arready = '1' then
            m2s.arvalid <= '0';
            arvalid := false;
         end if;
      end loop;
      m2s.rready <= '0';
      data := s2m.rdata;
      --report "axi_lite_sim_mrd returned " & integer'image(to_integer(unsigned(s2m.rdata)));
   end procedure;




end package body;
