---------------------------------------------------------------------------
-- Copyright (c) 2018, Filip Kliment
---------------------------------------------------------------------------
-- File           : bfm_pck.vhd
-- Author(s)      : Filip Kliment
---------------------------------------------------------------------------

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

package bfm_pck is

   -- BFM Handle Type
   type Handle is record
      req      : std_logic;   -- driven by the test controller through the BFM's package
      ack      : std_logic;   -- driven by the BFM
      ready    : std_logic;   -- driven by the BFM
      idx      : std_logic_vector(15 downto 0);  -- remains constant; used to store the BFM index in the handle array
   end record;

   -- BFM Handle Array Type
   type Handle_Array is array (natural range <>) of Handle;
    
   -- Initialization routine for the BFM handles
   function Handle_Array_init(size: natural) return Handle_Array;

   -- send a request to the BFM (usable outside of the BFM)
   procedure send_request (
      signal   handle      : inout Handle;
      constant blocking    : boolean := false
   );

   -- wait until a BFM reqeust is issued (usable in a BFM)
   procedure wait_for_request(
      signal   handle      : inout Handle
   );

   -- indicate that the BFM request handling is finished (usable in a BFM)
   procedure ack_request(
      signal   handle      : inout Handle
   );

end package;


package body bfm_pck is

   -- Initialization routine for the BFM handles
   function Handle_Array_init(size: natural) return Handle_Array is
      variable ret : Handle_Array(0 to size - 1);
   begin
      for i in ret'range loop
         -- initialize control signals to 'Z' (overriden from BFM or test controller)
         -- the index is set only during initialization and remaind constant
         ret(i) := (req   => 'W',
                    ack   => 'Z',
                    ready => 'Z',
                    idx   => std_logic_vector(to_unsigned(i, 16)));
      end loop;
      return ret;
   end function;


   -- send a request to the BFM (usable outside of the BFM)
   procedure send_request (
      signal   handle      : inout Handle;
      constant blocking    : boolean := false
   ) is
   begin
      -- wait until BFM is ready to process command
      if handle.ready /= '1' then
         wait until handle.ready = '1';
      end if;

      -- wake up the BFM
      handle.req                <= '1';
      -- wait until the command is acknowledged and release the request
      wait on handle.ack;
      handle.req                <= 'Z';

      -- wait until finished when blocking
      if blocking and handle.ready /= '1' then
         wait until handle.ready = '1';
      -- insert delta-cycle when non-blocking
      else
         wait for 0 ns;
      end if;
   end procedure;

   -- wait until a BFM reqeust is issued (usable in a BFM)
   procedure wait_for_request(
      signal   handle      : inout Handle
   ) is 
   begin
      -- Wait for Request
      handle.ready <= '1';
      wait until handle.req = '1';
      handle.ready <= '0';
   end procedure;


   -- indicate that the BFM request was accepted (usable in a BFM)
   procedure ack_request(
      signal   handle      : inout Handle
   ) is 
   begin

      if handle.ack = '1' then
         handle.ack <= '0';
      else
         handle.ack <= '1';
      end if;
   end procedure;

end package body;

