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

Added MM master multiplexer with test bench, to support multiple masters on a single MM bus.

parent 9e9ba667
No related branches found
No related tags found
No related merge requests found
......@@ -164,6 +164,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_master_mux.vhd
tb/vhdl/tb_common_mem_bus.vhd
tb/vhdl/tb_common_mem_mux.vhd
tb/vhdl/tb_common_multiplexer.vhd
......@@ -208,6 +209,7 @@ test_bench_files =
regression_test_vhdl =
tb/vhdl/tb_common_fifo_rd.vhd
tb/vhdl/tb_common_mem_master_mux.vhd
tb/vhdl/tb_common_mem_mux.vhd
tb/vhdl/tb_common_paged_ram_crw_crw.vhd
tb/vhdl/tb_common_pulser_us_ms_s.vhd
......
......@@ -24,16 +24,20 @@
-- Purpose: Multiplex an array of MM master interfaces to a single MM master
-- interface
-- Description:
-- This common_mem_master_mux is a simple multiplexer and does not provide
-- arbitration between the masters in the array. Therefore the precondition
-- is that the application takes care that the MM accesses of the multiple
-- masters in the array do not overlap.
-- For read accesses the g_rd_latency defines how much idle time there needs
-- to be after the last read access by one master and a next MM access by
-- another master.
-- This common_mem_master_mux is a simple multiplexer that allows multiple
-- masters to access the same MM port. The common_mem_master_mux does not
-- provide arbitration between the masters in the array. Therefore the
-- precondition is that the external application takes care that the MM
-- accesses of the multiple masters in the array do not overlap in time.
--
-- Write accesses from multiple masters occur may without gaps. After a read
-- access from one master the read latency must first be accounted for by
-- the application introducing a gap, before a read access by another master
-- can be multiplexed.
--
-- The common_mem_master_mux operates combinatorially, so it introduces no
-- extra latency. The mm_clk is needed to time the g_rd_latency of the
-- external mux_miso, to ensure that the read data.is passed on to the
-- extra latency. The mm_clk is needed to hold the index of the master that
-- is currently active, to ensure that the read data.is passed on to the
-- master that did the rd access.
-- Remarks:
-- . The mux_miso.waitrequest is not supported.
......@@ -49,7 +53,7 @@ USE common_lib.common_mem_pkg.ALL;
ENTITY common_mem_master_mux IS
GENERIC (
g_nof_masters : POSITIVE; -- Number of MM masters
g_rd_latency : NATURAL -- Maximum read latency
g_rd_latency_min : NATURAL -- Minimum read latency
);
PORT (
mm_clk : IN STD_LOGIC;
......@@ -62,8 +66,8 @@ END common_mem_master_mux;
ARCHITECTURE rtl OF common_mem_master_mux IS
SIGNAL index_reg : NATURAL := 0;
SIGNAL index_pipeline : t_natural_arr(0 TO g_rd_latency) := (OTHERS=>0);
SIGNAL index : NATURAL := 0;
SIGNAL index_hold : NATURAL := 0;
BEGIN
......@@ -75,41 +79,50 @@ BEGIN
gen_multiple : IF g_nof_masters>1 GENERATE
-- Detect which master in the array is active
p_index : PROCESS(master_mosi_arr, index_reg)
-- The pre condition is that the input masters will only start an access
-- when the mux master is free. For a rd access this means that the
-- read latency of the rdval has passed. Therefor it is not necessary
-- that this common_mem_master_mux maintains an index pipeline
-- from rd until expected rdval. Instead it is sufficient to hold the
-- index of the active master, until the next master does an access. For
-- rd access hold the last active index to ensure that rdval will be
-- directed to the master that orginated the rd access. For wr access
-- hold last active index instead of reset to '0' to ease observation of
-- the index value in wave window.
p_index : PROCESS(master_mosi_arr, index_hold)
BEGIN
-- default hold last active index instead of reset to 0 to ease observing index in wave window
index_pipeline(0) <= index_reg;
index <= index_hold; -- default hold index of last active master
FOR I IN 0 TO g_nof_masters-1 LOOP
IF master_mosi_arr(I).wr='1' OR master_mosi_arr(I).rd='1' THEN
index_pipeline(0) <= I; -- index of active master
index <= I; -- index of active master
EXIT;
END IF;
END LOOP;
END PROCESS;
index_reg <= index_pipeline(0) WHEN rising_edge(mm_clk); -- hold index
index_pipeline(1 TO g_rd_latency) <= index_pipeline(0 TO g_rd_latency-1) WHEN rising_edge(mm_clk);
index_hold <= index WHEN rising_edge(mm_clk); -- hold index of last active master
-- Master access, can be write or read
p_mosi : PROCESS(master_mosi_arr)
BEGIN
mux_mosi <= c_mem_mosi_rst; -- default clear, to avoid latches
FOR I IN 0 TO g_nof_masters-1 LOOP
IF I = index_pipeline(0) THEN -- pass on selected master
mux_mosi <= master_mosi_arr(I);
END IF;
END LOOP;
END PROCESS;
-- Multiplex master access, can be write or read
mux_mosi <= master_mosi_arr(index);
-- Slave response to read access after g_rd_latency mm_clk cycles
p_miso : PROCESS(mux_miso, index_pipeline)
-- Multiplex slave read response
p_miso : PROCESS(mux_miso, index)
BEGIN
master_miso_arr <= (OTHERS=>mux_miso); -- default assign to all, to avoid latches
FOR I IN 0 TO g_nof_masters-1 LOOP
master_miso_arr(I).rdval <= '0';
IF I = index_pipeline(g_rd_latency) THEN -- check index for read response
master_miso_arr(I).rdval <= mux_miso.rdval;
-- If the minimal read latency is g_rd_latency_min = 0, then the mux
-- has to use the combinatorial index, else it use the registered
-- index, to ease achieving timing closure.
IF g_rd_latency_min=0 THEN
IF I = index THEN
master_miso_arr(I).rdval <= mux_miso.rdval;
END IF;
ELSE
IF I = index_hold THEN
master_miso_arr(I).rdval <= mux_miso.rdval;
END IF;
END IF;
END LOOP;
END PROCESS;
......
-------------------------------------------------------------------------------
--
-- 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_master_mux.vhd and also common_mem_bus
-- Description:
-- The test bench uses common_mem_master_mux to access a RAM via an array of
-- masters. The array of masters is modelled using a stimuli from a single
-- master that get demultiplexed to the array of masters using
-- common_mem_bus. The address space of the RAM is defined by the g_base_arr
-- and g_width_arr that define the common_mem_bus. Therefore this test bench
-- implicitely also verifies common_mem_bus.vhd.
--
-- stimuli master mux
-- mosi mosi_arr mosi
-- p_stimuli ----------> common -----------> common --------> RAM
-- mem mem
-- bus master
-- mux
--
-- Remark:
-- In an application it is typical to use common_mem_master_mux to connect
-- mulitple masters to multiple slabes via a common_mem_bus MM bus.
-------------------------------------------------------------------------------
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_master_mux IS
GENERIC (
g_nof_masters : POSITIVE := 2; -- Number of master memory interfaces on the MM bus array.
g_base_arr : t_nat_natural_arr := (0, 256); -- Address base per slave port of common_mem_bus
g_width_arr : t_nat_natural_arr := (4, 8) -- Address width per slave port of common_mem_bus
);
END tb_common_mem_master_mux;
-- Usage:
-- > as 10
-- > run -all
ARCHITECTURE tb OF tb_common_mem_master_mux IS
CONSTANT mm_clk_period : TIME := 10 ns;
CONSTANT c_rd_latency : NATURAL := 1;
CONSTANT c_rd_latency_arr : t_nat_natural_arr := array_init(c_rd_latency, g_nof_masters);
CONSTANT c_rd_latency_min : NATURAL := smallest(c_rd_latency_arr);
CONSTANT c_addr_w : NATURAL := largest(ceil_log2(largest(g_base_arr)), largest(g_width_arr)) + 1;
CONSTANT c_data_w : NATURAL := 32;
CONSTANT c_test_ram : t_c_mem := (latency => c_rd_latency,
adr_w => c_addr_w,
dat_w => c_data_w,
nof_dat => 2**c_addr_w,
init_sl => '0');
SIGNAL mm_rst : STD_LOGIC;
SIGNAL mm_clk : STD_LOGIC := '1';
SIGNAL tb_end : STD_LOGIC;
SIGNAL stimuli_mosi : t_mem_mosi := c_mem_mosi_rst;
SIGNAL stimuli_miso : t_mem_miso := c_mem_miso_rst;
SIGNAL master_mosi_arr : t_mem_mosi_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_mosi_rst);
SIGNAL master_miso_arr : t_mem_miso_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_miso_rst);
SIGNAL mux_mosi : t_mem_mosi := c_mem_mosi_rst;
SIGNAL mux_miso : t_mem_miso := c_mem_miso_rst;
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 v_base : NATURAL;
VARIABLE v_span : NATURAL;
VARIABLE v_data : INTEGER;
BEGIN
tb_end <= '0';
stimuli_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);
-- Repeat twice to have wr all, rd all, wr all, rd all
FOR R IN 0 TO 1 LOOP
-- Write the whole memory range
FOR I IN 0 TO g_nof_masters-1 LOOP
v_base := g_base_arr(I);
v_span := 2**g_width_arr(I);
FOR J IN 0 TO v_span-1 LOOP
proc_mem_mm_bus_wr(v_base + J, R+J, mm_clk, stimuli_mosi);
END LOOP;
END LOOP;
-- Read back the whole range in reverse order and check if data is as expected
FOR I IN g_nof_masters-1 DOWNTO 0 LOOP
v_base := g_base_arr(I);
v_span := 2**g_width_arr(I);
FOR J IN v_span-1 DOWNTO 0 LOOP
proc_mem_mm_bus_rd(v_base + J, mm_clk, stimuli_mosi);
proc_common_wait_some_cycles(mm_clk, c_rd_latency);
v_data := TO_UINT(stimuli_miso.rddata(31 DOWNTO 0));
IF v_data /= R+J THEN
REPORT "Error! Readvalue is not as expected" SEVERITY ERROR;
END IF;
END LOOP;
END LOOP;
END LOOP;
proc_common_wait_some_cycles(mm_clk, 10);
tb_end <= '1';
WAIT;
END PROCESS;
-- Model multiple masters using stimuli from a single master
u_masters : ENTITY work.common_mem_bus
GENERIC MAP (
g_nof_slaves => g_nof_masters,
g_base_arr => g_base_arr,
g_width_arr => g_width_arr,
g_rd_latency_arr => c_rd_latency_arr,
g_pipeline_mosi => FALSE,
g_pipeline_miso => FALSE
)
PORT MAP (
mm_clk => mm_clk,
master_mosi => stimuli_mosi,
master_miso => stimuli_miso,
slave_mosi_arr => master_mosi_arr,
slave_miso_arr => master_miso_arr
);
-- DUT = device under test
u_dut: ENTITY work.common_mem_master_mux
GENERIC MAP (
g_nof_masters => g_nof_masters,
g_rd_latency_min => c_rd_latency_min
)
PORT MAP (
mm_clk => mm_clk,
master_mosi_arr => master_mosi_arr,
master_miso_arr => master_miso_arr,
mux_mosi => mux_mosi,
mux_miso => mux_miso
);
-- Model master access to MM bus with multiple slaves using a single RAM
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,
wr_en => mux_mosi.wr,
wr_adr => mux_mosi.address(c_addr_w-1 DOWNTO 0),
wr_dat => mux_mosi.wrdata(c_data_w-1 DOWNTO 0),
rd_en => mux_mosi.rd,
rd_adr => mux_mosi.address(c_addr_w-1 DOWNTO 0),
rd_dat => mux_miso.rddata(c_data_w-1 DOWNTO 0),
rd_val => mux_miso.rdval
);
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment