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;