library ieee;
library flasher;

use std.textio.all;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all; 
use ieee.std_logic_unsigned.all;	 
use ieee.numeric_std.all;

use flasher.test_pkg.all;
use flasher.ks8695px_bfm_pkg.all;

entity ks8695px_bfm  is
   generic(
      timing_chk     			: boolean := false;
      x_check        			: boolean := false;
      Taddr          			: time := 3 ns;  
	  Tcs			   			: time := 3 ns;
      Trd            			: time := 3 ns;
      Twr            			: time := 3 ns;
      Tdata_oe       			: time := 6 ns;
      Tdata_z        			: time := 2 ns;
      Tsh_ibc_data   			: ibc_timing_rec_t := (2 ns, 1 ns); -- setup,hold
      Tsh_ibc_rdy_n  			: ibc_timing_rec_t := (3 ns, 1 ns); -- setup,hold 
	  oe_we_active_time			: integer := 3;			            -- In clocks
	  chip_select_hold_time		: integer := 1;
	  address_setup_time		: integer := 1;
	  chip_select_setup_time	: integer := 1
   );
   port ( 
       clock            : in 	std_logic;
       async_reset_n    : in 	std_logic;
       address_out      : out 	std_logic_vector( 5 downto 2);
       data_io          : inout	std_logic_vector(15 downto 0); 	  
	   cs_n_out			: out 	std_logic;
       rd_n_out       	: out 	std_logic;
       wr_1_n_out       : out 	std_logic;		
       wr_0_n_out       : out 	std_logic;
       rdy_n_in      	: in 	std_logic
   ); 
end ks8695px_bfm;


