--******************************************************************************
--*
--* @short   VME Interface Block for the whole GMT Board. Located in the ROP Chip.
--*
--*          The VME Interface decodes the upper four bits (23 downto 20) of the
--*          VME address and generates the necessary vme_en signals for all the
--*          chips on the board.
--*
--*          The vme_en_xxx signals are asserted with a DSPULS. In write
--*          cycles a vme_en_xxx is a pulse of one clock cycle length. In read cycles
--*          the vme_en_xxx is cleared after a rising edge of DTACK or when the
--*          DSSYNC from VME64 is cleared.
--*
--*          For internal use in the ROP chip a vme_strobe_ROP is
--*          generated when reading and when writing. This is needed in the
--*          JTAGController since actions are triggered at the end of the strobe.
--*
--*          DTACK_EXT is set after a rising edge of the dtack pulses from the
--*          chips. It is cleared synchronously after a falling edge of DSSYNC
--*          or asynchronously with DSCYC.
--*       
--*
--*          Check: is it worth clearing the inteternal vme_en_signals  already with
--*                 the DTACK? Do we gain any speed? 
--*                 
--*          Check: is it worth clearing the esternal DTACK asynchonously with
--*                 DSCYC? Do we gain any speed?
--*
--* @diagram vme_waveforms.jpg VME Interface waveforms
--*
--*          (Taurok, Noebauer - 29/09/2005) vme_en_XXX are delayed by one tick
--*          to make them arrive well after the VME addresses
--******************************************************************************
--* @author  SAKULIN Hannes  <hsakulin@dsy-srv3.cern.ch>
--* @version $Revision: 1.7 $
--* @date    $Date: 2005/11/22 14:45:42 $
--******************************************************************************
--/
library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.ROPVMEAddrMap.all;

entity ROPVMEInterface is
  port (
    -- VME port (from/to VME 64 chip)
    -- vme addr and data will be buffered to get shorter traces on the board
    vme_addr      : in    std_logic_vector(23 downto 1);  
    vme_wrall_mask : in std_logic_vector(9 downto 0);
    
    --  VME64x chip signals :
    BERR_EXT      : out   std_logic;
    DTACK_EXT     : out   std_logic;
    IRQ_X         : out   std_logic;    -- currently not used
    SINGLE_ACCESS : in    std_logic;    -- address mod indicates single access
    BLT_ACCESS    : in    std_logic;    -- address mod indicates block transfer
    DSPULS        : in    std_logic;    -- Strobe pulse 25 ns long, 25ns after
                                        -- DSSYNC
    DSCYC         : in    std_logic;    -- AND of two VME strobes (not synced)
    DSSYNC        : in    std_logic;    -- DSCYC synchronized
    WRITE_I       : in    std_logic;    -- write from VME
    RESET_MODE    : in    std_logic;    -- currently not used
    D08_O         : in    std_logic;    -- currently not used
    D08_E         : in    std_logic;    -- currently not used
    D16_EO        : in    std_logic;    -- currently not used
    ASCYC         : in    std_logic;    -- currently not used
    ASSYNC        : in    std_logic;    -- currently not used
    ASPULS        : in    std_logic;    -- currently not used


    -- VME signals to the other chips (including IRQ, but without BERR)
    vme_en_INF     : out std_logic;
    vme_wr_INF     : out std_logic;
    vme_ndtack_INF : in  std_logic;
    vme_nirq_INF   : in  std_logic;
                                   
    vme_en_INC     : out std_logic;
    vme_wr_INC     : out std_logic;
    vme_ndtack_INC : in  std_logic;
    vme_nirq_INC   : in  std_logic;
                                                                      
    vme_en_IND     : out std_logic;
    vme_wr_IND     : out std_logic;
    vme_ndtack_IND : in  std_logic;
    vme_nirq_IND   : in  std_logic;
                                   
    vme_en_INB     : out std_logic;
    vme_wr_INB     : out std_logic;
    vme_ndtack_INB : in  std_logic;
    vme_nirq_INB   : in  std_logic;

    vme_en_AUF     : out std_logic;
    vme_wr_AUF     : out std_logic;
    vme_ndtack_AUF : in  std_logic;
    vme_nirq_AUF   : in  std_logic;
                                   
    vme_en_LFB     : out std_logic;
    vme_wr_LFB     : out std_logic;
    vme_ndtack_LFB : in  std_logic;
    vme_nirq_LFB   : in  std_logic;
                                   
    vme_en_LFF     : out std_logic;
    vme_wr_LFF     : out std_logic;
    vme_ndtack_LFF : in  std_logic;
    vme_nirq_LFF   : in  std_logic;
                                   
    vme_en_AUB     : out std_logic;
    vme_wr_AUB     : out std_logic;
    vme_ndtack_AUB : in  std_logic;
    vme_nirq_AUB   : in  std_logic;
                                   
    vme_en_SRT     : out std_logic;
    vme_wr_SRT     : out std_logic;
    vme_ndtack_SRT : in  std_logic;
    vme_nirq_SRT   : in  std_logic;

    vme_strobe_ROP : out std_logic;     -- used internally
                                        -- strobe when writing and reading
    vme_en_ROP     : out std_logic;     -- used internally
                                     
    vme_wr_ROP     : out std_logic;
    vme_ndtack_ROP : in  std_logic;
    vme_nirq_ROP   : in  std_logic;

    -- Clock and control                
    clk            : in  std_logic;
    reset          : in  std_logic);

end;


architecture behavioral of ROPVMEInterface is
  attribute syn_useioff               : boolean;
  attribute syn_useioff of behavioral : architecture is true;

  signal SINGLE_ACCESS_reg : std_logic;    -- address mod indicates single access
  signal BLT_ACCESS_reg    : std_logic;    -- address mod indicates block transfer

  signal DSSYNC_reg        : std_logic;    -- DSCYC cynchronized
  signal DSSYNC_reg_d      : std_logic;
  signal DSSYNC_reg_d2     : std_logic;
  signal WRITE_I_reg       : std_logic;    -- write from VME

  signal dssync_pulse     : std_logic;

  signal vme_wr_ROP_reg   : std_logic;
  signal vme_wr_INF_reg   : std_logic;
  signal vme_wr_INC_reg   : std_logic;
  signal vme_wr_IND_reg   : std_logic;
  signal vme_wr_INB_reg   : std_logic;
  signal vme_wr_AUF_reg   : std_logic;
  signal vme_wr_LFF_reg   : std_logic;
  signal vme_wr_LFB_reg   : std_logic;
  signal vme_wr_AUB_reg   : std_logic; 
  signal vme_wr_SRT_reg   : std_logic;

  signal s_vme_en_ROP    : std_logic;
  signal s_vme_en_INF    : std_logic;
  signal s_vme_en_INC    : std_logic;
  signal s_vme_en_IND    : std_logic;
  signal s_vme_en_INB    : std_logic;
  signal s_vme_en_AUF    : std_logic;
  signal s_vme_en_LFF    : std_logic;  
  signal s_vme_en_LFB    : std_logic;  
  signal s_vme_en_AUB    : std_logic;  
  signal s_vme_en_SRT    : std_logic;  
  signal s_vme_strobe_ROP    : std_logic;     
  
  attribute syn_preserve                  : boolean;
  attribute syn_preserve of vme_wr_ROP_reg : signal is true;
  attribute syn_preserve of vme_wr_INF_reg : signal is true;
  attribute syn_preserve of vme_wr_INC_reg : signal is true;
  attribute syn_preserve of vme_wr_IND_reg : signal is true;
  attribute syn_preserve of vme_wr_INB_reg : signal is true;
  attribute syn_preserve of vme_wr_AUF_reg : signal is true;
  attribute syn_preserve of vme_wr_LFF_reg : signal is true;
  attribute syn_preserve of vme_wr_LFB_reg : signal is true;
  attribute syn_preserve of vme_wr_AUB_reg : signal is true;
  attribute syn_preserve of vme_wr_SRT_reg : signal is true;

  signal nirq_ROP_reg   : std_logic;
  signal nirq_INF_reg   : std_logic;
  signal nirq_INC_reg   : std_logic;
  signal nirq_IND_reg   : std_logic;
  signal nirq_INB_reg   : std_logic;
  signal nirq_AUF_reg   : std_logic;
  signal nirq_LFF_reg   : std_logic;
  signal nirq_LFB_reg   : std_logic;
  signal nirq_AUB_reg   : std_logic; 
  signal nirq_SRT_reg   : std_logic;
         
  signal ndtack_ROP_reg : std_logic;
  signal ndtack_INF_reg : std_logic;
  signal ndtack_INC_reg : std_logic;
  signal ndtack_IND_reg : std_logic;
  signal ndtack_INB_reg : std_logic;
  signal ndtack_AUF_reg : std_logic;
  signal ndtack_LFF_reg : std_logic;
  signal ndtack_LFB_reg : std_logic;
  signal ndtack_AUB_reg : std_logic;
  signal ndtack_SRT_reg : std_logic;

  signal dtack_or   : std_logic;
  signal dtack_or_d : std_logic;

  
function addr_match (
    constant vme_addr : std_logic_vector;
    constant address  : integer)        -- in bytes
    return std_logic is

    variable my_addr_vec : std_logic_vector(vme_addr'high downto 0);
    variable retval : std_logic;
      
  begin  -- process vme_addr_decode
    my_addr_vec := std_logic_vector( TO_UNSIGNED ( address, vme_addr'high+1 ) );
    if my_addr_vec(vme_addr'range) = vme_addr(vme_addr'range) then
      retval := '1';
    else
      retval := '0';
    end if;
    return retval;
  end;

begin

  -----------------------------------------------------------------------------
  --* register (and delay) VME64 signals
  -----------------------------------------------------------------------------
  
  register_vme64: process (clk, reset) is
  begin  -- process register_vme64
    if reset = '1'  then
      SINGLE_ACCESS_reg  <= '0'; 
      BLT_ACCESS_reg     <= '0';
      DSSYNC_reg         <= '0';
      DSSYNC_reg_d       <= '0';
      DSSYNC_reg_d2      <= '0';
      WRITE_I_reg        <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      SINGLE_ACCESS_reg  <= SINGLE_ACCESS;  
      BLT_ACCESS_reg     <= BLT_ACCESS;
     
      DSSYNC_reg         <= DSSYNC;   
      DSSYNC_reg_d       <= DSSYNC_reg;   
      DSSYNC_reg_d2      <= DSSYNC_reg_d;
      
      WRITE_I_reg        <= WRITE_I;  
    end if;
  end process register_vme64;

  dssync_pulse <= DSSYNC_reg and (not DSSYNC_reg_d2);  

  -----------------------------------------------------------------------------
  --* register NIRQ and NDTACK inputs
  -----------------------------------------------------------------------------
  

  register_inputs: process (clk) is
  begin  -- process register_inputs
    if clk'event and clk = '1' then  -- rising clock edge
      nirq_ROP_reg <= vme_nirq_ROP;      ndtack_ROP_reg <= vme_ndtack_ROP;
      nirq_INF_reg <= vme_nirq_INF;      ndtack_INF_reg <= vme_ndtack_INF; 
      nirq_INC_reg <= vme_nirq_INC;      ndtack_INC_reg <= vme_ndtack_INC;    
      nirq_IND_reg <= vme_nirq_IND;      ndtack_IND_reg <= vme_ndtack_IND;    
      nirq_INB_reg <= vme_nirq_INB;      ndtack_INB_reg <= vme_ndtack_INB;    
      nirq_AUF_reg <= vme_nirq_AUF;      ndtack_AUF_reg <= vme_ndtack_AUF;    
      nirq_LFF_reg <= vme_nirq_LFF;      ndtack_LFF_reg <= vme_ndtack_LFF;    
      nirq_LFB_reg <= vme_nirq_LFB;      ndtack_LFB_reg <= vme_ndtack_LFB;    
      nirq_AUB_reg <= vme_nirq_AUB;      ndtack_AUB_reg <= vme_ndtack_AUB;    
      nirq_SRT_reg <= vme_nirq_SRT;      ndtack_SRT_reg <= vme_ndtack_SRT;    

      -- generate IRQ
      IRQ_X     <= ( not nirq_ROP_reg ) or
                   ( not nirq_INF_reg ) or
                   ( not nirq_INC_reg ) or
                   ( not nirq_IND_reg ) or
                   ( not nirq_INB_reg ) or
                   ( not nirq_AUF_reg ) or
                   ( not nirq_LFF_reg ) or
                   ( not nirq_LFB_reg ) or
                   ( not nirq_AUB_reg ) or
                   ( not nirq_SRT_reg );     
    end if;
  end process register_inputs;


  -----------------------------------------------------------------------------
  --* VME enable and strobe signal generation
  -----------------------------------------------------------------------------
  
  register_signals : process (clk, reset) is
    variable vme_a2320 : std_logic_vector(23 downto 20);                                        
  begin  -- process register_outputs
    if reset = '1'  then
        s_vme_en_ROP <= '0';
        s_vme_en_INF <= '0';
        s_vme_en_INC <= '0';
        s_vme_en_IND <= '0';    
        s_vme_en_INB <= '0';
        s_vme_en_AUF <= '0';
        s_vme_en_LFF <= '0';    
        s_vme_en_LFB <= '0';
        s_vme_en_AUB <= '0';
        s_vme_en_SRT <= '0';

        s_vme_strobe_ROP <= '0';
      
    elsif clk'event and clk = '1' then     -- rising clock edge

      if (SINGLE_ACCESS_reg = '1' or BLT_ACCESS_reg = '1' ) then
        -- rise vme enable on ds pulse (2 cycles)
        vme_a2320(23 downto 20) := vme_addr(23 downto 20);
        if dssync_pulse = '1' then 
          s_vme_en_ROP <= addr_match(vme_a2320 , ROP_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(0));
          s_vme_en_INF <= addr_match(vme_a2320 , INF_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(1));
          s_vme_en_INC <= addr_match(vme_a2320 , INC_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(2));
          s_vme_en_IND <= addr_match(vme_a2320 , IND_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(3));
          s_vme_en_INB <= addr_match(vme_a2320 , INB_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(4));
          s_vme_en_AUF <= addr_match(vme_a2320 , AUF_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(5));
          s_vme_en_LFF <= addr_match(vme_a2320 , LFF_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(6));
          s_vme_en_LFB <= addr_match(vme_a2320 , LFB_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(7));
          s_vme_en_AUB <= addr_match(vme_a2320 , AUB_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(8));
          s_vme_en_SRT <= addr_match(vme_a2320 , SRT_base) or (addr_match(vme_a2320 , ALL_base) and WRITE_I_reg and vme_wrall_mask(9));
        -- clear after one clock when writing, or upon DTACK, or when cleared
        -- from VME
        elsif ( dssync_pulse = '0' and WRITE_I_reg = '1' ) or
              ( DSSYNC_reg = '0' ) then
          s_vme_en_ROP <= '0';
          s_vme_en_INF <= '0';
          s_vme_en_INC <= '0';
          s_vme_en_IND <= '0';
          s_vme_en_INB <= '0';
          s_vme_en_AUF <= '0';
          s_vme_en_LFF <= '0';
          s_vme_en_LFB <= '0';
          s_vme_en_AUB <= '0';
          s_vme_en_SRT <= '0';
        end if;
        
        -- rise vme strobe on ds pulse
        if dssync_pulse = '1' then
          s_vme_strobe_ROP <= addr_match(vme_addr(23 downto 20), ROP_base);
        -- clear strobe or upon DTACK, or when cleared from VME
        elsif ( DSSYNC_reg = '0' ) then
          s_vme_strobe_ROP <= '0';
        end if;            
      end if;
    end if;
  end process register_signals;

  -- Connect VME signals to outputs: vme_en_XXX are delayed by one tick
  -- to make them arrive well after the VME addresses
  register_outputs : process (clk, reset) is                                        
  begin  -- process register_outputs
    if reset = '1'  then
        vme_en_ROP <= '0';
        vme_en_INF <= '0';
        vme_en_INC <= '0';
        vme_en_IND <= '0';    
        vme_en_INB <= '0';
        vme_en_AUF <= '0';
        vme_en_LFF <= '0';    
        vme_en_LFB <= '0';
        vme_en_AUB <= '0';
        vme_en_SRT <= '0';
        vme_strobe_ROP <= '0';     
    elsif clk'event and clk = '1' then     -- rising clock edge
          vme_en_ROP <= s_vme_en_ROP;
          vme_en_INF <= s_vme_en_INF;
          vme_en_INC <= s_vme_en_INC;
          vme_en_IND <= s_vme_en_IND;
          vme_en_INB <= s_vme_en_INB;
          vme_en_AUF <= s_vme_en_AUF;
          vme_en_LFF <= s_vme_en_LFF;
          vme_en_LFB <= s_vme_en_LFB;
          vme_en_AUB <= s_vme_en_AUB;
          vme_en_SRT <= s_vme_en_SRT;
          vme_strobe_ROP <= s_vme_strobe_ROP;
    end if;
  end process register_outputs;

  -----------------------------------------------------------------------------
  --* register VME wr signals
  -----------------------------------------------------------------------------

  register_vme_wr: process (clk, reset) is
  begin  
    if reset = '1' then
      vme_wr_ROP_reg <= '0'; 
      vme_wr_INF_reg <= '0'; 
      vme_wr_INC_reg <= '0'; 
      vme_wr_INB_reg <= '0'; 
      vme_wr_IND_reg <= '0'; 
      vme_wr_AUF_reg <= '0'; 
      vme_wr_LFF_reg <= '0'; 
      vme_wr_LFB_reg <= '0'; 
      vme_wr_AUB_reg <= '0'; 
      vme_wr_SRT_reg <= '0';      
    elsif clk'event and clk = '1' then  -- rising clock edge
      vme_wr_ROP_reg <= WRITE_I_reg; 
      vme_wr_INF_reg <= WRITE_I_reg; 
      vme_wr_INC_reg <= WRITE_I_reg; 
      vme_wr_INB_reg <= WRITE_I_reg; 
      vme_wr_IND_reg <= WRITE_I_reg; 
      vme_wr_AUF_reg <= WRITE_I_reg; 
      vme_wr_LFF_reg <= WRITE_I_reg; 
      vme_wr_LFB_reg <= WRITE_I_reg; 
      vme_wr_AUB_reg <= WRITE_I_reg; 
      vme_wr_SRT_reg <= WRITE_I_reg;      
    end if;
  end process register_vme_wr;
  
  vme_wr_ROP <= vme_wr_ROP_reg; 
  vme_wr_INF <= vme_wr_INF_reg; 
  vme_wr_INC <= vme_wr_INC_reg; 
  vme_wr_INB <= vme_wr_INB_reg; 
  vme_wr_IND <= vme_wr_IND_reg; 
  vme_wr_AUF <= vme_wr_AUF_reg; 
  vme_wr_LFF <= vme_wr_LFF_reg; 
  vme_wr_LFB <= vme_wr_LFB_reg; 
  vme_wr_AUB <= vme_wr_AUB_reg; 
  vme_wr_SRT <= vme_wr_SRT_reg;


  -----------------------------------------------------------------------------
  -- DTACK generation
  -----------------------------------------------------------------------------

  dtack_or <= ( not ndtack_ROP_reg ) or
              ( not ndtack_INF_reg ) or
              ( not ndtack_INC_reg ) or
              ( not ndtack_IND_reg ) or
              ( not ndtack_INB_reg ) or
              ( not ndtack_AUF_reg ) or
              ( not ndtack_LFF_reg ) or
              ( not ndtack_LFB_reg ) or
              ( not ndtack_AUB_reg ) or
              ( not ndtack_SRT_reg ) ;

  --* delay or'ed DTACK
  delay_dtack_or: process (clk, reset) is
  begin  -- process delay_dtack_or
    if reset = '1' then
      dtack_or_d <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      dtack_or_d <= dtack_or;
    end if;
  end process delay_dtack_or;

  -----------------------------------------------------------------------------
  --* register dtack and clear asynchronously when DSCYC = 0
  -----------------------------------------------------------------------------
  register_dtack: process (clk, reset, DSCYC) is
  begin  -- process register_dtack
    if reset = '1' or DSCYC = '0' then
      DTACK_EXT <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      -- set DTACK on rising flank from chips
      if dtack_or = '1' and dtack_or_d = '0' then 
        DTACK_EXT <= '1';
      -- clear DTACK on falling edge of DSSYNC
      elsif DSSYNC_reg = '0' and DSSYNC_reg_d = '1' then
        DTACK_EXT <= '0';       
      end if;
      
    end if;
  end process register_dtack;

  -----------------------------------------------------------------------------
  -- Bus error generation
  -----------------------------------------------------------------------------

  
  BERR_EXT <= '0';



end architecture behavioral;