From 6cedf793d20a2a85e2fbe4b11a02aaa99624d97c Mon Sep 17 00:00:00 2001
From: Eric Kooistra <kooistra@astron.nl>
Date: Fri, 10 Jan 2020 11:27:29 +0100
Subject: [PATCH] Added common_mem_bus.vhd with test bench. This implements a
 MM bus that can be used to replace the MM bus that we make with Qsys.

---
 libraries/base/common/hdllib.cfg              |   4 +
 .../base/common/src/vhdl/common_mem_bus.vhd   | 228 ++++++++++++++++++
 .../base/common/tb/vhdl/tb_common_mem_bus.vhd | 175 ++++++++++++++
 .../common/tb/vhdl/tb_tb_common_mem_bus.vhd   |  54 +++++
 4 files changed, 461 insertions(+)
 create mode 100644 libraries/base/common/src/vhdl/common_mem_bus.vhd
 create mode 100644 libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd
 create mode 100644 libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd

diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg
index 209919bdf6..7b6228f14d 100644
--- a/libraries/base/common/hdllib.cfg
+++ b/libraries/base/common/hdllib.cfg
@@ -109,6 +109,7 @@ synth_files =
     src/vhdl/common_fifo_rd.vhd
     src/vhdl/common_blockreg.vhd
     src/vhdl/common_fifo_dc_lock_control.vhd
+    src/vhdl/common_mem_bus.vhd
     src/vhdl/common_mem_mux.vhd
     src/vhdl/common_mem_demux.vhd
     src/vhdl/common_reg_cross_domain.vhd
@@ -162,6 +163,7 @@ test_bench_files =
     tb/vhdl/tb_common_init.vhd
     tb/vhdl/tb_common_int2float.vhd
     tb/vhdl/tb_common_led_controller.vhd
+    tb/vhdl/tb_common_mem_bus.vhd
     tb/vhdl/tb_common_mem_mux.vhd
     tb/vhdl/tb_common_multiplexer.vhd
     tb/vhdl/tb_common_operation_tree.vhd
@@ -192,6 +194,7 @@ test_bench_files =
     
     tb/vhdl/tb_tb_common_add_sub.vhd
     tb/vhdl/tb_tb_common_adder_tree.vhd
+    tb/vhdl/tb_tb_common_mem_bus.vhd
     tb/vhdl/tb_tb_common_fanout_tree.vhd
     tb/vhdl/tb_tb_common_multiplexer.vhd
     tb/vhdl/tb_tb_common_operation_tree.vhd
@@ -218,6 +221,7 @@ regression_test_vhdl =
 
     tb/vhdl/tb_tb_common_adder_tree.vhd
     tb/vhdl/tb_tb_common_add_sub.vhd
+    tb/vhdl/tb_tb_common_mem_bus.vhd
     tb/vhdl/tb_tb_common_fanout_tree.vhd
     tb/vhdl/tb_tb_common_multiplexer.vhd
     tb/vhdl/tb_tb_common_operation_tree.vhd