architecture ks8695px_bfm_hdl of ks8695px_bfm is

   type bstate_t is ( reset, idle, address_setup, cs_setup, stall_1, stall_2, stall_3, hold, finish ); 	 

   signal bstate        : bstate_t := reset;
   signal next_bstate   : bstate_t := idle;
   signal last_bstate   : bstate_t := idle;
   signal s_data        : std_logic_vector(15 downto 0);
   signal irdy_n        : std_logic;
   signal iaddr         : std_logic_vector( 5 downto 2);  
   signal ics_n			: std_logic;
   signal ird_n         : std_logic;
   signal iwr_n         : std_logic;
   signal idata         : std_logic_vector(15 downto 0); 
   signal data_oe       : std_logic; 
   signal dcheck        : std_logic; 

   procedure chk_write_warning (
      instance    : string;
      message     : string;
      constraint  : time;
      actual      : time
   ) is
      variable l  : line;
   begin
      deallocate(l);
      write (l, string'("{"));
      write (l, instance );
      write (l, string'("} "));
      write (l, message);
      write (l, string'((1=>lf)));
      write (l, string'("     required = "));
      write (l, constraint);
      write (l, string'(" actual = "));
      write (l, actual);
      assert false report l.all severity error; deallocate (l);
   end;
   
   procedure chk_write_warning (
      instance    : string;
      message     : string
   ) is
      variable l  : line;
   begin
      deallocate(l);
      write (l, string'("{"));
      write (l, instance );
      write (l, string'("} "));
      write (l, message);
      assert false report l.all severity error; deallocate (l);
   end;

begin

   --------------------------------------------------------
   -- sample pins to internal signals, XOR with '0' to get 
   -- a "translated" {X,1,0} vals from {U,H,L} values
   --------------------------------------------------------

   irdy_n <= rdy_n_in xor '0';


   -------------------------------------------------------------------
   -- IBC state machine process.  The main guts of the BFM.  Note that 
   -- even if we specify odd-byte addresses, the BFM really only 
   -- understands 32-bit aligned addressing and the addr(1:0) bits are
   -- masked off and not used.  Procedures could provide 'em, but
   -- the BFM won't use 'em.
   ------------------------------------------------------------------

   ibcsm : process (cmd, bstate)
      variable addr_v      : std_logic_vector( 5 downto 2); 
      variable data_v      : std_logic_vector(15 downto 0); 
      variable cycle_v     : cycle_t;		   
   begin

      -- default 
      cmd.busy <= '1';
      data_oe <= '0';
      dcheck <= '0';
      ird_n <= '1';
      iwr_n <= '1';	 
	  ics_n <= '1';

      case bstate is

         when reset => 		 
	  		ics_n <= '1';
		 	ird_n <= '1';	 
			iwr_n <= '1';	
            addr_v := (others => 'X');
            data_v := (others => 'X');
            next_bstate <= idle;

         when idle =>        -- idle and reset condition   
            cmd.busy <= '0';
            if cmd.request = '1' then
               cycle_v := cmd.bus_cycle;	 
			   ics_n <= '0';
               case cycle_v is
                  when write =>
                  		next_bstate <= address_setup;
                  when read =>
				  		next_bstate <= address_setup;  
				  when others =>
				  		next_bstate <= idle;
               end case;
            else
               next_bstate <= idle;
            end if;		   
			ics_n <= '1';  
		 	ird_n <= '1';
            iwr_n <= '1'; 
	
         when address_setup =>
            addr_v := cmd.addr;
            data_v := cmd.data;
		 	if (cycle_v = write) then	
		 		data_oe <= '1';	
		 	else		 
 		 		data_oe <= '0';	
			end if; 
			ics_n <= '1';
            next_bstate <= cs_setup;
			
		 when cs_setup =>
		 	ics_n <= '0';   				 
		 	if (cycle_v = write) then	
		 		data_oe <= '1';	
		 	else		 
 		 		data_oe <= '0';	
			end if; 
		 	next_bstate <= stall_1;  
		 
		 when stall_1 =>			 
		 	ics_n <= '0';		
		 	if (cycle_v = write) then	
				iwr_n 	<= '0';	
		 		data_oe <= '1';			 
		 	else		 
				ird_n 	<= '0';	
 		 		data_oe <= '0';	
			end if;  	
			next_bstate <= stall_2;

		 when stall_2 =>			 
		 	ics_n <= '0';		
		 	if (cycle_v = write) then	
				iwr_n 	<= '0';  
		 		data_oe <= '1';	
		 	else		 
				ird_n 	<= '0';		
		 		data_oe <= '0';	
			end if;   
			next_bstate <= stall_3;
			
		 when stall_3 =>			 
		 	ics_n <= '0';		
		 	if (cycle_v = write) then
		 		data_oe <= '1';	
				iwr_n 	<= '0';
		 	else		 		   
		 		data_oe <= '0';	
				ird_n 	<= '0';
			end if;
	 		if (cycle_v = read) then	 
				next_bstate <= finish;
				cmd.rdata	<= data_io;
	 		else		 
 				next_bstate <= hold; 
			end if; 

		 when hold =>		   
		 	ics_n <= '0';  
		 	iwr_n 	<= '1';	
	 		data_oe <= '1';	
		 	next_bstate <= finish;	 
			 
		 when finish =>		   
		 	ics_n <= '1';  
            iwr_n <= '1'; 
			ird_n <= '1';	
		 	data_oe <= '0';	
		 	next_bstate <= idle;
		 
      end case;

      -- drive address & data signals from variables
      iaddr <= addr_v;
      idata <= data_v;

   end process ibcsm;


   -------------------------------------------------------
   -- state registers, read data comparison check
   -------------------------------------------------------

   ibcsm_reg: process (clock)
   begin
	if clock'event and clock ='1' then   
		if async_reset_n = '0' then
         	bstate <= idle;
         	last_bstate <= idle; 
		end if;
		if dcheck = '1' then
			if (compare(idata, s_data)) then
               data_fail("ibc_bfm", iaddr, idata, s_data, false);
			end if;	  
		end if;
		last_bstate <= bstate;
		bstate <= next_bstate;  -- assign next state
	end if;
   end process ibcsm_reg;


   ---------------------------------------------------
   -- drive outputs based upon Tspec & OE's defined in
   -- ibc_bfm_pkg (as constants)
   ---------------------------------------------------

   data_io <= idata(15 downto 0) after Tdata_oe when data_oe='1' else 
	             (others => 'Z') after Tdata_z;	
   address_out <= iaddr after Taddr;
   rd_n_out <= ird_n after Trd;
   wr_1_n_out <= iwr_n after Twr;	 
   wr_0_n_out <= iwr_n after Twr;	
   cs_n_out <= ics_n after Tcs;


   ------------------------------------------------------------
   -- check timing if generic is set.  Also use the x_check to
   -- determine X-propogation if set through generic
   ------------------------------------------------------------

   tchk: if (timing_chk) generate
    
      check_block: block
      begin

         -------------------------------------
         -- check the ibc_rdy_n input signal
         -------------------------------------

         chk_ibc_rdy_n: process (rdy_n_in, clock)
            variable clk_edge    : time := 0 ns;
            variable data_edge   : time := 0 ns;
            variable chk_hold    : boolean;
            variable has_been_x  : boolean := false;
         begin
            if (clock'event and clock = '1') then
               if x_check then
                  if is_x(rdy_n_in) then
                     if not has_been_x then
                        chk_write_warning("ibc_rdy_n - clk rise","Input Unknown");
                     end if;
                     has_been_x := true;
                  else
                     has_been_x := false;
                  end if;
               end if;
               if (now - data_edge < Tsh_ibc_rdy_n.su) then
                  chk_write_warning("ibc_rdy_n - clk rise","Setup Violation", 
                  Tsh_ibc_rdy_n.su, now - data_edge);
               end if ;
               chk_hold := true;
               clk_edge := now;
            end if;
            if (rdy_n_in'event) then
               if (chk_hold and (now - clk_edge < Tsh_ibc_rdy_n.hld) ) then
                  chk_write_warning("ibc_rdy_n - clk rise","Hold Violation", 
                  Tsh_ibc_rdy_n.hld, now - clk_edge);
               end if;
               data_edge := now;
               chk_hold := false; -- don't report every transition violation
            end if;
         end process chk_ibc_rdy_n;


         -------------------------------------
         -- check the ibc_data input bus
         -------------------------------------

         chk_data: process (data_io, clock, last_bstate)
            variable clk_edge    : time := 0 ns;
            variable data_edge   : time := 0 ns;
            variable chk_hold    : boolean;
            variable has_been_x  : boolean := false;
         begin
            if (clock'event and clock = '1') then
               if (now - data_edge < Tsh_ibc_data.su) then
                  chk_write_warning("ibc_data - clk rise","Setup Violation", 
                  Tsh_ibc_data.su, now - data_edge);
               end if ;
               chk_hold := true;
               clk_edge := now;
            end if;
            if (data_io'event and last_bstate /= finish) then
               if (chk_hold and (now - clk_edge < Tsh_ibc_data.hld) ) then
                  chk_write_warning("Tsh_ibc_data - clk rise","Hold Violation", 
                  Tsh_ibc_data.hld, now - clk_edge);
               end if;
               data_edge := now;
               chk_hold := false; -- don't report every bit transition violation
            end if;
         end process chk_data;

      end block check_block;

   end generate tchk;

end ks8695px_bfm_hdl;

