library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library unisim; 

entity spill_emulator is

  port(
    rst: in std_logic; 
    clk: in std_logic;
    buffer_rst: in std_logic;
    --
    reset_length: in std_logic_vector(15 downto 0);
    bx_count_start: in std_logic_vector(12 downto 0);
    bx_length: in std_logic_vector(7 downto 0);
    bt_length: in std_logic_vector(12 downto 0);
    --
    busy: out std_logic;
    spill_start: in std_logic;
    spill_active: out std_logic;
    bx_end: out std_logic;

    start_tt: in std_logic_vector(31 downto 0);
    end_tt: in std_logic_vector(31 downto 0);
    test_trigger: out std_logic;
    
    -- trigger buffer interface:
    trigger_input: in std_logic_vector(15 downto 0);

    ro_clk: in std_logic;
    buf_overflow: out std_logic;
    buf_empty: out std_logic;
    buf_rden: in std_logic;
    buf_data: out std_logic_vector(15 downto 0)
    
    );

end entity spill_emulator;

architecture v0 of spill_emulator is
  
  component trigger_record_buf
    port(
      din : in std_logic_vector(15 downto 0);
      rd_clk : in std_logic;
      rd_en : in std_logic;
      rst : in std_logic;
      wr_clk : in std_logic;
      wr_en : in std_logic;
      dout : out std_logic_vector(15 downto 0);
      empty : out std_logic;
      full : out std_logic
      );
  end component trigger_record_buf;

  for all : trigger_record_buf use entity work.trigger_record_buf;




  type spill_emu_t is (s_idle,
                       s_init,
                       s_count_rst,
                       s_count_pre_bx,
                       s_count_bx,
                       s_end_spill);

  signal spill_state: spill_emu_t;

  signal int_reset_cnt: std_logic_vector(15 downto 0);

  signal int_bx_idx_en: std_logic;
  signal int_bx_end: std_logic;
  signal int_bx_idx: std_logic_vector(7 downto 0);
  signal int_bx_length:std_logic_vector(7 downto 0);
  
  signal int_bx_cnt: std_logic_vector(12 downto 0);

  
  signal int_bx_count_start:std_logic_vector(12 downto 0);
  signal int_reset_length: std_logic_vector(15 downto 0);
  signal int_bt_length: std_logic_vector(12 downto 0);

  signal int_spill_active: std_logic;

  signal int_full: std_logic;
  signal int_wren: std_logic;
  signal int_trigger_record_data: std_logic_vector(15 downto 0);
  signal int_overflow: std_logic;

  signal int_tt_count: std_logic_vector(31 downto 0);
  signal int_start_tt: std_logic_vector(31 downto 0);
  signal int_end_tt: std_logic_vector(31 downto 0);
  signal int_test_trigger: std_logic;
  signal int_tt_active: std_logic;


  signal int_busy:std_logic;

begin

  latch_counters:process(clk)
  begin 
    if(rising_edge(clk))then
      int_bx_count_start<=bx_count_start;
      int_reset_length<=reset_length;
      int_bt_length<=bt_length;
      int_bx_length<=bx_length;
      int_start_tt<=start_tt;
      int_end_tt<=end_tt;
    end if;
  end process latch_counters;
  
  busy<=int_busy;
  
  bx_end<=int_bx_end when int_spill_active='1'
           else '0';

  spill_active<=int_spill_active;
  
  emulate_spill:process(rst, clk)
  begin 
    if(rst='1')then
      spill_state<=s_idle;
      int_bx_idx_en<='0';
      int_spill_active<='0';
      
    elsif(rising_edge(clk))then 
      int_bx_idx_en<='0';
      int_spill_active<='0';
      int_busy<='1';
      
      case (spill_state) is
        when s_idle =>
          spill_state<=s_idle;

          int_busy<='0';
          if(spill_start='1')then
            spill_state<=s_init;
          end if;
          
        when s_init =>
          spill_state<=s_count_rst;
          int_reset_cnt<=(others=>'0');
          int_bx_cnt<=int_bx_count_start;
          
        when s_count_rst =>
          spill_state<=s_count_rst;
          int_reset_cnt<=int_reset_cnt+1;
          if(int_reset_cnt=int_reset_length)then 
            spill_state<=s_count_pre_bx;
          end if;
          
        when s_count_pre_bx =>
          spill_state<=s_count_pre_bx;
          int_bx_idx_en<='1';

          if(int_bx_end='1')then
            int_bx_cnt<=int_bx_cnt+1;
          end if;
          if(int_bx_cnt="0000000000000")then
            spill_state<=s_count_bx;
          end if;
          
        when s_count_bx =>
          spill_state<=s_count_bx;
          int_bx_idx_en<='1';
          int_spill_active<='1';
          
          if(int_bx_end='1')then
            int_bx_cnt<=int_bx_cnt+1;

            if(int_bx_cnt=int_bt_length)then
              spill_state<=s_end_spill;
            end if;

          end if;

                
        when s_end_spill =>
          
          spill_state<=s_idle;
          
        when others=>
          -- all states are accounted
          -- for above: this should never
          -- happen.
          spill_state<=s_idle;
          
      end case;

    end if; 

  end process emulate_spill;


  
  bx_x:process(clk)
  begin 
    if(rising_edge(clk))then
      int_bx_end<='0';
      if(int_bx_idx_en='0')then
        int_bx_idx<="00000000";
      else
        int_bx_idx<=int_bx_idx+1;
        if(int_bx_idx=int_bx_length)then
          int_bx_idx<="00000000";
          int_bx_end<='1';
        end if;
      end if;
    end if;
  end process bx_x;


  -- pipeline the write procedure: 

  pipeline:process(rst, clk)
  begin
    if(rst='1')then 
      int_wren<='0';
    elsif(rising_edge(clk))then 
      -- we mustn't record
      -- anything during the
      -- prespill wait:
      int_wren<='0';

      if(int_spill_active='1')then 
        int_wren<=int_bx_end;
      end if;
      
      int_trigger_record_data<=trigger_input;
    end if;
  end process pipeline;
  
  --
  --
  --
  --
  
  u_trigger_record_buf:trigger_record_buf
    port map(
      rst=>buffer_rst,
      
      rd_clk=>ro_clk,
      rd_en=>buf_rden,
      dout=>buf_data,
      empty=>buf_empty,
      
      wr_clk=>clk,
      full=>int_full,
      wr_en=>int_wren,
      din=>int_trigger_record_data
      );
  
  detect_overflow:process(buffer_rst, clk)
  begin
    if(buffer_rst='1')then 
      int_overflow<='0';
    elsif(rising_edge(clk))then
      if(int_wren='1' and int_full='1')then 
        int_overflow<='1';
      end if;
    end if; 
  end process detect_overflow;

  buf_overflow<=int_overflow;


  tt_count:process(clk)
  begin
    if(rising_edge(clk))then  
      if(spill_start='1')then 
        int_tt_active<='1';
        int_tt_count<=(others=>'0');    
        int_test_trigger<='0';
      end if;

      if(int_tt_active='1')then
        int_tt_count<=int_tt_count+1;
      end if;

      if(int_tt_count>=int_start_tt)then
        int_test_trigger<='1';
      end if;

      if(int_tt_count>=int_end_tt)then 
        int_test_trigger<='0';
        int_tt_active<='0';
      end if;

    end if; 
  end process tt_count;

  test_trigger<=int_test_trigger;
  
end architecture v0;
