diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg index df646d9c66c8a5c163941d091858556656929887..c7c3d8323ed975947cecfff9b124f78a0391aa0a 100644 --- a/libraries/base/common/hdllib.cfg +++ b/libraries/base/common/hdllib.cfg @@ -109,8 +109,6 @@ 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_master_mux.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 @@ -164,8 +162,6 @@ 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 tb/vhdl/tb_common_operation_tree.vhd @@ -197,7 +193,6 @@ 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 @@ -210,7 +205,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 @@ -225,7 +219,6 @@ 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_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd index 4023ea913639562ed8c86cbafeadf4bbde0ac14c..fe7d12376a4fb6a861405c4c1f49cf099ec65fb6 100644 --- a/libraries/base/common/src/vhdl/common_pkg.vhd +++ b/libraries/base/common/src/vhdl/common_pkg.vhd @@ -123,8 +123,8 @@ PACKAGE common_pkg IS TYPE t_slv_512_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(511 DOWNTO 0); TYPE t_slv_1024_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(1023 DOWNTO 0); - CONSTANT c_boolean_arr : t_boolean_arr := (TRUE, FALSE); -- array all possible values that can be iterated over - CONSTANT c_nat_boolean_arr : t_nat_boolean_arr := (TRUE, FALSE); -- array all possible values that can be iterated over + CONSTANT c_boolean_arr : t_boolean_arr := (TRUE, FALSE); -- array the two possible boolean values that can be iterated over + CONSTANT c_nat_boolean_arr : t_nat_boolean_arr := (TRUE, FALSE); -- array the two possible boolean values that can be iterated over TYPE t_integer_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF INTEGER; TYPE t_boolean_matrix IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF BOOLEAN; @@ -206,6 +206,9 @@ PACKAGE common_pkg IS FUNCTION orv( slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- alias of vector_or FUNCTION xorv(slv : STD_LOGIC_VECTOR) RETURN STD_LOGIC; -- alias of vector_xor + FUNCTION array_and(arr : t_nat_boolean_arr) RETURN BOOLEAN; + FUNCTION array_or( arr : t_nat_boolean_arr) RETURN BOOLEAN; + FUNCTION matrix_and(mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC; -- '1' when all matrix bits are '1' else '0' FUNCTION matrix_or( mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC; -- '0' when all matrix bits are '0' else '1' @@ -296,6 +299,7 @@ PACKAGE common_pkg IS FUNCTION sel_n(sel : NATURAL; a, b, c, d, e, f, g, h, i, j : STRING) RETURN STRING; -- 10 FUNCTION array_init(init : STD_LOGIC; nof : NATURAL) RETURN STD_LOGIC_VECTOR; -- useful to init a unconstrained array of size 1 + FUNCTION array_init(init : BOOLEAN; nof : NATURAL) RETURN t_nat_boolean_arr; -- useful to init a unconstrained array of size 1 FUNCTION array_init(init, nof : NATURAL) RETURN t_natural_arr; -- useful to init a unconstrained array of size 1 FUNCTION array_init(init, nof : NATURAL) RETURN t_nat_natural_arr; -- useful to init a unconstrained array of size 1 FUNCTION array_init(init, nof, incr : NATURAL) RETURN t_natural_arr; -- useful to init an array with incrementing numbers @@ -309,12 +313,21 @@ PACKAGE common_pkg IS FUNCTION init_slv_64_matrix(nof_a, nof_b, k : INTEGER) RETURN t_slv_64_matrix; -- initialize all elements in t_slv_64_matrix to value k -- Concatenate two or more STD_LOGIC_VECTORs into a single STD_LOGIC_VECTOR or extract one of them from a concatenated STD_LOGIC_VECTOR + -- . Note that using func_slv_concat() without the BOOLEAN use_* is equivalent to using the + -- slv concatenation operator & directly. However this overloaded func_slv_concat() is + -- still nice to have, because it shows the relation with the inverse func_slv_extract(). FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a, b, c, d, e, f, g : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a, b, c, d, e, f : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat( use_a, use_b, use_c, use_d, use_e : BOOLEAN; a, b, c, d, e : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat( use_a, use_b, use_c, use_d : BOOLEAN; a, b, c, d : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat( use_a, use_b, use_c : BOOLEAN; a, b, c : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat( use_a, use_b : BOOLEAN; a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b, c, d, e, f, g : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b, c, d, e, f : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b, c, d, e : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b, c, d : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b, c : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_concat( a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL) RETURN NATURAL; FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w : NATURAL) RETURN NATURAL; FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e : BOOLEAN; a_w, b_w, c_w, d_w, e_w : NATURAL) RETURN NATURAL; @@ -327,6 +340,12 @@ PACKAGE common_pkg IS FUNCTION func_slv_extract( use_a, use_b, use_c, use_d : BOOLEAN; a_w, b_w, c_w, d_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_extract( use_a, use_b, use_c : BOOLEAN; a_w, b_w, c_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; FUNCTION func_slv_extract( use_a, use_b : BOOLEAN; a_w, b_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w, c_w, d_w, e_w, f_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w, c_w, d_w, e_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w, c_w, d_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w, c_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; + FUNCTION func_slv_extract( a_w, b_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR; FUNCTION TO_UINT(vec : STD_LOGIC_VECTOR) RETURN NATURAL; -- beware: NATURAL'HIGH = 2**31-1, not 2*32-1, use TO_SINT to avoid warning FUNCTION TO_SINT(vec : STD_LOGIC_VECTOR) RETURN INTEGER; @@ -742,6 +761,20 @@ PACKAGE BODY common_pkg IS RETURN vector_tree(slv, "XOR"); END; + FUNCTION array_and(arr : t_nat_boolean_arr) RETURN BOOLEAN IS + VARIABLE v_slv : STD_LOGIC_VECTOR(arr'RANGE); + BEGIN + FOR I IN arr'RANGE LOOP v_slv(I) := sel_a_b(arr(I), '1', '0'); END LOOP; -- wire map boolean arr to slv + RETURN sel_a_b(vector_and(v_slv) = '1', TRUE, FALSE); -- use vector_tree to determine result + END; + + FUNCTION array_or(arr : t_nat_boolean_arr) RETURN BOOLEAN IS + VARIABLE v_slv : STD_LOGIC_VECTOR(arr'RANGE); + BEGIN + FOR I IN arr'RANGE LOOP v_slv(I) := sel_a_b(arr(I), '1', '0'); END LOOP; -- wire map boolean arr to slv + RETURN sel_a_b(vector_or(v_slv) = '1', TRUE, FALSE); -- use vector_tree to determine result + END; + FUNCTION matrix_and(mat : t_sl_matrix; wi, wj : NATURAL) RETURN STD_LOGIC IS VARIABLE v_mat : t_sl_matrix(0 TO wi-1, 0 TO wj-1) := mat; -- map to fixed range VARIABLE v_result : STD_LOGIC := '1'; @@ -1310,6 +1343,15 @@ PACKAGE BODY common_pkg IS RETURN v_arr; END; + FUNCTION array_init(init : BOOLEAN; nof : NATURAL) RETURN t_nat_boolean_arr IS + VARIABLE v_arr : t_nat_boolean_arr(0 TO nof-1); + BEGIN + FOR I IN v_arr'RANGE LOOP + v_arr(I) := init; + END LOOP; + RETURN v_arr; + END; + FUNCTION array_init(init, nof : NATURAL) RETURN t_natural_arr IS VARIABLE v_arr : t_natural_arr(0 TO nof-1); BEGIN @@ -1459,6 +1501,36 @@ PACKAGE BODY common_pkg IS RETURN func_slv_concat(use_a, use_b, FALSE, FALSE, FALSE, FALSE, FALSE, a, b, "0", "0", "0", "0", "0"); END func_slv_concat; + FUNCTION func_slv_concat(a, b, c, d, e, f, g : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, a, b, c, d, e, f, g); + END func_slv_concat; + + FUNCTION func_slv_concat(a, b, c, d, e, f : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, a, b, c, d, e, f); + END func_slv_concat; + + FUNCTION func_slv_concat(a, b, c, d, e: STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, TRUE, TRUE, TRUE, a, b, c, d, e); + END func_slv_concat; + + FUNCTION func_slv_concat(a, b, c, d : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, TRUE, TRUE, a, b, c, d); + END func_slv_concat; + + FUNCTION func_slv_concat(a, b, c : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, TRUE, a, b, c); + END func_slv_concat; + + FUNCTION func_slv_concat(a, b : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_concat(TRUE, TRUE, a, b); + END func_slv_concat; + FUNCTION func_slv_concat_w(use_a, use_b, use_c, use_d, use_e, use_f, use_g : BOOLEAN; a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL) RETURN NATURAL IS VARIABLE v_len : NATURAL := 0; BEGIN @@ -1569,6 +1641,36 @@ PACKAGE BODY common_pkg IS RETURN func_slv_extract(use_a, use_b, FALSE, FALSE, FALSE, FALSE, FALSE, a_w, b_w, 0, 0, 0, 0, 0, vec, sel); END func_slv_extract; + FUNCTION func_slv_extract(a_w, b_w, c_w, d_w, e_w, f_w, g_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, a_w, b_w, c_w, d_w, e_w, f_w, g_w, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(a_w, b_w, c_w, d_w, e_w, f_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, a_w, b_w, c_w, d_w, e_w, f_w, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(a_w, b_w, c_w, d_w, e_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, TRUE, TRUE, TRUE, a_w, b_w, c_w, d_w, e_w, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(a_w, b_w, c_w, d_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, TRUE, TRUE, a_w, b_w, c_w, d_w, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(a_w, b_w, c_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, TRUE, a_w, b_w, c_w, vec, sel); + END func_slv_extract; + + FUNCTION func_slv_extract(a_w, b_w : NATURAL; vec : STD_LOGIC_VECTOR; sel : NATURAL) RETURN STD_LOGIC_VECTOR IS + BEGIN + RETURN func_slv_extract(TRUE, TRUE, a_w, b_w, vec, sel); + END func_slv_extract; + FUNCTION TO_UINT(vec : STD_LOGIC_VECTOR) RETURN NATURAL IS BEGIN diff --git a/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd b/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd deleted file mode 100644 index ed4bb56f4d0a4556f216165683a8dd45b02a8c9d..0000000000000000000000000000000000000000 --- a/libraries/base/common/tb/vhdl/tb_common_mem_bus.vhd +++ /dev/null @@ -1,177 +0,0 @@ -------------------------------------------------------------------------------- --- --- 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, but --- this tb_common_mem_bus test bench uses an array of fixed width slaves. --- 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. The tb_common_mem_master_mux also uses a --- common_mem_bus.vhd and the tb_common_mem_master_mux does uses an array of --- arbitrary width 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_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_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_nat_natural_arr(0 TO g_nof_slaves-1) := c_base_arr; - SIGNAL dbg_c_width_arr : t_nat_natural_arr(0 TO g_nof_slaves-1) := c_width_arr; - SIGNAL dbg_c_rd_latency_arr : t_nat_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 v_data : 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); - 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; - 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_common_mem_master_mux.vhd b/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd deleted file mode 100644 index bab3efcee8670c745fb3cfedd34c336f3fc0fac2..0000000000000000000000000000000000000000 --- a/libraries/base/common/tb/vhdl/tb_common_mem_master_mux.vhd +++ /dev/null @@ -1,196 +0,0 @@ -------------------------------------------------------------------------------- --- --- 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 - g_pipeline_bus_mosi : BOOLEAN := FALSE; - g_pipeline_bus_miso : BOOLEAN := FALSE - ); -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_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_read_latency : NATURAL := c_bus_mosi_latency + c_ram_rd_latency + c_bus_miso_latency; - - 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_ram_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_read_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_ram_rd_latency_arr, - g_pipeline_mosi => g_pipeline_bus_mosi, - g_pipeline_miso => g_pipeline_bus_miso - ) - 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_read_latency - ) - 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; 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 deleted file mode 100644 index 2a98bc21908d137d16e330aee1f6889ab95bd884..0000000000000000000000000000000000000000 --- a/libraries/base/common/tb/vhdl/tb_tb_common_mem_bus.vhd +++ /dev/null @@ -1,54 +0,0 @@ -------------------------------------------------------------------------------- --- --- 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; diff --git a/libraries/base/mm/hdllib.cfg b/libraries/base/mm/hdllib.cfg index b3180241c7a1cc59125c22d754d1cd75fd96bd30..cd1b224af35e9aa0140662040d39fa5723b74aea 100644 --- a/libraries/base/mm/hdllib.cfg +++ b/libraries/base/mm/hdllib.cfg @@ -8,21 +8,39 @@ synth_files = src/vhdl/mm_fields.vhd tb/vhdl/mm_file_pkg.vhd tb/vhdl/mm_file_unb_pkg.vhd + src/verilog/timeout.v src/verilog/wbs_arbiter.v src/vhdl/mm_arbiter.vhd + + src/vhdl/mm_pipeline.vhd + src/vhdl/mm_latency_adapter.vhd + src/vhdl/mm_slave_enable.vhd + src/vhdl/mm_bus_comb.vhd + src/vhdl/mm_bus_pipe.vhd + src/vhdl/mm_bus.vhd + src/vhdl/mm_master_mux.vhd + src/vhdl/mm_slave_mux.vhd test_bench_files = tb/vhdl/mm_file.vhd tb/vhdl/tb_mm_file.vhd + + tb/vhdl/mm_waitrequest_model.vhd + tb/vhdl/tb_mm_bus.vhd + tb/vhdl/tb_mm_master_mux.vhd + tb/vhdl/tb_tb_mm_file.vhd + tb/vhdl/tb_tb_mm_bus.vhd + tb/vhdl/tb_tb_mm_master_mux.vhd regression_test_vhdl = tb/vhdl/tb_tb_mm_file.vhd + tb/vhdl/tb_tb_mm_bus.vhd + tb/vhdl/tb_tb_mm_master_mux.vhd [modelsim_project_file] [quartus_project_file] - diff --git a/libraries/base/mm/src/vhdl/mm_bus.vhd b/libraries/base/mm/src/vhdl/mm_bus.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0b447521408adcd9d45d60cdb8cd48983c5acfa1 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_bus.vhd @@ -0,0 +1,183 @@ +------------------------------------------------------------------------------- +-- +-- 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 using mm_bus_pipe. +-- Description: +-- In addition to mm_bus_pipe this mm_bus takes care of: +-- - not connected slaves +-- - slaves that do not need flow control +-- +-- FOR g_nof_slaves: +-- g_slave_enable_arr +-- g_waitrequest_arr +-- g_rd_latency_arr +-- | | +-- | | +-- g_base_arr | | +-- g_width_arr | | +-- g_pipeline_mosi | | +-- g_pipeline_miso_rdval | | +-- g_pipeline_miso_wait | | +-- | | | +-- __v___v_ ____v___ +-- master_miso <-------| mm_bus |<--| mm |<-- slave_miso_arr +-- master_mosi ------->| pipe |-->| slave |--> slave_mosi_arr +-- |________| | enable | +-- |________| +-- The mm_bus_comb takes care of: +-- - MM bus multiplexer between master and slaves +-- - MM slave address allocation on the MM bus +-- The mm_bus_pipe takes care of: +-- - Pipelining the MM bus, the mm_bus_comb and mm_slave_enable are +-- combinatorial. +-- +-- Usage: +-- The ascii drawing shows how this mm_bus can be used in combination +-- with other MM bus components to create an memory mapped bus: +-- +-- . mm_bus : connects a master to multiple independent slaves +-- . mm_slave_mux : connects an array of slave to a single slave port +-- . mm_master_mux : connects mulitple masters to a single slave +-- +-- mm_slave_mux +-- mm_bus |---S +-- |-------|---S +-- |---S |---S +-- M ---| +-- |---S +-- |---S +-- |-------| +-- |---S |---S +-- | +-- M -----------| +-- mm_master_mux +-- +-- The mm_slave_mux and mm_master_mux should typically be combinatorial, +-- because all MM bus pipelining is concentrated in mm_bus_pipe. +-- +-- * The mm_slave_mux is useful to present an array of equal slave MM +-- ports via a single port on the MM bus. Otherwise the mm_bus could +-- instead directly present each slave MM array port. +-- The mm_slave_mux introduces hierarchy in the MM bus structure. This +-- can help to influcence the timing closure. Using only mm_bus or +-- the a combination of mm_bus and mm_slave_mux can help to steer +-- where pipelining is inserted in the MM bus. +-- * The MM bus based on mm_bus could be automatically generated by ARGS +-- based on a set of MM slave ports described in YAML configuration +-- files. +-- +-- Limitations --> see mm_bus_comb +-- +-- Todo (only if necessary): +-- * The mm_bus assumes that the MM slave will eventually acknowledge an +-- mosi.wr or rd access by pulling miso.waitrequest low. If the MM slave +-- malfunctions then the MM bus access will stall. Therefore a MM slave +-- port that uses mosi flow control should also support a waitrequest +-- timeout mechanism. Such a waitrequest timeout mechanism could be made +-- part of mm_slave_enable. +-- * The master can then be informed about a failing mosi access using +-- the miso.response field of the Avalon bus. A failing mosi access can +-- be due to a not connected slave, an out-of-range address, a slave that +-- times out on flow control. +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_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_slave_enable_arr : t_nat_boolean_arr; -- Use FALSE for not connected slaves, else TRUE + g_waitrequest_arr : t_nat_boolean_arr; -- Enable waitrequest flow control per slave, else fixed '0' + g_pipeline_mosi : BOOLEAN := FALSE; -- Pipeline MM access (wr, rd) + g_pipeline_miso_rdval : BOOLEAN := FALSE; -- Pipeline MM read (rdval) + g_pipeline_miso_wait : BOOLEAN := FALSE -- Pipeline MM access flow control (waitrequest) + ); + PORT ( + mm_rst : IN STD_LOGIC := '0'; + 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); + slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) + ); +END mm_bus; + +ARCHITECTURE str OF mm_bus IS + + SIGNAL bus_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL bus_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + +BEGIN + + -- MM bus + u_mm_bus_pipe : ENTITY work.mm_bus_pipe + GENERIC MAP ( + g_nof_slaves => g_nof_slaves, + g_base_arr => g_base_arr, + g_width_arr => g_width_arr, + g_rd_latency_arr => g_rd_latency_arr, + g_waitrequest_arr => g_waitrequest_arr, + g_pipeline_mosi => g_pipeline_mosi, + g_pipeline_miso_rdval => g_pipeline_miso_rdval, + g_pipeline_miso_wait => g_pipeline_miso_wait + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + master_mosi => master_mosi, + master_miso => master_miso, + slave_mosi_arr => bus_mosi_arr, + slave_miso_arr => bus_miso_arr + ); + + -- The MM bus interface with the MM slaves + gen_slave_ports : FOR I IN 0 TO g_nof_slaves-1 GENERATE + -- Rewire not connected slaves and slave that do not need mosi flow control via miso.waitrequest + u_slave_enable : ENTITY work.mm_slave_enable + GENERIC MAP ( + g_enable => g_slave_enable_arr(I), + g_waitrequest => g_waitrequest_arr(I), + g_rd_latency => g_rd_latency_arr(I) + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + -- MM input RL = 1 + in_mosi => bus_mosi_arr(I), + in_miso => bus_miso_arr(I), + -- MM output RL = 0 + out_mosi => slave_mosi_arr(I), + out_miso => slave_miso_arr(I) + ); + END GENERATE; + +END str; diff --git a/libraries/base/common/src/vhdl/common_mem_bus.vhd b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd similarity index 53% rename from libraries/base/common/src/vhdl/common_mem_bus.vhd rename to libraries/base/mm/src/vhdl/mm_bus_comb.vhd index 32078476336ad8c684d3ec53fafc0d2231994b51..4fda2cd307a9c12189e85bbfbf5522b4fe04a364 100644 --- a/libraries/base/common/src/vhdl/common_mem_bus.vhd +++ b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd @@ -1,233 +1,216 @@ -------------------------------------------------------------------------------- --- --- 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 > 0, 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. --- The index_pipeline is used to support that a new wr access or rd access --- can already start, while a current rd access still has to finish with --- a rdval. Without the index_pipeline the master would have to wait with --- a new rd or wr access to another slave until the read response from the --- current slave has finished. --- ________ --- | pipe | --- master_mosi.address[h:w] = index --+-->| line |--\ --- | |______| | --- | | --- v | --- master_mosi --> slave_mosi_arr.wr[ ]----------------> slave_mosi_arr --- rd | --- v --- master_miso <--------------------slave_miso_arr[ ]<-- slave_miso_arr --- --- A limitation is that if one slave has a read latency of 2 and another --- slave has a read latency of 1 then it is not possible to access them --- without a gap of 1 mm_clk cycle, because the rdval will then be active --- simultaneously from both slaves. Therefore the master can only use --- random read access between slaves if all slaves have the same read --- latency. For slaves that have larger read latency the master must --- insert an gap, before it can read a slave that has less read latency. --- --- * 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. --- --- 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_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 - ); - 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_nat_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_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 master_miso_comb : t_mem_miso := c_mem_miso_rst; - -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; - 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 - slave_mosi_arr_comb <= (OTHERS=>master_mosi); -- default assign to all, to avoid latches - 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).rd <= master_mosi.rd; - slave_mosi_arr_comb(I).wr <= master_mosi.wr; - 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; -- 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); - 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: Connect a single MM master interface to a list of MM slave +-- interfaces using a combinatorial muliplexer as bus. +-- Description: +-- * MM bus +-- The mm_bus_comb 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, to +-- pipeline the slave_index_arr for the master_miso.rddata/rdval. +-- Typically a master will wait for the last rdval, before accessing +-- another slave port, so then it is not benecessary to pipeline the +-- slave_index_arr. However registering the slave_index_arr eases timing +-- closure on the miso part and will allow reading from different slave +-- ports without waiting, provided that both slaves have the same read +-- latency. +-- +-- * Read latency +-- For read accesses a slave will typically have a read latency > 0, 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. +-- The slave_index_arr is used to support that a new wr access or rd access +-- can already start, while a current rd access still has to finish with +-- a rdval. Without the slave_index_arr the master would have to wait with +-- a new rd or wr access to another slave until the read response from the +-- current slave has finished. +-- ________ +-- | delay| +-- master_mosi.address[h:w] = index --+-->| line |--\ +-- | |______| | +-- | | +-- v | +-- master_mosi --> slave_mosi_arr.wr[ ]----------------> slave_mosi_arr +-- rd | +-- v +-- master_miso <--------------------slave_miso_arr[ ]<-- slave_miso_arr +-- +-- +-- * No pipelining +-- The mm_bus_comb is combinatorial, so there is no pipelining between +-- the master interface and the slave interfaces. Use mm_bus_pipe to add +-- pipelining. +-- +-- Usage: +-- See mm_bus.vhd. +-- +-- Limitations: +-- * A limitation is that if one slave has a read latency of 2 and another +-- slave has a read latency of 1 then it is not possible to access them +-- without a gap of 1 mm_clk cycle, because the rdval will then be active +-- simultaneously from both slaves. Therefore the master can only use +-- random read access between slaves if all slaves have the same read +-- latency. For slaves that have larger read latency the master must +-- insert an gap, before it can read a slave that has less read latency. +-- An alternative workaround would be to use the same read latency for all +-- slaves on the bus, by pipelining the miso.rd, rddata for MM slaves that +-- have a smaller read latency. +-- +-- Remarks: +-- . The mm_bus_comb 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 mm_bus_comb to provide hierarchy by reprensenting an array +-- of slave ports via a single slave port on the MM bus. +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_bus_comb 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 + ); + 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); + slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) + ); +END mm_bus_comb; + +ARCHITECTURE rtl OF mm_bus_comb 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_nat_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_rd_latency_max : NATURAL := largest(g_rd_latency_arr); + + SIGNAL slave_index_arr : t_nat_natural_arr(0 TO c_rd_latency_max) := (OTHERS=>0); + +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 + slave_index_arr(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 + slave_index_arr(0) <= I; -- return index of addressed slave + EXIT; -- Found addressed slave, no need to loop further. EXIT is + -- not realy needed, because there can only be one + -- addressed slave so loop further will not change the index. + END IF; + END LOOP; + END PROCESS; + + slave_index_arr(1 TO c_rd_latency_max) <= slave_index_arr(0 TO c_rd_latency_max-1) WHEN rising_edge(mm_clk); + + -- Master access, can be write or read + p_slave_mosi_arr : PROCESS(master_mosi, slave_index_arr) + BEGIN + slave_mosi_arr <= (OTHERS=>master_mosi); -- default assign to all, to avoid latches + FOR I IN 0 TO g_nof_slaves-1 LOOP + slave_mosi_arr(I).rd <= '0'; + slave_mosi_arr(I).wr <= '0'; + IF I = slave_index_arr(0) THEN -- check index for read or write access + slave_mosi_arr(I).rd <= master_mosi.rd; + slave_mosi_arr(I).wr <= master_mosi.wr; + END IF; + END LOOP; + END PROCESS; + + + -- Slave response to read access after read latency mm_clk cycles + p_master_miso : PROCESS(slave_miso_arr, slave_index_arr) + VARIABLE v_rd_latency : NATURAL; + BEGIN + master_miso <= c_mem_miso_rst; -- default clear, to avoid latches + FOR I IN 0 TO g_nof_slaves-1 LOOP + v_rd_latency := g_rd_latency_arr(I); + IF I = slave_index_arr(v_rd_latency) THEN -- check index for read response + master_miso <= slave_miso_arr(I); + END IF; + END LOOP; + FOR I IN 0 TO g_nof_slaves-1 LOOP + IF I = slave_index_arr(0) THEN -- check index for waitrequest + master_miso.waitrequest <= slave_miso_arr(I).waitrequest; + END IF; + END LOOP; + END PROCESS; + + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd b/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd new file mode 100644 index 0000000000000000000000000000000000000000..2b8f8ecba4dad4d87fdc9dbb6607df467bcd6075 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd @@ -0,0 +1,223 @@ +------------------------------------------------------------------------------- +-- +-- 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 pipelining to the combinatorial mm_bus_comb +-- Description: +-- The mm_bus_comb 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 extra logic and to avoid +-- increasing the read latency. Instead first try achieve timing closure +-- by lower clock rate for the MM bus. Pipelining the MM bus can be +-- necessary to achieve timing closure. Thanks to mm_bus_comb the +-- pipelining is clearly separated from the MM bus multiplexer. The +-- pipelining is placed at the output of the bus, so at the slave side +-- for mosi and at the master side for miso: +-- +-- FOR g_nof_slaves: +-- g_pipeline_miso_rdval g_pipeline_mosi +-- g_pipeline_miso_wait | g_pipeline_miso_wait +-- | | | +-- v ________ ____v___ ___v___ +-- <-- p_miso_pipe <--| mm_bus |<--|mm |<--|mm |<-------- +-- ------------------>| comb |-->|pipeline|-->|latency|--------> +-- . . |________| . |________| |adapter| . . +-- master_miso . . . |_______| . slave_miso_arr +-- master_mosi . . . . slave_mosi_arr +-- m_miso bus_miso_arr pipe_miso_arr adapt_miso_arr +-- m_mosi bus_mosi_arr pipe_mosi_arr adapt_mosi_arr +-- +-- The MM bus pipelining is defined by: +-- +-- * 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 both mosi write and mosi read +-- use the same g_pipeline_mosi pipelining. +-- +-- * g_pipeline_miso_rdval +-- Pipelining the miso read data increases the read latency. +-- +-- * g_pipeline_miso_wait +-- Pipelining the miso waitrequest increases the write and read latency +-- for slaves that need MM flow control. Only applies to slave that +-- have g_waitrequest_arr is TRUE. +-- +-- The pipelining generics are defined as BOOLEAN (rather than NATURAL), +-- because the pipelining only needs to be 0 or 1. +-- +-- The total write latency from master to slave is 1 when either +-- g_pipeline_mosi or g_pipeline_miso_wait. +-- The total read latency from master via slave back to master is +-- write latency + g_rd_latency_arr of the selected slave + 1 or 0 +-- dependend on g_pipeline_miso_rdval. +-- +-- Usage: +-- See mm_bus.vhd +-- +-- Remark: +-- * It is not allowed to simultaneously use g_pipeline_miso_wait = TRUE +-- and g_pipeline_mosi = TRUE, because this leads to a combinatorial loop +-- of the miso.waitrequest that is used at the output of the mm_pipeline +-- and at the input of the mm_latency adapter: +-- - at the mm_pipeline output the waitrequest gates the mosi.wr and rd +-- - at the mm_latency_adapter input in common_rl_decrease the wr or +-- rd strobe is used to set the waitrequest. +-- This combinatorial loop seems unavoidable when the interface between +-- mm_pipeline and mm_latency_adpater is at RL = 0. A solution could be +-- to increase the RL at the output of the mm_pipeline to RL = 1 by +-- registering the waitrequest from the mm_latency_adapter. The total +-- RL for the input of the MM latency adapter then becomes RL = 2, so +-- then the mm_latency_adapter needs to adapt from RL = 2 to 0. +-- Currently the mm_latency_adapter only supports RL 1 to 0. Is possible +-- to extent this to RL = N to 0, similar as in dp_latency_adapter. +-- However fortunately it is not necessary to support g_pipeline_mosi = +-- TRUE when g_pipeline_miso_wait = TRUE, because g_pipeline_miso_wait = +-- TRUE by itself already also pipeplines the mosi. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_bus_pipe 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_waitrequest_arr : t_nat_boolean_arr; -- Enable waitrequest flow control per slave, else fixed '0' + g_pipeline_mosi : BOOLEAN := FALSE; -- Pipeline MM access (wr, rd) + g_pipeline_miso_rdval : BOOLEAN := FALSE; -- Pipeline MM read (rdval) + g_pipeline_miso_wait : BOOLEAN := FALSE -- Pipeline MM access flow control (waitrequest) + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + 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); + slave_miso_arr : IN t_mem_miso_arr(0 TO g_nof_slaves-1) := (OTHERS=>c_mem_miso_rst) + ); +END mm_bus_pipe; + +ARCHITECTURE str OF mm_bus_pipe IS + + SIGNAL m_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL m_miso : t_mem_miso := c_mem_miso_rst; + SIGNAL m_miso_reg : t_mem_miso := c_mem_miso_rst; + + SIGNAL bus_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL bus_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + SIGNAL pipe_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL pipe_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + SIGNAL adapt_mosi_arr : t_mem_mosi_arr(0 TO g_nof_slaves-1); + SIGNAL adapt_miso_arr : t_mem_miso_arr(0 TO g_nof_slaves-1); + +BEGIN + + ASSERT NOT(g_pipeline_miso_wait = TRUE AND g_pipeline_mosi = TRUE) + REPORT "Do not use g_pipeline_mosi = TRUE if g_pipeline_miso_wait = TRUE" + SEVERITY FAILURE; + + -- Master side + m_mosi <= master_mosi; + + m_miso_reg <= m_miso WHEN rising_edge(mm_clk); + + p_miso_pipe : PROCESS(m_miso, m_miso_reg) + BEGIN + -- Default no miso pipelining + master_miso <= m_miso; + -- Use pipelining + IF g_pipeline_miso_rdval THEN + master_miso.rddata <= m_miso_reg.rddata; + master_miso.rdval <= m_miso_reg.rdval; + END IF; + IF g_pipeline_miso_wait THEN + master_miso.waitrequest <= m_miso_reg.waitrequest; + END IF; + END PROCESS; + + -- MM bus + u_mm_bus_comb : ENTITY work.mm_bus_comb + GENERIC MAP ( + g_nof_slaves => g_nof_slaves, + g_base_arr => g_base_arr, + g_width_arr => g_width_arr, + g_rd_latency_arr => g_rd_latency_arr + ) + PORT MAP ( + mm_clk => mm_clk, + master_mosi => m_mosi, + master_miso => m_miso, + slave_mosi_arr => bus_mosi_arr, + slave_miso_arr => bus_miso_arr + ); + + -- Slaves side + gen_slave_pipes : FOR I IN 0 TO g_nof_slaves-1 GENERATE + u_slave_pipe_mosi : ENTITY work.mm_pipeline + GENERIC MAP ( + g_pipeline => g_pipeline_mosi + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + in_mosi => bus_mosi_arr(I), + in_miso => bus_miso_arr(I), + out_mosi => pipe_mosi_arr(I), + out_miso => pipe_miso_arr(I) + ); + + gen_wires : IF g_waitrequest_arr(I) = FALSE GENERATE + adapt_mosi_arr(I) <= pipe_mosi_arr(I); + pipe_miso_arr(I) <= adapt_miso_arr(I); + END GENERATE; + + gen_slave_latency_adapter : IF g_waitrequest_arr(I) = TRUE GENERATE + u_slave_latency_adapter : ENTITY work.mm_latency_adapter + GENERIC MAP ( + g_adapt => g_pipeline_miso_wait + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + -- MM input RL = 1 + in_mosi => pipe_mosi_arr(I), + in_miso => pipe_miso_arr(I), + -- MM output RL = 0 + out_mosi => adapt_mosi_arr(I), + out_miso => adapt_miso_arr(I) + ); + END GENERATE; + END GENERATE; + + slave_mosi_arr <= adapt_mosi_arr; + adapt_miso_arr <= slave_miso_arr; + +END str; diff --git a/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd b/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd new file mode 100644 index 0000000000000000000000000000000000000000..137efa3891e545ffab1c485b056ab4fe79904e57 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd @@ -0,0 +1,109 @@ +------------------------------------------------------------------------------- +-- +-- 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: Adapt miso.waitrequest latency from 1 to 0, to support pipelining +-- of the waitrequest flow control +-- Description: +-- Wraps common_rl_decrease.vhd. +-- The common_rl_decrease.vhd latency adapter FIFO buffers the in_mosi, to +-- create time to compensate for the pipeline of the in_mosi.waitrequest. +-- When the in_mosi.waitrequest goes high, then this FIFO buffer can hold +-- the in_mosi input that may still arrive, due to that the master at the +-- input only notices the in_mosi.waitrequest from the output slave one +-- cycle later due to the pipelining. + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_latency_adapter IS + GENERIC ( + g_adapt : BOOLEAN := TRUE -- default when TRUE then decrease sink RL 1 to source RL 0, else then implement wires + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + -- MM input RL = 1 + in_mosi : IN t_mem_mosi; + in_miso : OUT t_mem_miso; + -- MM output RL = 0 + out_mosi : OUT t_mem_mosi; + out_miso : IN t_mem_miso + ); +END mm_latency_adapter; + + +ARCHITECTURE str OF mm_latency_adapter IS + + -- Sum of all t_mem_mosi fields widths (synthesis will optimize away unused address and data bits) + CONSTANT c_data_w : NATURAL := c_mem_address_w + c_mem_data_w + 2; -- 32 + 72 + 1 (wr) + 1 (rd) = 106 + + SIGNAL in_waitrequest : STD_LOGIC; + SIGNAL in_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + SIGNAL in_val : STD_LOGIC; + SIGNAL in_ready : STD_LOGIC; + SIGNAL out_ready : STD_LOGIC; + SIGNAL out_data : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + SIGNAL out_val : STD_LOGIC; + +BEGIN + + in_data <= func_slv_concat(in_mosi.address, in_mosi.wrdata, slv(in_mosi.wr), slv(in_mosi.rd)); + in_val <= in_mosi.wr OR in_mosi.rd; + + p_miso : PROCESS(out_miso, in_waitrequest) + BEGIN + in_miso <= out_miso; + --in_miso.rdval <= out_miso.rdval AND NOT in_waitrequest; + in_miso.waitrequest <= in_waitrequest; + END PROCESS; + + -- Account for opposite meaning of waitrequest and ready + in_waitrequest <= NOT in_ready; + out_ready <= NOT out_miso.waitrequest; + + u_rl : ENTITY common_lib.common_rl_decrease + GENERIC MAP ( + g_adapt => g_adapt, + g_dat_w => c_data_w + ) + PORT MAP ( + rst => mm_rst, + clk => mm_clk, + -- ST sink: RL = 1 + snk_out_ready => in_ready, + snk_in_dat => in_data, + snk_in_val => in_val, + -- ST source: RL = 0 + src_in_ready => out_ready, + src_out_dat => out_data, + src_out_val => out_val + ); + + out_mosi.address <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, out_data, 0); + out_mosi.wrdata <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, out_data, 1); + out_mosi.wr <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, out_data, 2)); + out_mosi.rd <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, out_data, 3)); + +END str; diff --git a/libraries/base/common/src/vhdl/common_mem_master_mux.vhd b/libraries/base/mm/src/vhdl/mm_master_mux.vhd similarity index 72% rename from libraries/base/common/src/vhdl/common_mem_master_mux.vhd rename to libraries/base/mm/src/vhdl/mm_master_mux.vhd index 82e5fff65c07f3f870fe92a2869ff3ae607e4517..abac065ce676b536b61ea87e0d741c85253c3bd0 100644 --- a/libraries/base/common/src/vhdl/common_mem_master_mux.vhd +++ b/libraries/base/mm/src/vhdl/mm_master_mux.vhd @@ -1,132 +1,147 @@ -------------------------------------------------------------------------------- --- --- 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: Multiplex an array of MM master interfaces to a single MM master --- interface --- Description: --- 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 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. --- -------------------------------------------------------------------------------- - - -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_master_mux IS - GENERIC ( - g_nof_masters : POSITIVE; -- Number of MM masters - g_rd_latency_min : NATURAL -- Minimum read latency - ); - PORT ( - mm_clk : IN STD_LOGIC; - master_mosi_arr : IN t_mem_mosi_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_mosi_rst); - master_miso_arr : OUT t_mem_miso_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_miso_rst); - mux_mosi : OUT t_mem_mosi; - mux_miso : IN t_mem_miso - ); -END common_mem_master_mux; - -ARCHITECTURE rtl OF common_mem_master_mux IS - - SIGNAL index : NATURAL := 0; - SIGNAL index_hold : NATURAL := 0; - -BEGIN - - gen_single : IF g_nof_masters=1 GENERATE - mux_mosi <= master_mosi_arr(0); - master_miso_arr(0) <= mux_miso; - END GENERATE; - - gen_multiple : IF g_nof_masters>1 GENERATE - - -- Detect which master in the array is active - -- 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 - 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 <= I; -- index of active master - EXIT; - END IF; - END LOOP; - END PROCESS; - - index_hold <= index WHEN rising_edge(mm_clk); -- hold index of last active master - - - -- Multiplex master access, can be write or read - mux_mosi <= master_mosi_arr(index); - - -- 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 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; - - 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: Multiplex an array of MM master interfaces to a single MM master +-- interface +-- Description: +-- This mm_master_mux is a simple multiplexer that allows multiple +-- masters to access the same MM port. The mm_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 mm_master_mux operates combinatorially, so it introduces no +-- 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: +-- . This resembles common_mem_demux.vhd, but is not identical. The difference +-- is that common_mem_demux is the inverse of common_mem_demux and therefore +-- assumes that all the mux_mosi spans the entire array whereas for this +-- mm_master_mux the mux_mosi spans one element. +-- . There is no bus arbitrator. This is sufficient for use cases where e.g. +-- one master only does some initialization accesses after reset and the +-- other master is the main master that does all subsequent accesses. +-- Therefore this mm_master_mux is typically suited per MM slave +-- that needs dual master access, rather then to select between two main +-- central MM masters. +-- . There is no pipelining. The advantage is that the mux_miso.waitrequest is +-- supported without extra effort. +-- +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_master_mux IS + GENERIC ( + g_nof_masters : POSITIVE; -- Number of MM masters + g_rd_latency_min : NATURAL -- Minimum read latency + ); + PORT ( + mm_clk : IN STD_LOGIC; + master_mosi_arr : IN t_mem_mosi_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_mosi_rst); + master_miso_arr : OUT t_mem_miso_arr(0 TO g_nof_masters-1) := (OTHERS=>c_mem_miso_rst); + mux_mosi : OUT t_mem_mosi; + mux_miso : IN t_mem_miso + ); +END mm_master_mux; + +ARCHITECTURE rtl OF mm_master_mux IS + + SIGNAL index : NATURAL := 0; + SIGNAL index_hold : NATURAL := 0; + +BEGIN + + gen_single : IF g_nof_masters=1 GENERATE + mux_mosi <= master_mosi_arr(0); + master_miso_arr(0) <= mux_miso; + END GENERATE; + + gen_multiple : IF g_nof_masters>1 GENERATE + + -- Detect which master in the array is active + -- 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 mm_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 + 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 <= I; -- index of active master + EXIT; -- Found active master, no need to loop further. EXIT is not + -- realy needed, because there should be only one active + -- master, and if there are more active masters, then it + -- does not matter whether the first or the last is selected. + END IF; + END LOOP; + END PROCESS; + + index_hold <= index WHEN rising_edge(mm_clk); -- hold index of last active master + + + -- Multiplex master access, can be write or read + mux_mosi <= master_mosi_arr(index); + + -- 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 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; + + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_pipeline.vhd b/libraries/base/mm/src/vhdl/mm_pipeline.vhd new file mode 100644 index 0000000000000000000000000000000000000000..179a3d51adfa85d3b5ed77c5bb1a449a2c50ff24 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_pipeline.vhd @@ -0,0 +1,153 @@ +------------------------------------------------------------------------------- +-- +-- 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: Pipeline MM mosi +-- Description: +-- The mm_pipeline mosi registers the in_mosi if g_pipeline = TRUE, else it +-- defaults to wires. +-- +-- Background information +-- The MM waitrequest resembles the behaviour of the streaming backpressure +-- ready for ready latency RL = 0. For RL = 0 the ready acts as an +-- acknowledge to pending data. For RL > 0 the ready acts as a request for +-- new data. The miso.waitrequest is defined for RL = 0 but for analysis +-- the timing diagrams below show an example of both RL = 0 and RL = 1. The +-- miso.waitrequest is equivalent to NOT sosi.ready. +-- +-- * RL=1 +-- _ _ _ _ _ _ _ _ _ _ _ _ +-- clk _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ +-- +-- in_dat |a |b |c |d +-- _________ ___ ___ +-- in_val |_______| |_______| |_______________ +-- _____ ___ _______ ___________ +-- ready |_______| |___|... |_______|........... +-- _________ ___ _______ _______ +-- reg_ready |_______| |___|... |_______|....... +-- +-- reg_dat |a |b |c |d +-- _____________________________ ___________ +-- reg_val |___| |___ +-- _________ ___ ___ ___ +-- out_val |a |_______|b |___|c |___________|d |___ +-- +-- +-- * RL=0 +-- _ _ _ _ _ _ _ _ _ _ _ _ _ +-- clk _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ +-- +-- in_dat |a |b |c |d |e +-- _________ _______________ ___ +-- in_val |_______| |_______| |_______ +-- _____________ ___ _______ ___________ +-- ack |_______| |___| |___| +-- +-- reg_dat |a |b |c |d |e +-- _____________ ___________ ___ +-- reg_val |___________| |_______| |___ +-- _____________ _______ ___ +-- out_val |a |b |_______________|c |d |_______|e |___ +-- +-- In these timing diagrams the out_ready is wired to the in_ready, so +-- therefore they are identical and called ready. +-- The ready for RL = 0 or the reg_ready for RL = 1 is used to gate the +-- out_val. The ready/reg_ready is used and not the in_val, because by +-- using the ready/reg_ready the pipeline register is emptied as soon +-- as the ready is active, rather than to wait for a next in_val to push +-- it out. The ready/reg_ready have the same latency as the in_val, +-- because they are both derived using the same RL. +-- +-- Remark: +-- * The mm_pipeline could be optimized regarding the miso.waitrequest flow +-- control if it would be implemented similar as dp_pipeline.vhd. This +-- involves using the pipeline register to accept an access when it is +-- empty. In this way the waitrequest to the in_mosi only needs to apply +-- when the out_miso is not ready and the pipeline is full. This would +-- achieve the maximum throughput. The advantage of simply registering +-- in_mosi and wiring in_miso is that it is simpler and does not put extra +-- logic into the combinatorial miso.waitrequest path. It is better to +-- keep it simpler and with less logic, then to try to win the last few +-- percent of throughput. + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_pipeline IS + GENERIC ( + g_pipeline : BOOLEAN := TRUE + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + in_mosi : IN t_mem_mosi; + in_miso : OUT t_mem_miso; + out_mosi : OUT t_mem_mosi; + out_miso : IN t_mem_miso + ); +END mm_pipeline; + + +ARCHITECTURE rtl OF mm_pipeline IS + + SIGNAL mosi_reg : t_mem_mosi := c_mem_mosi_rst; + SIGNAL nxt_mosi_reg : t_mem_mosi; + SIGNAL ready : STD_LOGIC; + +BEGIN + + -- Pass on miso + in_miso <= out_miso; + + -- Pipeline the mosi when g_pipeline = TRUE, else default to wires + gen_wires : IF g_pipeline = FALSE GENERATE + out_mosi <= in_mosi; + END GENERATE; + + gen_pipeline : IF g_pipeline = TRUE GENERATE + p_reg : PROCESS(mm_rst, mm_clk) + BEGIN + IF mm_rst = '1' THEN + mosi_reg <= c_mem_mosi_rst; + ELSIF rising_edge(mm_clk) THEN + mosi_reg <= nxt_mosi_reg; + END IF; + END PROCESS; + + ready <= NOT out_miso.waitrequest; + + nxt_mosi_reg <= in_mosi WHEN ready = '1' ELSE mosi_reg; + + p_out_mosi : PROCESS(mosi_reg, ready) + BEGIN + out_mosi <= mosi_reg; + IF ready /= '1' THEN + out_mosi.wr <= '0'; -- out_mosi.wr = mosi_reg.wr AND ready + out_mosi.rd <= '0'; -- out_mosi.rd = mosi_reg.rd AND ready + END IF; + END PROCESS; + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_slave_enable.vhd b/libraries/base/mm/src/vhdl/mm_slave_enable.vhd new file mode 100644 index 0000000000000000000000000000000000000000..7803a14da266adc98274703022c01b4c8945d3b3 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_slave_enable.vhd @@ -0,0 +1,122 @@ +------------------------------------------------------------------------------- +-- +-- 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 an MM slave to the MM bus or represent an not connected +-- slave. Force waitrequest = '0' if slave does not need mosi flow +-- control +-- Description: +-- * g_enable +-- When FALSE then the in_miso output tot the master is forced to +-- c_mem_miso_rst to represent a not connected MM slave. When TRUE then +-- the out_miso signal from the slave is passed on to the master. +-- * g_waitrequest +-- When FALSE then the in_miso.waitrequest is forced to '0' to indicate +-- that the MM slave does not need mosi flow control. When FALSE then +-- the miso.waitrequest from the connected slave is passed on to the +-- master. +-- * g_rd_latency +-- Used to derive in_miso.rdval from in_mosi.rd and out_miso.waitrequest, +-- to provide rdval for MM slaves that do not drive rdval. Typically any +-- MM slave that needs miso.waitrequest flow control, also should support +-- rdval themselves. +-- +-- Todo: +-- * Add miso.response field as defined in Avalon bus, to inform master about +-- rd status (00 = okay, 01 = rsvd, 10 = slaveerror, 11 = decodeerror). +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_slave_enable IS + GENERIC ( + g_enable : BOOLEAN; + g_waitrequest : BOOLEAN; + g_rd_latency : NATURAL + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + -- MM input RL = 1 + in_mosi : IN t_mem_mosi; + in_miso : OUT t_mem_miso; + -- MM output RL = 0 + out_mosi : OUT t_mem_mosi; + out_miso : IN t_mem_miso + ); +END mm_slave_enable; + + +ARCHITECTURE rtl OF mm_slave_enable IS + + SIGNAL rd : STD_LOGIC; + SIGNAL rdval : STD_LOGIC; + SIGNAL waitrequest : STD_LOGIC; + +BEGIN + + -- Use mosi.rd to create miso.rdval for unconnected slave or for slaves that do not support rdval + u_rdval : ENTITY common_lib.common_pipeline_sl + GENERIC MAP ( + g_pipeline => g_rd_latency + ) + PORT MAP ( + rst => mm_rst, + clk => mm_clk, + in_dat => rd, + out_dat => rdval + ); + + + no_slave : IF g_enable = FALSE GENERATE + out_mosi <= c_mem_mosi_rst; + + rd <= in_mosi.rd; + + p_in_miso : PROCESS(rdval) + BEGIN + in_miso <= c_mem_miso_rst; -- force all miso to 0, so rddata = 0 and no waitrequest + in_miso.rdval <= rdval; -- support rdval to avoid hanging master that waits for rdval + END PROCESS; + END GENERATE; + + gen_slave : IF g_enable = TRUE GENERATE + out_mosi <= in_mosi; + + -- Use waitrequest from slave, or force waitrequest = '0' if slave does not need mosi flow control + waitrequest <= out_miso.waitrequest WHEN g_waitrequest = TRUE ELSE '0'; + + rd <= in_mosi.rd AND NOT waitrequest; + + p_in_miso : PROCESS(out_miso, rdval, waitrequest) + BEGIN + in_miso <= out_miso; + in_miso.rdval <= rdval; + in_miso.waitrequest <= waitrequest; + END PROCESS; + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_slave_mux.vhd b/libraries/base/mm/src/vhdl/mm_slave_mux.vhd new file mode 100644 index 0000000000000000000000000000000000000000..c2a61ace47ece1df90a026338eceb366c79e7341 --- /dev/null +++ b/libraries/base/mm/src/vhdl/mm_slave_mux.vhd @@ -0,0 +1,70 @@ +------------------------------------------------------------------------------- +-- +-- 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: Combines an array of MM interfaces into a single MM interface. +-- Description: +-- Wraps common_mem_mux.vhd. +-- Remark: +-- No need for g_rd_latency pipelining, so pure combinatorial and no need +-- for clk. If necessary apply pipelining via mm_bus.vhd. +------------------------------------------------------------------------------- + + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; + +ENTITY mm_slave_mux IS + GENERIC ( + g_broadcast : BOOLEAN := FALSE; -- TRUE use port[0] to access all, else use separate ports + g_nof_mosi : POSITIVE := 256; -- Number of slave memory interfaces in the array. + g_mosi_addr_w : POSITIVE := 8 -- Address width per slave + ); + PORT ( + mosi : IN t_mem_mosi; + miso : OUT t_mem_miso; + mosi_arr : OUT t_mem_mosi_arr(g_nof_mosi - 1 DOWNTO 0); + miso_arr : IN t_mem_miso_arr(g_nof_mosi - 1 DOWNTO 0) := (OTHERS=>c_mem_miso_rst) + ); +END mm_slave_mux; + +ARCHITECTURE str OF mm_slave_mux IS +BEGIN + + u_common_mem_mux : ENTITY common_lib.common_mem_mux + GENERIC MAP ( + g_broadcast => g_broadcast, + g_nof_mosi => g_nof_mosi, + g_mult_addr_w => g_mosi_addr_w, + g_rd_latency => 0 + ) + PORT MAP ( + clk => '0', -- only used when g_rd_latency > 0 + mosi => mosi, + miso => miso, + mosi_arr => mosi_arr, + miso_arr => miso_arr + ); + +END str; diff --git a/libraries/base/mm/tb/vhdl/mm_file_pkg.vhd b/libraries/base/mm/tb/vhdl/mm_file_pkg.vhd index f253848f810b786efb2ead64f221e735c510d625..1b0148e99c85b83e36a8ed7e61c82d1ea343c83e 100644 --- a/libraries/base/mm/tb/vhdl/mm_file_pkg.vhd +++ b/libraries/base/mm/tb/vhdl/mm_file_pkg.vhd @@ -1,757 +1,757 @@ -------------------------------------------------------------------------------- --- --- Copyright (C) 2017 --- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> --- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands --- --- This program is free software: you can redistribute it and/or modify --- it under the terms of the GNU General Public License as published by --- the Free Software Foundation, either version 3 of the License, or --- (at your option) any later version. --- --- This program is distributed in the hope that it will be useful, --- but WITHOUT ANY WARRANTY; without even the implied warranty of --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --- GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License --- along with this program. If not, see <http://www.gnu.org/licenses/>. --- -------------------------------------------------------------------------------- - --- Author : --- D. van der Schuur May 2012 Original for Python - file IO - VHDL --- E. Kooistra feb 2017 Added purpose and description --- Added procedures for external control in a --- pure VHDL test bench. --- --- Purpose: Provide DUT access via MM bus through file IO per MM slave --- Description: --- This package provides file IO access to MM slaves and to the status of --- the simulation: --- --- 1) MM slave access --- Access to MM slaves is provided by component mm_file.vhd that first calls --- mmf_file_create() and loop forever calling mmf_mm_from_file(). Each MM --- slave has a dedicated pair of request (.ctrl) and response (.stat) IO --- files. --- The mmf_file_create() creates the .ctrl file and mmf_mm_from_file() reads --- it to check whether there is a WR or RD access request. For a WR request --- the wr_data and wr_addr are read from the .ctrl and output on the MM bus --- via mm_mosi. For a RD access request the rd_addr is read from the .ctrl --- and output on the MM bus via mm_mosi. The after the read latency the --- rd_data is written to the .stat file that is then created and closed. --- --- wr rd _________ __________ --- mmf_mm_bus_wr() ---> ctrl file --->| |---mm_mosi-->| | --- | mm_file | | MM slave | --- mmf_mm_bus_rd() <--- stat file <---|___\_____|<--mm_miso---|__________| --- rd wr \ --- \--> loop: mmf_mm_from_file() --- --- The ctrl file is created by mm_file at initialization and recreated by --- every call of mmf_mm_from_file(). --- The stat file is recreated by every call of mmf_mm_bus_rd(). --- --- 2) Simulator access --- External access to the simulation is provided via a .ctrl file that --- supports GET_SIM_TIME and then report the NOW time via the .stat file. --- The simulation access is provided via a procedure mmf_poll_sim_ctrl_file() --- that works similar component mm_file.vhd. --- --- wr rd --- |---> ctrl file --->| --- mmf_sim_get_now()| |mmf_poll_sim_ctrl_file() --- |<--- stat file <---| \ --- rd wr \ --- \--> loop: mmf_sim_ctrl_from_file() --- --- The ctrl file is created by mmf_poll_sim_ctrl_file at initialization and --- recreated by every call of mmf_sim_ctrl_from_file(). --- The stat file is recreated by every call of mmf_sim_get_now(). --- --- A) External control by a Python script --- A Python script can issue requests via the .ctrl files to control the --- simulation and read the .stat files. This models the MM access via a --- Monitoring and Control protocol via 1GbE. --- --- Internal procedures: --- . mmf_file_create(filename: IN STRING); --- . mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; --- . mmf_sim_ctrl_from_file(rd_filename: IN STRING; --- --- External procedures (used in a VHDL design to provide access to the MM --- slaves and simulation via file IO): --- . mm_file.vhd --> instead of a procedure MM slave file IO uses a component --- . mmf_poll_sim_ctrl_file() --- --- B) External control by a VHDL process --> see tb_mm_file.vhd --- Instead of a Python script the file IO access to the MM slaves can also --- be used in a pure VHDL testbench. This is useful when the MM slave bus --- signals (mm_mosi, mm_miso) are not available on the entity of the DUT --- (device under test), which is typically the case when a complete FPGA --- design needs to be simulated. --- --- Internal procedures: --- . mmf_wait_for_file_status() --- . mmf_wait_for_file_empty() --- . mmf_wait_for_file_not_empty() --- --- External procedures (used in a VHDL test bench to provide access to the --- MM slaves in a DUT VHDL design and simulation via file IO): --- . mmf_mm_bus_wr() --- . mmf_mm_bus_rd() --- . mmf_sim_get_now() --- --- External function to create unique sim.ctrl/sim.stat filename per test bench in a multi tb --- . mmf_slave_prefix() --- --- Remarks: --- . The timing of the MM access in mmf_mm_bus_wr() and mmf_mm_bus_rd() and the --- simulation access in mmf_sim_get_now() is not critical. The timing of the first --- access depends on the tb. Due to falling_edge(mm_clk) in mmf_wait_for_file_*() --- all subsequent accesses will start at falling_edge(mm_clk) - -LIBRARY IEEE, common_lib; -USE IEEE.STD_LOGIC_1164.ALL; -USE IEEE.NUMERIC_STD.ALL; -USE common_lib.common_pkg.ALL; -USE common_lib.tb_common_pkg.ALL; -USE common_lib.common_mem_pkg.ALL; -USE common_lib.tb_common_mem_pkg.ALL; -USE std.textio.ALL; -USE IEEE.std_logic_textio.ALL; -USE common_lib.common_str_pkg.ALL; - -PACKAGE mm_file_pkg IS - - -- Constants used by mm_file.vhd - CONSTANT c_mmf_mm_clk_period : TIME := 100 ps; -- Default mm_clk period in simulation. Set much faster than DP clock to speed up - -- simulation of MM access. Without file IO throttling 100 ps is a good balance - -- between simulation speed and file IO rate. - CONSTANT c_mmf_mm_timeout : TIME := 1000 ns; -- Default MM file IO timeout period. Set large enough to account for MM-DP clock - -- domain crossing delays. Use 0 ns to disable file IO throttling, to have file IO - -- at the mm_clk rate. - CONSTANT c_mmf_mm_pause : TIME := 100 ns; -- Default MM file IO pause period after timeout. Balance between file IO rate - -- reduction and responsiveness to new MM access. - - -- Procedure to (re)create empty file - PROCEDURE mmf_file_create(filename: IN STRING); - - -- Procedure to perform an MM access from file - PROCEDURE mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; - SIGNAL mm_rst : IN STD_LOGIC; - SIGNAL mm_mosi : OUT t_mem_mosi; - SIGNAL mm_miso : IN t_mem_miso; - rd_filename: IN STRING; - wr_filename: IN STRING; - rd_latency: IN NATURAL); - - -- Procedure to process a simulation status request from the .ctrl file and provide response via the .stat file - PROCEDURE mmf_sim_ctrl_from_file(rd_filename: IN STRING; - wr_filename: IN STRING); - - -- Procedure to poll the simulation status - PROCEDURE mmf_poll_sim_ctrl_file(rd_file_name: IN STRING; - wr_file_name: IN STRING); - - -- Procedure to poll the simulation status - PROCEDURE mmf_poll_sim_ctrl_file(SIGNAL mm_clk : IN STD_LOGIC; - rd_file_name: IN STRING; - wr_file_name: IN STRING); - - -- Procedures that keep reading the file until it has been made empty or not empty by some other program, - -- to ensure the file is ready for a new write access - PROCEDURE mmf_wait_for_file_status(rd_filename : IN STRING; -- file name with extension - exit_on_empty : IN BOOLEAN; - SIGNAL mm_clk : IN STD_LOGIC); - - PROCEDURE mmf_wait_for_file_empty(rd_filename : IN STRING; -- file name with extension - SIGNAL mm_clk : IN STD_LOGIC); - PROCEDURE mmf_wait_for_file_not_empty(rd_filename : IN STRING; -- file name with extension - SIGNAL mm_clk : IN STD_LOGIC); - - -- Procedure to issue a write access via the MM request .ctrl file - PROCEDURE mmf_mm_bus_wr(filename : IN STRING; -- file name without extension - wr_addr : IN INTEGER; -- use integer to support full 32 bit range - wr_data : IN INTEGER; - SIGNAL mm_clk : IN STD_LOGIC); - - -- Procedure to issue a read access via the MM request .ctrl file and get the read data from the MM response file - PROCEDURE mmf_mm_bus_rd(filename : IN STRING; -- file name without extension - rd_latency : IN NATURAL; - rd_addr : IN INTEGER; -- use integer to support full 32 bit range - SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL mm_clk : IN STD_LOGIC); - -- . rd_latency = 1 - PROCEDURE mmf_mm_bus_rd(filename : IN STRING; - rd_addr : IN INTEGER; - SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL mm_clk : IN STD_LOGIC); - - -- Procedure that reads the rd_data every rd_interval until has the specified rd_value, the proc arguments can be understood as a sentence - PROCEDURE mmf_mm_wait_until_value(filename : IN STRING; -- file name without extension - rd_addr : IN INTEGER; - c_representation : IN STRING; -- treat rd_data as "SIGNED" or "UNSIGNED" 32 bit word - SIGNAL rd_data : INOUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - c_condition : IN STRING; -- ">", ">=", "=", "<=", "<", "/=" - c_rd_value : IN INTEGER; - c_rd_interval : IN TIME; - SIGNAL mm_clk : IN STD_LOGIC); - - -- Procedure to get NOW via simulator status - PROCEDURE mmf_sim_get_now(filename : IN STRING; -- file name without extension - SIGNAL rd_now : OUT STRING; - SIGNAL mm_clk : IN STD_LOGIC); - - -- Functions to create prefixes for the mmf file filename - FUNCTION mmf_prefix(name : STRING; index : NATURAL) RETURN STRING; -- generic prefix name with index to be used for a file IO filename - FUNCTION mmf_tb_prefix(tb : INTEGER) RETURN STRING; -- fixed test bench prefix with index tb to allow file IO with multi tb - FUNCTION mmf_subrack_prefix(subrack : INTEGER) RETURN STRING; -- fixed subrack prefix with index subrack to allow file IO with multi subracks that use same unb numbers - - -- Functions to create mmf file prefix that is unique per slave, for increasing number of hierarchy levels: - -- . return "filepath/s0_i0_" - -- . return "filepath/s0_i0_s1_i1_" - -- . return "filepath/s0_i0_s1_i1_s2_i2_" - -- . return "filepath/s0_i0_s1_i1_s2_i2_s3_i3_" - -- . return "filepath/s0_i0_s1_i1_s2_i2_s3_i3_s4_i4_" - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING; - - CONSTANT c_mmf_local_dir_path : STRING := "mmfiles/"; -- local directory in project file build directory - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING; - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING; - - ---------------------------------------------------------------------------- - -- Declare mm_file component to support positional generic and port mapping of many instances in a TB - ---------------------------------------------------------------------------- - COMPONENT mm_file - GENERIC( - g_file_prefix : STRING; - g_file_enable : STD_LOGIC := '1'; - g_mm_rd_latency : NATURAL := 2; - g_mm_timeout : TIME := c_mmf_mm_timeout; - g_mm_pause : TIME := c_mmf_mm_pause - ); - PORT ( - mm_rst : IN STD_LOGIC; - mm_clk : IN STD_LOGIC; - mm_master_out : OUT t_mem_mosi; - mm_master_in : IN t_mem_miso - ); - END COMPONENT; - -END mm_file_pkg; - -PACKAGE BODY mm_file_pkg IS - - PROCEDURE mmf_file_create(filename: IN STRING) IS - FILE created_file : TEXT OPEN write_mode IS filename; - BEGIN - -- Write the file with nothing in it - write(created_file, ""); - END; - - PROCEDURE mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; - SIGNAL mm_rst : IN STD_LOGIC; - SIGNAL mm_mosi : OUT t_mem_mosi; - SIGNAL mm_miso : IN t_mem_miso; - rd_filename: IN STRING; - wr_filename: IN STRING; - rd_latency: IN NATURAL) IS - FILE rd_file : TEXT; - FILE wr_file : TEXT; - - VARIABLE open_status_rd: file_open_status; - VARIABLE open_status_wr: file_open_status; - - VARIABLE rd_line : LINE; - VARIABLE wr_line : LINE; - - -- Note: Both the address and the data are interpreted as 32-bit data! - -- This means one has to use leading zeros in the file when either is - -- less than 8 hex characters, e.g.: - -- (address) 0000000A - -- (data) DEADBEEF - -- ...as a hex address 'A' would fit in only 4 bits, causing an error in hread(). - VARIABLE v_addr_slv : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - VARIABLE v_data_slv : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - - VARIABLE v_rd_wr_str : STRING(1 TO 2); -- Contains 'RD' or 'WR' - - BEGIN - - proc_common_wait_until_low(mm_clk, mm_rst); - - -- We have to open the file explicitely so we can check the status - file_open(open_status_rd, rd_file, rd_filename, read_mode); - - -- open_status may throw an error if the file is being written to by some other program - IF open_status_rd=open_ok THEN - - IF NOT endfile(rd_file) THEN - -- The file is not empty: process its contents - - -- Read a line from it, first line indicates RD or WR - readline(rd_file, rd_line); - read(rd_line, v_rd_wr_str); - - -- The second line represents the address offset: - readline(rd_file, rd_line); - hread(rd_line, v_addr_slv); -- read the string as HEX and assign to SLV. - - -- Write only: The third line contains the data to write: - IF v_rd_wr_str="WR" THEN - readline(rd_file, rd_line); - hread(rd_line, v_data_slv); -- read the string as HEX and assign to SLV. - END IF; - - -- We're done reading MM request from the .ctrl file. - -- Clear the .ctrl file by closing and recreating it, because we don't want to do the same - -- MM request again the next time this procedure is called. - file_close(rd_file); - mmf_file_create(rd_filename); - - -- Execute the MM request to the MM slave - IF v_rd_wr_str="WR" THEN - print_str("[" & time_to_str(now) & "] " & rd_filename & ": Writing 0x" & slv_to_hex(v_data_slv) & " to address 0x" & slv_to_hex(v_addr_slv)); - -- Treat 32 bit hex data from file as 32 bit VHDL INTEGER, so need to use signed TO_SINT() to avoid out of NATURAL range - -- warning in simulation due to '1' sign bit, because unsigned VHDL NATURAL only fits 31 bits - proc_mem_mm_bus_wr(TO_UINT(v_addr_slv), TO_SINT(v_data_slv), mm_clk, mm_miso, mm_mosi); - - ELSIF v_rd_wr_str="RD" THEN - proc_mem_mm_bus_rd(TO_UINT(v_addr_slv), mm_clk, mm_miso, mm_mosi); - IF rd_latency>0 THEN - proc_mem_mm_bus_rd_latency(rd_latency, mm_clk); - END IF; - v_data_slv := mm_miso.rddata(31 DOWNTO 0); - print_str("[" & time_to_str(now) & "] " & rd_filename & ": Reading from address 0x" & slv_to_hex(v_addr_slv) & ": 0x" & slv_to_hex(v_data_slv)); - - -- Write the RD response read data to the .stat file - file_open(open_status_wr, wr_file, wr_filename, write_mode); - hwrite(wr_line, v_data_slv); - writeline(wr_file, wr_line); - file_close(wr_file); - END IF; - - ELSE - -- Nothing to process; wait one MM clock cycle. - proc_common_wait_some_cycles(mm_clk, 1); - END IF; - - ELSE - REPORT "mmf_mm_from_file() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; - -- Try again next time; wait one MM clock cycle. - proc_common_wait_some_cycles(mm_clk, 1); - END IF; - - -- The END implicitely close the rd_file, if still necessary. - END; - - - PROCEDURE mmf_sim_ctrl_from_file(rd_filename: IN STRING; - wr_filename: IN STRING) IS - - FILE rd_file : TEXT; - FILE wr_file : TEXT; - - VARIABLE open_status_rd: file_open_status; - VARIABLE open_status_wr: file_open_status; - - VARIABLE rd_line : LINE; - VARIABLE wr_line : LINE; - - VARIABLE v_rd_wr_str : STRING(1 TO 12); -- "GET_SIM_TIME" - - BEGIN - - -- We have to open the file explicitely so we can check the status - file_open(open_status_rd, rd_file, rd_filename, read_mode); - - -- open_status may throw an error if the file is being written to by some other program - IF open_status_rd=open_ok THEN - - IF NOT endfile(rd_file) THEN - -- The file is not empty: process its contents - - -- Read a line from it, interpret the simulation request - readline(rd_file, rd_line); - read(rd_line, v_rd_wr_str); - - -- We're done reading this simulation request .ctrl file. Clear the file by closing and recreating it. - file_close(rd_file); - mmf_file_create(rd_filename); - - -- Execute the simulation request - IF v_rd_wr_str="GET_SIM_TIME" THEN - -- Write the GET_SIM_TIME response time NOW to the .stat file - file_open(open_status_wr, wr_file, wr_filename, write_mode); - write(wr_line, time_to_str(now)); - writeline(wr_file, wr_line); - file_close(wr_file); - END IF; - - ELSE - -- Nothing to process; wait in procedure mmf_poll_sim_ctrl_file - NULL; - END IF; - - ELSE - REPORT "mmf_mm_from_file() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; - -- Try again next time; wait in procedure mmf_poll_sim_ctrl_file - END IF; - - -- The END implicitely close the rd_file, if still necessary. - END; - - - PROCEDURE mmf_poll_sim_ctrl_file(rd_file_name: IN STRING; wr_file_name : IN STRING) IS - BEGIN - -- Create the ctrl file that we're going to read from - print_str("[" & time_to_str(now) & "] " & rd_file_name & ": Created" ); - mmf_file_create(rd_file_name); - - WHILE TRUE LOOP - mmf_sim_ctrl_from_file(rd_file_name, wr_file_name); - WAIT FOR 1 ns; - END LOOP; - - END; - - - PROCEDURE mmf_poll_sim_ctrl_file(SIGNAL mm_clk : IN STD_LOGIC; - rd_file_name: IN STRING; wr_file_name : IN STRING) IS - BEGIN - -- Create the ctrl file that we're going to read from - print_str("[" & time_to_str(now) & "] " & rd_file_name & ": Created" ); - mmf_file_create(rd_file_name); - - WHILE TRUE LOOP - mmf_sim_ctrl_from_file(rd_file_name, wr_file_name); - proc_common_wait_some_cycles(mm_clk, 1); - END LOOP; - - END; - - - PROCEDURE mmf_wait_for_file_status(rd_filename : IN STRING; -- file name with extension - exit_on_empty : IN BOOLEAN; - SIGNAL mm_clk : IN STD_LOGIC) IS - FILE rd_file : TEXT; - VARIABLE open_status_rd : file_open_status; - VARIABLE v_endfile : BOOLEAN; - BEGIN - -- Check on falling_edge(mm_clk) because mmf_mm_from_file() operates on rising_edge(mm_clk) - -- Note: In fact the file IO also works fine when rising_edge() is used, but then - -- tb_tb_mm_file.vhd takes about 1% more mm_clk cycles - WAIT UNTIL falling_edge(mm_clk); - - -- Keep reading the file until it has become empty by some other program - WHILE TRUE LOOP - -- Open the file in read mode to check whether it is empty - file_open(open_status_rd, rd_file, rd_filename, read_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_rd=open_ok THEN - v_endfile := endfile(rd_file); - file_close(rd_file); - IF exit_on_empty THEN - IF v_endfile THEN - -- The file is empty; continue - EXIT; - ELSE - -- The file is not empty; wait one MM clock cycle. - WAIT UNTIL falling_edge(mm_clk); - END IF; - ELSE - IF v_endfile THEN - -- The file is empty; wait one MM clock cycle. - WAIT UNTIL falling_edge(mm_clk); - ELSE - -- The file is not empty; continue - EXIT; - END IF; - END IF; - ELSE - REPORT "mmf_wait_for_file_status() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; - WAIT UNTIL falling_edge(mm_clk); - END IF; - END LOOP; - -- The END implicitely close the file, if still necessary. - END; - - PROCEDURE mmf_wait_for_file_empty(rd_filename : IN STRING; -- file name with extension - SIGNAL mm_clk : IN STD_LOGIC) IS - BEGIN - mmf_wait_for_file_status(rd_filename, TRUE, mm_clk); - END; - - PROCEDURE mmf_wait_for_file_not_empty(rd_filename : IN STRING; -- file name with extension - SIGNAL mm_clk : IN STD_LOGIC) IS - BEGIN - mmf_wait_for_file_status(rd_filename, FALSE, mm_clk); - END; - - PROCEDURE mmf_mm_bus_wr(filename : IN STRING; -- file name without extension - wr_addr : IN INTEGER; -- use integer to support full 32 bit range - wr_data : IN INTEGER; - SIGNAL mm_clk : IN STD_LOGIC) IS - CONSTANT ctrl_filename : STRING := filename & ".ctrl"; - FILE ctrl_file : TEXT; - VARIABLE open_status_wr : file_open_status; - VARIABLE wr_line : LINE; - - BEGIN - -- Write MM WR access to the .ctrl file. - -- The MM device is ready for a new MM request, because any previous MM request has finished at - -- mmf_mm_bus_wr() or mmf_mm_bus_rd() procedure exit, therefore just overwrite the .ctrl file. - file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_wr=open_ok THEN - write(wr_line, STRING'("WR")); - writeline(ctrl_file, wr_line); - hwrite(wr_line, TO_SVEC(wr_addr, c_word_w)); - writeline(ctrl_file, wr_line); - hwrite(wr_line, TO_SVEC(wr_data, c_word_w)); - writeline(ctrl_file, wr_line); - file_close(ctrl_file); - ELSE - REPORT "mmf_mm_bus_wr() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY NOTE; - END IF; - - -- Prepare for next MM request - -- Keep reading the .ctrl file until it is empty, to ensure that the MM device is ready for a new MM request - mmf_wait_for_file_empty(ctrl_filename, mm_clk); - - -- The END implicitely close the ctrl_file, if still necessary. - END; - - PROCEDURE mmf_mm_bus_rd(filename : IN STRING; -- file name without extension - rd_latency : IN NATURAL; - rd_addr : IN INTEGER; -- use integer to support full 32 bit range - SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL mm_clk : IN STD_LOGIC) IS - CONSTANT ctrl_filename : STRING := filename & ".ctrl"; - CONSTANT stat_filename : STRING := filename & ".stat"; - FILE ctrl_file : TEXT; - FILE stat_file : TEXT; - VARIABLE open_status_wr : file_open_status; - VARIABLE open_status_rd : file_open_status; - VARIABLE wr_line : LINE; - VARIABLE rd_line : LINE; - VARIABLE v_rd_data : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - - BEGIN - -- Clear the .stat file by recreating it, because we don't want to do read old file data again - mmf_file_create(stat_filename); - - -- Write MM RD access to the .ctrl file. - -- The MM device is ready for a new MM request, because any previous MM request has finished at - -- mmf_mm_bus_wr() or mmf_mm_bus_rd() procedure exit, therefore just overwrite the .ctrl file. - file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_wr=open_ok THEN - write(wr_line, STRING'("RD")); - writeline(ctrl_file, wr_line); - hwrite(wr_line, TO_SVEC(rd_addr, c_word_w)); - writeline(ctrl_file, wr_line); - file_close(ctrl_file); - ELSE - REPORT "mmf_mm_bus_rd() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY FAILURE; - END IF; - - -- Wait until the MM RD access has written the read data to the .stat file - mmf_wait_for_file_not_empty(stat_filename, mm_clk); - - -- Read the MM RD access read data from the .stat file - file_open(open_status_rd, stat_file, stat_filename, read_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_rd=open_ok THEN - readline(stat_file, rd_line); - hread(rd_line, v_rd_data); - file_close(stat_file); - rd_data <= v_rd_data; - -- wait to ensure rd_data has got v_rd_data, otherwise rd_data still holds the old data on procedure exit - -- the wait should be < mm_clk period/2 to not affect the read rate - WAIT FOR 1 fs; - ELSE - REPORT "mmf_mm_bus_rd() could not open " & stat_filename & " at " & time_to_str(now) SEVERITY FAILURE; - END IF; - - -- No need to prepare for next MM request, because: - -- . the .ctrl file must already be empty because the .stat file was there - -- . the .stat file will be cleared on this procedure entry - - -- The END implicitely closes the files, if still necessary - END; - - -- rd_latency = 1 - PROCEDURE mmf_mm_bus_rd(filename : IN STRING; - rd_addr : IN INTEGER; - SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - SIGNAL mm_clk : IN STD_LOGIC) IS - BEGIN - mmf_mm_bus_rd(filename, 1, rd_addr, rd_data, mm_clk); - END; - - PROCEDURE mmf_mm_wait_until_value(filename : IN STRING; -- file name without extension - rd_addr : IN INTEGER; - c_representation : IN STRING; -- treat rd_data as "SIGNED" or "UNSIGNED" 32 bit word - SIGNAL rd_data : INOUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); - c_condition : IN STRING; -- ">", ">=", "=", "<=", "<", "/=" - c_rd_value : IN INTEGER; - c_rd_interval : IN TIME; - SIGNAL mm_clk : IN STD_LOGIC) IS - BEGIN - WHILE TRUE LOOP - -- Read current - mmf_mm_bus_rd(filename, rd_addr, rd_data, mm_clk); -- only read low part - IF c_representation="SIGNED" THEN - IF c_condition=">" THEN IF TO_SINT(rd_data)> c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition=">=" THEN IF TO_SINT(rd_data)>=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="/=" THEN IF TO_SINT(rd_data)/=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="<=" THEN IF TO_SINT(rd_data)<=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="<" THEN IF TO_SINT(rd_data)< c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSE IF TO_SINT(rd_data) =c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; -- default: "=" - END IF; - ELSE -- default: UNSIGED - IF c_condition=">" THEN IF TO_UINT(rd_data)> c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition=">=" THEN IF TO_UINT(rd_data)>=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="/=" THEN IF TO_UINT(rd_data)/=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="<=" THEN IF TO_UINT(rd_data)<=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSIF c_condition="<" THEN IF TO_UINT(rd_data)< c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; - ELSE IF TO_UINT(rd_data) =c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; -- default: "=" - END IF; - END IF; - END LOOP; - END mmf_mm_wait_until_value; - - - PROCEDURE mmf_sim_get_now(filename : IN STRING; -- file name without extension - SIGNAL rd_now : OUT STRING; - SIGNAL mm_clk : IN STD_LOGIC) IS - CONSTANT ctrl_filename : STRING := filename & ".ctrl"; - CONSTANT stat_filename : STRING := filename & ".stat"; - FILE ctrl_file : TEXT; - FILE stat_file : TEXT; - VARIABLE open_status_wr : file_open_status; - VARIABLE open_status_rd : file_open_status; - VARIABLE wr_line : LINE; - VARIABLE rd_line : LINE; - VARIABLE v_rd_now : STRING(rd_now'RANGE); - - BEGIN - -- Clear the sim.stat file by recreating it, because we don't want to do read old simulator status again - mmf_file_create(stat_filename); - - -- Write GET_SIM_TIME to the sim.ctrl file - -- The simulation is ready for a new simulation status request, because any previous simulation status request has finished at - -- mmf_sim_get_now() procedure exit, therefore just overwrite the .ctrl file. - file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_wr=open_ok THEN - write(wr_line, STRING'("GET_SIM_TIME")); - writeline(ctrl_file, wr_line); - file_close(ctrl_file); - ELSE - REPORT "mmf_sim_get_now() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY FAILURE; - END IF; - - -- Wait until the simulation has written the simulation status to the sim.stat file - mmf_wait_for_file_not_empty(stat_filename, mm_clk); - - -- Read the GET_SIM_TIME simulation status from the .stat file - file_open(open_status_rd, stat_file, stat_filename, read_mode); - -- open_status may throw an error if the file is being written to by some other program - IF open_status_rd=open_ok THEN - readline(stat_file, rd_line); - read(rd_line, v_rd_now); - file_close(stat_file); - rd_now <= v_rd_now; - print_str("GET_SIM_TIME = " & v_rd_now & " at " & time_to_str(now)); - ELSE - REPORT "mmf_sim_get_now() could not open " & stat_filename & " at " & time_to_str(now) SEVERITY FAILURE; - END IF; - - -- No need to prepare for next simulation status request, because: - -- . the .ctrl file must already be empty because the .stat file was there - -- . the .stat file will be cleared on this procedure entry - - -- The END implicitely closes the files, if still necessary - END; - - -- Functions to create prefixes for the mmf file filename - FUNCTION mmf_prefix(name : STRING; index : NATURAL) RETURN STRING IS - BEGIN - RETURN name & "_" & int_to_str(index) & "_"; - END; - - FUNCTION mmf_tb_prefix(tb : INTEGER) RETURN STRING IS - BEGIN - RETURN mmf_prefix("TB", tb); - END; - - FUNCTION mmf_subrack_prefix(subrack : INTEGER) RETURN STRING IS - BEGIN - RETURN mmf_prefix("SUBRACK", subrack); - END; - - -- Functions to create mmf file prefix that is unique per slave, for increasing number of hierarchy levels: - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL) RETURN STRING IS - BEGIN - RETURN dir_path & mmf_prefix(s0, i0); - END; - - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING IS - BEGIN - RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1); - END; - - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING IS - BEGIN - RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2); - END; - - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING IS - BEGIN - RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3); - END; - - FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING IS - BEGIN - RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3) & mmf_prefix(s4, i4); - END; - - -- Use local dir_path - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL) RETURN STRING IS - BEGIN - RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0); - END; - - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING IS - BEGIN - RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1); - END; - - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING IS - BEGIN - RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2); - END; - - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING IS - BEGIN - RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3); - END; - - FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING IS - BEGIN - RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3) & mmf_prefix(s4, i4); - END; - -END mm_file_pkg; - +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2017 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +-- Author : +-- D. van der Schuur May 2012 Original for Python - file IO - VHDL +-- E. Kooistra feb 2017 Added purpose and description +-- Added procedures for external control in a +-- pure VHDL test bench. +-- +-- Purpose: Provide DUT access via MM bus through file IO per MM slave +-- Description: +-- This package provides file IO access to MM slaves and to the status of +-- the simulation: +-- +-- 1) MM slave access +-- Access to MM slaves is provided by component mm_file.vhd that first calls +-- mmf_file_create() and loop forever calling mmf_mm_from_file(). Each MM +-- slave has a dedicated pair of request (.ctrl) and response (.stat) IO +-- files. +-- The mmf_file_create() creates the .ctrl file and mmf_mm_from_file() reads +-- it to check whether there is a WR or RD access request. For a WR request +-- the wr_data and wr_addr are read from the .ctrl and output on the MM bus +-- via mm_mosi. For a RD access request the rd_addr is read from the .ctrl +-- and output on the MM bus via mm_mosi. The after the read latency the +-- rd_data is written to the .stat file that is then created and closed. +-- +-- wr rd _________ __________ +-- mmf_mm_bus_wr() ---> ctrl file --->| |---mm_mosi-->| | +-- | mm_file | | MM slave | +-- mmf_mm_bus_rd() <--- stat file <---|___\_____|<--mm_miso---|__________| +-- rd wr \ +-- \--> loop: mmf_mm_from_file() +-- +-- The ctrl file is created by mm_file at initialization and recreated by +-- every call of mmf_mm_from_file(). +-- The stat file is recreated by every call of mmf_mm_bus_rd(). +-- +-- 2) Simulator access +-- External access to the simulation is provided via a .ctrl file that +-- supports GET_SIM_TIME and then report the NOW time via the .stat file. +-- The simulation access is provided via a procedure mmf_poll_sim_ctrl_file() +-- that works similar component mm_file.vhd. +-- +-- wr rd +-- |---> ctrl file --->| +-- mmf_sim_get_now()| |mmf_poll_sim_ctrl_file() +-- |<--- stat file <---| \ +-- rd wr \ +-- \--> loop: mmf_sim_ctrl_from_file() +-- +-- The ctrl file is created by mmf_poll_sim_ctrl_file at initialization and +-- recreated by every call of mmf_sim_ctrl_from_file(). +-- The stat file is recreated by every call of mmf_sim_get_now(). +-- +-- A) External control by a Python script +-- A Python script can issue requests via the .ctrl files to control the +-- simulation and read the .stat files. This models the MM access via a +-- Monitoring and Control protocol via 1GbE. +-- +-- Internal procedures: +-- . mmf_file_create(filename: IN STRING); +-- . mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; +-- . mmf_sim_ctrl_from_file(rd_filename: IN STRING; +-- +-- External procedures (used in a VHDL design to provide access to the MM +-- slaves and simulation via file IO): +-- . mm_file.vhd --> instead of a procedure MM slave file IO uses a component +-- . mmf_poll_sim_ctrl_file() +-- +-- B) External control by a VHDL process --> see tb_mm_file.vhd +-- Instead of a Python script the file IO access to the MM slaves can also +-- be used in a pure VHDL testbench. This is useful when the MM slave bus +-- signals (mm_mosi, mm_miso) are not available on the entity of the DUT +-- (device under test), which is typically the case when a complete FPGA +-- design needs to be simulated. +-- +-- Internal procedures: +-- . mmf_wait_for_file_status() +-- . mmf_wait_for_file_empty() +-- . mmf_wait_for_file_not_empty() +-- +-- External procedures (used in a VHDL test bench to provide access to the +-- MM slaves in a DUT VHDL design and simulation via file IO): +-- . mmf_mm_bus_wr() +-- . mmf_mm_bus_rd() +-- . mmf_sim_get_now() +-- +-- External function to create unique sim.ctrl/sim.stat filename per test bench in a multi tb +-- . mmf_slave_prefix() +-- +-- Remarks: +-- . The timing of the MM access in mmf_mm_bus_wr() and mmf_mm_bus_rd() and the +-- simulation access in mmf_sim_get_now() is not critical. The timing of the first +-- access depends on the tb. Due to falling_edge(mm_clk) in mmf_wait_for_file_*() +-- all subsequent accesses will start at falling_edge(mm_clk) + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; +USE std.textio.ALL; +USE IEEE.std_logic_textio.ALL; +USE common_lib.common_str_pkg.ALL; + +PACKAGE mm_file_pkg IS + + -- Constants used by mm_file.vhd + CONSTANT c_mmf_mm_clk_period : TIME := 100 ps; -- Default mm_clk period in simulation. Set much faster than DP clock to speed up + -- simulation of MM access. Without file IO throttling 100 ps is a good balance + -- between simulation speed and file IO rate. + CONSTANT c_mmf_mm_timeout : TIME := 1000 ns; -- Default MM file IO timeout period. Set large enough to account for MM-DP clock + -- domain crossing delays. Use 0 ns to disable file IO throttling, to have file IO + -- at the mm_clk rate. + CONSTANT c_mmf_mm_pause : TIME := 100 ns; -- Default MM file IO pause period after timeout. Balance between file IO rate + -- reduction and responsiveness to new MM access. + + -- Procedure to (re)create empty file + PROCEDURE mmf_file_create(filename: IN STRING); + + -- Procedure to perform an MM access from file + PROCEDURE mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; + SIGNAL mm_rst : IN STD_LOGIC; + SIGNAL mm_mosi : OUT t_mem_mosi; + SIGNAL mm_miso : IN t_mem_miso; + rd_filename: IN STRING; + wr_filename: IN STRING; + rd_latency: IN NATURAL); + + -- Procedure to process a simulation status request from the .ctrl file and provide response via the .stat file + PROCEDURE mmf_sim_ctrl_from_file(rd_filename: IN STRING; + wr_filename: IN STRING); + + -- Procedure to poll the simulation status + PROCEDURE mmf_poll_sim_ctrl_file(rd_file_name: IN STRING; + wr_file_name: IN STRING); + + -- Procedure to poll the simulation status + PROCEDURE mmf_poll_sim_ctrl_file(SIGNAL mm_clk : IN STD_LOGIC; + rd_file_name: IN STRING; + wr_file_name: IN STRING); + + -- Procedures that keep reading the file until it has been made empty or not empty by some other program, + -- to ensure the file is ready for a new write access + PROCEDURE mmf_wait_for_file_status(rd_filename : IN STRING; -- file name with extension + exit_on_empty : IN BOOLEAN; + SIGNAL mm_clk : IN STD_LOGIC); + + PROCEDURE mmf_wait_for_file_empty(rd_filename : IN STRING; -- file name with extension + SIGNAL mm_clk : IN STD_LOGIC); + PROCEDURE mmf_wait_for_file_not_empty(rd_filename : IN STRING; -- file name with extension + SIGNAL mm_clk : IN STD_LOGIC); + + -- Procedure to issue a write access via the MM request .ctrl file + PROCEDURE mmf_mm_bus_wr(filename : IN STRING; -- file name without extension + wr_addr : IN INTEGER; -- use integer to support full 32 bit range + wr_data : IN INTEGER; + SIGNAL mm_clk : IN STD_LOGIC); + + -- Procedure to issue a read access via the MM request .ctrl file and get the read data from the MM response file + PROCEDURE mmf_mm_bus_rd(filename : IN STRING; -- file name without extension + rd_latency : IN NATURAL; + rd_addr : IN INTEGER; -- use integer to support full 32 bit range + SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL mm_clk : IN STD_LOGIC); + -- . rd_latency = 1 + PROCEDURE mmf_mm_bus_rd(filename : IN STRING; + rd_addr : IN INTEGER; + SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL mm_clk : IN STD_LOGIC); + + -- Procedure that reads the rd_data every rd_interval until has the specified rd_value, the proc arguments can be understood as a sentence + PROCEDURE mmf_mm_wait_until_value(filename : IN STRING; -- file name without extension + rd_addr : IN INTEGER; + c_representation : IN STRING; -- treat rd_data as "SIGNED" or "UNSIGNED" 32 bit word + SIGNAL rd_data : INOUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + c_condition : IN STRING; -- ">", ">=", "=", "<=", "<", "/=" + c_rd_value : IN INTEGER; + c_rd_interval : IN TIME; + SIGNAL mm_clk : IN STD_LOGIC); + + -- Procedure to get NOW via simulator status + PROCEDURE mmf_sim_get_now(filename : IN STRING; -- file name without extension + SIGNAL rd_now : OUT STRING; + SIGNAL mm_clk : IN STD_LOGIC); + + -- Functions to create prefixes for the mmf file filename + FUNCTION mmf_prefix(name : STRING; index : NATURAL) RETURN STRING; -- generic prefix name with index to be used for a file IO filename + FUNCTION mmf_tb_prefix(tb : INTEGER) RETURN STRING; -- fixed test bench prefix with index tb to allow file IO with multi tb + FUNCTION mmf_subrack_prefix(subrack : INTEGER) RETURN STRING; -- fixed subrack prefix with index subrack to allow file IO with multi subracks that use same unb numbers + + -- Functions to create mmf file prefix that is unique per slave, for increasing number of hierarchy levels: + -- . return "filepath/s0_i0_" + -- . return "filepath/s0_i0_s1_i1_" + -- . return "filepath/s0_i0_s1_i1_s2_i2_" + -- . return "filepath/s0_i0_s1_i1_s2_i2_s3_i3_" + -- . return "filepath/s0_i0_s1_i1_s2_i2_s3_i3_s4_i4_" + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING; + + CONSTANT c_mmf_local_dir_path : STRING := "mmfiles/"; -- local directory in project file build directory + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING; + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING; + + ---------------------------------------------------------------------------- + -- Declare mm_file component to support positional generic and port mapping of many instances in a TB + ---------------------------------------------------------------------------- + COMPONENT mm_file + GENERIC( + g_file_prefix : STRING; + g_file_enable : STD_LOGIC := '1'; + g_mm_rd_latency : NATURAL := 2; + g_mm_timeout : TIME := c_mmf_mm_timeout; + g_mm_pause : TIME := c_mmf_mm_pause + ); + PORT ( + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + mm_master_out : OUT t_mem_mosi; + mm_master_in : IN t_mem_miso + ); + END COMPONENT; + +END mm_file_pkg; + +PACKAGE BODY mm_file_pkg IS + + PROCEDURE mmf_file_create(filename: IN STRING) IS + FILE created_file : TEXT OPEN write_mode IS filename; + BEGIN + -- Write the file with nothing in it + write(created_file, ""); + END; + + PROCEDURE mmf_mm_from_file(SIGNAL mm_clk : IN STD_LOGIC; + SIGNAL mm_rst : IN STD_LOGIC; + SIGNAL mm_mosi : OUT t_mem_mosi; + SIGNAL mm_miso : IN t_mem_miso; + rd_filename: IN STRING; + wr_filename: IN STRING; + rd_latency: IN NATURAL) IS + FILE rd_file : TEXT; + FILE wr_file : TEXT; + + VARIABLE open_status_rd: file_open_status; + VARIABLE open_status_wr: file_open_status; + + VARIABLE rd_line : LINE; + VARIABLE wr_line : LINE; + + -- Note: Both the address and the data are interpreted as 32-bit data! + -- This means one has to use leading zeros in the file when either is + -- less than 8 hex characters, e.g.: + -- (address) 0000000A + -- (data) DEADBEEF + -- ...as a hex address 'A' would fit in only 4 bits, causing an error in hread(). + VARIABLE v_addr_slv : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + VARIABLE v_data_slv : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + + VARIABLE v_rd_wr_str : STRING(1 TO 2); -- Contains 'RD' or 'WR' + + BEGIN + + proc_common_wait_until_low(mm_clk, mm_rst); + + -- We have to open the file explicitely so we can check the status + file_open(open_status_rd, rd_file, rd_filename, read_mode); + + -- open_status may throw an error if the file is being written to by some other program + IF open_status_rd=open_ok THEN + + IF NOT endfile(rd_file) THEN + -- The file is not empty: process its contents + + -- Read a line from it, first line indicates RD or WR + readline(rd_file, rd_line); + read(rd_line, v_rd_wr_str); + + -- The second line represents the address offset: + readline(rd_file, rd_line); + hread(rd_line, v_addr_slv); -- read the string as HEX and assign to SLV. + + -- Write only: The third line contains the data to write: + IF v_rd_wr_str="WR" THEN + readline(rd_file, rd_line); + hread(rd_line, v_data_slv); -- read the string as HEX and assign to SLV. + END IF; + + -- We're done reading MM request from the .ctrl file. + -- Clear the .ctrl file by closing and recreating it, because we don't want to do the same + -- MM request again the next time this procedure is called. + file_close(rd_file); + mmf_file_create(rd_filename); + + -- Execute the MM request to the MM slave + IF v_rd_wr_str="WR" THEN + print_str("[" & time_to_str(now) & "] " & rd_filename & ": Writing 0x" & slv_to_hex(v_data_slv) & " to address 0x" & slv_to_hex(v_addr_slv)); + -- Treat 32 bit hex data from file as 32 bit VHDL INTEGER, so need to use signed TO_SINT() to avoid out of NATURAL range + -- warning in simulation due to '1' sign bit, because unsigned VHDL NATURAL only fits 31 bits + proc_mem_mm_bus_wr(TO_UINT(v_addr_slv), TO_SINT(v_data_slv), mm_clk, mm_miso, mm_mosi); + + ELSIF v_rd_wr_str="RD" THEN + proc_mem_mm_bus_rd(TO_UINT(v_addr_slv), mm_clk, mm_miso, mm_mosi); + IF rd_latency>0 THEN + proc_mem_mm_bus_rd_latency(rd_latency, mm_clk); + END IF; + v_data_slv := mm_miso.rddata(31 DOWNTO 0); + print_str("[" & time_to_str(now) & "] " & rd_filename & ": Reading from address 0x" & slv_to_hex(v_addr_slv) & ": 0x" & slv_to_hex(v_data_slv)); + + -- Write the RD response read data to the .stat file + file_open(open_status_wr, wr_file, wr_filename, write_mode); + hwrite(wr_line, v_data_slv); + writeline(wr_file, wr_line); + file_close(wr_file); + END IF; + + ELSE + -- Nothing to process; wait one MM clock cycle. + proc_common_wait_some_cycles(mm_clk, 1); + END IF; + + ELSE + REPORT "mmf_mm_from_file() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; + -- Try again next time; wait one MM clock cycle. + proc_common_wait_some_cycles(mm_clk, 1); + END IF; + + -- The END implicitely close the rd_file, if still necessary. + END; + + + PROCEDURE mmf_sim_ctrl_from_file(rd_filename: IN STRING; + wr_filename: IN STRING) IS + + FILE rd_file : TEXT; + FILE wr_file : TEXT; + + VARIABLE open_status_rd: file_open_status; + VARIABLE open_status_wr: file_open_status; + + VARIABLE rd_line : LINE; + VARIABLE wr_line : LINE; + + VARIABLE v_rd_wr_str : STRING(1 TO 12); -- "GET_SIM_TIME" + + BEGIN + + -- We have to open the file explicitely so we can check the status + file_open(open_status_rd, rd_file, rd_filename, read_mode); + + -- open_status may throw an error if the file is being written to by some other program + IF open_status_rd=open_ok THEN + + IF NOT endfile(rd_file) THEN + -- The file is not empty: process its contents + + -- Read a line from it, interpret the simulation request + readline(rd_file, rd_line); + read(rd_line, v_rd_wr_str); + + -- We're done reading this simulation request .ctrl file. Clear the file by closing and recreating it. + file_close(rd_file); + mmf_file_create(rd_filename); + + -- Execute the simulation request + IF v_rd_wr_str="GET_SIM_TIME" THEN + -- Write the GET_SIM_TIME response time NOW to the .stat file + file_open(open_status_wr, wr_file, wr_filename, write_mode); + write(wr_line, time_to_str(now)); + writeline(wr_file, wr_line); + file_close(wr_file); + END IF; + + ELSE + -- Nothing to process; wait in procedure mmf_poll_sim_ctrl_file + NULL; + END IF; + + ELSE + REPORT "mmf_mm_from_file() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; + -- Try again next time; wait in procedure mmf_poll_sim_ctrl_file + END IF; + + -- The END implicitely close the rd_file, if still necessary. + END; + + + PROCEDURE mmf_poll_sim_ctrl_file(rd_file_name: IN STRING; wr_file_name : IN STRING) IS + BEGIN + -- Create the ctrl file that we're going to read from + print_str("[" & time_to_str(now) & "] " & rd_file_name & ": Created" ); + mmf_file_create(rd_file_name); + + WHILE TRUE LOOP + mmf_sim_ctrl_from_file(rd_file_name, wr_file_name); + WAIT FOR 1 ns; + END LOOP; + + END; + + + PROCEDURE mmf_poll_sim_ctrl_file(SIGNAL mm_clk : IN STD_LOGIC; + rd_file_name: IN STRING; wr_file_name : IN STRING) IS + BEGIN + -- Create the ctrl file that we're going to read from + print_str("[" & time_to_str(now) & "] " & rd_file_name & ": Created" ); + mmf_file_create(rd_file_name); + + WHILE TRUE LOOP + mmf_sim_ctrl_from_file(rd_file_name, wr_file_name); + proc_common_wait_some_cycles(mm_clk, 1); + END LOOP; + + END; + + + PROCEDURE mmf_wait_for_file_status(rd_filename : IN STRING; -- file name with extension + exit_on_empty : IN BOOLEAN; + SIGNAL mm_clk : IN STD_LOGIC) IS + FILE rd_file : TEXT; + VARIABLE open_status_rd : file_open_status; + VARIABLE v_endfile : BOOLEAN; + BEGIN + -- Check on falling_edge(mm_clk) because mmf_mm_from_file() operates on rising_edge(mm_clk) + -- Note: In fact the file IO also works fine when rising_edge() is used, but then + -- tb_tb_mm_file.vhd takes about 1% more mm_clk cycles + WAIT UNTIL falling_edge(mm_clk); + + -- Keep reading the file until it has become empty by some other program + WHILE TRUE LOOP + -- Open the file in read mode to check whether it is empty + file_open(open_status_rd, rd_file, rd_filename, read_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_rd=open_ok THEN + v_endfile := endfile(rd_file); + file_close(rd_file); + IF exit_on_empty THEN + IF v_endfile THEN + -- The file is empty; continue + EXIT; + ELSE + -- The file is not empty; wait one MM clock cycle. + WAIT UNTIL falling_edge(mm_clk); + END IF; + ELSE + IF v_endfile THEN + -- The file is empty; wait one MM clock cycle. + WAIT UNTIL falling_edge(mm_clk); + ELSE + -- The file is not empty; continue + EXIT; + END IF; + END IF; + ELSE + REPORT "mmf_wait_for_file_status() could not open " & rd_filename & " at " & time_to_str(now) SEVERITY NOTE; + WAIT UNTIL falling_edge(mm_clk); + END IF; + END LOOP; + -- The END implicitely close the file, if still necessary. + END; + + PROCEDURE mmf_wait_for_file_empty(rd_filename : IN STRING; -- file name with extension + SIGNAL mm_clk : IN STD_LOGIC) IS + BEGIN + mmf_wait_for_file_status(rd_filename, TRUE, mm_clk); + END; + + PROCEDURE mmf_wait_for_file_not_empty(rd_filename : IN STRING; -- file name with extension + SIGNAL mm_clk : IN STD_LOGIC) IS + BEGIN + mmf_wait_for_file_status(rd_filename, FALSE, mm_clk); + END; + + PROCEDURE mmf_mm_bus_wr(filename : IN STRING; -- file name without extension + wr_addr : IN INTEGER; -- use integer to support full 32 bit range + wr_data : IN INTEGER; + SIGNAL mm_clk : IN STD_LOGIC) IS + CONSTANT ctrl_filename : STRING := filename & ".ctrl"; + FILE ctrl_file : TEXT; + VARIABLE open_status_wr : file_open_status; + VARIABLE wr_line : LINE; + + BEGIN + -- Write MM WR access to the .ctrl file. + -- The MM device is ready for a new MM request, because any previous MM request has finished at + -- mmf_mm_bus_wr() or mmf_mm_bus_rd() procedure exit, therefore just overwrite the .ctrl file. + file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_wr=open_ok THEN + write(wr_line, STRING'("WR")); + writeline(ctrl_file, wr_line); + hwrite(wr_line, TO_SVEC(wr_addr, c_word_w)); + writeline(ctrl_file, wr_line); + hwrite(wr_line, TO_SVEC(wr_data, c_word_w)); + writeline(ctrl_file, wr_line); + file_close(ctrl_file); + ELSE + REPORT "mmf_mm_bus_wr() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY NOTE; + END IF; + + -- Prepare for next MM request + -- Keep reading the .ctrl file until it is empty, to ensure that the MM device is ready for a new MM request + mmf_wait_for_file_empty(ctrl_filename, mm_clk); + + -- The END implicitely close the ctrl_file, if still necessary. + END; + + PROCEDURE mmf_mm_bus_rd(filename : IN STRING; -- file name without extension + rd_latency : IN NATURAL; + rd_addr : IN INTEGER; -- use integer to support full 32 bit range + SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL mm_clk : IN STD_LOGIC) IS + CONSTANT ctrl_filename : STRING := filename & ".ctrl"; + CONSTANT stat_filename : STRING := filename & ".stat"; + FILE ctrl_file : TEXT; + FILE stat_file : TEXT; + VARIABLE open_status_wr : file_open_status; + VARIABLE open_status_rd : file_open_status; + VARIABLE wr_line : LINE; + VARIABLE rd_line : LINE; + VARIABLE v_rd_data : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + + BEGIN + -- Clear the .stat file by recreating it, because we don't want to do read old file data again + mmf_file_create(stat_filename); + + -- Write MM RD access to the .ctrl file. + -- The MM device is ready for a new MM request, because any previous MM request has finished at + -- mmf_mm_bus_wr() or mmf_mm_bus_rd() procedure exit, therefore just overwrite the .ctrl file. + file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_wr=open_ok THEN + write(wr_line, STRING'("RD")); + writeline(ctrl_file, wr_line); + hwrite(wr_line, TO_SVEC(rd_addr, c_word_w)); + writeline(ctrl_file, wr_line); + file_close(ctrl_file); + ELSE + REPORT "mmf_mm_bus_rd() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY FAILURE; + END IF; + + -- Wait until the MM RD access has written the read data to the .stat file + mmf_wait_for_file_not_empty(stat_filename, mm_clk); + + -- Read the MM RD access read data from the .stat file + file_open(open_status_rd, stat_file, stat_filename, read_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_rd=open_ok THEN + readline(stat_file, rd_line); + hread(rd_line, v_rd_data); + file_close(stat_file); + rd_data <= v_rd_data; + -- wait to ensure rd_data has got v_rd_data, otherwise rd_data still holds the old data on procedure exit + -- the wait should be < mm_clk period/2 to not affect the read rate + WAIT FOR 1 fs; + ELSE + REPORT "mmf_mm_bus_rd() could not open " & stat_filename & " at " & time_to_str(now) SEVERITY FAILURE; + END IF; + + -- No need to prepare for next MM request, because: + -- . the .ctrl file must already be empty because the .stat file was there + -- . the .stat file will be cleared on this procedure entry + + -- The END implicitely closes the files, if still necessary + END; + + -- rd_latency = 1 + PROCEDURE mmf_mm_bus_rd(filename : IN STRING; + rd_addr : IN INTEGER; + SIGNAL rd_data : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + SIGNAL mm_clk : IN STD_LOGIC) IS + BEGIN + mmf_mm_bus_rd(filename, 1, rd_addr, rd_data, mm_clk); + END; + + PROCEDURE mmf_mm_wait_until_value(filename : IN STRING; -- file name without extension + rd_addr : IN INTEGER; + c_representation : IN STRING; -- treat rd_data as "SIGNED" or "UNSIGNED" 32 bit word + SIGNAL rd_data : INOUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0); + c_condition : IN STRING; -- ">", ">=", "=", "<=", "<", "/=" + c_rd_value : IN INTEGER; + c_rd_interval : IN TIME; + SIGNAL mm_clk : IN STD_LOGIC) IS + BEGIN + WHILE TRUE LOOP + -- Read current + mmf_mm_bus_rd(filename, rd_addr, rd_data, mm_clk); -- only read low part + IF c_representation="SIGNED" THEN + IF c_condition=">" THEN IF TO_SINT(rd_data)> c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition=">=" THEN IF TO_SINT(rd_data)>=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="/=" THEN IF TO_SINT(rd_data)/=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="<=" THEN IF TO_SINT(rd_data)<=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="<" THEN IF TO_SINT(rd_data)< c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSE IF TO_SINT(rd_data) =c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; -- default: "=" + END IF; + ELSE -- default: UNSIGED + IF c_condition=">" THEN IF TO_UINT(rd_data)> c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition=">=" THEN IF TO_UINT(rd_data)>=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="/=" THEN IF TO_UINT(rd_data)/=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="<=" THEN IF TO_UINT(rd_data)<=c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSIF c_condition="<" THEN IF TO_UINT(rd_data)< c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; + ELSE IF TO_UINT(rd_data) =c_rd_value THEN EXIT; ELSE WAIT FOR c_rd_interval; END IF; -- default: "=" + END IF; + END IF; + END LOOP; + END mmf_mm_wait_until_value; + + + PROCEDURE mmf_sim_get_now(filename : IN STRING; -- file name without extension + SIGNAL rd_now : OUT STRING; + SIGNAL mm_clk : IN STD_LOGIC) IS + CONSTANT ctrl_filename : STRING := filename & ".ctrl"; + CONSTANT stat_filename : STRING := filename & ".stat"; + FILE ctrl_file : TEXT; + FILE stat_file : TEXT; + VARIABLE open_status_wr : file_open_status; + VARIABLE open_status_rd : file_open_status; + VARIABLE wr_line : LINE; + VARIABLE rd_line : LINE; + VARIABLE v_rd_now : STRING(rd_now'RANGE); + + BEGIN + -- Clear the sim.stat file by recreating it, because we don't want to do read old simulator status again + mmf_file_create(stat_filename); + + -- Write GET_SIM_TIME to the sim.ctrl file + -- The simulation is ready for a new simulation status request, because any previous simulation status request has finished at + -- mmf_sim_get_now() procedure exit, therefore just overwrite the .ctrl file. + file_open(open_status_wr, ctrl_file, ctrl_filename, write_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_wr=open_ok THEN + write(wr_line, STRING'("GET_SIM_TIME")); + writeline(ctrl_file, wr_line); + file_close(ctrl_file); + ELSE + REPORT "mmf_sim_get_now() could not open " & ctrl_filename & " at " & time_to_str(now) SEVERITY FAILURE; + END IF; + + -- Wait until the simulation has written the simulation status to the sim.stat file + mmf_wait_for_file_not_empty(stat_filename, mm_clk); + + -- Read the GET_SIM_TIME simulation status from the .stat file + file_open(open_status_rd, stat_file, stat_filename, read_mode); + -- open_status may throw an error if the file is being written to by some other program + IF open_status_rd=open_ok THEN + readline(stat_file, rd_line); + read(rd_line, v_rd_now); + file_close(stat_file); + rd_now <= v_rd_now; + print_str("GET_SIM_TIME = " & v_rd_now & " at " & time_to_str(now)); + ELSE + REPORT "mmf_sim_get_now() could not open " & stat_filename & " at " & time_to_str(now) SEVERITY FAILURE; + END IF; + + -- No need to prepare for next simulation status request, because: + -- . the .ctrl file must already be empty because the .stat file was there + -- . the .stat file will be cleared on this procedure entry + + -- The END implicitely closes the files, if still necessary + END; + + -- Functions to create prefixes for the mmf file filename + FUNCTION mmf_prefix(name : STRING; index : NATURAL) RETURN STRING IS + BEGIN + RETURN name & "_" & int_to_str(index) & "_"; + END; + + FUNCTION mmf_tb_prefix(tb : INTEGER) RETURN STRING IS + BEGIN + RETURN mmf_prefix("TB", tb); + END; + + FUNCTION mmf_subrack_prefix(subrack : INTEGER) RETURN STRING IS + BEGIN + RETURN mmf_prefix("SUBRACK", subrack); + END; + + -- Functions to create mmf file prefix that is unique per slave, for increasing number of hierarchy levels: + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL) RETURN STRING IS + BEGIN + RETURN dir_path & mmf_prefix(s0, i0); + END; + + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING IS + BEGIN + RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1); + END; + + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING IS + BEGIN + RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2); + END; + + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING IS + BEGIN + RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3); + END; + + FUNCTION mmf_slave_prefix(dir_path, s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING IS + BEGIN + RETURN dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3) & mmf_prefix(s4, i4); + END; + + -- Use local dir_path + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL) RETURN STRING IS + BEGIN + RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0); + END; + + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL) RETURN STRING IS + BEGIN + RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1); + END; + + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL) RETURN STRING IS + BEGIN + RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2); + END; + + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL) RETURN STRING IS + BEGIN + RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3); + END; + + FUNCTION mmf_slave_prefix(s0 : STRING; i0 : NATURAL; s1 : STRING; i1 : NATURAL; s2 : STRING; i2 : NATURAL; s3 : STRING; i3 : NATURAL; s4 : STRING; i4 : NATURAL) RETURN STRING IS + BEGIN + RETURN c_mmf_local_dir_path & mmf_prefix(s0, i0) & mmf_prefix(s1, i1) & mmf_prefix(s2, i2) & mmf_prefix(s3, i3) & mmf_prefix(s4, i4); + END; + +END mm_file_pkg; + diff --git a/libraries/base/mm/tb/vhdl/mm_waitrequest_model.vhd b/libraries/base/mm/tb/vhdl/mm_waitrequest_model.vhd new file mode 100644 index 0000000000000000000000000000000000000000..fc530c0b9df5c8c0868441ba17b9344e333c7c29 --- /dev/null +++ b/libraries/base/mm/tb/vhdl/mm_waitrequest_model.vhd @@ -0,0 +1,138 @@ +------------------------------------------------------------------------------- +-- +-- 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: +-- . To some extend the ASSERTs check the flow control. The testbench has to +-- verify the rddata to ensure more test coverage. +-- +------------------------------------------------------------------------------- + + +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 mm_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 mm_waitrequest_model; + +ARCHITECTURE rtl OF mm_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 waitrequest : STD_LOGIC; + + SIGNAL prev_bus_mosi : t_mem_mosi; + SIGNAL prev_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 + -- random waitrequest flow control + prsg <= func_common_random(prsg); + -- check MM access + prev_bus_mosi <= bus_mosi; + prev_waitrequest <= waitrequest; + 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; + + -- Verify that MM access is not removed before it is acknowledged by waitrequest + p_verify : PROCESS(bus_mosi, prev_bus_mosi, prev_waitrequest) + BEGIN + IF prev_waitrequest = '1' THEN + IF prev_bus_mosi.wr = '1' AND bus_mosi.wr = '0' THEN REPORT "Aborted slave write." SEVERITY ERROR; END IF; + IF prev_bus_mosi.rd = '1' AND bus_mosi.rd = '0' THEN REPORT "Aborted slave read." SEVERITY ERROR; END IF; + IF prev_bus_mosi.wr = '1' AND bus_mosi.address /= prev_bus_mosi.address THEN REPORT "Address change during pending slave write." SEVERITY ERROR; END IF; + IF prev_bus_mosi.rd = '1' AND bus_mosi.address /= prev_bus_mosi.address THEN REPORT "Address change during pending slave read." SEVERITY ERROR; END IF; + END IF; + END PROCESS; + + END GENERATE; + +END rtl; diff --git a/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd b/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd new file mode 100644 index 0000000000000000000000000000000000000000..44cb20799724a60185b5ad273e029245b8f3ee52 --- /dev/null +++ b/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd @@ -0,0 +1,249 @@ +------------------------------------------------------------------------------- +-- +-- 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 mm_bus.vhd +-- Remark: +-- . This test bench covers: +-- . g_nof_slaves >= 1 +-- . g_waitrequest for g_pipeline_miso_wait = FALSE +-- . g_pipeline_mosi +-- . g_pipeline_miso_rdval +-- . g_pipeline_miso_wait = FALSE +-- . g_rd_latency >= 1 (using 0 is supported by mm_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 mm_bus.vhd can support a list of arbitrary width slaves, but +-- this tb_mm_bus test bench uses an array of fixed width slaves. +-- 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. The tb_mm_master_mux also uses a +-- mm_bus.vhd and the tb_mm_master_mux does uses an array of +-- arbitrary width slaves. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; + +ENTITY tb_mm_bus IS + GENERIC ( + 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 + g_waitrequest : BOOLEAN := FALSE; -- When TRUE model waitrequest by MM slaves, else fixed '0' + g_pipeline_mosi : BOOLEAN := FALSE; + g_pipeline_miso_rdval : BOOLEAN := FALSE; + g_pipeline_miso_wait : BOOLEAN := FALSE + ); +END tb_mm_bus; + +-- Usage: +-- > as 10 +-- > run -all + + +ARCHITECTURE tb OF tb_mm_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_slave_enable_arr: t_nat_boolean_arr := array_init( TRUE, g_nof_slaves); -- TRUE for connected slaves + CONSTANT c_waitrequest_arr : t_nat_boolean_arr := array_init(g_waitrequest, g_nof_slaves); -- Flow control per slave + + CONSTANT c_bus_pipelining : BOOLEAN := g_pipeline_mosi OR g_pipeline_miso_rdval OR g_pipeline_miso_wait; + CONSTANT c_pipeline_mosi : NATURAL := sel_a_b(g_pipeline_mosi, 1, 0); + CONSTANT c_pipeline_miso_rdval : NATURAL := sel_a_b(g_pipeline_miso_rdval, 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_rdval; + + 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 => c_slave_span, + init_sl => '0'); + SIGNAL mm_rst : STD_LOGIC; + SIGNAL mm_clk : STD_LOGIC := '1'; + SIGNAL tb_end : STD_LOGIC; + + SIGNAL cnt_rd : NATURAL := 0; + SIGNAL cnt_rdval : NATURAL := 0; + + -- MM bus + 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; + SIGNAL dbg_c_width_arr : t_nat_natural_arr(0 TO g_nof_slaves-1) := c_width_arr; + SIGNAL dbg_c_rd_latency_arr : t_nat_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; + + ----------------------------------------------------------------------------- + -- Write stimuli and readback to verify + ----------------------------------------------------------------------------- + p_stimuli : PROCESS + VARIABLE v_wrdata : INTEGER; -- write data + BEGIN + tb_end <= '0'; + 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); + + -- Repeat twice to have wr all, rd all, wr all, rd all + v_wrdata := 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; + + -- 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); -- not needed, see p_verify + cnt_rd <= cnt_rd + 1; + END LOOP; + proc_common_wait_some_cycles(mm_clk, 10); + END LOOP; + END LOOP; + + proc_common_wait_some_cycles(mm_clk, 10); + + -- Verify that test has indeed ran + WAIT FOR 1 ns; -- wait 1 ns to ensure that assert report appears at end of transcript log + ASSERT cnt_rdval = cnt_rd AND cnt_rdval > 0 REPORT "Wrong number of rdval" SEVERITY ERROR; + + tb_end <= '1'; + WAIT; + END PROCESS; + + -- Use miso.rdval to know when to verify the rddata, rather than to wait for a fixed c_read_latency after + -- the mosi.rd. The advantage is that then rd accesses can be done on every mm_clk, without having to + -- wait for the c_read_latency. In case of g_pipeline_mosi = TRUE or g_pipeline_miso_wait = TRUE it is + -- even essential to use rdval, because then the latency between rd and rdval can become larger than + -- c_read_latency and even variable (in case of g_waitrequest = TRUE). The disadvantage is that the MM + -- slave must support rdval, but that is ensured by mm_slave_enable. + p_verify : PROCESS + VARIABLE v_expdata : INTEGER := 0; -- expected data + VARIABLE v_rddata : INTEGER; -- read data + BEGIN + WAIT UNTIL rising_edge(mm_clk); + IF master_miso.rdval = '1' THEN + cnt_rdval <= cnt_rdval + 1; + 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; + END IF; + END PROCESS; + + ----------------------------------------------------------------------------- + -- The MM bus + ----------------------------------------------------------------------------- + u_mm_bus: ENTITY work.mm_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_slave_enable_arr => c_slave_enable_arr, + g_waitrequest_arr => c_waitrequest_arr, + g_pipeline_mosi => g_pipeline_mosi, + g_pipeline_miso_rdval => g_pipeline_miso_rdval, + g_pipeline_miso_wait => g_pipeline_miso_wait + ) + PORT MAP ( + mm_rst => mm_rst, + mm_clk => mm_clk, + master_mosi => master_mosi, + master_miso => master_miso, + slave_mosi_arr => slave_mosi_arr, + slave_miso_arr => slave_miso_arr + ); + + ----------------------------------------------------------------------------- + -- Model the MM slaves + ----------------------------------------------------------------------------- + gen_slaves : FOR I IN 0 TO g_nof_slaves-1 GENERATE + u_waitrequest_model : ENTITY work.mm_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 common_lib.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 => 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; + +END tb; diff --git a/libraries/base/mm/tb/vhdl/tb_mm_master_mux.vhd b/libraries/base/mm/tb/vhdl/tb_mm_master_mux.vhd new file mode 100644 index 0000000000000000000000000000000000000000..d3550ff0001464c698daef02c55ab19cedc57373 --- /dev/null +++ b/libraries/base/mm/tb/vhdl/tb_mm_master_mux.vhd @@ -0,0 +1,225 @@ +------------------------------------------------------------------------------- +-- +-- 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 mm_master_mux.vhd and also mm_bus +-- Description: +-- The test bench uses mm_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 +-- mm_bus. The address space of the RAM is defined by the g_base_arr +-- and g_width_arr that define the mm_bus. Therefore this test bench +-- implicitely also verifies mm_bus.vhd. +-- +-- stimuli master mux +-- mosi mosi_arr mosi +-- common -------/----> common +-- p_stimuli ----------> mem ------/-----> mem --------> RAM +-- bus -----/------> master +-- / mux +-- g_nof_masters +-- Remark: +-- In an application it is typical to use mm_master_mux to connect +-- mulitple masters to multiple slabes via a mm_bus MM bus. +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.STD_LOGIC_1164.ALL; +USE IEEE.NUMERIC_STD.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; + +ENTITY tb_mm_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 mm_bus + g_width_arr : t_nat_natural_arr := (4, 8); -- Address width per slave port of mm_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_rdval : BOOLEAN := FALSE; + g_pipeline_bus_miso_wait : BOOLEAN := FALSE + ); +END tb_mm_master_mux; + +-- Usage: +-- > as 10 +-- > run -all + + +ARCHITECTURE tb OF tb_mm_master_mux IS + + CONSTANT mm_clk_period : TIME := 10 ns; + + 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_rdval : NATURAL := sel_a_b(g_pipeline_bus_miso_rdval, 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_slave_enable_arr : t_nat_boolean_arr := array_init(TRUE, g_nof_masters); + CONSTANT c_waitrequest_arr : t_nat_boolean_arr := array_init(g_waitrequest, g_nof_masters); + + CONSTANT c_read_latency : NATURAL := c_bus_pipeline_mosi + c_ram_rd_latency + c_bus_pipeline_miso_rdval; + + 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_ram_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; + SIGNAL ram_mosi : t_mem_mosi := c_mem_mosi_rst; + SIGNAL ram_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_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; + + -- 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 + 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_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 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_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; + + 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.mm_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_slave_enable_arr => c_slave_enable_arr, + g_waitrequest_arr => c_waitrequest_arr, + g_pipeline_mosi => g_pipeline_bus_mosi, + g_pipeline_miso_rdval => g_pipeline_bus_miso_rdval, + g_pipeline_miso_wait => g_pipeline_bus_miso_wait + ) + 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.mm_master_mux + GENERIC MAP ( + g_nof_masters => g_nof_masters, + g_rd_latency_min => c_read_latency + ) + 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_waitrequest_model : ENTITY work.mm_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 common_lib.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 => 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 + ); + + +END tb; diff --git a/libraries/base/mm/tb/vhdl/tb_tb_mm_bus.vhd b/libraries/base/mm/tb/vhdl/tb_tb_mm_bus.vhd new file mode 100644 index 0000000000000000000000000000000000000000..e399076bb1f973284b4ed38e5a7722a0dbca39e8 --- /dev/null +++ b/libraries/base/mm/tb/vhdl/tb_tb_mm_bus.vhd @@ -0,0 +1,67 @@ +------------------------------------------------------------------------------- +-- +-- 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 mm_bus.vhd +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; + +ENTITY tb_tb_mm_bus IS +END tb_tb_mm_bus; + +ARCHITECTURE tb OF tb_tb_mm_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_waitrequest : BOOLEAN := FALSE; -- When TRUE model waitrequest by MM slaves, else fixed '0' + -- g_pipeline_mosi : BOOLEAN := FALSE; + -- g_pipeline_miso_rdval : BOOLEAN := TRUE; + -- g_pipeline_miso_wait : BOOLEAN := FALSE + + u_no_pipe : ENTITY work.tb_mm_bus GENERIC MAP (16, 0, 3, 1, FALSE, FALSE, FALSE, FALSE); + u_no_pipe_base_offset : ENTITY work.tb_mm_bus GENERIC MAP (16, 3*2**4, 4, 1, FALSE, FALSE, FALSE, FALSE); + u_pipe_mosi : ENTITY work.tb_mm_bus GENERIC MAP ( 3, 0, 4, 1, FALSE, TRUE, FALSE, FALSE); + u_pipe_mosi_miso_rdval : ENTITY work.tb_mm_bus GENERIC MAP ( 3, 0, 4, 1, FALSE, TRUE, TRUE, FALSE); + u_waitrequest_no_pipe : ENTITY work.tb_mm_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, FALSE, FALSE, FALSE); + u_waitrequest_pipe_miso_rdval : ENTITY work.tb_mm_bus GENERIC MAP ( 3, 0, 4, 1, TRUE, FALSE, TRUE, FALSE); + u_waitrequest_pipe_miso_rdval2 : ENTITY work.tb_mm_bus GENERIC MAP ( 3, 0, 4, 2, TRUE, FALSE, TRUE, FALSE); + u_waitrequest_pipe_miso_wait : ENTITY work.tb_mm_bus GENERIC MAP ( 2, 0, 4, 1, TRUE, FALSE, FALSE, TRUE); + u_waitrequest_pipe_mosi_one : ENTITY work.tb_mm_bus GENERIC MAP ( 1, 0, 4, 1, TRUE, TRUE, FALSE, FALSE); + u_waitrequest_pipe_mosi : ENTITY work.tb_mm_bus GENERIC MAP ( 2, 0, 4, 1, TRUE, TRUE, FALSE, FALSE); + u_waitrequest_pipe_mosi_miso_rdval : ENTITY work.tb_mm_bus GENERIC MAP ( 2, 0, 4, 1, TRUE, TRUE, TRUE, FALSE); + + -- Do not support simultaneous g_pipeline_mosi = TRUE and g_pipeline_miso_wait = TRUE, see mm_bus_pipe.vhd. + --u_waitrequest_pipe_mosi_miso_wait : ENTITY work.tb_mm_bus GENERIC MAP ( 2, 0, 4, 1, TRUE, TRUE, FALSE, TRUE); + --u_waitrequest_pipe_all : ENTITY work.tb_mm_bus GENERIC MAP ( 2, 0, 4, 1, TRUE, TRUE, TRUE, TRUE); + +END tb; diff --git a/libraries/base/mm/tb/vhdl/tb_tb_mm_master_mux.vhd b/libraries/base/mm/tb/vhdl/tb_tb_mm_master_mux.vhd new file mode 100644 index 0000000000000000000000000000000000000000..978b06ac803a73275570f583d334638ed0878a88 --- /dev/null +++ b/libraries/base/mm/tb/vhdl/tb_tb_mm_master_mux.vhd @@ -0,0 +1,61 @@ +------------------------------------------------------------------------------- +-- +-- 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 mm_master_mux.vhd +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; + +ENTITY tb_tb_mm_master_mux IS +END tb_tb_mm_master_mux; + +ARCHITECTURE tb OF tb_tb_mm_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 mm_bus + -- g_width_arr : t_nat_natural_arr := (4, 8); -- Address width per slave port of mm_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_rdval : BOOLEAN := FALSE; + -- g_pipeline_bus_miso_wait : BOOLEAN := FALSE + + u_no_pipe : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, FALSE, FALSE, FALSE); + u_pipe_mosi : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, TRUE, FALSE, FALSE); + u_pipe_miso_rdval : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), FALSE, FALSE, TRUE, FALSE); + u_waitrequest_no_pipe : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, FALSE, FALSE, FALSE); + u_waitrequest_pipe_miso_rdval : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, FALSE, TRUE, FALSE); + u_waitrequest_pipe_mosi : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, TRUE, FALSE, FALSE); + u_waitrequest_pipe_mosi_miso_rdval : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, TRUE, TRUE, FALSE); + + -- Do not support simultaneous g_pipeline_mosi = TRUE and g_pipeline_miso_wait = TRUE, see mm_bus_pipe.vhd. + --u_waitrequest_pipe_all : ENTITY work.tb_mm_master_mux GENERIC MAP (2, (0, 256), (4, 8), TRUE, TRUE, TRUE, TRUE); + +END tb;