---------------------------------------------------------------------------
-- Copyright (c) 2012, TTTech Computertechnik AG
-- Project        : Pegasus E/S
---------------------------------------------------------------------------
-- File           : p_generic_bfm.vhd
-- Author(s)      : Petr Grillinger
-- Created        : September 4th, 2012
---------------------------------------------------------------------------
-- Description    : Generic BFM types and functions
--
-- The BFM should have the following structure:
--   process 
--   begin
--      bfm_wait_for_request(bfm_handle);  -- indicates readyness
--      ...store_request_information...
--      bfm_ack_request(bfm_handle);
--      ...request_execution...
--   end process;
--
--   The test controller should use the following sequence to talk to the BFM
--     prepare_request_information
--     bfm_send_request(handle)
---------------------------------------------------------------------------

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

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

package p_generic_bfm is

   -- BFM Handle Type
   type tBfmHandle 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;

   -- usable for single-instance BFMs
   constant CONST_BFM_HANDLE_INIT : tBfmHandle := (
      req   => 'W',
      ack   => 'Z',
      ready => 'Z',
      idx   => (others => '0')
   );

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

   -- send a request to the BFM (usable outside of the BFM)
   procedure bfm_send_request (
      signal   handle      : inout tBfmHandle;
      constant log_name    : string  := "p_generic_bfm";
      constant timeout     : time    := 10 us;
      constant blocking    : boolean := false  -- waits until the processing is finished (BFM is ready again)
   );

   -- wait until a BFM reqeust is issued (usable in a BFM)
   procedure bfm_wait_for_request(
      signal   handle      : inout tBfmHandle;
      constant log_name    : in    string  := "bfm_wait_for_request"
   );

   -- indicate that the BFM request handling is finished (usable in a BFM)
   procedure bfm_ack_request(
      signal   handle      : inout tBfmHandle;
      constant log_name    : in    string  := "bfm_ack_request"
   );

end package;


use work.p_report_utils.all;

package body p_generic_bfm is

   constant C_VERBOSITY : tVerbosity := INFO;

   -- Initialization routine for the BFM handles
   function tBfmHandleArray_init(size: natural) return tBfmHandleArray is
      variable ret : tBfmHandleArray(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 bfm_send_request (
      signal   handle      : inout tBfmHandle;
      constant log_name    : string  := "p_generic_bfm";
      constant timeout     : time    := 10 us;
      constant blocking    : boolean := false  -- waits until the processing is finished (BFM is ready again)
   ) is
   begin
      report_debug("p_generic_bfm.bfm_send_request", log_name & " - start ...", C_VERBOSITY);

      -- make sure that the BFM is even present (somebody drives the ready signal)
      if handle.ready /= '1' and handle.ready /= '0' then
         report_info(log_name, "Skipping this BFM, it is not initialized or present, because the 'ready' signal is " & to_string(handle.ready));
         return;
      end if;
   
      -- wait until BFM is ready to process command
      while handle.ready /= '1' loop
         wait until handle.ready = '1' for timeout;
         if handle.ready /= '1' then
            report_error(log_name, "The BFM is not ready, timeout " & to_string(timeout) & " exceeded");
            -- keep waiting without timeout now
            wait until handle.ready = '1';
         end if;
      end loop;

      report_debug("p_generic_bfm.bfm_send_request", log_name & " - req, wait on ack ...", C_VERBOSITY);
      -- wake up the BFM
      handle.req                <= '1';
      -- wait until the command is acknowledged and release the request
      wait on handle.ack;
      handle.req                <= 'Z';

      report_debug("p_generic_bfm.bfm_send_request", log_name & " - blocking, wait on ready ...", C_VERBOSITY);
      -- wait until finished when blocking
      if blocking and handle.ready /= '1' then
         wait until handle.ready = '1';
      end if;
      -- insert delta-cycle to let the BFM run
      wait for 0 ns;
      report_debug("p_generic_bfm.bfm_send_request", log_name & " .... done", C_VERBOSITY);
   end procedure;


   -- wait until a BFM reqeust is issued (usable in a BFM)
   procedure bfm_wait_for_request(
      signal   handle      : inout tBfmHandle;
      constant log_name    : in    string  := "bfm_wait_for_request"
   ) is 
   begin
      report_debug(log_name, "waiting ...", C_VERBOSITY);
      -- Wait for Request
      handle.ready <= '1';
      wait until handle.req = '1';
      handle.ready <= '0';
      report_debug(log_name, ".... ready.", C_VERBOSITY);
   end procedure;


   -- indicate that the BFM request was accepted (usable in a BFM)
   procedure bfm_ack_request(
      signal   handle      : inout tBfmHandle;
      constant log_name    : in    string  := "bfm_ack_request"
   ) is 
   begin
      -- transition from '1' to '0'
      -- transition from 'Z'/'0' to '1'
      if handle.ack = '1' then
         handle.ack <= '0';
      else
         handle.ack <= '1';
      end if;
      report_debug(log_name, "done", C_VERBOSITY);
   end procedure;

end package body;

