--
-- Common Interface Package 
--
-- (C) 2006 Alpha Data
--
-- Abstract Interface layer package to simplify
-- top level system design in FPGA co-processor designs
--
-- Top-level of user design contains only 4 signals:
-- clk : system clock
-- rst : global reset
-- system_in  : std_logic_vector of system_width
-- system_out : std_logic_vector of system_width
--
-- system_in and system_out are very wide logic vectors
-- containing complicated abstract data type encapsulating all
-- necessary top level interface signals
-- basic data type is used to allow multi-langauge portability
--
-- Package components connect in a ring to the system bus
-- (i.e. pass through and/or modify signals of the system type)
--
-- Package Interface components provided:
--
-- control_register
-- status_register
-- interrupt_register
-- internal_memory
-- external_memory
-- locallink


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;

package common_interface is

  constant system_width : integer := 512*3;

  constant reg_width         : integer := 32;
  constant reg_addr_width    : integer := 7;
  constant bram_width        : integer := 64;
  constant bram_addr_width   : integer := 14;
  constant bram_id_width     : integer := 5;
  constant extmem_width      : integer := 64;
  constant extmem_addr_width : integer := 32;
  constant link_width        : integer := 16;

  type ctrl_bus_type is record
    data : std_logic_vector(reg_width-1 downto 0);
    addr : std_logic_vector(reg_addr_width-1 downto 0);
    wstb : std_logic;
  end record;
  constant ctrl_bus_width : integer := reg_width+reg_addr_width+1;

  type status_bus_type is record
    addr : std_logic_vector(reg_addr_width-1 downto 0);
    data : std_logic_vector(reg_width-1 downto 0);
  end record;
  constant status_bus_width : integer := reg_width+reg_addr_width;

  type interrupt_bus_type is record
    ier : std_logic_vector(reg_width-1 downto 0);
    isr : std_logic_vector(reg_width-1 downto 0);
  end record;
  constant interrupt_bus_width : integer := 2*reg_width;

  type bram_bus_type is record
    data : std_logic_vector(bram_width-1 downto 0);
    addr : std_logic_vector(bram_addr_width-1 downto 0);
    id   : std_logic_vector(bram_id_width-1 downto 0);
    wstb : std_logic;
    rstb : std_logic;
    q    : std_logic_vector(bram_width-1 downto 0);
    qv   : std_logic;
  end record;
  constant bram_bus_width : integer := bram_width*2+bram_addr_width+bram_id_width+3;

  type locallink_type is record
    rx_data    : std_logic_vector(link_width-1 downto 0);
    rx_src_rdy : std_logic;
    rx_sof     : std_logic;
    rx_eof     : std_logic;
    tx_dst_rdy : std_logic;
    tx_data    : std_logic_vector(link_width-1 downto 0);
    tx_src_rdy : std_logic;
    tx_sof     : std_logic;
    tx_eof     : std_logic;
  end record;
  constant locallink_type_width : integer := link_width*2 + 7;


  type extmem_type is record
    r    : std_logic;
    w    : std_logic;
    a    : std_logic_vector(extmem_addr_width-1 downto 0);
    d    : std_logic_vector(extmem_width-1 downto 0);
    q    : std_logic_vector(extmem_width-1 downto 0);
    qv   : std_logic;
    full : std_logic;
    req  : std_logic;
    gnt  : std_logic;
  end record;
  constant extmem_type_width : integer := 2*extmem_width+extmem_addr_width+6;

  function ctrl_bus_to_slv (
    signal ctrl_bus_sig : ctrl_bus_type)
    return std_logic_vector;

  function slv_to_ctrl_bus (
    signal slv : std_logic_vector(ctrl_bus_width-1 downto 0))
    return ctrl_bus_type;
  
  function status_bus_to_slv (
    signal status_bus_sig : status_bus_type)
    return std_logic_vector;

  function slv_to_status_bus (
    signal slv : std_logic_vector(status_bus_width-1 downto 0))
    return status_bus_type;

  function irq_bus_to_slv (
    signal irq_bus_sig : interrupt_bus_type)
    return std_logic_vector;

  function slv_to_irq_bus (
    signal slv : std_logic_vector(interrupt_bus_width-1 downto 0))
    return interrupt_bus_type;
  
  function bram_bus_to_slv (
    signal bram_bus_sig : bram_bus_type)
    return std_logic_vector;

  function slv_to_bram_bus (
    signal slv : std_logic_vector(bram_bus_width-1 downto 0))
    return bram_bus_type;

  function locallink_type_to_slv (
    signal locallink_type_sig : locallink_type)
    return std_logic_vector;

  function slv_to_locallink_type (
    signal slv : std_logic_vector(locallink_type_width-1 downto 0))
    return locallink_type;
  
  function extmem_type_to_slv (
    signal ems : extmem_type)
    return std_logic_vector;

  function slv_to_extmem_type (
    signal slv : std_logic_vector(extmem_type_width-1 downto 0))
    return extmem_type;
  
  procedure build_system_signal (
    signal system     : out std_logic_vector(system_width-1 downto 0);
    signal lclk       : in  std_logic;
    signal ctrl_bus   : in  std_logic_vector(ctrl_bus_width-1 downto 0);
    signal status_bus : in  std_logic_vector(status_bus_width-1 downto 0);
    signal irq_bus    : in  std_logic_vector(interrupt_bus_width-1 downto 0);
    signal bram_bus   : in  std_logic_vector(bram_bus_width-1 downto 0);
    signal locallinks : in  std_logic_vector(4*locallink_type_width-1 downto 0);
    signal extrams    : in  std_logic_vector(6*extmem_type_width-1 downto 0));

  procedure split_system_signal (
    signal system     : in  std_logic_vector(system_width-1 downto 0);
    signal lclk       : out std_logic;
    signal ctrl_bus   : out std_logic_vector(ctrl_bus_width-1 downto 0);
    signal status_bus : out std_logic_vector(status_bus_width-1 downto 0);
    signal irq_bus    : out std_logic_vector(interrupt_bus_width-1 downto 0);
    signal bram_bus   : out std_logic_vector(bram_bus_width-1 downto 0);
    signal locallinks : out std_logic_vector(4*locallink_type_width-1 downto 0);
    signal extrams    : out std_logic_vector(6*extmem_type_width-1 downto 0));

  function log2(
    constant x : in natural)
    return integer;

  component control_register
    generic (
      width     : integer := 32;
      addr      : integer := 0;
      sys_width : integer := system_width);
    port (
      clk          : in  std_logic;
      rst          : in  std_logic;
      system_in    : in  std_logic_vector(sys_width-1 downto 0);
      system_out   : out std_logic_vector(sys_width-1 downto 0);
      control_data : out std_logic_vector(width-1 downto 0);
      control_wstb : out std_logic);
  end component;

  component status_register
    generic (
      width     : integer := 32;
      addr      : integer := 0;
      sys_width : integer := system_width);
    port (
      clk         : in  std_logic;
      rst         : in  std_logic;
      system_in   : in  std_logic_vector(sys_width-1 downto 0);
      system_out  : out std_logic_vector(sys_width-1 downto 0);
      status_data : in  std_logic_vector(width-1 downto 0));
  end component;
  
  component readable_control_register
    generic (
      width     : integer := 32;
      addr      : integer := 0;
      sys_width : integer := system_width);
    port (
      clk          : in  std_logic;
      rst          : in  std_logic;
      system_in    : in  std_logic_vector(sys_width-1 downto 0);
      system_out   : out std_logic_vector(sys_width-1 downto 0);
      control_data : out std_logic_vector(width-1 downto 0);
      control_wstb : out std_logic);
  end component;
  
  component interrupt_register
    generic (
      width     : integer := 32;
      sys_width : integer := system_width);
    port (
      clk               : in  std_logic;
      rst               : in  std_logic;
      system_in         : in  std_logic_vector(sys_width-1 downto 0);
      system_out        : out std_logic_vector(sys_width-1 downto 0);
      interrupt_enable  : out std_logic_vector(width-1 downto 0);
      interrupt_trigger : in  std_logic_vector(width-1 downto 0));
  end component;


  component internal_memory
    generic (
      width     : integer := 8;
      depth     : integer := 14;
      bank_id   : integer := 0;
      sys_width : integer := system_width);
    port (
      clk        : in  std_logic;
      rst        : in  std_logic;
      system_in  : in  std_logic_vector(sys_width-1 downto 0);
      system_out : out std_logic_vector(sys_width-1 downto 0);
      w          : in  std_logic;
      a          : in  std_logic_vector(depth-1 downto 0);
      d          : in  std_logic_vector(width-1 downto 0);
      q          : out std_logic_vector(width-1 downto 0));
  end component;

  component external_memory
    generic (
      width     : integer := 64;
      depth     : integer := 26;
      bank_id   : integer := 0;
      sys_width : integer := system_width);
    port (
      clk        : in  std_logic;
      rst        : in  std_logic;
      system_in  : in  std_logic_vector(sys_width-1 downto 0);
      system_out : out std_logic_vector(sys_width-1 downto 0);
      w          : in  std_logic;
      r          : in  std_logic;
      a          : in  std_logic_vector(depth-1 downto 0);
      d          : in  std_logic_vector(width-1 downto 0);
      q          : out std_logic_vector(width-1 downto 0);
      qv         : out std_logic;
      full       : out std_logic;
      req        : in  std_logic;
      gnt        : out std_logic);
  end component;


  component locallink
      generic (
    link_id   : integer := 0;
    sys_width : integer := system_width);
  port (
    clk        : in  std_logic;
    rst        : in  std_logic;
    system_in  : in  std_logic_vector(sys_width-1 downto 0);
    system_out : out std_logic_vector(sys_width-1 downto 0);
    tx_src_rdy : in  std_logic;
    tx_dst_rdy : out std_logic;
    tx_data    : in  std_logic_vector(link_width-1 downto 0);
    tx_sof     : in  std_logic;
    tx_eof     : in  std_logic;
    rx_data    : out std_logic_vector(link_width-1 downto 0);
    rx_src_rdy : out std_logic;
    rx_sof     : out std_logic;
    rx_eof     : out std_logic);
  end component;


