diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg index df646d9c66c8a5c163941d091858556656929887..b5d72799425624032e63ecb259a57ef505110fd8 100644 --- a/libraries/base/common/hdllib.cfg +++ b/libraries/base/common/hdllib.cfg @@ -147,6 +147,7 @@ synth_files = tb/vhdl/tb_common_mem_pkg.vhd test_bench_files = + tb/vhdl/common_mem_waitrequest_model.vhd tb/vhdl/tb_common_acapture.vhd tb/vhdl/tb_common_add_sub.vhd tb/vhdl/tb_common_adder_tree.vhd @@ -198,6 +199,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_mem_master_mux.vhd tb/vhdl/tb_tb_common_fanout_tree.vhd tb/vhdl/tb_tb_common_multiplexer.vhd tb/vhdl/tb_tb_common_operation_tree.vhd @@ -210,7 +212,6 @@ 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 @@ -226,6 +227,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_mem_master_mux.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 index 32078476336ad8c684d3ec53fafc0d2231994b51..9e372e8f2c552529af5226924ca17c7689a8d871 100644 --- a/libraries/base/common/src/vhdl/common_mem_bus.vhd +++ b/libraries/base/common/src/vhdl/common_mem_bus.vhd @@ -88,11 +88,15 @@ -- 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 +-- . g_pipeline_miso_rd -- Pipelining the miso read data increases the read latency. --- The total write latency from master to slave is c_mosi_latency. +-- . g_pipeline_miso_wait +-- Pipelining the miso waitrequest increases the write and read latency +-- for slaves that need MM flow control. +-- The total write latency from master to slave is c_pipeline_mosi. -- 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. +-- c_pipeline_mosi + g_rd_latency_arr of the selected slave + +-- c_pipeline_miso_rd. -- -- Remarks: -- . The common_mem_bus resembles common_mem_mux, but the difference is that @@ -114,18 +118,19 @@ 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_nat_natural_arr; -- Address base per slave - g_width_arr : t_nat_natural_arr; -- Address width per slave - g_rd_latency_arr : t_nat_natural_arr; -- Read latency per slave - g_pipeline_mosi : BOOLEAN := FALSE; - g_pipeline_miso : BOOLEAN := FALSE + g_nof_slaves : POSITIVE; -- Number of MM slave interfaces on the bus + g_base_arr : t_nat_natural_arr; -- Address base per slave + g_width_arr : t_nat_natural_arr; -- Address width per slave + g_rd_latency_arr : t_nat_natural_arr; -- Read latency per slave + g_pipeline_mosi : BOOLEAN := FALSE; -- Pipeline MM access (wr, rd) + g_pipeline_miso_rd : BOOLEAN := FALSE; -- Pipeline MM read (rdval) + g_pipeline_miso_wait : BOOLEAN := FALSE -- Pipeline MM access flow control (waitrequest) ); 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_mosi_arr : OUT t_mem_mosi_arr(0 TO g_nof_slaves-1); slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) ); END common_mem_bus; @@ -153,13 +158,16 @@ ARCHITECTURE rtl OF common_mem_bus IS 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); + CONSTANT c_pipeline_mosi : NATURAL := sel_a_b(g_pipeline_mosi, 1, 0); + CONSTANT c_pipeline_miso_rd : NATURAL := sel_a_b(g_pipeline_miso_rd, 1, 0); + CONSTANT c_pipeline_miso_wait : NATURAL := sel_a_b(g_pipeline_miso_wait, 1, 0); + CONSTANT c_index_latency_max : NATURAL := c_pipeline_mosi + largest(g_rd_latency_arr); SIGNAL index_pipeline : t_nat_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 slave_mosi_arr_reg : 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 master_miso_reg : t_mem_miso := c_mem_miso_rst; BEGIN @@ -188,7 +196,7 @@ BEGIN 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) + p_slave_mosi_arr : PROCESS(master_mosi, index_pipeline) BEGIN slave_mosi_arr_comb <= (OTHERS=>master_mosi); -- default assign to all, to avoid latches FOR I IN 0 TO g_nof_slaves-1 LOOP @@ -201,33 +209,42 @@ BEGIN 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_mosi_arr_reg <= slave_mosi_arr_comb WHEN rising_edge(mm_clk); + + slave_mosi_arr <= slave_mosi_arr_comb WHEN g_pipeline_mosi = FALSE ELSE slave_mosi_arr_reg; + -- Slave response to read access after read latency mm_clk cycles - p_miso : PROCESS(slave_miso_arr, index_pipeline) + p_master_miso_comb : PROCESS(slave_miso_arr, index_pipeline) VARIABLE v_rd_latency : NATURAL; BEGIN master_miso_comb <= c_mem_miso_rst; -- default clear, to avoid latches FOR I IN 0 TO g_nof_slaves-1 LOOP - v_rd_latency := c_mosi_latency + g_rd_latency_arr(I); + v_rd_latency := c_pipeline_mosi + 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; + FOR I IN 0 TO g_nof_slaves-1 LOOP + IF I = index_pipeline(0) THEN -- check index for waitrequest + master_miso_comb.waitrequest <= slave_miso_arr(I).waitrequest; + 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; - + master_miso_reg <= master_miso_comb WHEN rising_edge(mm_clk); + + p_master_miso : PROCESS(master_miso_comb, master_miso_reg) + BEGIN + master_miso <= master_miso_comb; -- default no miso pipelining + IF g_pipeline_miso_rd THEN + master_miso.rddata <= master_miso_reg.rddata; + master_miso.rdval <= master_miso_reg.rdval; + END IF; + IF g_pipeline_miso_wait THEN + master_miso.waitrequest <= master_miso_reg.waitrequest; + END IF; + END PROCESS; END GENERATE; END rtl; diff --git a/libraries/base/common/tb/vhdl/common_mem_waitrequest_model.vhd b/libraries/base/common/tb/vhdl/common_mem_waitrequest_model.vhd new file mode 100644 index 0000000000000000000000000000000000000000..4e3fae4d41016ab0542fa98e9b4d1b8bb436267a --- /dev/null +++ b/libraries/base/common/tb/vhdl/common_mem_waitrequest_model.vhd @@ -0,0 +1,119 @@ +------------------------------------------------------------------------------- +-- +-- 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: Provide waitrequest stimuli to model a slave with MM flow control +-- Description: +-- The model applies random waitrequest stimuli for a MM slave that does not +-- need MM flow control. In this way the MM slave acts like a MM slave that +-- does need MM flow control. +-- * The model only controls the bus_miso.waitrequest. The other slave_miso +-- fields are wired to the bus_miso. The bus master will act upon the +-- waitrequest, so model can rely on that regarding the bus_mosi. However +-- towards the MM slave that has no flow control the model has to gate the +-- bus_mosi wr and rd with the waitrequest, so that the MM slave only gets +-- a ram_mosi rd or wr when it was acknowledged. +-- * When g_waitrequest = TRUE then the waitrequest model is applied to the +-- bus_miso. Use g_waitrequest = FALSE to bypass the waitrequest model, +-- so then bus_miso.waitrequest is fixed '0'. +-- * The g_seed is used to initalize the random PRSG, e.g use slave instance +-- index as g_seed to have different stimuli per instance. +-- * The maximum number of cycles that waitrequest depends on the period of +-- the LFSR random sequence generator and can be: +-- . '1' for g_prsg_w mm_clk cycles +-- . '0' for g_prsg_w-1 mm_clk cycles +-- Remarks: +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.common_lfsr_sequences_pkg.ALL; + +ENTITY common_mem_waitrequest_model IS + GENERIC ( + g_waitrequest : BOOLEAN; + g_seed : NATURAL := 0; + g_prsg_w : NATURAL := 16 + ); + PORT ( + mm_clk : IN STD_LOGIC; + bus_mosi : IN t_mem_mosi; + bus_miso : OUT t_mem_miso; + slave_mosi : OUT t_mem_mosi; + slave_miso : IN t_mem_miso + ); +END common_mem_waitrequest_model; + +ARCHITECTURE rtl OF common_mem_waitrequest_model IS + + CONSTANT c_prsg_init : NATURAL := g_seed + 1; -- PRSG init must be > 0 + + SIGNAL prsg : STD_LOGIC_VECTOR(g_prsg_w-1 DOWNTO 0) := TO_UVEC(c_prsg_init, g_prsg_w); + SIGNAL nxt_prsg : STD_LOGIC_VECTOR(g_prsg_w-1 DOWNTO 0); + + SIGNAL waitrequest : STD_LOGIC; + +BEGIN + + no_waitrequest : IF g_waitrequest=FALSE GENERATE + slave_mosi <= bus_mosi; + + p_waitrequest : PROCESS(slave_miso) + BEGIN + bus_miso <= slave_miso; + bus_miso.waitrequest <= '0'; + END PROCESS; + END GENERATE; + + gen_waitrequest : IF g_waitrequest=TRUE GENERATE + -- Model MM flow control using random waitrequest + p_reg : PROCESS(mm_clk) + BEGIN + IF rising_edge(mm_clk) THEN + prsg <= func_common_random(prsg); + END IF; + END PROCESS; + + waitrequest <= prsg(0); + + -- Apply MM flow control to bus master using waitrequest + p_bus_miso : PROCESS(waitrequest, slave_miso) + BEGIN + bus_miso <= slave_miso; + bus_miso.waitrequest <= waitrequest; + END PROCESS; + + -- Gate MM rd and wr access to RAM slave that has no flow control + p_slave_mosi : PROCESS(waitrequest, bus_mosi) + BEGIN + slave_mosi <= bus_mosi; + slave_mosi.wr <= bus_mosi.wr AND NOT waitrequest; + slave_mosi.rd <= bus_mosi.rd AND NOT waitrequest; + END PROCESS; + + 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 index ed4bb56f4d0a4556f216165683a8dd45b02a8c9d..ceb65b25a60629f1d1c1e4c982031f8212c8716a 100644 --- a/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd +++ b/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd @@ -25,7 +25,10 @@ -- Remark: -- . This test bench covers: -- . g_nof_slaves >= 1 --- . g_pipeline_mosi, g_pipeline_miso +-- . g_waitrequest for g_pipeline_miso_wait = FALSE +-- . g_pipeline_mosi +-- . g_pipeline_miso_rd +-- . g_pipeline_miso_wait = FALSE -- . 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 @@ -51,12 +54,14 @@ 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 + g_nof_slaves : POSITIVE := 1; -- 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_waitrequest : BOOLEAN := TRUE; -- When TRUE model waitrequest by MM slaves, else fixed '0' + g_pipeline_mosi : BOOLEAN := FALSE; + g_pipeline_miso_rd : BOOLEAN := TRUE; + g_pipeline_miso_wait : BOOLEAN := FALSE ); END tb_common_mem_bus; @@ -69,29 +74,33 @@ ARCHITECTURE tb OF tb_common_mem_bus IS CONSTANT mm_clk_period : TIME := 10 ns; + CONSTANT c_repeat : NATURAL := 10;--sel_a_b(g_waitrequest, 10, 2); -- repeat 2 for deterministic, more often for random CONSTANT c_slave_span : NATURAL := 2**g_width_w; CONSTANT c_base_arr : t_nat_natural_arr := array_init(g_base_offset, g_nof_slaves, c_slave_span); -- Address base per slave CONSTANT c_width_arr : t_nat_natural_arr := array_init( g_width_w, g_nof_slaves); -- Address width per slave CONSTANT c_rd_latency_arr : t_nat_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_pipeline_mosi : NATURAL := sel_a_b(g_pipeline_mosi, 1, 0); + CONSTANT c_pipeline_miso_rd : NATURAL := sel_a_b(g_pipeline_miso_rd, 1, 0); + CONSTANT c_pipeline_miso_wait : NATURAL := sel_a_b(g_pipeline_miso_wait, 1, 0); + CONSTANT c_read_latency : NATURAL := c_pipeline_mosi + g_rd_latency + c_pipeline_miso_rd; 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, + nof_dat => c_slave_span, 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; + SIGNAL master_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL master_miso : t_mem_miso := c_mem_miso_rst; + SIGNAL slave_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_mosi_rst); + SIGNAL slave_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst); + SIGNAL ram_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_mosi_rst); + SIGNAL ram_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst); -- Debug signals for monitoring in simulation Wave window SIGNAL dbg_c_base_arr : t_nat_natural_arr(0 TO g_nof_slaves-1) := c_base_arr; @@ -104,31 +113,42 @@ BEGIN mm_rst <= '1', '0' AFTER mm_clk_period*5; p_stimuli : PROCESS - VARIABLE v_data : INTEGER; + VARIABLE v_wrdata : INTEGER; -- write data + VARIABLE v_rddata : INTEGER; -- read data + VARIABLE v_expdata : INTEGER; -- expected data BEGIN tb_end <= '0'; - mosi <= c_mem_mosi_rst; + master_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); + -- Repeat twice to have wr all, rd all, wr all, rd all + v_wrdata := 0; + v_expdata := 0; + FOR vR IN 0 TO c_repeat-1 LOOP + -- Write the whole memory range + FOR vI IN 0 TO g_nof_slaves-1 LOOP + FOR vJ IN 0 TO c_slave_span-1 LOOP + proc_mem_mm_bus_wr(g_base_offset + vI*c_slave_span + vJ, v_wrdata, mm_clk, master_miso, master_mosi); + v_wrdata := v_wrdata + 1; + END LOOP; + --proc_common_wait_some_cycles(mm_clk, 10); 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); - v_data := TO_UINT(miso.rddata(31 DOWNTO 0)); - IF v_data /= I+J THEN - REPORT "Error! Readvalue is not as expected" SEVERITY ERROR; - END IF; + + -- Read back the whole range and check if data is as expected + FOR vI IN 0 TO g_nof_slaves-1 LOOP + FOR vJ IN 0 TO c_slave_span-1 LOOP + proc_mem_mm_bus_rd(g_base_offset + vI*c_slave_span + vJ, mm_clk, master_miso, master_mosi); + proc_common_wait_some_cycles(mm_clk, c_read_latency); + v_rddata := TO_UINT(master_miso.rddata(c_data_w-1 DOWNTO 0)); + IF v_rddata /= v_expdata THEN + REPORT "Error! Readvalue is not as expected" SEVERITY ERROR; + END IF; + v_expdata := v_expdata + 1; + --proc_common_wait_some_cycles(mm_clk, 10); + END LOOP; END LOOP; END LOOP; @@ -138,6 +158,19 @@ BEGIN END PROCESS; u_slaves : FOR I IN 0 TO g_nof_slaves-1 GENERATE + u_waitrequest_model : ENTITY work.common_mem_waitrequest_model + GENERIC MAP ( + g_waitrequest => g_waitrequest, + g_seed => I + ) + PORT MAP ( + mm_clk => mm_clk, + bus_mosi => slave_mosi_arr(I), + bus_miso => slave_miso_arr(I), + slave_mosi => ram_mosi_arr(I), + slave_miso => ram_miso_arr(I) + ); + u_ram : ENTITY work.common_ram_r_w GENERIC MAP ( g_ram => c_test_ram, @@ -147,31 +180,32 @@ BEGIN 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 + wr_en => ram_mosi_arr(I).wr, + wr_adr => ram_mosi_arr(I).address(g_width_w-1 DOWNTO 0), + wr_dat => ram_mosi_arr(I).wrdata(c_data_w-1 DOWNTO 0), + rd_en => ram_mosi_arr(I).rd, + rd_adr => ram_mosi_arr(I).address(g_width_w-1 DOWNTO 0), + rd_dat => ram_miso_arr(I).rddata(c_data_w-1 DOWNTO 0), + rd_val => ram_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 + 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_rd => g_pipeline_miso_rd, + g_pipeline_miso_wait => g_pipeline_miso_wait ) PORT MAP ( mm_clk => mm_clk, - master_mosi => mosi, - master_miso => miso, - slave_mosi_arr => mosi_arr, - slave_miso_arr => miso_arr + master_mosi => master_mosi, + master_miso => master_miso, + slave_mosi_arr => slave_mosi_arr, + slave_miso_arr => slave_miso_arr ); END tb; diff --git a/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd b/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd index bab3efcee8670c745fb3cfedd34c336f3fc0fac2..8039bb0ab543e9ef5731f2e09bd97e01cbd49251 100644 --- a/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd +++ b/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd @@ -32,11 +32,11 @@ -- -- stimuli master mux -- mosi mosi_arr mosi --- p_stimuli ----------> common -----------> common --------> RAM --- mem mem --- bus master --- mux --- +-- common -------/----> common +-- p_stimuli ----------> mem ------/-----> mem --------> RAM +-- bus -----/------> master +-- / mux +-- g_nof_masters -- 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. @@ -52,11 +52,13 @@ 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 - g_pipeline_bus_mosi : BOOLEAN := FALSE; - g_pipeline_bus_miso : BOOLEAN := FALSE + 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 + g_waitrequest : BOOLEAN := TRUE; -- When TRUE model waitrequest by the MM RAM slave, else fixed '0' + g_pipeline_bus_mosi : BOOLEAN := FALSE; + g_pipeline_bus_miso_rd : BOOLEAN := FALSE; + g_pipeline_bus_miso_wait : BOOLEAN := FALSE ); END tb_common_mem_master_mux; @@ -69,12 +71,14 @@ ARCHITECTURE tb OF tb_common_mem_master_mux IS CONSTANT mm_clk_period : TIME := 10 ns; - CONSTANT c_bus_mosi_latency : NATURAL := sel_a_b(g_pipeline_bus_mosi, 1, 0); - CONSTANT c_bus_miso_latency : NATURAL := sel_a_b(g_pipeline_bus_miso, 1, 0); - CONSTANT c_ram_rd_latency : NATURAL := 1; - CONSTANT c_ram_rd_latency_arr : t_nat_natural_arr := array_init(c_ram_rd_latency, g_nof_masters); + CONSTANT c_repeat : NATURAL := sel_a_b(g_waitrequest, 10, 2); -- repeat 2 for deterministic, more often for random + CONSTANT c_bus_pipeline_mosi : NATURAL := sel_a_b(g_pipeline_bus_mosi, 1, 0); + CONSTANT c_bus_pipeline_miso_rd : NATURAL := sel_a_b(g_pipeline_bus_miso_rd, 1, 0); + CONSTANT c_bus_pipeline_miso_wait : NATURAL := sel_a_b(g_pipeline_bus_miso_wait, 1, 0); + CONSTANT c_ram_rd_latency : NATURAL := 1; + CONSTANT c_ram_rd_latency_arr : t_nat_natural_arr := array_init(c_ram_rd_latency, g_nof_masters); - CONSTANT c_read_latency : NATURAL := c_bus_mosi_latency + c_ram_rd_latency + c_bus_miso_latency; + CONSTANT c_read_latency : NATURAL := c_bus_pipeline_mosi + c_ram_rd_latency + c_bus_pipeline_miso_rd; CONSTANT c_addr_w : NATURAL := largest(ceil_log2(largest(g_base_arr)), largest(g_width_arr)) + 1; CONSTANT c_data_w : NATURAL := 32; @@ -93,6 +97,8 @@ ARCHITECTURE tb OF tb_common_mem_master_mux IS 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; + SIGNAL ram_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL ram_miso : t_mem_miso := c_mem_miso_rst; BEGIN @@ -100,9 +106,11 @@ BEGIN mm_rst <= '1', '0' AFTER mm_clk_period*5; p_stimuli : PROCESS - VARIABLE v_base : NATURAL; - VARIABLE v_span : NATURAL; - VARIABLE v_data : INTEGER; + VARIABLE v_base : NATURAL; + VARIABLE v_span : NATURAL; + VARIABLE v_wrdata : INTEGER; -- write data + VARIABLE v_rddata : INTEGER; -- read data + VARIABLE v_expdata : INTEGER; -- expected data BEGIN tb_end <= '0'; stimuli_mosi <= c_mem_mosi_rst; @@ -112,27 +120,31 @@ BEGIN 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 + v_wrdata := 0; + v_expdata := 0; + FOR vR IN 0 TO c_repeat-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); + FOR vI IN 0 TO g_nof_masters-1 LOOP + v_base := g_base_arr(vI); + v_span := 2**g_width_arr(vI); + FOR vJ IN 0 TO v_span-1 LOOP + proc_mem_mm_bus_wr(v_base + vJ, v_wrdata, mm_clk, stimuli_miso, stimuli_mosi); + v_wrdata := v_wrdata + 1; 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); + -- Read back the whole range and check if data is as expected + FOR vI IN 0 TO g_nof_masters-1 LOOP + v_base := g_base_arr(vI); + v_span := 2**g_width_arr(vI); + FOR vJ IN 0 TO v_span-1 LOOP + proc_mem_mm_bus_rd(v_base + vJ, mm_clk, stimuli_miso, stimuli_mosi); proc_common_wait_some_cycles(mm_clk, c_read_latency); - v_data := TO_UINT(stimuli_miso.rddata(31 DOWNTO 0)); - IF v_data /= R+J THEN + v_rddata := TO_UINT(stimuli_miso.rddata(c_data_w-1 DOWNTO 0)); + IF v_rddata /= v_expdata THEN REPORT "Error! Readvalue is not as expected" SEVERITY ERROR; END IF; + v_expdata := v_expdata + 1; END LOOP; END LOOP; END LOOP; @@ -145,12 +157,13 @@ BEGIN -- 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_ram_rd_latency_arr, - g_pipeline_mosi => g_pipeline_bus_mosi, - g_pipeline_miso => g_pipeline_bus_miso + g_nof_slaves => g_nof_masters, + g_base_arr => g_base_arr, + g_width_arr => g_width_arr, + g_rd_latency_arr => c_ram_rd_latency_arr, + g_pipeline_mosi => g_pipeline_bus_mosi, + g_pipeline_miso_rd => g_pipeline_bus_miso_rd, + g_pipeline_miso_wait => g_pipeline_bus_miso_wait ) PORT MAP ( mm_clk => mm_clk, @@ -175,6 +188,18 @@ BEGIN ); -- Model master access to MM bus with multiple slaves using a single RAM + u_waitrequest_model : ENTITY work.common_mem_waitrequest_model + GENERIC MAP ( + g_waitrequest => g_waitrequest + ) + PORT MAP ( + mm_clk => mm_clk, + bus_mosi => mux_mosi, + bus_miso => mux_miso, + slave_mosi => ram_mosi, + slave_miso => ram_miso + ); + u_ram : ENTITY work.common_ram_r_w GENERIC MAP ( g_ram => c_test_ram, @@ -183,13 +208,13 @@ BEGIN 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 + wr_en => ram_mosi.wr, + wr_adr => ram_mosi.address(c_addr_w-1 DOWNTO 0), + wr_dat => ram_mosi.wrdata(c_data_w-1 DOWNTO 0), + rd_en => ram_mosi.rd, + rd_adr => ram_mosi.address(c_addr_w-1 DOWNTO 0), + rd_dat => ram_miso.rddata(c_data_w-1 DOWNTO 0), + rd_val => ram_miso.rdval ); 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 index 2a98bc21908d137d16e330aee1f6889ab95bd884..b174582b07707e5e236db46e725a5cf697d21543 100644 --- a/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd +++ b/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd @@ -39,16 +39,23 @@ BEGIN -- > 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 + -- 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_waitrequest : BOOLEAN := FALSE; -- When TRUE model waitrequest by MM slaves, else fixed '0' + -- g_pipeline_mosi : BOOLEAN := FALSE; + -- g_pipeline_miso_rd : BOOLEAN := TRUE; + -- g_pipeline_miso_wait : 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); + u_no_pipe : ENTITY work.tb_common_mem_bus GENERIC MAP (16, 0, 3, 1, FALSE, FALSE, FALSE, FALSE); + u_no_pipe_base_offset : ENTITY work.tb_common_mem_bus GENERIC MAP (16, 3*2**4, 4, 1, FALSE, FALSE, FALSE, FALSE); + u_pipe_mosi : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, FALSE, TRUE, FALSE, FALSE); + u_pipe_mosi_miso_rd : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, FALSE, TRUE, TRUE, FALSE); + u_waitrequest_no_pipe : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, FALSE, FALSE, FALSE); + u_waitrequest_pipe_miso_rd : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, FALSE, TRUE, FALSE); + u_waitrequest_pipe_miso_rd_rlat2 : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 2, TRUE, FALSE, TRUE, FALSE); + --u_waitrequest_pipe_mosi : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, TRUE, FALSE, FALSE); + --u_waitrequest_pipe_mosi_miso_rd : ENTITY work.tb_common_mem_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, TRUE, TRUE, FALSE); END tb; diff --git a/libraries/base/common/tb/vhdl/tb_tb_common_mem_master_mux.vhd b/libraries/base/common/tb/vhdl/tb_tb_common_mem_master_mux.vhd new file mode 100644 index 0000000000000000000000000000000000000000..c5ccc24770023649c87d422786d39bfbad49dd82 --- /dev/null +++ b/libraries/base/common/tb/vhdl/tb_tb_common_mem_master_mux.vhd @@ -0,0 +1,58 @@ +------------------------------------------------------------------------------- +-- +-- 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_master_mux.vhd +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE; +USE IEEE.std_logic_1164.ALL; +USE work.common_pkg.ALL; + +ENTITY tb_tb_common_mem_master_mux IS +END tb_tb_common_mem_master_mux; + +ARCHITECTURE tb OF tb_tb_common_mem_master_mux 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_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 + -- g_waitrequest : BOOLEAN := FALSE; -- When TRUE model waitrequest by the MM RAM slave, else fixed '0' + -- g_pipeline_bus_mosi : BOOLEAN := FALSE; + -- g_pipeline_bus_miso_rd : BOOLEAN := FALSE; + -- g_pipeline_bus_miso_wait : BOOLEAN := FALSE + + u_no_pipe : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, FALSE, FALSE, FALSE); + u_pipe_mosi : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, TRUE, FALSE, FALSE); + u_pipe_miso_rd : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, FALSE, TRUE, FALSE); + u_waitrequest_no_pipe : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, FALSE, FALSE, FALSE); + u_waitrequest_pipe_miso_rd : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, FALSE, TRUE, FALSE); + --u_waitrequest_pipe_mosi : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, TRUE, FALSE, FALSE); + --u_waitrequest_pipe_mosi_miso_rd : ENTITY work.tb_common_mem_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, TRUE, TRUE, FALSE); + +END tb;