---------------------------------------------------------------------------
-- File           : p_avalon_mem_master_bfm.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : December 30th, 2018
---------------------------------------------------------------------------
-- Description    : Avalon memory map emulator
---------------------------------------------------------------------------

---------------------------------------------------------------------------

library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_report_utils.all;
   use work.p_generic_bfm.all;

package p_avalon_mem_master_bfm is
  -- defines range of valid handle numbers
   subtype rAvalonMemMasterBfmHandleNo is natural range 0 to 3;

   -- BFMs Handle
   signal hAvalonMemMasterBfm : tBfmHandleArray(rAvalonMemMasterBfmHandleNo) := tBfmHandleArray_init(rAvalonMemMasterBfmHandleNo'high-rAvalonMemMasterBfmHandleNo'low+1);
   ----------------------------------------------------------------------------
   -- TODO: Update constants!!! for now these are just approximate values.
   ----------------------------------------------------------------------------
   constant C_AVALON_MEM_DATA_WIDTH  : natural  := 8;
   constant C_BYTES_PER_WORD         : natural  := C_AVALON_MEM_DATA_WIDTH / 8;
   -- TODO: this number is random, it needs real calculation (according to FIFO)
   constant C_MAX_ADDR_SIZE_BYTES    : natural  := 1800; 
   -- In this design will be used address with 5 bits; max address size should be 2**5 -1
   constant C_BURSTCOUNT_WIDTH       : positive := 5;
   ----------------------------------------------------------------------------

   subtype tBurstcount is natural range 0 to 2**C_BURSTCOUNT_WIDTH - 1;
   subtype tByteEnable is std_logic_vector(C_BYTES_PER_WORD-1 downto 0);
   subtype tAddr      is natural range 0 to C_MAX_ADDR_SIZE_BYTES / C_BYTES_PER_WORD - 1;
   subtype tData      is std_logic_vector(C_AVALON_MEM_DATA_WIDTH-1 downto 0);
   -- TODO: data length (tDataArray length) needs constant, for now it's resolved that data length is eaqule to address lengt 
   type    tDataArray is array (natural range <>) of tData;

   -- -------------------------------------------------------------------------
   -- BFM Command Parameters
   -- -------------------------------------------------------------------------

   -- Operation type
   type tOpType is ( SET_VERBOSITY,  -- change current verbosity level
                     SET_BURST_DATA, -- write burst countents
                     VERIFY,         -- read & compare contents
                     GET_BURST_DATA);-- read burst contents

   -- BFM Command Type (Used for passing data between BFM and Package)
   type tBfmCmd is record
      op              : tOpType;
      waddr           : natural;
      burstcount      : natural;
      byteenable      : std_logic_vector(C_AVALON_MEM_DATA_WIDTH/8-1 downto 0);
      read_data_burst : tDataArray(tAddr);
      data            : tDataArray(tAddr);
      verbosity       : tVerbosity;
   end record;
   
   -- -------------------------------------------------------------------------
   -- PUBLIC PROCEDURES
   -- -------------------------------------------------------------------------

   -- Set the BFM's verbosity level
   procedure avalon_mem_master_set_verbosity(
      signal   handle      : inout tBfmHandle;
      constant verbosity   : tVerbosity);

   -- burst write
   procedure avalon_mem_master_set_burst(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural := 0;
      constant burstcount  : natural := 1;
      constant byteenable  : std_logic_vector(C_AVALON_MEM_DATA_WIDTH/8-1 downto 0) := (others => '1');
      constant data        : in tDataArray(tAddr));

   -- burst read 
   procedure avalon_mem_master_get_burst(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural;
      constant burstcount  : natural := 1;
      constant byteenable  : std_logic_vector(C_AVALON_MEM_DATA_WIDTH/8-1 downto 0) := (others => '1');
      variable data        : out tDataArray(tAddr));

   -- verify a N words
   procedure avalon_mem_master_verify(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural;
      constant burstcount  : natural := 1;
      constant data        : in tDataArray(tAddr));

   ----------------------------------------------------------------------------
   -- INTERNAL PROCEDURES (Only called by BFM)
   ----------------------------------------------------------------------------

   -- Reads command record   
   impure function get_bfm_cmd (handle_no: rAvalonMemMasterBfmHandleNo) return tBfmCmd;

   -- Writes command record
   procedure setBfmCmd ( handle_no: rAvalonMemMasterBfmHandleNo; cmd: in tBfmCmd);

