diff --git a/libraries/base/mm/src/vhdl/mm_bus.vhd b/libraries/base/mm/src/vhdl/mm_bus.vhd index a0d5c5568e21c8dbaf30d216500f21a91cfee704..0b447521408adcd9d45d60cdb8cd48983c5acfa1 100644 --- a/libraries/base/mm/src/vhdl/mm_bus.vhd +++ b/libraries/base/mm/src/vhdl/mm_bus.vhd @@ -1,183 +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, +------------------------------------------------------------------------------- +-- +-- 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; + ) + 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/mm/src/vhdl/mm_bus_comb.vhd b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd index 14c887d1185e93c49e3523c960e413ac42997e54..77302f2c02d85e625e583efd7d4396acf9145ebc 100644 --- a/libraries/base/mm/src/vhdl/mm_bus_comb.vhd +++ b/libraries/base/mm/src/vhdl/mm_bus_comb.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- Author: E. Kooistra -- Purpose: Connect a single MM master interface to a list of MM slave -- interfaces using a combinatorial muliplexer as bus. @@ -32,9 +32,9 @@ -- 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 +-- 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 +-- 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. @@ -72,15 +72,15 @@ -- 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 @@ -119,13 +119,13 @@ ENTITY mm_bus_comb IS 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_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; @@ -145,7 +145,7 @@ ARCHITECTURE rtl OF mm_bus_comb IS -- 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); @@ -153,12 +153,12 @@ ARCHITECTURE rtl OF mm_bus_comb IS BEGIN - gen_single : IF g_nof_slaves=1 GENERATE + 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 + + 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; @@ -173,9 +173,9 @@ BEGIN 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 @@ -190,7 +190,7 @@ BEGIN 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; @@ -209,6 +209,6 @@ BEGIN END LOOP; END PROCESS; - END GENERATE; - + 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 index 4f1b6fe1bfaa2f83f167fde1f024634191c9b67b..2b8f8ecba4dad4d87fdc9dbb6607df467bcd6075 100644 --- a/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd +++ b/libraries/base/mm/src/vhdl/mm_bus_pipe.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- Author: E. Kooistra -- Purpose: Provide pipelining to the combinatorial mm_bus_comb -- Description: @@ -28,7 +28,7 @@ -- 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 +-- 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: @@ -36,10 +36,10 @@ -- 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|--------> +-- <-- p_miso_pipe <--| mm_bus |<--|mm |<--|mm |<-------- +-- ------------------>| comb |-->|pipeline|-->|latency|--------> -- . . |________| . |________| |adapter| . . -- master_miso . . . |_______| . slave_miso_arr -- master_mosi . . . . slave_mosi_arr @@ -68,15 +68,15 @@ -- 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 +-- 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. --- +-- 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 @@ -96,7 +96,7 @@ -- 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; @@ -120,7 +120,7 @@ ENTITY mm_bus_pipe IS 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_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; @@ -130,14 +130,14 @@ 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_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_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_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) @@ -146,9 +146,9 @@ BEGIN -- 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 @@ -162,7 +162,7 @@ BEGIN master_miso.waitrequest <= m_miso_reg.waitrequest; END IF; END PROCESS; - + -- MM bus u_mm_bus_comb : ENTITY work.mm_bus_comb GENERIC MAP ( @@ -198,7 +198,7 @@ BEGIN 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 ( @@ -216,7 +216,7 @@ BEGIN ); END GENERATE; END GENERATE; - + slave_mosi_arr <= adapt_mosi_arr; adapt_miso_arr <= slave_miso_arr; diff --git a/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd b/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd index 9ba97a6b80322b5ccd283c538c7132dee9ef8a95..137efa3891e545ffab1c485b056ab4fe79904e57 100644 --- a/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd +++ b/libraries/base/mm/src/vhdl/mm_latency_adapter.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- Author: E. Kooistra -- Purpose: Adapt miso.waitrequest latency from 1 to 0, to support pipelining -- of the waitrequest flow control @@ -66,14 +66,14 @@ ARCHITECTURE str OF mm_latency_adapter IS 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 + BEGIN in_miso <= out_miso; --in_miso.rdval <= out_miso.rdval AND NOT in_waitrequest; in_miso.waitrequest <= in_waitrequest; @@ -82,7 +82,7 @@ BEGIN -- 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, @@ -100,10 +100,10 @@ BEGIN 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/mm/src/vhdl/mm_master_mux.vhd b/libraries/base/mm/src/vhdl/mm_master_mux.vhd index 20d31d78bc8968c0afa57e3051acf7046b340b38..1c03f46a71d3245b40eab82e2476b62b978c0ad2 100644 --- a/libraries/base/mm/src/vhdl/mm_master_mux.vhd +++ b/libraries/base/mm/src/vhdl/mm_master_mux.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- Author: E. Kooistra -- Purpose: Multiplex an array of MM master interfaces to a single MM master -- interface @@ -42,7 +42,7 @@ -- -- 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 +-- 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. @@ -71,24 +71,24 @@ ENTITY mm_master_mux IS 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_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 + 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 + + 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 @@ -111,13 +111,13 @@ BEGIN 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 @@ -138,7 +138,7 @@ BEGIN END IF; END LOOP; END PROCESS; - - END GENERATE; - + + END GENERATE; + END rtl; diff --git a/libraries/base/mm/src/vhdl/mm_pipeline.vhd b/libraries/base/mm/src/vhdl/mm_pipeline.vhd index 9ef80d3a57141807ed0cd4f466fc489693f33e28..179a3d51adfa85d3b5ed77c5bb1a449a2c50ff24 100644 --- a/libraries/base/mm/src/vhdl/mm_pipeline.vhd +++ b/libraries/base/mm/src/vhdl/mm_pipeline.vhd @@ -37,7 +37,7 @@ -- * RL=1 -- _ _ _ _ _ _ _ _ _ _ _ _ -- clk _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ --- +-- -- in_dat |a |b |c |d -- _________ ___ ___ -- in_val |_______| |_______| |_______________ @@ -45,24 +45,24 @@ -- 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 |___________| |_______| |___ @@ -125,7 +125,7 @@ BEGIN 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 @@ -135,11 +135,11 @@ BEGIN 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; @@ -148,6 +148,6 @@ BEGIN out_mosi.rd <= '0'; -- out_mosi.rd = mosi_reg.rd AND ready END IF; END PROCESS; - END GENERATE; + 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 index 5336d437c906170efb75be36cfdebe3007552e3e..7803a14da266adc98274703022c01b4c8945d3b3 100644 --- a/libraries/base/mm/src/vhdl/mm_slave_enable.vhd +++ b/libraries/base/mm/src/vhdl/mm_slave_enable.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- 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 @@ -32,7 +32,7 @@ -- * 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 +-- 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, @@ -75,7 +75,7 @@ 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 @@ -89,28 +89,28 @@ BEGIN 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; @@ -118,5 +118,5 @@ BEGIN 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 index 3fe30179d334a0d0e0ce596c7f651df30efe56bf..c2a61ace47ece1df90a026338eceb366c79e7341 100644 --- a/libraries/base/mm/src/vhdl/mm_slave_mux.vhd +++ b/libraries/base/mm/src/vhdl/mm_slave_mux.vhd @@ -3,13 +3,13 @@ -- 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. @@ -19,7 +19,7 @@ ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- +-- -- Author: E. Kooistra -- Purpose: Combines an array of MM interfaces into a single MM interface. -- Description: @@ -44,7 +44,7 @@ ENTITY mm_slave_mux IS PORT ( mosi : IN t_mem_mosi; miso : OUT t_mem_miso; - mosi_arr : OUT t_mem_mosi_arr(g_nof_mosi - 1 DOWNTO 0); + 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; @@ -66,5 +66,5 @@ BEGIN 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/tb_mm_bus.vhd b/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd index b592947cbaaba7f68a13fbf695f83c68575c6f50..44cb20799724a60185b5ad273e029245b8f3ee52 100644 --- a/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd +++ b/libraries/base/mm/tb/vhdl/tb_mm_bus.vhd @@ -1,249 +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; +------------------------------------------------------------------------------- +-- +-- 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; + + -- 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 index 6771af2080e1263dba50c035b26b05a21c734d3f..d3550ff0001464c698daef02c55ab19cedc57373 100644 --- a/libraries/base/mm/tb/vhdl/tb_mm_master_mux.vhd +++ b/libraries/base/mm/tb/vhdl/tb_mm_master_mux.vhd @@ -1,225 +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; +------------------------------------------------------------------------------- +-- +-- 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; + + -- 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;