end common_interface;


package body common_interface is
  
  function ctrl_bus_to_slv (
    signal ctrl_bus_sig : in ctrl_bus_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(ctrl_bus_width-1 downto 0);
  begin
    x := ctrl_bus_sig.wstb & ctrl_bus_sig.addr & ctrl_bus_sig.data;
    return x;
  end function ctrl_bus_to_slv;

  function slv_to_ctrl_bus (
    signal slv : in std_logic_vector(ctrl_bus_width-1 downto 0))
    return ctrl_bus_type is
    variable x : ctrl_bus_type;
  begin
    x.data := slv(reg_width-1 downto 0);
    x.addr := slv(reg_width+reg_addr_width-1 downto reg_width);
    x.wstb := slv(reg_width+reg_addr_width);
    return x;
  end function slv_to_ctrl_bus;

  function status_bus_to_slv (
    signal status_bus_sig : in status_bus_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(status_bus_width-1 downto 0);
  begin
    x := status_bus_sig.addr & status_bus_sig.data;
    return x;
  end function status_bus_to_slv;

  function slv_to_status_bus (
    signal slv : in std_logic_vector(status_bus_width-1 downto 0))
    return status_bus_type is
    variable x : status_bus_type;
  begin
    x.data := slv(reg_width-1 downto 0);
    x.addr := slv(reg_width+reg_addr_width-1 downto reg_width);
    return x;
  end function slv_to_status_bus;

  function irq_bus_to_slv (
    signal irq_bus_sig : interrupt_bus_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(interrupt_bus_width-1 downto 0);
  begin
    x := irq_bus_sig.ier & irq_bus_sig.isr;
    return x;
  end function irq_bus_to_slv;

  function slv_to_irq_bus (
    signal slv : std_logic_vector(interrupt_bus_width-1 downto 0))
    return interrupt_bus_type is
    variable x : interrupt_bus_type;
  begin
    x.isr := slv(reg_width-1 downto 0);
    x.ier := slv(2*reg_width-1 downto reg_width);
    return x;
  end function slv_to_irq_bus;
  
  
  function bram_bus_to_slv (
    signal bram_bus_sig : in bram_bus_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(bram_bus_width-1 downto 0);
  begin
    x := bram_bus_sig.qv & bram_bus_sig.q & bram_bus_sig.rstb & bram_bus_sig.wstb & bram_bus_sig.id & bram_bus_sig.addr & bram_bus_sig.data;
    return x;
  end function bram_bus_to_slv;

  function slv_to_bram_bus (
    signal slv : in std_logic_vector(bram_bus_width-1 downto 0))
    return bram_bus_type is
    variable x : bram_bus_type;
  begin
    x.data := slv(bram_width-1 downto 0);
    x.addr := slv(bram_width+bram_addr_width-1 downto bram_width);
    x.id   := slv(bram_width+bram_addr_width+bram_id_width-1 downto bram_width+bram_addr_width);
    x.wstb := slv(bram_width+bram_addr_width+bram_id_width);
    x.rstb := slv(bram_width+bram_addr_width+bram_id_width+1);
    x.q    := slv(bram_width+2+bram_width+bram_addr_width+bram_id_width-1 downto 2+bram_id_width+bram_width+bram_addr_width);
    x.qv   := slv(bram_width+2+bram_width+bram_addr_width+bram_id_width);
    return x;
  end function slv_to_bram_bus;
  
  function locallink_type_to_slv (
    signal locallink_type_sig : in locallink_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(locallink_type_width-1 downto 0);
  begin
    x := locallink_type_sig.tx_data & locallink_type_sig.rx_data & locallink_type_sig.rx_src_rdy & locallink_type_sig.rx_sof & locallink_type_sig.rx_eof & locallink_type_sig.tx_dst_rdy & locallink_type_sig.tx_src_rdy & locallink_type_sig.tx_sof & locallink_type_sig.tx_eof;
    return x;
  end function locallink_type_to_slv;

  function slv_to_locallink_type (
    signal slv : in std_logic_vector(locallink_type_width-1 downto 0))
    return locallink_type is
    variable x : locallink_type;
  begin
    x.tx_eof     := slv(0);
    x.tx_sof     := slv(1);
    x.tx_src_rdy := slv(2);
    x.tx_dst_rdy := slv(3);
    x.rx_eof     := slv(4);
    x.rx_sof     := slv(5);
    x.rx_src_rdy := slv(6);
    x.rx_data    := slv(link_width+6 downto 7);
    x.tx_data    := slv(2*link_width+6 downto link_width+7);
    return x;
  end function slv_to_locallink_type;

  
  function extmem_type_to_slv (
    signal ems : in extmem_type)
    return std_logic_vector
  is
    variable x : std_logic_vector(extmem_type_width-1 downto 0);
  begin
    x := ems.d & ems.q & ems.a & ems.r & ems.w & ems.qv & ems.full & ems.req & ems.gnt;
    return x;
  end function extmem_type_to_slv;

  function slv_to_extmem_type (
    signal slv : in std_logic_vector(extmem_type_width-1 downto 0))
    return extmem_type is
    variable x : extmem_type;
  begin
    x.gnt  := slv(0);
    x.req  := slv(1);
    x.full := slv(2);
    x.qv   := slv(3);
    x.w    := slv(4);
    x.r    := slv(5);
    x.a    := slv(extmem_addr_width+5 downto 6);
    x.q    := slv(extmem_width+extmem_addr_width+5 downto extmem_addr_width+6);
    x.d    := slv(extmem_width+extmem_width+extmem_addr_width+5 downto extmem_width+extmem_addr_width+6);
    return x;
  end function slv_to_extmem_type;

  procedure build_system_signal (
    signal system     : out std_logic_vector(system_width-1 downto 0);
    signal lclk       : in  std_logic;
    signal ctrl_bus   : in  std_logic_vector(ctrl_bus_width-1 downto 0);
    signal status_bus : in  std_logic_vector(status_bus_width-1 downto 0);
    signal irq_bus    : in  std_logic_vector(interrupt_bus_width-1 downto 0);
    signal bram_bus   : in  std_logic_vector(bram_bus_width-1 downto 0);
    signal locallinks : in  std_logic_vector(4*locallink_type_width-1 downto 0);
    signal extrams    : in  std_logic_vector(6*extmem_type_width-1 downto 0)) is
  begin
    system(6*extmem_type_width+4*locallink_type_width+bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width downto 0) <= extrams & locallinks & bram_bus & irq_bus & status_bus & ctrl_bus & lclk;
  end procedure build_system_signal;
  
  procedure split_system_signal (
    signal system     : in  std_logic_vector(system_width-1 downto 0);
    signal lclk       : out std_logic;
    signal ctrl_bus   : out std_logic_vector(ctrl_bus_width-1 downto 0);
    signal status_bus : out std_logic_vector(status_bus_width-1 downto 0);
    signal irq_bus    : out std_logic_vector(interrupt_bus_width-1 downto 0);
    signal bram_bus   : out std_logic_vector(bram_bus_width-1 downto 0);
    signal locallinks : out std_logic_vector(4*locallink_type_width-1 downto 0);
    signal extrams    : out std_logic_vector(6*extmem_type_width-1 downto 0)) is        
  begin
    lclk       <= system(0);
    ctrl_bus   <= system(ctrl_bus_width downto 1);
    status_bus <= system(status_bus_width+ctrl_bus_width downto ctrl_bus_width+1);
    irq_bus    <= system(interrupt_bus_width+status_bus_width+ctrl_bus_width downto status_bus_width+ctrl_bus_width+1);
    bram_bus   <= system(bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width downto interrupt_bus_width+status_bus_width+ctrl_bus_width+1);
    locallinks <= system(4*locallink_type_width+bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width downto bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width+1);
    extrams    <= system(6*extmem_type_width+4*locallink_type_width+bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width downto 4*locallink_type_width+bram_bus_width+interrupt_bus_width+status_bus_width+ctrl_bus_width+1);
  end procedure split_system_signal;
  
  function log2 (
    constant x : in natural)
    return integer is
  begin
    for i in 0 to 31 loop
      if x <= 2**i then
        return i;
      end if;
    end loop;
  
    assert false
      report "log2() - unsupported input value, must be power of 2"
      severity failure;
    return 0;
  end;
  
end common_interface;