end package;



package body p_avalon_mem_master_bfm is
 
   -- Command record variable used for communication between this package and GMII_GEN BFM
   type tBfmCmdArray is array (natural range <>) of tBfmCmd;
   shared variable BFMCmd : tBfmCmdArray(rAvalonMemMasterBfmHandleNo);

   ----------------------------------------------------------------------------
   -- PUBLIC PROCEDURES
   ----------------------------------------------------------------------------

   -- Set the BFM's verbosity level
   procedure avalon_mem_master_set_verbosity(
      signal   handle      : inout tBfmHandle;
      constant verbosity   : tVerbosity
   ) is
      constant idx : rAvalonMemMasterBfmHandleNo := to_integer(unsigned(handle.idx));
   begin
      BFMCmd(idx).op        := SET_VERBOSITY;
      BFMCmd(idx).verbosity := verbosity;
      bfm_send_request(handle => handle, blocking => false, log_name => "AVALON_MEM_MASTER_BFM");
   end procedure;

   -- burst write
   procedure avalon_mem_master_set_burst(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural := 0;
      constant burstcount  : natural := 1;
      constant byteenable  : std_logic_vector(C_AVALON_MEM_DATA_WIDTH/8-1 downto 0) := (others => '1');
      constant data        : in tDataArray(tAddr)
   ) is
      constant idx : rAvalonMemMasterBfmHandleNo := to_integer(unsigned(handle.idx));
   begin
      BFMCmd(idx).op        := SET_BURST_DATA;
      BFMCmd(idx).waddr     := waddr;
      BFMCmd(idx).data      := data;
      BFMCmd(idx).burstcount:= burstcount;
      BFMCmd(idx).byteenable:= byteenable;
      bfm_send_request(handle => handle, blocking => false, log_name => "AVALON_MEM_MASTER_BFM");
   end procedure;

   -- read burst
   procedure avalon_mem_master_get_burst(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural;
      constant burstcount  : natural := 1;
      constant byteenable  : std_logic_vector(C_AVALON_MEM_DATA_WIDTH/8-1 downto 0) := (others => '1');
      variable data        : out tDataArray(tAddr)
   ) is
      constant idx : rAvalonMemMasterBfmHandleNo := to_integer(unsigned(handle.idx));
   begin
      BFMCmd(idx).op        := GET_BURST_DATA;
      BFMCmd(idx).waddr     := waddr;
      BFMCmd(idx).burstcount:= burstcount;
      BFMCmd(idx).byteenable:= byteenable;
      bfm_send_request(handle => handle, blocking => true, log_name => "AVALON_MEM_MASTER_BFM");
      data                  := BFMCmd(idx).read_data_burst;
   end procedure;

   -- compare data 
   procedure avalon_mem_master_verify(
      signal   handle      : inout tBfmHandle;
      constant waddr       : natural;
      constant burstcount  : natural := 1;
      constant data        : in tDataArray(tAddr)
   ) is
      constant idx : rAvalonMemMasterBfmHandleNo := to_integer(unsigned(handle.idx));
   begin
      BFMCmd(idx).op              := VERIFY;
      BFMCmd(idx).waddr           := waddr;
      BFMCmd(idx).read_data_burst := data;
      BFMCmd(idx).burstcount      := burstcount;
      bfm_send_request(handle => handle, blocking => false, log_name => "AVALON_MEM_MASTER_BFM");
   end procedure;

   ----------------------------------------------------------------------------
   -- INTERNAL PROCEDURES (Only called by BFM)
   ----------------------------------------------------------------------------   

   -- Reads command record   
   impure function get_bfm_cmd (handle_no: rAvalonMemMasterBfmHandleNo) return tBfmCmd is
   begin
      return BFMCmd(handle_no);
   end function;

   -- Writes command record
   procedure setBfmCmd ( handle_no: rAvalonMemMasterBfmHandleNo; cmd: in tBfmCmd) is
   begin
      BFMCmd(handle_no) := cmd;
   end procedure;


end package body;

