library ieee;
library std;
library flasher; 

use std.textio.all;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all; 
use flasher.test_pkg.all;

package ks8695px_bfm_pkg is

   ---------------------------------------------------------------------
   -- pipeline signal.  Set this to true in the test process to enable 
   -- a "pipeline execution" style of BFM operation.  The BFM will 
   -- consume the command and return control back to the test code in 
   -- the next clock, allowing other operations in the test code to be
   -- executed (test execution is not waiting for the BFM to completely
   -- finish the operation).
   --------------------------------------------------------------------- 
   signal allZeros : std_logic_vector(31 downto 0) := X"00000000";

   ----------------------------------
   -- timing record type used in BFM
   ----------------------------------

   type ibc_timing_rec_t is record 
      su       : time;  -- setup time
      hld      : time;  -- hold time
   end record;

   ----------------------------------------------------------------
   -- BFM-specific cycle types, instruction record, and 
   -- procedure definitions
   ----------------------------------------------------------------

   type cycle_t is (idle, write, read);

   type instruction is record
      bus_cycle         : cycle_t;
      addr              : std_logic_vector( 5 downto 2);
      data              : std_logic_vector(15 downto 0); 
      rdata             : std_logic_vector(15 downto 0); 
      request           : std_logic;
      busy              : std_logic;
   end record;

   signal cmd           : instruction :=  (idle, 
                                          (others => '0'),
                                          (others => '0'),
                                          (others => 'Z'),
                                          '0',
                                          'Z');

   -- main procdure all others call to access BFM
   procedure ibc_cyc(      
      constant p_cycle  : in cycle_t;
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   );

   -- used to get actual read value from BFM
   procedure get_rdata (
      variable p_data   : out std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction 
   );

   -- drive BFM idle cycle
   procedure idle(
      signal p_cmd      : inout instruction
   );

   -- drive BFM read cycle (std_logic input)
   procedure rd(
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   );

   -- drive BFM write cycle (std_logic input)
   procedure wr(
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   );

   -- drive BFM read cycle (string input)
   procedure srd (
      constant p_addr   : in string;
      constant p_data   : in string;
      signal p_cmd      : inout instruction
   );

   -- drive BFM write cycle (string input)
   procedure swr (
      constant p_addr   : in string;
      constant p_data   : in string;
      signal p_cmd      : inout instruction
   );

end ks8695px_bfm_pkg;


package body ks8695px_bfm_pkg is

   procedure info (
      constant p_type   : cycle_t;
      constant p_addr   : std_logic_vector;
      constant p_data   : std_logic_vector
   ) is
      variable ll       : line;
   begin
      deallocate(ll);
      write(ll, string'("<"));
      write(ll, time'(now));
      write(ll, string'("> ibc_bfm: "));
      case p_type is
         when idle  =>
            write(ll, string'("idle")); 
         when read =>
            write(ll, string'("read")); 
         when write =>
            write(ll, string'("write")); 
      end case;
      if p_type /= idle then
         write(ll, string'(" ( ")); 
         lprint(ll, p_addr, HEX);
         write(ll, string'(", ")); 
         lprint(ll, p_data, HEX);
         write(ll, string'(" )")); 
      end if;
      writeline(output,ll);
   end info;

   procedure ibc_cyc(
      constant p_cycle  : in cycle_t;
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   ) is
      variable l        : line;
      variable tmp_be   : std_logic_vector(1 downto 0); 
   begin
      p_cmd.request <= '0';
      if p_cmd.busy /= '0' then
         wait on p_cmd.busy until p_cmd.busy = '0';
         --wait for 1 ns;  -- hack!
      end if;
      p_cmd.request <= '1';
      p_cmd.bus_cycle <= p_cycle;
      p_cmd.addr <= p_addr;
      p_cmd.data <= p_data;
      if p_cmd.busy = '0' then
         wait on p_cmd.busy until p_cmd.busy = '1';
      end if;
      wait on p_cmd.busy until p_cmd.busy = '0';
   end;

   procedure get_rdata (
      variable p_data   : out std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   ) is
   begin
      p_data := p_cmd.rdata(15 downto 0);
   end get_rdata;

   procedure idle (
      signal p_cmd      : inout instruction
   ) is
   begin
      info(idle, allZeros, allZeros);
      ibc_cyc(idle, (others => '1'),(others => 'Z'), p_cmd);
   end idle;

   procedure rd (
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   ) is
   begin
      info(read,p_addr,p_data);
      ibc_cyc(read,p_addr,p_data,p_cmd);
   end rd;

   procedure wr (
      constant p_addr   : in std_logic_vector( 5 downto 2);
      constant p_data   : in std_logic_vector(15 downto 0);
      signal p_cmd      : inout instruction
   ) is
   begin
      info(write,p_addr,p_data);
      ibc_cyc(write,p_addr,p_data,p_cmd);
   end wr;

   procedure srd (
      constant p_addr   : in string;
      constant p_data   : in string;
      signal p_cmd      : inout instruction
   ) is
      variable av       : std_logic_vector( 5 downto 2);
      variable dv       : std_logic_vector(15 downto 0);
   begin 
      av := htov(p_addr, 4);
      dv := htov(p_data,16);
      if p_addr'length = 8 and p_data'length = 8 then
         info(read,av,dv);
         ibc_cyc(read,av,dv,p_cmd);
      else
         assert false report "srd:  data size is invalid"
         severity failure;
      end if;
   end srd;
   
   procedure swr (
      constant p_addr   : in string;
      constant p_data   : in string;
      signal p_cmd      : inout instruction
   ) is
      variable av       : std_logic_vector( 5 downto 2);
      variable dv       : std_logic_vector(15 downto 0);
   begin 
      av := htov(p_addr, 4);
      dv := htov(p_data,16);
      if p_addr'length = 8 and p_data'length = 8 then
         info(write,av,dv);
         ibc_cyc(write,av,dv,p_cmd);
      else
         assert false report "swr:  data size is invalid"
         severity failure;
      end if;
   end swr;

end ks8695px_bfm_pkg;