diff --git a/libraries/base/common/src/vhdl/common_mem_bus.vhd b/libraries/base/common/src/vhdl/common_mem_bus.vhd
new file mode 100644
index 0000000000..f0c78aaf7b
--- /dev/null
+++ b/libraries/base/common/src/vhdl/common_mem_bus.vhd
@@ -0,0 +1,228 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+-- 
+-- Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the 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.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- 
+-- Author: E. Kooistra
+-- Purpose: Connect a single MM master interface to a list of MM slave
+--          interfaces
+-- Description:
+-- * MM bus
+--   The common_mem_bus creates a memory mapped (MM) bus that connects read
+--   and write accesses from the master interface to the addressed slave
+--   interface. There is one master that controls the bus and there are
+--   g_nof_slaves on the bus. Per slave the start address and address span
+--   have to be specified via g_base_arr and g_width_arr.
+-- 
+-- * Slave allocation
+--   The slaves have to be located on the bus such that the MSbits of the 
+--   global address can be used to select the slave and the LSbits of the
+--   global address can directly be used to select the address within the 
+--   slave. Therefore:
+--   . The width of a slave is the power of 2 that fits the address range of
+--     the slave.
+--   . The span of a slave is 2**width.
+--   . The base address of a slave has to be a power of 2 multiple of the
+--     slave span.
+--
+-- * The mm_clk is only used when there is a slave with read latency > 0 or
+--   when the MM bus uses pipelining.
+--
+-- * Read latency
+--   For read accesses a slave will
+--   typically have a read latency, which means that when the rd and address
+--   are active, then it takes read latency number of clock cycles until the
+--   rddata becomes available. The read latency can be specified per slave via
+--   g_rd_latency_arr.
+--   
+-- * Pipelining
+--   Default the common_mm_bus is combinatorial, so there is no pipelining
+--   between the master interface and the slave interfaces. If possible do not
+--   use pipelining of mosi and miso to avoid increasing the read latency and
+--   achieve timing closure by lower clock rate for the MM bus. Pipelining the
+--   MM bus can be necessary to achieve timing closure:
+--   . g_pipeline_mosi
+--     Pipelining mosi write accesses introduces an extra latency from master
+--     to slave, which is typically not a problem. Pipelining mosi read
+--     accesses increases the read latency between accessing the slave and
+--     getting the rddata. Using a different pipelining for the wr and the rd
+--     pulse would yield a different pipelining of the address for write and
+--     for read, which is akward. Therefore assume that both mosi write and
+--     mosi read have the same pipelining.
+--   . g_pipeline_miso
+--     Pipelining the miso read data increases the read latency.
+--   The total write latency from master to slave is c_mosi_latency.
+--   The total read latency from master via slave back to master is
+--   c_mosi_latency + g_rd_latency_arr of the selected slave + c_miso_latency. 
+--     
+--                                         ______________
+--                strip index:             |            |
+--         master_mosi.address[h:w] ---+-->| delay line |--\
+--                                     |   |____________|  |
+--                                     |                   |
+--                            selected v                   |
+--   master_mosi --> slave_mosi_arr.wr[ ]----------------------> slave_mosi_arr
+--                                  rd                     |
+--                                                selected v
+--   master_miso <--------------------------slave_miso_arr[ ]<-- slave_miso_arr
+--
+--        . not selected slave_mosi_arr get master_mosi but with wr='0', rd='0'
+--        . not selected slave_miso_arr are ignored
+--
+-- Remarks:
+-- . The common_mem_bus resembles common_mem_mux, but the difference is that
+--   with common_mem_mux all slaves have the same address range and are
+--   spaced without address gaps. It is possible to use common_mem_mux in
+--   series with common_mem_bus to provide hierarchy by reprensenting an array
+--   of slave ports via a single slave port on the MM bus.
+-- . In simulation selecting an unused element address will cause a simulation
+--   failure. Therefore the element index is only accepted when it is in the
+--   0 TO g_nof_slaves-1 range.
+--
+-------------------------------------------------------------------------------
+
+
+LIBRARY IEEE, common_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+
+ENTITY common_mem_bus IS
+  GENERIC (
+    g_nof_slaves      : POSITIVE;       -- Number of MM slave interfaces on the bus
+    g_base_arr        : t_natural_arr;  -- Address base per slave
+    g_width_arr       : t_natural_arr;  -- Address width per slave
+    g_rd_latency_arr  : t_natural_arr;  -- Read latency per slave
+    g_pipeline_mosi   : BOOLEAN := FALSE;
+    g_pipeline_miso   : BOOLEAN := FALSE
+  );
+  PORT (
+    mm_clk         : IN  STD_LOGIC := '0';
+    master_mosi    : IN  t_mem_mosi;
+    master_miso    : OUT t_mem_miso;
+    slave_mosi_arr : OUT t_mem_mosi_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_mosi_rst); 
+    slave_miso_arr : IN  t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst)
+  );
+END common_mem_bus;
+
+ARCHITECTURE rtl OF common_mem_bus IS
+  
+  -- Determine the address range of all slaves on the MM bus.
+  FUNCTION func_derive_mm_bus_addr_w(g_base_arr, g_width_arr : t_natural_arr) RETURN NATURAL IS
+    VARIABLE v_base            : NATURAL := 0;
+    VARIABLE v_width           : NATURAL;
+    VARIABLE v_mm_bus_addr_max : NATURAL;
+  BEGIN
+    FOR I IN g_base_arr'RANGE LOOP
+      IF g_base_arr(I) > v_base THEN
+        v_base  := g_base_arr(I);
+        v_width := g_width_arr(I);
+      END IF;
+    END LOOP;
+    -- Largest base address + the width of the slave at this address - 1. The
+    -- -1 is because the addresses count from 0 to N-1.
+    v_mm_bus_addr_max := v_base + 2**v_width - 1;
+    -- Return number of bits to represent the largest address that will be used
+    -- on the MM bus
+    RETURN ceil_log2(v_mm_bus_addr_max);
+  END;
+  
+  CONSTANT c_mm_bus_addr_w      : NATURAL := func_derive_mm_bus_addr_w(g_base_arr, g_width_arr);
+  CONSTANT c_mosi_latency       : NATURAL := sel_a_b(g_pipeline_mosi, 1, 0);
+  CONSTANT c_miso_latency       : NATURAL := sel_a_b(g_pipeline_miso, 1, 0);
+  CONSTANT c_index_latency_max  : NATURAL := c_mosi_latency + largest(g_rd_latency_arr);
+
+  SIGNAL index_pipeline      : t_natural_arr(0 TO c_index_latency_max) := (OTHERS=>0);
+  SIGNAL slave_mosi_arr_comb : t_mem_mosi_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_mosi_rst);
+  SIGNAL master_miso_comb    : t_mem_miso := c_mem_miso_rst;
+
+  SIGNAL x  : NATURAL;
+  SIGNAL y  : NATURAL;
+  SIGNAL z  : NATURAL;
+BEGIN
+
+  gen_single : IF g_nof_slaves=1 GENERATE 
+    slave_mosi_arr(0) <= master_mosi;
+    master_miso       <= slave_miso_arr(0);
+  END GENERATE;
+    
+  gen_multiple : IF g_nof_slaves>1 GENERATE 
+
+    -- Detect which slave in the array is addressed
+    p_index : PROCESS(master_mosi)
+      VARIABLE v_base : NATURAL;
+    BEGIN
+      index_pipeline(0) <= g_nof_slaves;   -- default index of none existing slave
+      FOR I IN 0 TO g_nof_slaves-1 LOOP
+        v_base := TO_UINT(master_mosi.address(c_mm_bus_addr_w-1 DOWNTO g_width_arr(I)));
+        ASSERT g_base_arr(I) MOD 2**g_width_arr(I) = 0 REPORT "Slave base address must be a multiple of the slave width." SEVERITY FAILURE;
+        IF v_base = g_base_arr(I) / 2**g_width_arr(I) THEN
+          index_pipeline(0) <= I;   -- return index of addressed slave
+          EXIT;
+        END IF;
+      END LOOP;
+      x <= c_mm_bus_addr_w;
+      y <= v_base;
+    END PROCESS;
+    
+    index_pipeline(1 TO c_index_latency_max) <= index_pipeline(0 TO c_index_latency_max-1) WHEN rising_edge(mm_clk);
+        
+    -- Master access, can be write or read
+    p_mosi : PROCESS(master_mosi, index_pipeline)
+    BEGIN
+      FOR I IN 0 TO g_nof_slaves-1 LOOP
+        slave_mosi_arr_comb(I).rd <= '0';
+        slave_mosi_arr_comb(I).wr <= '0';
+        IF I = index_pipeline(0) THEN   -- check index for read or write access
+          slave_mosi_arr_comb(I) <= master_mosi;
+        END IF;
+      END LOOP;
+    END PROCESS;
+    
+    no_pipeline_mosi : IF g_pipeline_mosi = FALSE GENERATE
+      slave_mosi_arr <= slave_mosi_arr_comb;
+    END GENERATE;
+    gen_pipeline_mosi : IF g_pipeline_mosi = TRUE GENERATE
+      slave_mosi_arr <= slave_mosi_arr_comb WHEN rising_edge(mm_clk);
+    END GENERATE;
+    
+    -- Slave response to read access after read latency mm_clk cycles
+    p_miso : PROCESS(slave_miso_arr, index_pipeline)
+      VARIABLE v_rd_latency : NATURAL;
+    BEGIN
+      master_miso_comb <= c_mem_miso_rst;
+      FOR I IN 0 TO g_nof_slaves-1 LOOP
+        v_rd_latency := c_mosi_latency + g_rd_latency_arr(I);
+        IF I = index_pipeline(v_rd_latency) THEN  -- check index for read response
+          master_miso_comb <= slave_miso_arr(I);
+        END IF;
+      END LOOP;
+    END PROCESS;
+
+    no_pipeline_miso : IF g_pipeline_miso = FALSE GENERATE
+      master_miso <= master_miso_comb;
+    END GENERATE;
+    gen_pipeline_miso : IF g_pipeline_miso = TRUE GENERATE
+      master_miso <= master_miso_comb WHEN rising_edge(mm_clk);
+    END GENERATE;
+
+  END GENERATE; 
+  
+END rtl;
diff --git a/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd b/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd
new file mode 100644
index 0000000000..eb3531a375
--- /dev/null
+++ b/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd
@@ -0,0 +1,175 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+-- 
+-- Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the 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.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- 
+-- Author: E. Kooistra
+-- Purpose: Test bench for common_mem_bus.vhd
+-- Remark:
+-- . This test bench covers:
+--   . g_nof_slaves >= 1
+--   . g_pipeline_mosi, g_pipeline_miso
+--   . g_rd_latency >= 1 (using 0 is supported by common_mem_bus, but not by
+--     the common_ram_r_w in u_slaves)
+--   . same g_rd_latency for all slaves
+--   . same g_width for all slaves
+--   . regular base address spacing of slaves in c_base_arr
+-- . The common_mem_bus.vhd can support a list of arbitrary width slaves. This
+--   test bench is derived from tb_common_mem_mux.vhd and therefore it uses an
+--   array of fixed width slaves.  For now it is considered sufficient 
+--   coverage for this tb and the corresponding multi tb_tb to also only
+--   support regular c_base_arr, same g_rd_latency, and same g_width for all
+--   slaves.
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE work.common_pkg.ALL;
+USE work.common_mem_pkg.ALL;
+USE work.tb_common_pkg.ALL;
+USE work.tb_common_mem_pkg.ALL;
+
+ENTITY tb_common_mem_bus IS
+ GENERIC (
+    g_nof_slaves    : POSITIVE := 2;       -- Number of slave memory interfaces on the MM bus array.
+    g_base_offset   : NATURAL := 0;        -- Address of first slave on the MM bus
+    g_width_w       : POSITIVE := 4;       -- Address width of each slave memory in the MM bus array.
+    g_rd_latency    : NATURAL := 1;        -- Read latency of the slaves slave
+    g_pipeline_mosi : BOOLEAN := FALSE;
+    g_pipeline_miso : BOOLEAN := TRUE
+  );
+END tb_common_mem_bus;
+
+-- Usage:
+--   > as 10
+--   > run -all
+
+
+ARCHITECTURE tb OF tb_common_mem_bus IS
+
+  CONSTANT mm_clk_period   : TIME    := 10 ns;
+
+  CONSTANT c_slave_span      : NATURAL := 2**g_width_w;
+  CONSTANT c_base_arr        : t_natural_arr := array_init(g_base_offset, g_nof_slaves, c_slave_span);  -- Address base per slave
+  CONSTANT c_width_arr       : t_natural_arr := array_init(    g_width_w, g_nof_slaves);                -- Address width per slave
+  CONSTANT c_rd_latency_arr  : t_natural_arr := array_init( g_rd_latency, g_nof_slaves);                -- Read latency per slave
+  
+  CONSTANT c_mosi_latency    : NATURAL := sel_a_b(g_pipeline_mosi, 1, 0);
+  CONSTANT c_miso_latency    : NATURAL := sel_a_b(g_pipeline_miso, 1, 0);
+  CONSTANT c_read_latency    : NATURAL := c_mosi_latency + g_rd_latency + c_miso_latency;
+
+  CONSTANT c_data_w     : NATURAL := 32;
+  CONSTANT c_test_ram   : t_c_mem := (latency  => g_rd_latency,
+                                      adr_w    => g_width_w,
+                                      dat_w    => c_data_w,
+                                      nof_dat  => 2**g_width_w,
+                                      init_sl  => '0');
+  SIGNAL mm_rst   : STD_LOGIC;
+  SIGNAL mm_clk   : STD_LOGIC := '1';
+  SIGNAL tb_end   : STD_LOGIC;
+
+  SIGNAL mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_mosi_rst);
+  SIGNAL miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst);
+  SIGNAL mosi     : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL miso     : t_mem_miso := c_mem_miso_rst;
+
+  -- Debug signals for monitoring in simulation Wave window
+  SIGNAL dbg_c_base_arr        : t_natural_arr(0 TO g_nof_slaves-1) := c_base_arr;
+  SIGNAL dbg_c_width_arr       : t_natural_arr(0 TO g_nof_slaves-1) := c_width_arr;
+  SIGNAL dbg_c_rd_latency_arr  : t_natural_arr(0 TO g_nof_slaves-1) := c_rd_latency_arr;
+  
+BEGIN
+
+  mm_clk <= NOT mm_clk OR tb_end AFTER mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER mm_clk_period*5;
+
+  p_stimuli : PROCESS
+    VARIABLE temp : INTEGER;
+  BEGIN
+    tb_end <= '0';
+    mosi   <= c_mem_mosi_rst;
+    
+    -- Wait until reset is released
+    proc_common_wait_until_low(mm_clk, mm_rst);
+    proc_common_wait_some_cycles(mm_clk, 10);
+    
+    -- Write the whole memory range
+    FOR I IN 0 TO g_nof_slaves-1 LOOP
+      FOR J IN 0 TO 2**g_width_w-1 LOOP
+        proc_mem_mm_bus_wr(g_base_offset + I*2**g_width_w + J, I+J, mm_clk, mosi);
+      END LOOP;
+    END LOOP;
+
+    -- Read back the whole range and check if data is as expected
+    FOR I IN 0 TO g_nof_slaves-1 LOOP
+      FOR J IN 0 TO 2**g_width_w-1 LOOP
+        proc_mem_mm_bus_rd(g_base_offset + I*2**g_width_w + J, mm_clk, mosi);
+        proc_common_wait_some_cycles(mm_clk, c_read_latency);
+        temp := TO_UINT(miso.rddata(31 DOWNTO 0));
+        IF temp /= I+J THEN
+          REPORT "Error! Readvalue is not as expected" SEVERITY ERROR;
+        END IF;
+      END LOOP;
+    END LOOP;
+
+    proc_common_wait_some_cycles(mm_clk, 10);
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  u_slaves : FOR I IN 0 TO g_nof_slaves-1 GENERATE
+    u_ram : ENTITY work.common_ram_r_w
+    GENERIC MAP (
+      g_ram       => c_test_ram,
+      g_init_file => "UNUSED"
+    )
+    PORT MAP (
+      rst       => mm_rst,
+      clk       => mm_clk,
+      clken     => '1',
+      wr_en     => mosi_arr(I).wr,
+      wr_adr    => mosi_arr(I).address(g_width_w-1 DOWNTO 0),
+      wr_dat    => mosi_arr(I).wrdata(c_data_w-1 DOWNTO 0),
+      rd_en     => mosi_arr(I).rd,
+      rd_adr    => mosi_arr(I).address(g_width_w-1 DOWNTO 0),
+      rd_dat    => miso_arr(I).rddata(c_data_w-1 DOWNTO 0),
+      rd_val    => miso_arr(I).rdval
+    );
+  END GENERATE;
+
+  d_dut: ENTITY work.common_mem_bus
+  GENERIC MAP (
+    g_nof_slaves      => g_nof_slaves,
+    g_base_arr        => c_base_arr,
+    g_width_arr       => c_width_arr,
+    g_rd_latency_arr  => c_rd_latency_arr,
+    g_pipeline_mosi   => g_pipeline_mosi,
+    g_pipeline_miso   => g_pipeline_miso
+  )
+  PORT MAP (
+    mm_clk         => mm_clk,
+    master_mosi    => mosi,
+    master_miso    => miso,
+    slave_mosi_arr => mosi_arr,
+    slave_miso_arr => miso_arr
+  );
+
+END tb;
diff --git a/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd b/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd
new file mode 100644
index 0000000000..2a98bc2190
--- /dev/null
+++ b/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd
@@ -0,0 +1,54 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+-- 
+-- Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the 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.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- 
+-- Author: E. Kooistra
+-- Purpose: Multi test bench for common_mem_bus.vhd
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE work.common_pkg.ALL;
+
+ENTITY tb_tb_common_mem_bus IS
+END tb_tb_common_mem_bus;
+
+ARCHITECTURE tb OF tb_tb_common_mem_bus IS
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+BEGIN
+  -- Usage:
+  -- > as 4
+  -- > run -all
+
+  -- g_nof_slaves    : POSITIVE := 2;       -- Number of slave memory interfaces on the MM bus array.
+  -- g_base_offset   : NATURAL := 0;        -- Address of first slave on the MM bus
+  -- g_width_w       : POSITIVE := 4;       -- Address width of each slave memory in the MM bus array.
+  -- g_rd_latency    : NATURAL := 1;        -- Read latency of the slaves slave
+  -- g_pipeline_mosi : BOOLEAN := FALSE;
+  -- g_pipeline_miso : BOOLEAN := FALSE
+    
+  u_rd_latency_1       : ENTITY work.tb_common_mem_bus GENERIC MAP (16,      0, 3, 1, FALSE, FALSE);
+  u_base_offset        : ENTITY work.tb_common_mem_bus GENERIC MAP (16, 3*2**4, 4, 1, FALSE, FALSE);
+  u_pipeline_mosi      : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3,      0, 4, 1, TRUE, FALSE);
+  u_pipeline_mosi_miso : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3,      0, 4, 1, TRUE, TRUE);
+  
+END tb;
-- 
GitLab