--
-- Hail Simulation Components
--

--
-- Link Connect module
--
-- Behavioral model of a single link connection
-- DST_RDY always '1'
-- Routes TX signals to RX

library ieee;
use ieee.std_logic_1164.all;

library work;
use work.common_interface.all;
use work.hail_simulation.all;

entity link_connect is
  port (
    linkA_out  : in  link_out_type;
    linkA_in   : out link_in_type;
    linkB_out  : in  link_out_type;
    linkB_in   : out link_in_type);
end link_connect;

architecture behave of link_connect is

begin  

  linkA_in.tx_dst_rdy <= '1';
  linkB_in.tx_dst_rdy <= '1';

  linkA_in.rx_data <= linkB_out.tx_data;
  linkA_in.rx_src_rdy <= linkB_out.tx_src_rdy;
  linkA_in.rx_sof <= linkB_out.tx_sof;
  linkA_in.rx_eof <= linkB_out.tx_eof;
  
  linkB_in.rx_data <= linkA_out.tx_data;
  linkB_in.rx_src_rdy <= linkA_out.tx_src_rdy;
  linkB_in.rx_sof <= linkA_out.tx_sof;
  linkB_in.rx_eof <= linkA_out.tx_eof;

end behave;


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

library work;
use work.common_interface.all;
use work.hail_simulation.all;


entity hail_simulator is
  generic (
    board              : board_type := adm_xp;
    number_of_memories : integer := 6);
  port (
    clk        : in  std_logic;
    rst        : in  std_logic;
    system_out : in  std_logic_vector(system_width-1 downto 0);
    system_in  : out std_logic_vector(system_width-1 downto 0);
    links_in   : in  links_in_type;
    links_out  : out links_out_type;
    host_in    : in  host_in_type;
    host_out   : out host_out_type);
end hail_simulator;

architecture behave of hail_simulator is

  signal ctrl_bus_ivec   : std_logic_vector(ctrl_bus_width-1 downto 0) := (others => '0');
  signal status_bus_ivec : std_logic_vector(status_bus_width-1 downto 0) := (others => '0');
  signal irq_bus_ivec    : std_logic_vector(interrupt_bus_width-1 downto 0) := (others => '0');
  signal bram_bus_ivec   : std_logic_vector(bram_bus_width-1 downto 0) := (others => '0');
  signal locallinks_ivec : std_logic_vector(4*locallink_type_width-1 downto 0) := (others => '0');
  signal extrams_ivec    : std_logic_vector(6*extmem_type_width-1 downto 0) := (others => '0');

  signal ctrl_bus_ovec   : std_logic_vector(ctrl_bus_width-1 downto 0) := (others => '0');
  signal status_bus_ovec : std_logic_vector(status_bus_width-1 downto 0) := (others => '0');
  signal irq_bus_ovec    : std_logic_vector(interrupt_bus_width-1 downto 0) := (others => '0');
  signal bram_bus_ovec   : std_logic_vector(bram_bus_width-1 downto 0) := (others => '0');
  signal locallinks_ovec : std_logic_vector(4*locallink_type_width-1 downto 0) := (others => '0');
  signal extrams_ovec    : std_logic_vector(6*extmem_type_width-1 downto 0) := (others => '0');
  signal lclk_dummy : std_logic := '0';

  signal irq_bus  : interrupt_bus_type := (ier => (others => '0'),
                                           isr => (others => '0'));
  
  type extmem_array_type is array (0 to 5) of extmem_type;
  signal eram_in,eram_out : extmem_array_type;
 
  type locallink_array is array (0 to 3) of locallink_type;
  signal locallinks_o,locallinks_i : locallink_array;

  constant bank_size : integer := 2**16;
  type memory_bank_type is array (0 to bank_size-1) of integer;
  type memory_bank_array is array (0 to number_of_memories-1) of memory_bank_type;
 
  
  type q_type is array (0 to 5) of std_logic_vector(63 downto 0);
  signal q : q_type;
  signal ram_gnt : std_logic_vector(5 downto 0) := "000000";
  signal ram_busy : std_logic_vector(5 downto 0) := "000000";

  type qpipe_type is array (0 to 15) of std_logic_vector(63 downto 0);
  
 
