Skip to content
Snippets Groups Projects
Commit 6cedf793 authored by Eric Kooistra's avatar Eric Kooistra
Browse files

Added common_mem_bus.vhd with test bench. This implements a MM bus that can be...

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.
parent 2f693bfc
No related branches found
No related tags found
No related merge requests found
...@@ -109,6 +109,7 @@ synth_files = ...@@ -109,6 +109,7 @@ synth_files =
src/vhdl/common_fifo_rd.vhd src/vhdl/common_fifo_rd.vhd
src/vhdl/common_blockreg.vhd src/vhdl/common_blockreg.vhd
src/vhdl/common_fifo_dc_lock_control.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_mux.vhd
src/vhdl/common_mem_demux.vhd src/vhdl/common_mem_demux.vhd
src/vhdl/common_reg_cross_domain.vhd src/vhdl/common_reg_cross_domain.vhd
...@@ -162,6 +163,7 @@ test_bench_files = ...@@ -162,6 +163,7 @@ test_bench_files =
tb/vhdl/tb_common_init.vhd tb/vhdl/tb_common_init.vhd
tb/vhdl/tb_common_int2float.vhd tb/vhdl/tb_common_int2float.vhd
tb/vhdl/tb_common_led_controller.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_mem_mux.vhd
tb/vhdl/tb_common_multiplexer.vhd tb/vhdl/tb_common_multiplexer.vhd
tb/vhdl/tb_common_operation_tree.vhd tb/vhdl/tb_common_operation_tree.vhd
...@@ -192,6 +194,7 @@ test_bench_files = ...@@ -192,6 +194,7 @@ test_bench_files =
tb/vhdl/tb_tb_common_add_sub.vhd tb/vhdl/tb_tb_common_add_sub.vhd
tb/vhdl/tb_tb_common_adder_tree.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_fanout_tree.vhd
tb/vhdl/tb_tb_common_multiplexer.vhd tb/vhdl/tb_tb_common_multiplexer.vhd
tb/vhdl/tb_tb_common_operation_tree.vhd tb/vhdl/tb_tb_common_operation_tree.vhd
...@@ -218,6 +221,7 @@ regression_test_vhdl = ...@@ -218,6 +221,7 @@ regression_test_vhdl =
tb/vhdl/tb_tb_common_adder_tree.vhd tb/vhdl/tb_tb_common_adder_tree.vhd
tb/vhdl/tb_tb_common_add_sub.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_fanout_tree.vhd
tb/vhdl/tb_tb_common_multiplexer.vhd tb/vhdl/tb_tb_common_multiplexer.vhd
tb/vhdl/tb_tb_common_operation_tree.vhd tb/vhdl/tb_tb_common_operation_tree.vhd
......
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment