--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- General Cores Library
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name:   wishbone_pkg
--
-- description: Collection of Wishbone modules and definitions used in various
-- OHWR projects.
--
--------------------------------------------------------------------------------
-- Copyright CERN 2011-2018
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package wishbone_pkg is

  constant c_wishbone_address_width : integer := 32;
  constant c_wishbone_data_width    : integer := 32;

  subtype t_wishbone_address is
    std_logic_vector(c_wishbone_address_width-1 downto 0);
  subtype t_wishbone_data is
    std_logic_vector(c_wishbone_data_width-1 downto 0);
  subtype t_wishbone_byte_select is
    std_logic_vector((c_wishbone_address_width/8)-1 downto 0);
  subtype t_wishbone_cycle_type is
    std_logic_vector(2 downto 0);
  subtype t_wishbone_burst_type is
    std_logic_vector(1 downto 0);

  type t_wishbone_interface_mode is (CLASSIC, PIPELINED);
  type t_wishbone_address_granularity is (BYTE, WORD);

  type t_wishbone_master_out is record
    cyc : std_logic;
    stb : std_logic;
    adr : t_wishbone_address;
    sel : t_wishbone_byte_select;
    we  : std_logic;
    dat : t_wishbone_data;
  end record t_wishbone_master_out;

  subtype t_wishbone_slave_in is t_wishbone_master_out;

  type t_wishbone_slave_out is record
    ack   : std_logic;
    err   : std_logic;
    rty   : std_logic;
    stall : std_logic;
    dat   : t_wishbone_data;
  end record t_wishbone_slave_out;

  subtype t_wishbone_master_in is t_wishbone_slave_out;

  -- variants for 64-bit data
  subtype t_wishbone_data64 is
    std_logic_vector(63 downto 0);
  subtype t_wishbone_byte_select_data64 is
    std_logic_vector(7 downto 0);

  type t_wishbone_master_data64_out is record
    cyc : std_logic;
    stb : std_logic;
    adr : t_wishbone_address;
    sel : t_wishbone_byte_select_data64;
    we  : std_logic;
    dat : t_wishbone_data64;
  end record t_wishbone_master_data64_out;

  subtype t_wishbone_slave_data64_in is t_wishbone_master_data64_out;

  type t_wishbone_slave_data64_out is record
    ack   : std_logic;
    err   : std_logic;
    rty   : std_logic;
    stall : std_logic;
    dat   : t_wishbone_data64;
  end record t_wishbone_slave_data64_out;

  subtype t_wishbone_master_data64_in is t_wishbone_slave_data64_out;

  type t_wishbone_master_data64_out_array is array (natural range <>) of t_wishbone_master_data64_out;
  subtype t_wishbone_slave_data64_in_array is t_wishbone_master_data64_out_array;
  type t_wishbone_slave_data64_out_array is array (natural range <>) of t_wishbone_slave_data64_out;
  subtype t_wishbone_master_data64_in_array is t_wishbone_slave_data64_out_array;

  subtype t_wishbone_device_descriptor is std_logic_vector(255 downto 0);

  type t_wishbone_byte_select_array is array(natural range <>) of t_wishbone_byte_select; 
  type t_wishbone_data_array is array(natural range <>) of t_wishbone_data; 
  type t_wishbone_address_array is array(natural range <>) of t_wishbone_address;
  type t_wishbone_master_out_array is array (natural range <>) of t_wishbone_master_out;
  subtype t_wishbone_slave_in_array is t_wishbone_master_out_array;
  type t_wishbone_slave_out_array is array (natural range <>) of t_wishbone_slave_out;
  subtype t_wishbone_master_in_array is t_wishbone_slave_out_array;

  constant c_DUMMY_WB_ADDR : std_logic_vector(c_WISHBONE_ADDRESS_WIDTH-1 downto 0) :=
    (others => 'X');
  constant c_DUMMY_WB_DATA : std_logic_vector(c_WISHBONE_DATA_WIDTH-1 downto 0) :=
    (others => 'X');
  constant c_DUMMY_WB_SEL : std_logic_vector(c_WISHBONE_DATA_WIDTH/8-1 downto 0) :=
    (others => 'X');
  constant c_DUMMY_WB_SLAVE_IN   : t_wishbone_slave_in :=
    ('0', '0', c_DUMMY_WB_ADDR, c_DUMMY_WB_SEL, 'X', c_DUMMY_WB_DATA);
  constant c_DUMMY_WB_MASTER_OUT : t_wishbone_master_out := c_DUMMY_WB_SLAVE_IN;
  constant c_DUMMY_WB_SLAVE_OUT  : t_wishbone_slave_out :=
    ('1', '0', '0', '0', c_DUMMY_WB_DATA);
  constant c_DUMMY_WB_MASTER_IN  : t_wishbone_master_in := c_DUMMY_WB_SLAVE_OUT;
  constant c_DUMMY_WB_ADDR_ARRAY : t_wishbone_address_array(0 downto 0) := (0 => c_DUMMY_WB_ADDR);
  -- Dangerous! c_STALL_WB_SLAVE_OUT and c_STALL_WB_MASTER_IN will stall the bus.
  -- Kept here for backward compatibility, if anyone was using cc_dummy_slave_out.
  constant c_STALL_WB_SLAVE_OUT  : t_wishbone_slave_out := ('X', 'X', 'X', 'X', c_DUMMY_WB_DATA);
  constant c_STALL_WB_MASTER_IN  : t_wishbone_master_in := c_DUMMY_WB_SLAVE_OUT;

  constant c_DUMMY_WB_SLAVE_D64_IN   : t_wishbone_slave_data64_in :=
    ('0', '0', c_DUMMY_WB_ADDR, (others => 'X'), 'X', (others => 'X'));

  -- For backward compatibility
  constant cc_dummy_address : std_logic_vector(c_wishbone_address_width-1 downto 0) := c_DUMMY_WB_ADDR;
  constant cc_dummy_data : std_logic_vector(c_wishbone_data_width-1 downto 0) := c_DUMMY_WB_DATA;
  constant cc_dummy_sel : std_logic_vector(c_wishbone_data_width/8-1 downto 0) := c_DUMMY_WB_SEL;
  constant cc_dummy_slave_in : t_wishbone_slave_in := c_DUMMY_WB_SLAVE_IN;
  constant cc_dummy_master_out : t_wishbone_master_out := c_DUMMY_WB_MASTER_OUT;
  constant cc_dummy_slave_out : t_wishbone_slave_out := c_STALL_WB_SLAVE_OUT;
  constant cc_dummy_master_in : t_wishbone_master_in := c_STALL_WB_MASTER_IN;
  constant cc_dummy_address_array : t_wishbone_address_array(0 downto 0) := c_DUMMY_WB_ADDR_ARRAY;

  -- A generally useful function.
  function f_ceil_log2(x   : natural) return natural;
  function f_bits2string(s : std_logic_vector) return string;

  function f_string2bits(s : string) return std_logic_vector;
  function f_string2svl (s : string) return std_logic_vector;
  function f_slv2string (slv : std_logic_vector) return string;

  function f_string_fix_len( s : string; ret_len : natural := 10; fill_char : character := '0'; justify_right : boolean := true ) return string;
  function f_hot_to_bin(x : std_logic_vector) return natural;
  
  -- *** Wishbone slave interface functions ***
  -- f_wb_wr:
  -- processes an incoming write reqest to a register while honoring the select lines
  -- valid modes are overwrite "owr", set "set" (bits are or'ed) and clear "clr" (bits are nand'ed)
  function f_wb_wr(pval : std_logic_vector; ival : std_logic_vector; sel : std_logic_vector; mode : string := "owr") return std_logic_vector;
------------------------------------------------------------------------------
-- SDB declaration
------------------------------------------------------------------------------

  constant c_sdb_device_length : natural := 512;  -- bits
  subtype  t_sdb_record is std_logic_vector(c_sdb_device_length-1 downto 0);
  type     t_sdb_record_array is array(natural range <>) of t_sdb_record;
  
  type t_sdb_product is record
    vendor_id : std_logic_vector(63 downto 0);
    device_id : std_logic_vector(31 downto 0);
    version   : std_logic_vector(31 downto 0);
    date      : std_logic_vector(31 downto 0);
    name      : string(1 to 19);
  end record t_sdb_product;

  type t_sdb_component is record
    addr_first : std_logic_vector(63 downto 0);
    addr_last  : std_logic_vector(63 downto 0);
    product    : t_sdb_product;
  end record t_sdb_component;

  constant c_sdb_endian_big    : std_logic := '0';
  constant c_sdb_endian_little : std_logic := '1';
  type     t_sdb_device is record
    abi_class     : std_logic_vector(15 downto 0);
    abi_ver_major : std_logic_vector(7 downto 0);
    abi_ver_minor : std_logic_vector(7 downto 0);
    wbd_endian    : std_logic;          -- 0 = big, 1 = little
    wbd_width     : std_logic_vector(3 downto 0);  -- 3=64-bit, 2=32-bit, 1=16-bit, 0=8-bit
    sdb_component : t_sdb_component;
  end record t_sdb_device;

  type t_sdb_msi is record
    wbd_endian    : std_logic;          -- 0 = big, 1 = little
    wbd_width     : std_logic_vector(3 downto 0);  -- 3=64-bit, 2=32-bit, 1=16-bit, 0=8-bit
    sdb_component : t_sdb_component;
  end record t_sdb_msi;

  type t_sdb_bridge is record
    sdb_child     : std_logic_vector(63 downto 0);
    sdb_component : t_sdb_component;
  end record t_sdb_bridge;

  type t_sdb_integration is record
    product : t_sdb_product;
  end record t_sdb_integration;

  type t_sdb_repo_url is record
    repo_url : string(1 to 63);
  end record t_sdb_repo_url;

  type t_sdb_synthesis is record
    syn_module_name  : string(1 to 16);
    syn_commit_id    : string(1 to 32);
    syn_tool_name    : string(1 to 8);
    syn_tool_version : std_logic_vector(31 downto 0);
    syn_date         : std_logic_vector(31 downto 0);
    syn_username     : string(1 to 15);
  end record t_sdb_synthesis;
  
  -- If you have a Wishbone master that does not receive MSI,
  -- list it in the layout as 'f_sdb_auto_msi(c_null_msi, false)'
  constant c_null_msi : t_sdb_msi := (
   wbd_endian    => c_sdb_endian_big,
   wbd_width     => x"0",
   sdb_component => (
   addr_first    => x"0000000000000000",
   addr_last     => x"0000000000000000",
   product => (
   vendor_id     => x"0000000000000000",
   device_id     => x"00000000",
   version       => x"00000000",
   date          => x"00000000",
   name          => "                   ")));  

  -- general crossbar building functions
  function f_sdb_create_array(g_enum_dev_id  : boolean := false;
                              g_dev_id_offs    : natural := 0;
                              g_enum_dev_name  : boolean := false;
                              g_dev_name_offs  : natural := 0;       
                              device           : t_sdb_device; 
                              instances        : natural := 1) return t_sdb_record_array;  
  function f_sdb_join_arrays(a : t_sdb_record_array; b : t_sdb_record_array) return t_sdb_record_array;
  function f_sdb_extract_base_addr(sdb_record : t_sdb_record) return std_logic_vector;
  function f_sdb_extract_end_addr(sdb_record : t_sdb_record)  return std_logic_vector;
  function f_sdb_automap_array(sdb_array : t_sdb_record_array; start_offset : t_wishbone_address := (others => '0')) return t_sdb_record_array;  
  function f_align_addr_offset(offs : unsigned; this_rng : unsigned; prev_rng : unsigned) return unsigned; 
  function f_sdb_create_rom_addr(sdb_array : t_sdb_record_array) return t_wishbone_address;


  -- Used to configure a device at a certain address
  function f_sdb_embed_device(device : t_sdb_device; address : t_wishbone_address) return t_sdb_record;
  function f_sdb_embed_bridge(bridge : t_sdb_bridge; address : t_wishbone_address) return t_sdb_record;
  function f_sdb_embed_msi(msi : t_sdb_msi; address : t_wishbone_address) return t_sdb_record;
  function f_sdb_embed_integration(integr : t_sdb_integration) return t_sdb_record;
  function f_sdb_embed_repo_url(url : t_sdb_repo_url) return t_sdb_record;
  function f_sdb_embed_synthesis(syn : t_sdb_synthesis) return t_sdb_record;

  function f_sdb_extract_device(sdb_record : t_sdb_record) return t_sdb_device;
  function f_sdb_extract_bridge(sdb_record : t_sdb_record) return t_sdb_bridge;
  function f_sdb_extract_msi(sdb_record : t_sdb_record) return t_sdb_msi;
  function f_sdb_extract_integration(sdb_record : t_sdb_record) return t_sdb_integration;
  function f_sdb_extract_repo_url(sdb_record : t_sdb_record) return t_sdb_repo_url;
  function f_sdb_extract_synthesis(sdb_record : t_sdb_record) return t_sdb_synthesis;

  -- Automatic crossbar mapping functions
  function f_sdb_auto_device(device : t_sdb_device; enable : boolean := true; name: string := "") return t_sdb_record;
  function f_sdb_auto_bridge(bridge : t_sdb_bridge; enable : boolean := true; name: string := "") return t_sdb_record;
  function f_sdb_auto_msi   (msi    : t_sdb_msi;    enable : boolean := true) return t_sdb_record;
  function f_sdb_auto_layout(records: t_sdb_record_array)                               return t_sdb_record_array;
  function f_sdb_auto_layout(slaves : t_sdb_record_array; masters : t_sdb_record_array) return t_sdb_record_array;
  function f_sdb_auto_sdb   (records: t_sdb_record_array)                               return t_wishbone_address;
  function f_sdb_auto_sdb   (slaves : t_sdb_record_array; masters : t_sdb_record_array) return t_wishbone_address;
  
  -- For internal use by the crossbar
  function f_sdb_bus_end(g_wraparound : boolean; g_layout : t_sdb_record_array; g_sdb_addr : t_wishbone_address; msi : boolean) return unsigned;
  function f_sdb_embed_product(product         : t_sdb_product) return std_logic_vector;  -- (319 downto 8)
  function f_sdb_embed_component(sdb_component : t_sdb_component; address : t_wishbone_address) return std_logic_vector;  -- (447 downto 8)
  function f_sdb_extract_product(sdb_record    : std_logic_vector(319 downto 8)) return t_sdb_product;
  function f_sdb_extract_component(sdb_record  : std_logic_vector(447 downto 8)) return t_sdb_component;