begin  
  
  -- Split output system signals and convert data types
    split0 : split_system_signal(
    system     => system_out,
    lclk       => lclk_dummy,
    ctrl_bus   => ctrl_bus_ovec,
    status_bus => status_bus_ovec,
    irq_bus    => irq_bus_ovec,
    bram_bus   => bram_bus_ovec,
    locallinks => locallinks_ovec,
    extrams    => extrams_ovec);

     
  -- Merge signals into system inputs  
  merge0 : build_system_signal (
    system     => system_in,
    lclk       => host_in.clk,
    ctrl_bus   => ctrl_bus_ivec,
    status_bus => status_bus_ivec,
    irq_bus    => irq_bus_ivec,
    bram_bus   => bram_bus_ivec,
    locallinks => locallinks_ivec,
    extrams    => extrams_ivec);


  -- Connect Local Bus signals directly to host procedures
    
  host_out.status_bus <= slv_to_status_bus(status_bus_ovec);
  host_out.interrupt_bus <= slv_to_irq_bus(irq_bus_ovec);
  host_out.bram_bus <= slv_to_bram_bus(bram_bus_ovec);

  ctrl_bus_ivec <= ctrl_bus_to_slv(host_in.ctrl_bus);
  status_bus_ivec <= status_bus_to_slv(host_in.status_bus); 
  irq_bus_ivec <=  irq_bus_to_slv(irq_bus);
  bram_bus_ivec <= bram_bus_to_slv(host_in.bram_bus);

  -- Update IER only on IER write strobe
  process (host_in.interrupt_bus.ier_wstb)
  begin  -- process
    if host_in.interrupt_bus.ier_wstb'event and host_in.interrupt_bus.ier_wstb = '1' then
      irq_bus.ier <=  host_in.interrupt_bus.ier;
    end if;
  end process;
 
  irq_bus.isr <=  host_in.interrupt_bus.isr;
   

  -- Connect Local Links to local link ports
    
  split_ll_out: for i in 0 to 3 generate
    locallinks_o(i) <= slv_to_locallink_type(locallinks_ovec((i+1)*locallink_type_width-1 downto i*locallink_type_width));
    links_out(i).tx_data <= locallinks_o(i).tx_data;
    links_out(i).tx_src_rdy <= locallinks_o(i).tx_src_rdy;
    links_out(i).tx_sof <= locallinks_o(i).tx_sof;
    links_out(i).tx_eof <= locallinks_o(i).tx_eof;
    locallinks_i(i).tx_dst_rdy <= links_in(i).tx_dst_rdy;
    locallinks_i(i).rx_data <= links_in(i).rx_data;
    locallinks_i(i).rx_src_rdy <= links_in(i).rx_src_rdy;
    locallinks_i(i).rx_sof <= links_in(i).rx_sof;
    locallinks_i(i).rx_eof <= links_in(i).rx_eof;    
  end generate split_ll_out;
    
  locallinks_ivec <= locallink_type_to_slv(locallinks_i(3)) & locallink_type_to_slv(locallinks_i(2))& locallink_type_to_slv(locallinks_i(1)) &  locallink_type_to_slv(locallinks_i(0));


  -- Model External RAM banks internally
    
  connect_ram: for i in 0 to number_of_memories-1 generate
    eram_out(i) <= slv_to_extmem_type(extrams_ovec((i+1)*extmem_type_width-1 downto i*extmem_type_width));
    extrams_ivec((i+1)*extmem_type_width-1 downto i*extmem_type_width) <= extmem_type_to_slv(eram_in(i));
    -- Behavioural module of multi-clock access to RAM
    model_ram: process (host_in.clk,clk,rst)
       variable mem_msw,mem_lsw : memory_bank_type;
       variable qpipe : qpipe_type;
       variable qvpipe : std_logic_vector(15 downto 0);
    begin  -- process model_ram
      if rst = '1' then
        for j in 0 to bank_size-1 loop
          mem_msw(j) := 0;
          mem_lsw(j) := 0;
        end loop;  -- i
        ram_gnt(i) <= '0';
        ram_busy(i) <= '0';
        for j in 0 to 15 loop
          qpipe(j) := (others => '0');
          qvpipe(j) := '0';
        end loop;  -- j
        eram_in(i).gnt <= '0';
        eram_in(i).q <= (others => '0');
        eram_in(i).qv <= '0';
        eram_in(i).full <= '0';
      elsif rising_edge(clk) then
        if ram_gnt(i) = '1' and ram_busy(i) = '0' then
          if eram_out(i).w = '1' then
            mem_msw(CONV_INTEGER(eram_out(i).a)) := CONV_INTEGER(eram_out(i).d(63 downto 32));
            mem_lsw(CONV_INTEGER(eram_out(i).a)) := CONV_INTEGER(eram_out(i).d(31 downto 0));       
          elsif eram_out(i).r = '1' then
            qpipe(0) := CONV_STD_LOGIC_VECTOR(mem_msw(CONV_INTEGER(eram_out(i).a)),32) & CONV_STD_LOGIC_VECTOR(mem_lsw(CONV_INTEGER(eram_out(i).a)),32);
          end if;
          qvpipe(0) :=  eram_out(i).r;
        else
          qvpipe(0) :=  '0';
        end if;
        eram_in(i).gnt <= ram_gnt(i);
        eram_in(i).full <= ram_busy(i);
        for j in 15 downto 1 loop
          qpipe(j) := qpipe(j-1);
          qvpipe(j) := qvpipe(j-1);
        end loop;  -- j
        eram_in(i).q <= qpipe(15);
        eram_in(i).qv <= qvpipe(15);
      elsif rising_edge(host_in.clk) then
        if host_in.extmem_bus.bank = i then
          if host_in.extmem_bus.gnt = '1' then
            ram_gnt(i) <= '1';
          elsif host_in.extmem_bus.clrgnt = '1' then
            ram_gnt(i) <= '0';
          elsif host_in.extmem_bus.wstb = '1' then
            mem_msw(host_in.extmem_bus.addr) := CONV_INTEGER(host_in.extmem_bus.data(63 downto 32));
            mem_lsw(host_in.extmem_bus.addr) := CONV_INTEGER(host_in.extmem_bus.data(31 downto 0));
          elsif host_in.extmem_bus.rstb = '1' then
            q(i) <= CONV_STD_LOGIC_VECTOR(mem_msw(host_in.extmem_bus.addr),32) & CONV_STD_LOGIC_VECTOR(mem_lsw(host_in.extmem_bus.addr),32);
          end if;
        end if;  
      end if;
    end process model_ram;
                                                                          

                                                                          
  end generate connect_ram;
    
  process (host_in.extmem_bus.addr,q,host_in.clk)
  begin  -- process
    host_out.extmem_bus.data <= q(host_in.extmem_bus.bank);
    if rising_edge(host_in.clk) then
      host_out.extmem_bus.req <= eram_out(host_in.extmem_bus.bank).req;
    end if;
  end process;
    

end behave;