------------------------------------------------------------------------------
-- Components declaration
-------------------------------------------------------------------------------

  component wb_slave_adapter
    generic (
      g_master_use_struct  : boolean;
      g_master_mode        : t_wishbone_interface_mode;
      g_master_granularity : t_wishbone_address_granularity;
      g_slave_use_struct   : boolean;
      g_slave_mode         : t_wishbone_interface_mode;
      g_slave_granularity  : t_wishbone_address_granularity);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      sl_adr_i   : in  std_logic_vector(c_wishbone_address_width-1 downto 0) := cc_dummy_address;
      sl_dat_i   : in  std_logic_vector(c_wishbone_data_width-1 downto 0)    := cc_dummy_data;
      sl_sel_i   : in  std_logic_vector(c_wishbone_data_width/8-1 downto 0)  := cc_dummy_sel;
      sl_cyc_i   : in  std_logic                                             := '0';
      sl_stb_i   : in  std_logic                                             := '0';
      sl_we_i    : in  std_logic                                             := '0';
      sl_dat_o   : out std_logic_vector(c_wishbone_data_width-1 downto 0);
      sl_err_o   : out std_logic;
      sl_rty_o   : out std_logic;
      sl_ack_o   : out std_logic;
      sl_stall_o : out std_logic;
      slave_i    : in  t_wishbone_slave_in                                   := cc_dummy_slave_in;
      slave_o    : out t_wishbone_slave_out;
      ma_adr_o   : out std_logic_vector(c_wishbone_address_width-1 downto 0);
      ma_dat_o   : out std_logic_vector(c_wishbone_data_width-1 downto 0);
      ma_sel_o   : out std_logic_vector(c_wishbone_data_width/8-1 downto 0);
      ma_cyc_o   : out std_logic;
      ma_stb_o   : out std_logic;
      ma_we_o    : out std_logic;
      ma_dat_i   : in  std_logic_vector(c_wishbone_data_width-1 downto 0)    := cc_dummy_data;
      ma_err_i   : in  std_logic                                             := '0';
      ma_rty_i   : in  std_logic                                             := '0';
      ma_ack_i   : in  std_logic                                             := '0';
      ma_stall_i : in  std_logic                                             := '0';
      master_i   : in  t_wishbone_master_in                                  := cc_dummy_slave_out;
      master_o   : out t_wishbone_master_out);
  end component;

  component wb_async_bridge
    generic (
      g_simulation          : integer;
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_cpu_address_width   : integer);
    port (
      rst_n_i     : in    std_logic;
      clk_sys_i   : in    std_logic;
      cpu_cs_n_i  : in    std_logic;
      cpu_wr_n_i  : in    std_logic;
      cpu_rd_n_i  : in    std_logic;
      cpu_bs_n_i  : in    std_logic_vector(3 downto 0);
      cpu_addr_i  : in    std_logic_vector(g_cpu_address_width-1 downto 0);
      cpu_data_b  : inout std_logic_vector(31 downto 0);
      cpu_nwait_o : out   std_logic;
      wb_adr_o    : out   std_logic_vector(c_wishbone_address_width - 1 downto 0);
      wb_dat_o    : out   std_logic_vector(31 downto 0);
      wb_stb_o    : out   std_logic;
      wb_we_o     : out   std_logic;
      wb_sel_o    : out   std_logic_vector(3 downto 0);
      wb_cyc_o    : out   std_logic;
      wb_dat_i    : in    std_logic_vector (c_wishbone_data_width-1 downto 0);
      wb_ack_i    : in    std_logic;
      wb_stall_i  : in    std_logic := '0');
  end component;

  component xwb_async_bridge
    generic (
      g_simulation          : integer;
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_cpu_address_width   : integer);
    port (
      rst_n_i     : in    std_logic;
      clk_sys_i   : in    std_logic;
      cpu_cs_n_i  : in    std_logic;
      cpu_wr_n_i  : in    std_logic;
      cpu_rd_n_i  : in    std_logic;
      cpu_bs_n_i  : in    std_logic_vector(3 downto 0);
      cpu_addr_i  : in    std_logic_vector(g_cpu_address_width-1 downto 0);
      cpu_data_b  : inout std_logic_vector(31 downto 0);
      cpu_nwait_o : out   std_logic;
      master_o    : out   t_wishbone_master_out;
      master_i    : in    t_wishbone_master_in);
  end component;

  component xwb_bus_fanout
    generic (
      g_num_outputs          : natural;
      g_bits_per_slave       : integer;
      g_address_granularity  : t_wishbone_address_granularity := WORD;
      g_slave_interface_mode : t_wishbone_interface_mode      := CLASSIC);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out;
      master_i  : in  t_wishbone_master_in_array(0 to g_num_outputs-1);
      master_o  : out t_wishbone_master_out_array(0 to g_num_outputs-1));
  end component;

  component xwb_crossbar
    generic (
      g_num_masters : integer;
      g_num_slaves  : integer;
      g_registered  : boolean;
      g_address     : t_wishbone_address_array;
      g_mask        : t_wishbone_address_array;
      g_verbose     : boolean := true);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave_i   : in  t_wishbone_slave_in_array(g_num_masters-1 downto 0);
      slave_o   : out t_wishbone_slave_out_array(g_num_masters-1 downto 0);
      master_i  : in  t_wishbone_master_in_array(g_num_slaves-1 downto 0);
      master_o  : out t_wishbone_master_out_array(g_num_slaves-1 downto 0);
      sdb_sel_o : out std_logic_vector(g_num_masters-1 downto 0)); -- leave open!
  end component;

  -- Use the f_xwb_bridge_*_sdb to bridge a crossbar to another
  function f_xwb_bridge_manual_sdb(     -- take a manual bus size
    g_size     : t_wishbone_address;
    g_sdb_addr : t_wishbone_address) return t_sdb_bridge;

  function f_xwb_bridge_layout_sdb(     -- determine bus size from layout
    g_wraparound : boolean := true;
    g_layout     : t_sdb_record_array;
    g_sdb_addr   : t_wishbone_address) return t_sdb_bridge;

  function f_xwb_msi_manual_sdb(        -- take a manual bus size
    g_size     : t_wishbone_address) return t_sdb_msi;

  function f_xwb_msi_layout_sdb(     -- determine MSI size from layout
    g_layout     : t_sdb_record_array) return t_sdb_msi;

  component xwb_sdb_crossbar
    generic (
      g_verbose     : boolean := true;
      g_num_masters : integer;
      g_num_slaves  : integer;
      g_registered  : boolean := false;
      g_wraparound  : boolean := true;
      g_layout      : t_sdb_record_array;
      g_sdb_addr    : t_wishbone_address;
      g_sdb_wb_mode : t_wishbone_interface_mode := CLASSIC;
      g_sdb_name    : string := "WB4-Crossbar-GSI   ");
    port (
      clk_sys_i     : in  std_logic;
      rst_n_i       : in  std_logic;
      slave_i       : in  t_wishbone_slave_in_array  (g_num_masters-1 downto 0);
      slave_o       : out t_wishbone_slave_out_array (g_num_masters-1 downto 0);
      msi_master_i  : in  t_wishbone_master_in_array (g_num_masters-1 downto 0) := (others => cc_dummy_master_in);
      msi_master_o  : out t_wishbone_master_out_array(g_num_masters-1 downto 0);
      master_i      : in  t_wishbone_master_in_array (g_num_slaves -1 downto 0);
      master_o      : out t_wishbone_master_out_array(g_num_slaves -1 downto 0);
      msi_slave_i   : in  t_wishbone_slave_in_array  (g_num_slaves -1 downto 0) := (others => cc_dummy_slave_in);
      msi_slave_o   : out t_wishbone_slave_out_array (g_num_slaves -1 downto 0));
  end component;
  
  component xwb_register_link  -- puts a register of delay between crossbars
    generic (
      g_WB_IN_MODE         : t_wishbone_interface_mode      := PIPELINED;
      g_WB_IN_GRANULARITY  : t_wishbone_address_granularity := BYTE;
      g_WB_OUT_MODE        : t_wishbone_interface_mode      := PIPELINED;
      g_WB_OUT_GRANULARITY : t_wishbone_address_granularity := BYTE);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out;
      master_i  : in  t_wishbone_master_in;
      master_o  : out t_wishbone_master_out);
  end component;

  component xwb_register is
    generic (
      g_WB_MODE  : t_wishbone_interface_mode := PIPELINED);
    port (
      rst_n_i  : in  std_logic;
      clk_i    : in  std_logic;
      slave_i  : in  t_wishbone_slave_in;
      slave_o  : out t_wishbone_slave_out;
      master_i : in  t_wishbone_master_in;
      master_o : out t_wishbone_master_out);
  end component xwb_register;

  -- skidpad. acts like a fifo in wb flow control, but costs less
  component wb_skidpad is
  generic(
     g_adrbits   : natural   := 32
  );
  Port(
     clk_i        : std_logic;
     rst_n_i      : std_logic;

     push_i       : in  std_logic;
     pop_i        : in  std_logic;
     full_o       : out std_logic;
     empty_o      : out std_logic;

     adr_i        : in  std_logic_vector(g_adrbits-1 downto 0);
     dat_i        : in  std_logic_vector(32-1 downto 0);
     sel_i        : in  std_logic_vector(4-1 downto 0);
     we_i         : in  std_logic;

     adr_o        : out std_logic_vector(g_adrbits-1 downto 0);
     dat_o        : out std_logic_vector(32-1 downto 0);
     sel_o        : out std_logic_vector(4-1 downto 0);
     we_o         : out std_logic
  );
  end component;

  component sdb_rom is
    generic(
      g_layout   : t_sdb_record_array;
      g_masters  : natural;
      g_bus_end  : unsigned(63 downto 0);
      g_wb_mode  : t_wishbone_interface_mode := CLASSIC;
      g_sdb_name : string                    := "WB4-Crossbar-GSI   ");
    port(
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic := '1';
      master_i  : in  std_logic_vector(g_masters-1 downto 0);
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out);
  end component;
  
  constant c_xwb_dma_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"00",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"000000000000001f",
      product     => (
        vendor_id => x"0000000000000651",  -- GSI
        device_id => x"cababa56",
        version   => x"00000001",
        date      => x"20120518",
        name      => "WB4-Streaming-DMA_0")));
  component xwb_dma is
    generic(
      -- Value 0 cannot stream
      -- Value 1 only slaves with async ACK can stream
      -- Value 2 only slaves with combined latency <= 2 can stream
      -- Value 3 only slaves with combined latency <= 6 can stream
      -- Value 4 only slaves with combined latency <= 14 can stream
      -- ....
      logRingLen : integer := 4
      );
    port(
      -- Common wishbone signals
      clk_i       : in  std_logic;
      rst_n_i     : in  std_logic;
      slave_i     : in  t_wishbone_slave_in;
      slave_o     : out t_wishbone_slave_out;
      -- Master reader port
      r_master_i  : in  t_wishbone_master_in;
      r_master_o  : out t_wishbone_master_out;
      -- Master writer port
      w_master_i  : in  t_wishbone_master_in;
      w_master_o  : out t_wishbone_master_out;
      -- Pulsed high completion signal
      interrupt_o : out std_logic
      );
  end component;

  -- If you reset one clock domain, you must reset BOTH!
  -- Release of the reset lines may be arbitrarily out-of-phase
  component xwb_clock_crossing is
    generic(
      g_size : natural := 16);
    port(
      -- Slave control port
      slave_clk_i    : in  std_logic;
      slave_rst_n_i  : in  std_logic;
      slave_i        : in  t_wishbone_slave_in;
      slave_o        : out t_wishbone_slave_out;
      -- Master reader port
      master_clk_i   : in  std_logic;
      master_rst_n_i : in  std_logic;
      master_i       : in  t_wishbone_master_in;
      master_o       : out t_wishbone_master_out;
      -- Flow control back-channel for acks
      slave_ready_o : out std_logic;
      slave_stall_i : in  std_logic := '0');
  end component;

  component xwb_clock_bridge is
    generic (
      g_SLAVE_PORT_WB_MODE  : t_wishbone_interface_mode := PIPELINED;
      g_MASTER_PORT_WB_MODE : t_wishbone_interface_mode := PIPELINED;
      g_SIZE                : natural                   := 16);
    port (
      slave_clk_i    : in  std_logic;
      slave_rst_n_i  : in  std_logic;
      slave_i        : in  t_wishbone_slave_in;
      slave_o        : out t_wishbone_slave_out;
      master_clk_i   : in  std_logic;
      master_rst_n_i : in  std_logic;
      master_i       : in  t_wishbone_master_in;
      master_o       : out t_wishbone_master_out);
  end component xwb_clock_bridge;

  -- g_size is in words
  function f_xwb_dpram(g_size : natural) return t_sdb_device;
  component xwb_dpram
    generic (
      g_size                  : natural;
      g_init_file             : string                         := "";
      g_must_have_init_file   : boolean                        := true;
      g_slave1_interface_mode : t_wishbone_interface_mode      := CLASSIC;
      g_slave2_interface_mode : t_wishbone_interface_mode      := CLASSIC;
      g_slave1_granularity    : t_wishbone_address_granularity := WORD;
      g_slave2_granularity    : t_wishbone_address_granularity := WORD);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave1_i  : in  t_wishbone_slave_in;
      slave1_o  : out t_wishbone_slave_out;
      slave2_i  : in  t_wishbone_slave_in;
      slave2_o  : out t_wishbone_slave_out);
  end component;

  component xwb_dpram_mixed
  generic(
    g_size                  : natural := 16384;
    g_init_file             : string  := "";
    g_must_have_init_file   : boolean := true;
    g_swap_word_endianness  : boolean := true;
    g_slave1_interface_mode : t_wishbone_interface_mode;
    g_slave2_interface_mode : t_wishbone_interface_mode;
    g_dpram_port_a_width    : integer := 16;
    g_dpram_port_b_width    : integer := 32;
    g_slave1_granularity    : t_wishbone_address_granularity;
    g_slave2_granularity    : t_wishbone_address_granularity);
  port(
    clk_slave1_i  : in std_logic;
    clk_slave2_i  : in std_logic;
    rst_n_i       : in std_logic;

    slave1_i      : in  t_wishbone_slave_in;
    slave1_o      : out t_wishbone_slave_out;
    slave2_i      : in  t_wishbone_slave_in;
    slave2_o      : out t_wishbone_slave_out);
  end component;
  
  -- Just like the DMA controller, but constantly at address 0
  component xwb_streamer is
    generic(
      -- Value 0 cannot stream
      -- Value 1 only slaves with async ACK can stream
      -- Value 2 only slaves with combined latency = 2 can stream
      -- Value 3 only slaves with combined latency = 6 can stream
      -- Value 4 only slaves with combined latency = 14 can stream
      -- ....
      logRingLen : integer := 4
    );
    port(
      -- Common wishbone signals
      clk_i       : in  std_logic;
      rst_n_i     : in  std_logic;
      -- Master reader port
      r_master_i  : in  t_wishbone_master_in;
      r_master_o  : out t_wishbone_master_out;
      -- Master writer port
      w_master_i  : in  t_wishbone_master_in;
      w_master_o  : out t_wishbone_master_out);
  end component;


  constant c_xwb_gpio_port_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"00000000000000ff",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"441c5143",
        version   => x"00000001",
        date      => x"20121129",
        name      => "WB-GPIO-Port       ")));


  component wb_gpio_port
    generic (
      g_interface_mode         : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity    : t_wishbone_address_granularity := WORD;
      g_num_pins               : natural range 1 to 256;
      g_with_builtin_sync      : boolean                        := true;
      g_with_builtin_tristates : boolean                        := false);
    port (
      clk_sys_i  : in    std_logic;
      rst_n_i    : in    std_logic;
      wb_sel_i   : in    std_logic_vector(c_wishbone_data_width/8-1 downto 0);
      wb_cyc_i   : in    std_logic;
      wb_stb_i   : in    std_logic;
      wb_we_i    : in    std_logic;
      wb_adr_i   : in    std_logic_vector(7 downto 0);
      wb_dat_i   : in    std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_dat_o   : out   std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_ack_o   : out   std_logic;
      wb_stall_o : out   std_logic;
      gpio_b     : inout std_logic_vector(g_num_pins-1 downto 0);
      gpio_out_o : out   std_logic_vector(g_num_pins-1 downto 0);
      gpio_in_i  : in    std_logic_vector(g_num_pins-1 downto 0);
      gpio_oen_o : out   std_logic_vector(g_num_pins-1 downto 0));
  end component;

  component xwb_gpio_port
    generic (
      g_interface_mode         : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity    : t_wishbone_address_granularity := WORD;
      g_num_pins               : natural range 1 to 256;
      g_with_builtin_sync      : boolean                        := true;
      g_with_builtin_tristates : boolean                        := false);
    port (
      clk_sys_i  : in    std_logic;
      rst_n_i    : in    std_logic;
      slave_i    : in    t_wishbone_slave_in;
      slave_o    : out   t_wishbone_slave_out;
      desc_o     : out   t_wishbone_device_descriptor;
      gpio_b     : inout std_logic_vector(g_num_pins-1 downto 0);
      gpio_out_o : out   std_logic_vector(g_num_pins-1 downto 0);
      gpio_in_i  : in    std_logic_vector(g_num_pins-1 downto 0);
      gpio_oen_o : out   std_logic_vector(g_num_pins-1 downto 0));
  end component;

  constant c_xwb_i2c_master_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"00000000000000ff",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"123c5443",
        version   => x"00000001",
        date      => x"20121129",
        name      => "WB-I2C-Master      ")));


  component wb_i2c_master
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_num_interfaces      : integer := 1);
    port (
      clk_sys_i    : in  std_logic;
      rst_n_i      : in  std_logic;
      wb_adr_i     : in  std_logic_vector(4 downto 0);
      wb_dat_i     : in  std_logic_vector(31 downto 0);
      wb_dat_o     : out std_logic_vector(31 downto 0);
      wb_sel_i     : in  std_logic_vector(3 downto 0);
      wb_stb_i     : in  std_logic;
      wb_cyc_i     : in  std_logic;
      wb_we_i      : in  std_logic;
      wb_ack_o     : out std_logic;
      wb_stall_o   : out std_logic;
      int_o        : out std_logic;
      scl_pad_i    : in  std_logic_vector(g_num_interfaces-1 downto 0);
      scl_pad_o    : out std_logic_vector(g_num_interfaces-1 downto 0);
      scl_padoen_o : out std_logic_vector(g_num_interfaces-1 downto 0);
      sda_pad_i    : in  std_logic_vector(g_num_interfaces-1 downto 0);
      sda_pad_o    : out std_logic_vector(g_num_interfaces-1 downto 0);
      sda_padoen_o : out std_logic_vector(g_num_interfaces-1 downto 0));
  end component;

  component xwb_i2c_master
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_num_interfaces      : integer := 1);
    port (
      clk_sys_i    : in  std_logic;
      rst_n_i      : in  std_logic;
      slave_i      : in  t_wishbone_slave_in;
      slave_o      : out t_wishbone_slave_out;
      desc_o       : out t_wishbone_device_descriptor;
      int_o        : out std_logic;
      scl_pad_i    : in  std_logic_vector(g_num_interfaces-1 downto 0);
      scl_pad_o    : out std_logic_vector(g_num_interfaces-1 downto 0);
      scl_padoen_o : out std_logic_vector(g_num_interfaces-1 downto 0);
      sda_pad_i    : in  std_logic_vector(g_num_interfaces-1 downto 0);
      sda_pad_o    : out std_logic_vector(g_num_interfaces-1 downto 0);
      sda_padoen_o : out std_logic_vector(g_num_interfaces-1 downto 0));
  end component;

  component xwb_lm32
    generic (
      g_profile : string;
      g_reset_vector : std_logic_vector(31 downto 0) := x"00000000";
      g_sdb_address  : std_logic_vector(31 downto 0) := x"00000000");
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      irq_i     : in  std_logic_vector(31 downto 0);
      dwb_o     : out t_wishbone_master_out;
      dwb_i     : in  t_wishbone_master_in;
      iwb_o     : out t_wishbone_master_out;
      iwb_i     : in  t_wishbone_master_in);
  end component;

  constant c_xwb_onewire_master_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"00000000000000ff",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"779c5443",
        version   => x"00000001",
        date      => x"20121129",
        name      => "WB-OneWire-Master  ")));

  component wb_onewire_master
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_num_ports           : integer;
      g_ow_btp_normal       : string;
      g_ow_btp_overdrive    : string;
      g_CDR_N               : integer;
      g_CDR_O               : integer);
    port (
      clk_sys_i   : in  std_logic;
      rst_n_i     : in  std_logic;
      wb_cyc_i    : in  std_logic;
      wb_sel_i    : in  std_logic_vector(c_wishbone_data_width/8-1 downto 0);
      wb_stb_i    : in  std_logic;
      wb_we_i     : in  std_logic;
      wb_adr_i    : in  std_logic_vector(2 downto 0);
      wb_dat_i    : in  std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_dat_o    : out std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_ack_o    : out std_logic;
      wb_stall_o  : out std_logic;
      int_o       : out std_logic;
      owr_pwren_o : out std_logic_vector(g_num_ports -1 downto 0);
      owr_en_o    : out std_logic_vector(g_num_ports -1 downto 0);
      owr_i       : in  std_logic_vector(g_num_ports -1 downto 0));
  end component;

  component xwb_onewire_master
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_num_ports           : integer;
      g_ow_btp_normal       : string                         := "5.0";
      g_ow_btp_overdrive    : string                         := "1.0");
    port (
      clk_sys_i   : in  std_logic;
      rst_n_i     : in  std_logic;
      slave_i     : in  t_wishbone_slave_in;
      slave_o     : out t_wishbone_slave_out;
      desc_o      : out t_wishbone_device_descriptor;
      int_o       : out std_logic;
      owr_pwren_o : out std_logic_vector(g_num_ports -1 downto 0);
      owr_en_o    : out std_logic_vector(g_num_ports -1 downto 0);
      owr_i       : in  std_logic_vector(g_num_ports -1 downto 0));
  end component;

  constant c_xwb_spi_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"000000000000001F",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"e503947e",       -- echo "WB-SPI.Control     " | md5sum | cut -c1-8
        version   => x"00000001",
        date      => x"20121116",
        name      => "WB-SPI.Control     ")));

  component wb_spi
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_divider_len         : integer := 16;
      g_max_char_len        : integer := 128;
      g_num_slaves          : integer := 8);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      wb_adr_i   : in  std_logic_vector(4 downto 0);
      wb_dat_i   : in  std_logic_vector(31 downto 0);
      wb_dat_o   : out std_logic_vector(31 downto 0);
      wb_sel_i   : in  std_logic_vector(3 downto 0);
      wb_stb_i   : in  std_logic;
      wb_cyc_i   : in  std_logic;
      wb_we_i    : in  std_logic;
      wb_ack_o   : out std_logic;
      wb_err_o   : out std_logic;
      wb_stall_o : out std_logic;
      int_o      : out std_logic;
      pad_cs_o   : out std_logic_vector(g_num_slaves-1 downto 0);
      pad_sclk_o : out std_logic;
      pad_mosi_o : out std_logic;
      pad_miso_i : in  std_logic);
  end component;

  component xwb_spi
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_divider_len         : integer := 16;
      g_max_char_len        : integer := 128;
      g_num_slaves          : integer := 8);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      slave_i    : in  t_wishbone_slave_in;
      slave_o    : out t_wishbone_slave_out;
      desc_o     : out t_wishbone_device_descriptor;
      int_o      : out std_logic;
      pad_cs_o   : out std_logic_vector(g_num_slaves-1 downto 0);
      pad_sclk_o : out std_logic;
      pad_mosi_o : out std_logic;
      pad_miso_i : in  std_logic);
  end component;

  component wb_simple_uart
    generic (
      g_with_virtual_uart   : boolean                        := false;
      g_with_physical_uart  : boolean                        := true;
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_vuart_fifo_size     : integer := 1024);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      wb_adr_i   : in  std_logic_vector(4 downto 0);
      wb_dat_i   : in  std_logic_vector(31 downto 0);
      wb_dat_o   : out std_logic_vector(31 downto 0);
      wb_cyc_i   : in  std_logic;
      wb_sel_i   : in  std_logic_vector(3 downto 0);
      wb_stb_i   : in  std_logic;
      wb_we_i    : in  std_logic;
      wb_ack_o   : out std_logic;
      wb_stall_o : out std_logic;
      int_o      : out std_logic;
      uart_rxd_i : in  std_logic := '1';
      uart_txd_o : out std_logic);
  end component;

  component xwb_simple_uart
    generic (
      g_with_virtual_uart   : boolean                        := false;
      g_with_physical_uart  : boolean                        := true;
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_vuart_fifo_size     : integer := 1024);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      slave_i    : in  t_wishbone_slave_in;
      slave_o    : out t_wishbone_slave_out;
      desc_o     : out t_wishbone_device_descriptor;
      int_o      : out std_logic;
      uart_rxd_i : in  std_logic := '1';
      uart_txd_o : out std_logic);
  end component;

  component wb_simple_pwm
    generic (
      g_num_channels        : integer range 1 to 8;
      g_regs_size           : integer range 1 to 16 := 16;
      g_default_period      : integer range 0 to 255 := 0;
      g_default_presc       : integer range 0 to 255 := 0;
      g_default_val         : integer range 0 to 255 := 0;
      g_interface_mode      : t_wishbone_interface_mode      := PIPELINED;
      g_address_granularity : t_wishbone_address_granularity := BYTE);
    port (
      clk_sys_i  : in  std_logic;
      rst_n_i    : in  std_logic;
      wb_adr_i   : in  std_logic_vector(5 downto 0);
      wb_dat_i   : in  std_logic_vector(31 downto 0);
      wb_dat_o   : out std_logic_vector(31 downto 0);
      wb_cyc_i   : in  std_logic;
      wb_sel_i   : in  std_logic_vector(3 downto 0);
      wb_stb_i   : in  std_logic;
      wb_we_i    : in  std_logic;
      wb_ack_o   : out std_logic;
      wb_stall_o : out std_logic;
      pwm_o      : out std_logic_vector(g_num_channels-1 downto 0));
  end component;

  component xwb_simple_pwm
    generic (
      g_num_channels        : integer range 1 to 8;
      g_regs_size           : integer range 1 to 16 := 16;
      g_default_period      : integer range 0 to 255 := 0;
      g_default_presc       : integer range 0 to 255 := 0;
      g_default_val         : integer range 0 to 255 := 0;
      g_interface_mode      : t_wishbone_interface_mode      := PIPELINED;
      g_address_granularity : t_wishbone_address_granularity := BYTE);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out;
      pwm_o     : out std_logic_vector(g_num_channels-1 downto 0));
  end component;

  component wb_tics
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_period              : integer);
    port (
      rst_n_i    : in  std_logic;
      clk_sys_i  : in  std_logic;
      wb_adr_i   : in  std_logic_vector(3 downto 0);
      wb_dat_i   : in  std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_dat_o   : out std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_cyc_i   : in  std_logic;
      wb_sel_i   : in  std_logic_vector(c_wishbone_data_width/8-1 downto 0);
      wb_stb_i   : in  std_logic;
      wb_we_i    : in  std_logic;
      wb_ack_o   : out std_logic;
      wb_stall_o : out std_logic);
  end component;

  component xwb_tics
    generic (
      g_interface_mode      : t_wishbone_interface_mode      := CLASSIC;
      g_address_granularity : t_wishbone_address_granularity := WORD;
      g_period              : integer);
    port (
      clk_sys_i : in  std_logic;
      rst_n_i   : in  std_logic;
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out;
      desc_o    : out t_wishbone_device_descriptor);
  end component;

  component wb_vic
    generic (
      g_interface_mode      : t_wishbone_interface_mode;
      g_address_granularity : t_wishbone_address_granularity;
      g_num_interrupts      : natural;
      g_init_vectors        : t_wishbone_address_array := cc_dummy_address_array;
      g_FIXED_POLARITY      : boolean := False;
      g_POLARITY            : std_logic := '1';
      g_retry_timeout : integer := 0
      );
    port (
      clk_sys_i    : in  std_logic;
      rst_n_i      : in  std_logic;
      wb_adr_i     : in  std_logic_vector(c_wishbone_address_width-1 downto 0);
      wb_dat_i     : in  std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_dat_o     : out std_logic_vector(c_wishbone_data_width-1 downto 0);
      wb_cyc_i     : in  std_logic;
      wb_sel_i     : in  std_logic_vector(c_wishbone_data_width/8-1 downto 0);
      wb_stb_i     : in  std_logic;
      wb_we_i      : in  std_logic;
      wb_ack_o     : out std_logic;
      wb_stall_o   : out std_logic;
      irqs_i       : in  std_logic_vector(g_num_interrupts-1 downto 0);
      irq_master_o : out std_logic);
  end component;

  constant c_xwb_vic_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"00000000000000ff",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"00000013",
        version   => x"00000002",
        date      => x"20120113",
        name      => "WB-VIC-Int.Control ")));   

  component xwb_vic
    generic (
      g_interface_mode      : t_wishbone_interface_mode;
      g_address_granularity : t_wishbone_address_granularity;
      g_num_interrupts      : natural;
      g_init_vectors        : t_wishbone_address_array := cc_dummy_address_array;
    g_retry_timeout : integer := 0);

    port (
      clk_sys_i    : in  std_logic;
      rst_n_i      : in  std_logic;
      slave_i      : in  t_wishbone_slave_in;
      slave_o      : out t_wishbone_slave_out;
      irqs_i       : in  std_logic_vector(g_num_interrupts-1 downto 0);
      irq_master_o : out std_logic);
  end component;

  constant c_wb_serial_lcd_sdb : t_sdb_device := (
    abi_class     => x"0000", -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"00",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7", -- 8/16/32-bit port granularity
    sdb_component => (
    addr_first    => x"0000000000000000",
    addr_last     => x"00000000000000ff",
    product => (
    vendor_id     => x"0000000000000651", -- GSI
    device_id     => x"b77a5045",
    version       => x"00000001",
    date          => x"20130222",
    name          => "SERIAL-LCD-DISPLAY ")));
  component wb_serial_lcd
    generic(
      g_cols : natural := 40;
      g_rows : natural := 24;
      g_hold : natural := 15; -- How many times to repeat a line  (for sharpness)
      g_wait : natural := 1); -- How many cycles per state change (for 20MHz timing)
    port(
      slave_clk_i  : in  std_logic;
      slave_rstn_i : in  std_logic;
      slave_i      : in  t_wishbone_slave_in;
      slave_o      : out t_wishbone_slave_out;
      di_clk_i     : in  std_logic;
      di_scp_o     : out std_logic;
      di_lp_o      : out std_logic;
      di_flm_o     : out std_logic;
      di_dat_o     : out std_logic);
  end component;

  function f_wb_spi_flash_sdb(g_bits : natural) return t_sdb_device;
  component wb_spi_flash is
    generic(
      g_port_width             : natural   := 1;  -- 1 for EPCS, 4 for EPCQ
      g_addr_width             : natural   := 24; -- log of memory (24=16MB)
      g_idle_time              : natural   := 3;
      g_dummy_time             : natural   := 8;
      -- leave these at defaults if you have:
      --   a) slow clock, b) valid constraints, or c) registered in/outputs
      g_input_latch_edge       : std_logic := '1'; -- rising
      g_output_latch_edge      : std_logic := '0'; -- falling
      g_input_to_output_cycles : natural   := 1);  -- between 1 and 8
    port(
      clk_i     : in  std_logic;
      rstn_i    : in  std_logic;
      slave_i   : in  t_wishbone_slave_in;
      slave_o   : out t_wishbone_slave_out;
      
      -- For properly constrained designs, set clk_out_i = clk_in_i.
      clk_out_i : in  std_logic;
      clk_in_i  : in  std_logic;
      ncs_o     : out std_logic;
      oe_o      : out std_logic_vector(g_port_width-1 downto 0);
      asdi_o    : out std_logic_vector(g_port_width-1 downto 0);
      data_i    : in  std_logic_vector(g_port_width-1 downto 0);
      
      external_request_i : in  std_logic := '0'; -- JTAG wants to use SPI?
      external_granted_o : out std_logic);
  end component;

  component xwb_remapper is
    generic (
      g_num_ranges : integer := 1;
      g_base_in    : t_wishbone_address_array;
      g_base_out   : t_wishbone_address_array;
      g_mask_in    : t_wishbone_address_array;
      g_mask_out   : t_wishbone_address_array);
    port (
      slave_i  : in  t_wishbone_slave_in;
      slave_o  : out t_wishbone_slave_out;
      master_i : in  t_wishbone_master_in;
      master_o : out t_wishbone_master_out);
  end component xwb_remapper;

  -----------------------------------------------------------------------------
  -- I2C to Wishbone bridge, following protocol defined with ELMA
  -----------------------------------------------------------------------------
  component wb_i2c_bridge is
    generic
    (
      -- FSM watchdog timeout, see Appendix A in the component documentation for
      -- an example of setting this generic
      g_fsm_wdt : positive
    );
    port
    (
      -- Clock, reset
      clk_i      : in  std_logic;
      rst_n_i    : in  std_logic;

      -- I2C lines
      scl_i      : in  std_logic;
      scl_o      : out std_logic;
      scl_en_o   : out std_logic;
      sda_i      : in  std_logic;
      sda_o      : out std_logic;
      sda_en_o   : out std_logic;

      -- I2C address
      i2c_addr_i : in  std_logic_vector(6 downto 0);

      -- Status outputs
      -- TIP  : Transfer In Progress
      --        '1' when the I2C slave detects a matching I2C address, thus a
      --            transfer is in progress
      --        '0' when idle
      -- ERR  : Error
      --       '1' when the SysMon attempts to access an invalid WB slave
      --       '0' when idle
      -- WDTO : Watchdog timeout (single clock cycle pulse)
      --        '1' -- timeout of watchdog occured
      --        '0' -- when idle
      tip_o      : out std_logic;
      err_p_o    : out std_logic;
      wdto_p_o   : out std_logic;

      -- Wishbone master signals
      wbm_stb_o  : out std_logic;
      wbm_cyc_o  : out std_logic;
      wbm_sel_o  : out std_logic_vector(3 downto 0);
      wbm_we_o   : out std_logic;
      wbm_dat_i  : in  std_logic_vector(31 downto 0);
      wbm_dat_o  : out std_logic_vector(31 downto 0);
      wbm_adr_o  : out std_logic_vector(31 downto 0);
      wbm_ack_i  : in  std_logic;
      wbm_rty_i  : in  std_logic;
      wbm_err_i  : in  std_logic
    );
  end component wb_i2c_bridge;

  ------------------------------------------------------------------------------
  -- MultiBoot component
  ------------------------------------------------------------------------------
  component xwb_xil_multiboot is
    port
    (
      -- Clock and reset input ports
      clk_i   : in  std_logic;
      rst_n_i : in  std_logic;

      -- Wishbone ports
      wbs_i   : in  t_wishbone_slave_in;
      wbs_o   : out t_wishbone_slave_out;

      -- SPI ports
      spi_cs_n_o : out std_logic;
      spi_sclk_o : out std_logic;
      spi_mosi_o : out std_logic;
      spi_miso_i : in  std_logic
    );
  end component xwb_xil_multiboot;

  constant c_xwb_xil_multiboot_sdb : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"00",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"000000000000001f",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"11da333d",          -- echo "WB-Xilinx-MultiBoot" | md5sum | cut -c1-8
        version   => x"00000001",
        date      => x"20140313",
        name      => "WB-Xilinx-MultiBoot")));

  constant c_DUMMY_SDB_DEVICE : t_sdb_device := (
    abi_class     => x"0000",              -- undocumented device
    abi_ver_major => x"01",
    abi_ver_minor => x"01",
    wbd_endian    => c_sdb_endian_big,
    wbd_width     => x"7",                 -- 8/16/32-bit port granularity
    sdb_component => (
      addr_first  => x"0000000000000000",
      addr_last   => x"00000000000000ff",
      product     => (
        vendor_id => x"000000000000CE42",  -- CERN
        device_id => x"ffffffff",
        version   => x"00000001",
        date      => x"20150722",
        name      => "Unused-Device      ")));

  -- For backward compatibility
  constant cc_dummy_sdb_device : t_sdb_device := c_DUMMY_SDB_DEVICE;

end wishbone_pkg;

package body wishbone_pkg is
   -- f_wb_wr: processes a write reqest to a slave register with select lines. valid modes are "owr", "set" and "clr"
   function f_wb_wr(pval : std_logic_vector; ival : std_logic_vector; sel : std_logic_vector; mode : string := "owr") return std_logic_vector is
      variable n_sel     : std_logic_vector(pval'range);
      variable n_val     : std_logic_vector(pval'range);
      variable result 	 : std_logic_vector(pval'range);   
   begin
     for i in pval'range loop 
      n_sel(i) := sel((i-pval'low) / 8); -- subtract the low index for when register width > wishbone data width
      n_val(i) := ival(i-pval'low);
     end loop;

     if(mode = "set") then  
      result := pval or (n_val and n_sel);
     elsif (mode = "clr") then
      result := pval and not (n_val and n_sel); 
     else
      result := (pval and not n_sel) or (n_val and n_sel);    
     end if;  
     
     return result;
   end f_wb_wr;
  
  function f_ceil_log2(x : natural) return natural is
  begin
    if x <= 1
    then return 0;
    else return f_ceil_log2((x+1)/2) +1;
    end if;
  end f_ceil_log2;

  function f_sdb_embed_product(product : t_sdb_product)
    return std_logic_vector             -- (319 downto 8)
  is
    variable result : std_logic_vector(319 downto 8);
  begin
    result(319 downto 256) := product.vendor_id;
    result(255 downto 224) := product.device_id;
    result(223 downto 192) := product.version;
    result(191 downto 160) := product.date;
    for i in 0 to 18 loop               -- string to ascii
      result(159-i*8 downto 152-i*8) :=
        std_logic_vector(to_unsigned(character'pos(product.name(i+1)), 8));
    end loop;
    return result;
  end;

  function f_sdb_extract_product(sdb_record : std_logic_vector(319 downto 8))
    return t_sdb_product
  is
    variable result : t_sdb_product;
  begin
    result.vendor_id := sdb_record(319 downto 256);
    result.device_id := sdb_record(255 downto 224);
    result.version   := sdb_record(223 downto 192);
    result.date      := sdb_record(191 downto 160);
    for i in 0 to 18 loop               -- ascii to string
      result.name(i+1) := character'val(to_integer(unsigned(sdb_record(159-i*8 downto 152-i*8))));
    end loop;
    return result;
  end;

  function f_sdb_embed_component(sdb_component : t_sdb_component; address : t_wishbone_address)
    return std_logic_vector             -- (447 downto 8)
  is
    variable result : std_logic_vector(447 downto 8);

    constant first : unsigned(63 downto 0) := unsigned(sdb_component.addr_first);
    constant last  : unsigned(63 downto 0) := unsigned(sdb_component.addr_last);
    variable base  : unsigned(63 downto 0) := (others => '0');
  begin
    base(address'length-1 downto 0) := unsigned(address);

    result(447 downto 384) := std_logic_vector(base);
    result(383 downto 320) := std_logic_vector(base + last - first);
    result(319 downto 8)   := f_sdb_embed_product(sdb_component.product);
    return result;
  end;

  function f_sdb_extract_component(sdb_record : std_logic_vector(447 downto 8))
    return t_sdb_component
  is
    variable result : t_sdb_component;
  begin
    result.addr_first := sdb_record(447 downto 384);
    result.addr_last  := sdb_record(383 downto 320);
    result.product    := f_sdb_extract_product(sdb_record(319 downto 8));
    return result;
  end;

  function f_sdb_embed_device(device : t_sdb_device; address : t_wishbone_address)
    return t_sdb_record
  is
    variable result : t_sdb_record;
  begin
    result(511 downto 496) := device.abi_class;
    result(495 downto 488) := device.abi_ver_major;
    result(487 downto 480) := device.abi_ver_minor;
    result(479 downto 456) := (others => '0');
    result(455)            := device.wbd_endian;
    result(454 downto 452) := (others => '0');
    result(451 downto 448) := device.wbd_width;
    result(447 downto 8)   := f_sdb_embed_component(device.sdb_component, address);
    result(7 downto 0)     := x"01";    -- device
    return result;
  end;

  function f_sdb_extract_device(sdb_record : t_sdb_record)
    return t_sdb_device
  is
    variable result : t_sdb_device;
  begin
    result.abi_class     := sdb_record(511 downto 496);
    result.abi_ver_major := sdb_record(495 downto 488);
    result.abi_ver_minor := sdb_record(487 downto 480);
    result.wbd_endian    := sdb_record(452);
    result.wbd_width     := sdb_record(451 downto 448);
    result.sdb_component := f_sdb_extract_component(sdb_record(447 downto 8));

    assert sdb_record(7 downto 0) = x"01"
      report "Cannot extract t_sdb_device from record of type " & integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
      severity failure;
    
    return result;
  end;

  function f_sdb_embed_msi(msi : t_sdb_msi; address : t_wishbone_address)
    return t_sdb_record
  is
    variable result : t_sdb_record;
  begin
    result(511 downto 456) := (others => '0');
    result(455)            := msi.wbd_endian;
    result(454 downto 452) := (others => '0');
    result(451 downto 448) := msi.wbd_width;
    result(447 downto 8)   := f_sdb_embed_component(msi.sdb_component, address);
    result(7 downto 0)     := x"03";    -- msi
    return result;
  end;

  function f_sdb_extract_msi(sdb_record : t_sdb_record)
    return t_sdb_msi
  is
    variable result : t_sdb_msi;
  begin
    result.wbd_endian    := sdb_record(452);
    result.wbd_width     := sdb_record(451 downto 448);
    result.sdb_component := f_sdb_extract_component(sdb_record(447 downto 8));

    assert sdb_record(7 downto 0) = x"03"
      report "Cannot extract t_sdb_msi from record of type " & integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
      severity failure;
    
    return result;
  end;

  function f_sdb_embed_integration(integr : t_sdb_integration)
    return t_sdb_record
  is
    variable result : t_sdb_record;
  begin
    result(511 downto 320) := (others => '0');
    result(319 downto 8)   := f_sdb_embed_product(integr.product);
    result(7 downto 0)     := x"80"; -- integration record
    return result;
  end f_sdb_embed_integration;

  function f_sdb_extract_integration(sdb_record : t_sdb_record)
    return t_sdb_integration
  is
    variable result : t_sdb_integration;
  begin
    result.product := f_sdb_extract_product(sdb_record(319 downto 8));

    assert sdb_record(7 downto 0) = x"80"
    report "Cannot extract t_sdb_integration from record of type " & Integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
    severity Failure;

    return result;
  end f_sdb_extract_integration;

  function f_sdb_embed_repo_url(url : t_sdb_repo_url)
    return t_sdb_record
  is
    variable result : t_sdb_record;
  begin
    result(511 downto 8) := f_string2svl(url.repo_url);
    result(  7 downto 0) := x"81"; -- repo_url record
    return result;
  end;

  function f_sdb_extract_repo_url(sdb_record : t_sdb_record)
    return t_sdb_repo_url
  is
    variable result : t_sdb_repo_url;
  begin
    result.repo_url     := f_slv2string(sdb_record(511 downto 8));

    assert sdb_record(7 downto 0) = x"81"
    report "Cannot extract t_sdb_repo_url from record of type " & Integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
    severity Failure;

    return result;
  end;

  function f_sdb_embed_synthesis(syn : t_sdb_synthesis)
    return t_sdb_record
  is
    variable result : t_sdb_record;
  begin
    result(511 downto 384) := f_string2svl(syn.syn_module_name);
    result(383 downto 256) := f_string2bits(syn.syn_commit_id);
    result(255 downto 192) := f_string2svl(syn.syn_tool_name);
    result(191 downto 160) := syn.syn_tool_version;
    result(159 downto 128) := syn.syn_date;
    result(127 downto   8) := f_string2svl(syn.syn_username);
    result(  7 downto   0) := x"82"; -- synthesis record
    return result;
  end;

  function f_sdb_extract_synthesis(sdb_record : t_sdb_record)
    return t_sdb_synthesis
  is
    variable result : t_sdb_synthesis;
  begin
    result.syn_module_name  := f_slv2string(sdb_record(511 downto 384));
    result.syn_commit_id    := f_bits2string(sdb_record(383 downto 256));
    result.syn_tool_name    := f_slv2string(sdb_record(255 downto 192));
    result.syn_tool_version := sdb_record(191 downto 160);
    result.syn_date         := sdb_record(159 downto 128);
    result.syn_username     := f_slv2string(sdb_record(127 downto   8));

    assert sdb_record(7 downto 0) = x"82"
    report "Cannot extract t_sdb_repo_url from record of type " & Integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
    severity Failure;

    return result;
  end;

  function f_sdb_embed_bridge(bridge : t_sdb_bridge; address : t_wishbone_address)
    return t_sdb_record
  is
    variable result : t_sdb_record;

    constant first : unsigned(63 downto 0) := unsigned(bridge.sdb_component.addr_first);
    constant child : unsigned(63 downto 0) := unsigned(bridge.sdb_child);
    variable base  : unsigned(63 downto 0) := (others => '0');
  begin
    base(address'length-1 downto 0) := unsigned(address);

    result(511 downto 448) := std_logic_vector(base + child - first);
    result(447 downto 8)   := f_sdb_embed_component(bridge.sdb_component, address);
    result(7 downto 0)     := x"02";    -- bridge
    return result;
  end;

  function f_sdb_extract_bridge(sdb_record : t_sdb_record)
    return t_sdb_bridge
  is
    variable result : t_sdb_bridge;
  begin
    result.sdb_child     := sdb_record(511 downto 448);
    result.sdb_component := f_sdb_extract_component(sdb_record(447 downto 8));

    assert sdb_record(7 downto 0) = x"02"
      report "Cannot extract t_sdb_bridge from record of type " & integer'image(to_integer(unsigned(sdb_record(7 downto 0)))) & "."
      severity failure;
    
    return result;
  end;
  
  function f_sdb_auto_device(device : t_sdb_device; enable : boolean := true; name: string := "")
    return t_sdb_record
  is
    constant c_zero  : t_wishbone_address := (others => '0');
    variable v_device: t_sdb_device := device;
    variable v_empty : t_sdb_record := (others => '0');
  begin
    v_empty(7 downto 0) := x"f1";
    if name /= "" then
      v_device.sdb_component.product.name := f_string_fix_len(name , 19, ' ', false);
    end if;
    if enable then
      v_empty := f_sdb_embed_device(v_device, c_zero);
    end if;
    return v_empty;
  end f_sdb_auto_device;
  
  function f_sdb_auto_bridge(bridge : t_sdb_bridge; enable : boolean := true; name: string := "")
    return t_sdb_record
  is
    constant c_zero  : t_wishbone_address := (others => '0');
    variable v_bridge: t_sdb_bridge := bridge;
    variable v_empty : t_sdb_record := (others => '0');
  begin
    v_empty(7 downto 0) := x"f2";
    if name /= "" then
      v_bridge.sdb_component.product.name := f_string_fix_len(name , 19, ' ', false);
    end if;
    if enable then
      v_empty := f_sdb_embed_bridge(v_bridge, c_zero);
    end if;
    return v_empty;
  end f_sdb_auto_bridge;
  
  function f_sdb_auto_msi(msi : t_sdb_msi; enable : boolean := true)
    return t_sdb_record
  is
    constant c_zero  : t_wishbone_address := (others => '0');
    variable v_empty : t_sdb_record := (others => '0');
  begin
    v_empty(7 downto 0) := x"f3";
    if enable then
      return f_sdb_embed_msi(msi, c_zero);
    else
      return v_empty;
    end if;
  end f_sdb_auto_msi;
  
  subtype t_usdb_address is unsigned(63 downto 0);
  type t_usdb_address_array is array(natural range <>) of t_usdb_address;
  
  -- We map devices by placing the smallest ones first.
  -- This is guaranteed to pack the maximum number of devices in the smallest space.
  -- If a device has an address != 0, we leave it alone and let the crossbar confirm
  -- that the address does not cause a conflict.
  function f_sdb_auto_layout_helper(records : t_sdb_record_array)
    return t_usdb_address_array
  is
    alias c_records : t_sdb_record_array(records'length-1 downto 0) is records;
    constant c_zero : t_usdb_address := (others => '0');
    
    constant c_used_entries : natural := c_records'length + 1;
    constant c_rom_entries  : natural := 2**f_ceil_log2(c_used_entries);
    constant c_rom_bytes    : natural := c_rom_entries * c_sdb_device_length / 8;
        
    variable v_component : t_sdb_component;
    variable v_sizes     : t_usdb_address_array(c_records'length downto 0);
    variable v_address   : t_usdb_address_array(c_records'length downto 0);
    variable v_bus_map   : std_logic_vector(c_records'length downto 0) := (others => '0');
    variable v_bus_cursor: unsigned(63 downto 0) := (others => '0');
    variable v_msi_map   : std_logic_vector(c_records'length downto 0) := (others => '0');
    variable v_msi_cursor: unsigned(63 downto 0) := (others => '0');
    variable v_increment : unsigned(63 downto 0) := (others => '0');
    variable v_type      : std_logic_vector(7 downto 0);
  begin
    -- First, extract the length of the devices, ignoring those not to be mapped
    for i in c_records'range loop
      v_component := f_sdb_extract_component(c_records(i)(447 downto 8));
      v_sizes(i)   := unsigned(v_component.addr_last);
      v_address(i) := unsigned(v_component.addr_first);
      
      -- Silently round up to a power of two; the crossbar will give a warning for us
      for j in 62 downto 0 loop
        v_sizes(i)(j) := v_sizes(i)(j+1) or v_sizes(i)(j);
      end loop;
      
      -- Only map devices/bridges at address zero
      if v_address(i) = c_zero then
        v_type := c_records(i)(7 downto 0);
        case v_type is
          when x"01" => v_bus_map(i) := '1';
          when x"02" => v_bus_map(i) := '1';
          when x"03" => v_msi_map(i) := '1';
          when others => null;
        end case;
      end if;
    end loop;
    
    -- Assign the SDB record a spot as well
    v_address(c_records'length) := (others => '0');
    v_sizes(c_records'length) := to_unsigned(c_rom_bytes-1, 64);
    v_bus_map(c_records'length) := '1';
    
    -- Start assigning addresses
    for j in 0 to 63 loop
      v_increment := (others => '0');
      v_increment(j) := '1';
      
      for i in 0 to c_records'length loop
        if v_bus_map(i) = '1' and v_sizes(i)(j) = '0' then
          v_bus_map(i) := '0';
          v_address(i) := v_bus_cursor;
          v_bus_cursor := v_bus_cursor + v_increment;
        end if;
        if v_msi_map(i) = '1' and v_sizes(i)(j) = '0' then
          v_msi_map(i) := '0';
          v_address(i) := v_msi_cursor;
          v_msi_cursor := v_msi_cursor + v_increment;
        end if;
      end loop;
      
      -- Round up to the next required alignment
      if v_bus_cursor(j) = '1' then
        v_bus_cursor := v_bus_cursor + v_increment;
      end if;
      if v_msi_cursor(j) = '1' then
        v_msi_cursor := v_msi_cursor + v_increment;
      end if;
    end loop;
    
    return v_address;
  end f_sdb_auto_layout_helper;
  
  function f_sdb_auto_layout(records : t_sdb_record_array)
    return t_sdb_record_array
  is
    alias    c_records : t_sdb_record_array(records'length-1 downto 0) is records;
    variable v_typ     : std_logic_vector(7 downto 0);
    variable v_result  : t_sdb_record_array(c_records'range) := c_records;
    constant c_address : t_usdb_address_array := f_sdb_auto_layout_helper(c_records);
    variable v_address : t_wishbone_address;
  begin
    -- Put the addresses into the mapping
    for i in v_result'range loop
      v_typ     := c_records(i)(7 downto 0);
      v_address := std_logic_vector(c_address(i)(t_wishbone_address'range));

      case v_typ is
        when x"01" => v_result(i) := f_sdb_embed_device(f_sdb_extract_device(v_result(i)), v_address);
        when x"02" => v_result(i) := f_sdb_embed_bridge(f_sdb_extract_bridge(v_result(i)), v_address);
        when x"03" => v_result(i) := f_sdb_embed_msi   (f_sdb_extract_msi   (v_result(i)), v_address);
        when others => null;
      end case;
    end loop;
    
    return v_result;
  end f_sdb_auto_layout;
  
  function f_sdb_auto_layout(slaves : t_sdb_record_array; masters : t_sdb_record_array)
    return t_sdb_record_array
  is begin
    return f_sdb_auto_layout(masters & slaves);
  end f_sdb_auto_layout;
  
  function f_sdb_auto_sdb(records : t_sdb_record_array)
    return t_wishbone_address
  is
    alias    c_records : t_sdb_record_array(records'length-1 downto 0) is records;
    constant c_address : t_usdb_address_array(c_records'length downto 0) := f_sdb_auto_layout_helper(c_records);
  begin
    return std_logic_vector(c_address(c_records'length)(t_wishbone_address'range));
  end f_sdb_auto_sdb;
  
  function f_sdb_auto_sdb(slaves : t_sdb_record_array; masters : t_sdb_record_array)
    return t_wishbone_address
  is begin
    return f_sdb_auto_sdb(masters & slaves);
  end f_sdb_auto_sdb;

--**************************************************************************************************************************--
-- START MAT's NEW FUNCTIONS FROM 18th Oct 2013
------------------------------------------------------------------------------------------------------------------------------


   function f_sdb_create_array(g_enum_dev_id    : boolean := false;
                            g_dev_id_offs    : natural := 0;
                            g_enum_dev_name  : boolean := false;
                            g_dev_name_offs  : natural := 0;       
                            device           : t_sdb_device; 
                            instances        : natural := 1) 
   return t_sdb_record_array is
      variable result   : t_sdb_record_array(instances-1 downto 0);  
      variable pos : natural;
      variable dev, newdev      : t_sdb_device;
      variable serial_no : string(1 to 3);
   begin
      dev := device;
             
      report "### Creating " & integer'image(instances) & " x " & dev.sdb_component.product.name
      severity note;       
      for i in 0 to instances-1 loop
         newdev := dev;        
         if(g_enum_dev_id) then         
            dev.sdb_component.product.device_id :=  
            std_logic_vector( unsigned(dev.sdb_component.product.device_id) 
                              + to_unsigned(i+g_dev_id_offs, dev.sdb_component.product.device_id'length));        
         end if;
         if(g_enum_dev_name) then         
         -- find end of name
            
            for j in dev.sdb_component.product.name'length downto 1 loop
               if(dev.sdb_component.product.name(j) /= ' ') then               
                  pos := j;                  
                  exit;
               end if;
            end loop;
         -- convert i+g_dev_name_offs to string
            serial_no := f_string_fix_len(integer'image(i+g_dev_name_offs), serial_no'length);
            report "### Now: " & serial_no & " of " & dev.sdb_component.product.name severity note;
         -- check if space is sufficient
            assert (serial_no'length+1 <= dev.sdb_component.product.name'length - pos)
            report "Not enough space in namestring of sdb_device " & dev.sdb_component.product.name
            & " to add serial number " & serial_no & ". Space available " & 
            integer'image(dev.sdb_component.product.name'length-pos-1) & ", required " 
            & integer'image(serial_no'length+1)    
            severity Failure;
         end if;
         if(g_enum_dev_name) then
            newdev.sdb_component.product.name(pos+1) := '_'; 
            for j in 1 to serial_no'length loop
               newdev.sdb_component.product.name(pos+1+j) := serial_no(j);
            end loop;            
         end if;
          
      -- insert
         report "### to: " & newdev.sdb_component.product.name severity note;
         result(i) := f_sdb_embed_device(newdev, (others=>'0'));
      end loop;
      return result;
   end f_sdb_create_array;

   function f_sdb_join_arrays(a : t_sdb_record_array; b : t_sdb_record_array) return t_sdb_record_array is
      variable result   : t_sdb_record_array(a'length+b'length-1 downto 0);  
   begin
      for i in 0 to a'left loop
         result(i) := a(i);
      end loop;
      for i in 0 to b'left loop
         result(i+a'length) := b(i);
      end loop;
      return result;
   end f_sdb_join_arrays;


   function f_sdb_extract_base_addr(sdb_record : t_sdb_record) return std_logic_vector is
   begin
      return sdb_record(447 downto 384);
   end f_sdb_extract_base_addr;

   function f_sdb_extract_end_addr(sdb_record : t_sdb_record) return std_logic_vector is
   begin
      return sdb_record(383 downto 320);
   end f_sdb_extract_end_addr;


   function f_align_addr_offset(offs : unsigned; this_rng : unsigned; prev_rng : unsigned)
   return unsigned is
      variable this_pow, prev_pow   : natural;  
      variable start, env, result : unsigned(63 downto 0) := (others => '0');
   begin
      start(offs'left downto 0) := offs;   
      --calculate address envelopes (next power of 2) for previous and this component and choose the larger one
      this_pow := f_hot_to_bin(std_logic_vector(this_rng)); 
      prev_pow := f_hot_to_bin(std_logic_vector(prev_rng));    
      -- no max(). thank you very much, std_numeric :-/   
      if(this_pow >= prev_pow) then
         env(this_pow) := '1';
      else
         env(prev_pow) := '1';
      end if;
      --round up to the next multiple of the envelope...
      if(prev_rng /= 0) then   
         result := start + env - (start mod env);
      else
         result := start;   --...except for first element, result is start. 
      end if;
      return result;
   end f_align_addr_offset;


 -- generates aligned address map for an sdb_record_array, accepts optional start offset 
   function f_sdb_automap_array(sdb_array : t_sdb_record_array; start_offset : t_wishbone_address := (others => '0'))
   return t_sdb_record_array is
      constant len         : natural := sdb_array'length;
      variable this_rng    : unsigned(63 downto 0) := (others => '0');   
      variable prev_rng    : unsigned(63 downto 0) := (others => '0');   
      variable prev_offs   : unsigned(63 downto 0) := (others => '0');   
      variable this_offs   : unsigned(63 downto 0) := (others => '0');   
      variable device      : t_sdb_device;
      variable bridge      : t_sdb_bridge;  
      variable sdb_type    : std_logic_vector(7 downto 0);
      variable result      : t_sdb_record_array(sdb_array'length-1 downto 0); -- last 
   begin
   
      prev_offs(start_offset'left downto 0) := unsigned(start_offset);
      --traverse the array   
      for i in 0 to len-1 loop
         -- find the fitting extraction function by evaling the type byte. 
         -- could also use the component, but it's safer to use Wes' embed and extract functions.      
         sdb_type := sdb_array(i)(7 downto 0);
         case sdb_type is
            --device         
            when x"01"  => device      := f_sdb_extract_device(sdb_array(i));
                           this_rng    := unsigned(device.sdb_component.addr_last) - unsigned(device.sdb_component.addr_first);                      
                           this_offs   := f_align_addr_offset(prev_offs, this_rng, prev_rng);
                           result(i)   := f_sdb_embed_device(device, std_logic_vector(this_offs(31 downto 0)));
            --bridge
            when x"02"  => bridge      := f_sdb_extract_bridge(sdb_array(i));
                           this_rng    := unsigned(bridge.sdb_component.addr_last) - unsigned(bridge.sdb_component.addr_first);
                           this_offs   := f_align_addr_offset(prev_offs, this_rng, prev_rng);
                           result(i)   := f_sdb_embed_bridge(bridge, std_logic_vector(this_offs(31 downto 0)) );
            --other
            when others => result(i) := sdb_array(i);   
         end case;
         -- doesnt hurt because this_* doesnt change if its not a device or bridge
         prev_rng    := this_rng;
         prev_offs   := this_offs;
      end loop;
      report "##* " & integer'image(sdb_array'length) & " Elements, last address: " & f_bits2string(std_logic_vector(this_offs+this_rng)) severity Note;
      return result;
   end f_sdb_automap_array;


  -- find place for sdb rom on crossbar and return address. try to put it in an address gap.
   function f_sdb_create_rom_addr(sdb_array : t_sdb_record_array) return t_wishbone_address is
      constant len                  : natural := sdb_array'length;
      constant rom_bytes            : natural := (2**f_ceil_log2(sdb_array'length + 1)) * (c_sdb_device_length / 8);
      variable result               : t_wishbone_address  := (others => '0');
      variable this_base, this_end  : unsigned(63 downto 0)          := (others => '0');    
      variable prev_base, prev_end  : unsigned(63 downto 0)          := (others => '0');
      variable rom_base             : unsigned(63 downto 0)          := (others => '0');
      variable sdb_type             : std_logic_vector(7 downto 0);     
   begin
      --traverse the array   
      for i in 0 to len-1 loop     
         sdb_type := sdb_array(i)(7 downto 0);
         if(sdb_type = x"01" or sdb_type = x"02") then
            -- get         
            this_base := unsigned(f_sdb_extract_base_addr(sdb_array(i)));
            this_end  := unsigned(f_sdb_extract_end_addr(sdb_array(i)));
            if(unsigned(result) = 0) then
               rom_base := f_align_addr_offset(prev_base, to_unsigned(rom_bytes-1, 64), (prev_end-prev_base));
               if(rom_base + to_unsigned(rom_bytes, 64) <= this_base) then
                  result := std_logic_vector(rom_base(t_wishbone_address'left downto 0));
               end if;   
            end if;
            prev_base := this_base;
            prev_end  := this_end;      
         end if;
      end loop;   
      -- if there was no gap to fit the sdb rom, place it at the end   
      if(unsigned(result) = 0) then
            result := std_logic_vector(f_align_addr_offset(this_base, to_unsigned(rom_bytes-1, 64), 
                                       this_end-this_base)(t_wishbone_address'left downto 0));   
      end if;      
      return result;
   end f_sdb_create_rom_addr; 
------------------------------------------------------------------------------------------------------------------------------
-- END MAT's NEW FUNCTIONS FROM  18th Oct 2013
------------------------------------------------------------------------------------------------------------------------------

  function f_sdb_bus_end(
    g_wraparound : boolean;
    g_layout     : t_sdb_record_array;
    g_sdb_addr   : t_wishbone_address;
    msi          : boolean) return unsigned
  is
    alias c_layout : t_sdb_record_array(g_layout'length-1 downto 0) is g_layout;

    -- How much space does the ROM need?
    constant c_used_entries : natural := c_layout'length + 1;
    constant c_rom_entries  : natural := 2**f_ceil_log2(c_used_entries);  -- next power of 2
    constant c_sdb_bytes    : natural := c_sdb_device_length / 8;
    constant c_rom_bytes    : natural := c_rom_entries * c_sdb_bytes;
    
    variable result : unsigned(63 downto 0) := (others => '0');
    variable typ    : std_logic_vector(7 downto 0);
    variable last   : unsigned(63 downto 0);
  begin
    if not msi then
      -- The ROM will be an addressed slave as well
      result := (others => '0');
      result(g_sdb_addr'range) := unsigned(g_sdb_addr);
      result := result + to_unsigned(c_rom_bytes, 64) - 1;
    end if;
    
    for i in c_layout'range loop
      typ  := c_layout(i)(7 downto 0);
      last := unsigned(f_sdb_extract_component(c_layout(i)(447 downto 8)).addr_last);
      case typ is
        when x"01" => if not msi and last > result then result := last; end if;
        when x"02" => if not msi and last > result then result := last; end if;
        when x"03" => if     msi and last > result then result := last; end if;
        when others => null;
      end case;
    end loop;
    
    -- round result up to a power of two -1
    for i in 62 downto 0 loop
      result(i) := result(i) or result(i+1);
    end loop;
    
    if not g_wraparound then
      result := (others => '0');
      for i in 0 to c_wishbone_address_width-1 loop
        result(i) := '1';
      end loop;
    end if;
    
    return result;
  end f_sdb_bus_end;

  function f_xwb_bridge_manual_sdb(
    g_size     : t_wishbone_address;
    g_sdb_addr : t_wishbone_address) return t_sdb_bridge
  is
    variable result : t_sdb_bridge;
  begin
    result.sdb_child                                      := (others => '0');
    result.sdb_child(c_wishbone_address_width-1 downto 0) := g_sdb_addr;

    result.sdb_component.addr_first                                     := (others => '0');
    result.sdb_component.addr_last                                      := (others => '0');
    result.sdb_component.addr_last(c_wishbone_address_width-1 downto 0) := g_size;

    result.sdb_component.product.vendor_id := x"0000000000000651";  -- GSI
    result.sdb_component.product.device_id := x"eef0b198";
    result.sdb_component.product.version   := x"00000001";
    result.sdb_component.product.date      := x"20120511";
    result.sdb_component.product.name      := "WB4-Bridge-GSI     ";

    return result;
  end f_xwb_bridge_manual_sdb;
  
  function f_xwb_bridge_layout_sdb(     -- determine bus size from layout
    g_wraparound : boolean := true;
    g_layout     : t_sdb_record_array;
    g_sdb_addr   : t_wishbone_address) return t_sdb_bridge
  is
    variable address : t_wishbone_address;
  begin
    address := std_logic_vector(f_sdb_bus_end(g_wraparound, g_layout, g_sdb_addr, false)(address'range));
    return f_xwb_bridge_manual_sdb(address, g_sdb_addr);
  end f_xwb_bridge_layout_sdb;

  function f_xwb_msi_manual_sdb(
    g_size     : t_wishbone_address) return t_sdb_msi
  is
    variable result : t_sdb_msi;
  begin
    result.wbd_endian := '0';
    result.wbd_width  := x"7";

    result.sdb_component.addr_first                                     := (others => '0');
    result.sdb_component.addr_last                                      := (others => '0');
    result.sdb_component.addr_last(c_wishbone_address_width-1 downto 0) := g_size;

    result.sdb_component.product.vendor_id := x"0000000000000651";  -- GSI
    result.sdb_component.product.device_id := x"aa7bfb3c";
    result.sdb_component.product.version   := x"00000001";
    result.sdb_component.product.date      := x"20160422";
    result.sdb_component.product.name      := "WB4-MSI-Bridge-GSI ";

    return result;
  end f_xwb_msi_manual_sdb;
  
  function f_xwb_msi_layout_sdb(     -- determine MSI size from layout
    g_layout     : t_sdb_record_array) return t_sdb_msi
  is
    constant zero : t_wishbone_address := (others => '0');
    variable address : t_wishbone_address;
  begin
    address := std_logic_vector(f_sdb_bus_end(true, g_layout, zero, true)(address'range));
    return f_xwb_msi_manual_sdb(address);
  end f_xwb_msi_layout_sdb;
  
  function f_xwb_dpram(g_size : natural) return t_sdb_device
  is
    variable result : t_sdb_device;
  begin
    result.abi_class     := x"0001";    -- RAM device
    result.abi_ver_major := x"01";
    result.abi_ver_minor := x"00";
    result.wbd_width     := x"7";       -- 32/16/8-bit supported
    result.wbd_endian    := c_sdb_endian_big;

    result.sdb_component.addr_first := (others => '0');
    result.sdb_component.addr_last  := std_logic_vector(to_unsigned(g_size*4-1, 64));

    result.sdb_component.product.vendor_id := x"000000000000CE42";  -- CERN
    result.sdb_component.product.device_id := x"66cfeb52";
    result.sdb_component.product.version   := x"00000001";
    result.sdb_component.product.date      := x"20120305";
    result.sdb_component.product.name      := "WB4-BlockRAM       ";

    return result;
  end f_xwb_dpram;

  function f_bits2string(s : std_logic_vector) return string is
    --- extend length to full hex nibble
    variable result : string((s'length+7)/4 downto 1);
    variable s_norm : std_logic_vector(result'length*4-1 downto 0) := (others=>'0');
    variable cut : natural;
    variable nibble: std_logic_vector(3 downto 0);
    constant len : natural := result'length;
  begin
    s_norm(s'length-1 downto 0) := s;
    for i in len-1 downto 0 loop
      nibble := s_norm(i*4+3 downto i*4);
      case nibble is
        when "0000" => result(i+1) := '0';
        when "0001" => result(i+1) := '1';
        when "0010" => result(i+1) := '2';
        when "0011" => result(i+1) := '3';
        when "0100" => result(i+1) := '4';
        when "0101" => result(i+1) := '5';
        when "0110" => result(i+1) := '6';
        when "0111" => result(i+1) := '7';
        when "1000" => result(i+1) := '8';
        when "1001" => result(i+1) := '9';
        when "1010" => result(i+1) := 'a';
        when "1011" => result(i+1) := 'b';
        when "1100" => result(i+1) := 'c';
        when "1101" => result(i+1) := 'd';
        when "1110" => result(i+1) := 'e';
        when "1111" => result(i+1) := 'f';
        when others => result(i+1) := 'X';
      end case;
    end loop;

    -- trim leading 0s
    strip : for i in result'length downto 1 loop
      cut := i;
      exit strip when result(i) /= '0';
    end loop;

    return "0x" & result(cut downto 1);
  end f_bits2string;

  -- Converts string (hex number, without leading 0x) to std_logic_vector
  function f_string2bits(s : string) return std_logic_vector is
    constant len : natural := s'length;
    variable slv : std_logic_vector(s'length*4-1 downto 0);
    variable nibble : std_logic_vector(3 downto 0);
  begin
    for i in 0 to len-1 loop
      case s(i+1) is
        when '0' => nibble := X"0";
        when '1' => nibble := X"1";
        when '2' => nibble := X"2";
        when '3' => nibble := X"3";
        when '4' => nibble := X"4";
        when '5' => nibble := X"5";
        when '6' => nibble := X"6";
        when '7' => nibble := X"7";
        when '8' => nibble := X"8";
        when '9' => nibble := X"9";
        when 'a' => nibble := X"A";
        when 'A' => nibble := X"A";
        when 'b' => nibble := X"B";
        when 'B' => nibble := X"B";
        when 'c' => nibble := X"C";
        when 'C' => nibble := X"C";
        when 'd' => nibble := X"D";
        when 'D' => nibble := X"D";
        when 'e' => nibble := X"E";
        when 'E' => nibble := X"E";
        when 'f' => nibble := X"F";
        when 'F' => nibble := X"F";
        when others => nibble := "XXXX";
      end case;
      if s'ascending then
        slv(slv'length-(i*4)-1 downto slv'length-(i+1)*4) := nibble;
      else
        slv(((i+1)*4)-1 downto i*4) := nibble;
      end if;
    end loop;
    return slv;
  end f_string2bits;

  -- Converts string to ascii (std_logic_vector)
  function f_string2svl (s : string) return std_logic_vector is
    constant len : natural := s'length;
    alias as : string(1 to len) is s;
    variable slv : std_logic_vector((len * 8) - 1 downto 0);
  begin
    for i in 0 to len-1 loop
      slv(slv'high-i*8 downto (slv'high-7)-i*8) :=
        std_logic_vector(to_unsigned(character'pos(as(i+1)), 8));
    end loop;
    return slv;
  end f_string2svl;

  -- Converts ascii (std_logic_vector) to string
  function f_slv2string (slv : std_logic_vector) return string is
    constant len : natural := slv'length;
    variable s : string(1 to slv'length/8);
  begin
    for i in 0 to (len/8)-1 loop
      s(i+1) := character'val(to_integer(unsigned(slv(slv'high-i*8 downto (slv'high-7)-i*8))));
    end loop;
    return s;
  end f_slv2string;

    -- pads a string of unknown length to a given length (useful for integer'image)
  function f_string_fix_len ( s : string; ret_len : natural := 10; fill_char : character := '0'; justify_right : boolean := true ) return string is
      variable ret_v : string (1 to ret_len);
      constant pad_len : integer := ret_len - s'length ;
      variable pad_v : string (1 to abs(pad_len));
   begin
      if pad_len < 1 then
         ret_v := s(ret_v'range);
      else
         pad_v := (others => fill_char);
         if justify_right then
           ret_v := pad_v & s;
         else
           ret_v := s & pad_v ;
         end if;
      end if;
      return ret_v;
   end f_string_fix_len;

   -- do not synthesize
   function f_hot_to_bin(x : std_logic_vector) return natural is
      variable rv : natural;
   begin
      rv := 0;
      -- if there are few ones set in _x_ then the most significant will be
      -- translated to bin
      for i in 0 to x'left loop
         if x(i) = '1' then
           rv := i+1;
         end if;
      end loop;
      return rv;
  end function;  

  function f_wb_spi_flash_sdb(g_bits : natural) return t_sdb_device is
    variable result : t_sdb_device := (
      abi_class     => x"0000", -- undocumented device
      abi_ver_major => x"01",
      abi_ver_minor => x"02",
      wbd_endian    => c_sdb_endian_big,
      wbd_width     => x"7", -- 8/16/32-bit port granularity
      sdb_component => (
      addr_first    => x"0000000000000000",
      addr_last     => x"0000000000ffffff",
      product => (
      vendor_id     => x"0000000000000651", -- GSI
      device_id     => x"5cf12a1c",
      version       => x"00000002",
      date          => x"20140417",
      name          => "SPI-FLASH-16M-MMAP ")));
  begin
    result.sdb_component.addr_last := std_logic_vector(to_unsigned(2**g_bits-1, 64));
    return result;
  end f_wb_spi_flash_sdb;
  
end wishbone_pkg;
