From 478ef67fbfce373f06936e8170963b1eabc53feb Mon Sep 17 00:00:00 2001
From: Reinier van der Walle <walle@astron.nl>
Date: Wed, 22 Mar 2023 11:31:49 +0100
Subject: [PATCH] ported axi4-lite components from gemini

---
 libraries/base/axi4/hdllib.cfg                |   4 +
 .../base/axi4/src/vhdl/axi4_lite_pkg.vhd      | 739 ++++++++++++++++++
 .../base/axi4/src/vhdl/mem_to_axi4_lite.vhd   | 213 +++++
 .../base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd    | 191 +++++
 4 files changed, 1147 insertions(+)
 create mode 100644 libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd
 create mode 100644 libraries/base/axi4/src/vhdl/mem_to_axi4_lite.vhd
 create mode 100644 libraries/base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd

diff --git a/libraries/base/axi4/hdllib.cfg b/libraries/base/axi4/hdllib.cfg
index 3174cab5d7..21929d4db4 100644
--- a/libraries/base/axi4/hdllib.cfg
+++ b/libraries/base/axi4/hdllib.cfg
@@ -5,15 +5,19 @@ hdl_lib_uses_sim =
 hdl_lib_technology =
 
 synth_files =
+    src/vhdl/axi4_lite_pkg.vhd
     src/vhdl/axi4_stream_pkg.vhd
     src/vhdl/axi4_stream_dp_bridge.vhd
+    src/vhdl/mem_to_axi4_lite.vhd
 
 test_bench_files =
     tb/vhdl/tb_axi4_stream_dp_bridge.vhd
     tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
+    tb/vhdl/tb_axi4_lite_ram.vhd
 
 regression_test_vhdl =
     tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
+    tb/vhdl/tb_axi4_lite_ram.vhd
 
 [modelsim_project_file]
 
diff --git a/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd b/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd
new file mode 100644
index 0000000000..4244b7262e
--- /dev/null
+++ b/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd
@@ -0,0 +1,739 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2023
+-- 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 : R vd Walle
+-- Purpose:  
+--   Package containing usefull definitions for working with AXI4-Lite
+-- Description:
+--   Ported from:
+--   https://git.astron.nl/desp/gemini/-/blob/master/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE std.textio.ALL;
+USE IEEE.STD_LOGIC_TEXTIO.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+
+PACKAGE axi4_lite_pkg IS
+
+  ------------------------------------------------------------------------------
+  -- Simple AXI4 lite memory access (for MM control interface)
+  ------------------------------------------------------------------------------
+  CONSTANT c_max_string           : NATURAL := 128;
+
+  CONSTANT c_axi4_lite_address_w  : NATURAL := 32;
+  CONSTANT c_axi4_lite_data_w     : NATURAL := 32;
+  CONSTANT c_axi4_lite_prot_w     : NATURAL := 3;
+  CONSTANT c_axi4_lite_resp_w     : NATURAL := 2;
+
+
+  TYPE t_axi4_lite_copi IS RECORD  -- Controller Out Peripheral In
+    -- write address channel
+    awaddr  : std_logic_vector(c_axi4_lite_address_w-1 downto 0);         -- write address
+    awprot  : std_logic_vector(c_axi4_lite_prot_w-1 downto 0);            -- access permission for write
+    awvalid : std_logic;                                                  -- write address valid
+    -- write data channel
+    wdata   : std_logic_vector(c_axi4_lite_data_w-1 downto 0);            -- write data
+    wstrb   : std_logic_vector((c_axi4_lite_data_w/c_byte_w)-1 downto 0); -- write strobes
+    wvalid  : std_logic;                                                  -- write valid
+    -- write response channel
+    bready  : std_logic;                                                  -- response ready
+    -- read address channel
+    araddr  : std_logic_vector(c_axi4_lite_address_w-1 downto 0);         -- read address
+    arprot  : std_logic_vector(c_axi4_lite_prot_w-1 downto 0);            -- access permission for read
+    arvalid : std_logic;                                                  -- read address valid
+    -- read data channel
+    rready  : std_logic;                                                  -- read ready
+  END RECORD;
+
+  TYPE t_axi4_lite_cipo IS RECORD  -- Controller In Peripheral Out
+    -- write_address channel
+    awready : std_logic;                                       -- write address ready
+    -- write data channel
+    wready  : std_logic;                                       -- write ready
+    -- write response channel
+    bresp   : std_logic_vector(c_axi4_lite_resp_w-1 downto 0); -- write response
+    bvalid  : std_logic;                                       -- write response valid
+    -- read address channel
+    arready : std_logic;                                       -- read address ready
+    -- read data channel
+    rdata   : std_logic_vector(c_axi4_lite_data_w-1 downto 0); -- read data
+    rresp   : std_logic_vector(c_axi4_lite_resp_w-1 downto 0); -- read response
+    rvalid  : std_logic;                                       -- read valid
+  END RECORD;
+
+   TYPE t_register_address IS RECORD
+      base_address    : NATURAL;
+      address         : NATURAL;
+      offset          : NATURAL;
+      width           : NATURAL;
+      name            : STRING(1 TO c_max_string);
+   END RECORD;
+
+   TYPE t_register_address_array IS ARRAY (INTEGER RANGE <>) OF t_register_address;
+
+  CONSTANT c_axi4_lite_copi_rst : t_axi4_lite_copi := ((OTHERS=>'0'), (OTHERS=>'0'), '0', (OTHERS=>'0'), (OTHERS=>'0'), '0', '0', (OTHERS=>'0'), (OTHERS=>'0'), '0', '0');
+  CONSTANT c_axi4_lite_cipo_rst : t_axi4_lite_cipo := ('0', '0', (OTHERS=>'0'), '0', '0', (OTHERS=>'0'), (OTHERS=>'0'), '0');
+
+  -- Multi port array for MM records
+  TYPE t_axi4_lite_cipo_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_lite_cipo;
+  TYPE t_axi4_lite_copi_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_lite_copi;
+
+  CONSTANT c_axi4_lite_resp_okay   : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "00"; -- normal access success
+  CONSTANT c_axi4_lite_resp_exokay : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "01"; -- exclusive access okay
+  CONSTANT c_axi4_lite_resp_slverr : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "10"; -- peripheral error
+  CONSTANT c_axi4_lite_resp_decerr : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "11"; -- decode error
+
+
+  -- Resize functions to fit an integer or an SLV in the corresponding t_axi4_lite_cipo or t_axi4_lite_copi field width
+  FUNCTION TO_AXI4_LITE_ADDRESS(n : INTEGER) RETURN STD_LOGIC_VECTOR;  -- unsigned, use integer to support 32 bit range
+  FUNCTION TO_AXI4_LITE_DATA(   n : INTEGER) RETURN STD_LOGIC_VECTOR;  -- unsigned, alias of TO_AXI4_LITE_DATA()
+  FUNCTION TO_AXI4_LITE_UDATA(  n : INTEGER) RETURN STD_LOGIC_VECTOR;  -- unsigned, use integer to support 32 bit range
+  FUNCTION TO_AXI4_LITE_SDATA(  n : INTEGER) RETURN STD_LOGIC_VECTOR;  -- sign extended
+  FUNCTION RESIZE_AXI4_LITE_ADDRESS(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;  -- unsigned
+  FUNCTION RESIZE_AXI4_LITE_DATA(   vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;  -- unsigned, alias of RESIZE_AXI4_LITE_UDATA
+  FUNCTION RESIZE_AXI4_LITE_UDATA(  vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;  -- unsigned
+  FUNCTION RESIZE_AXI4_LITE_SDATA(  vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;  -- sign extended
+  FUNCTION RESIZE_AXI4_LITE_XDATA(  vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;  -- set unused MSBits to 'X'
+
+
+  CONSTANT c_axi4_lite_reg_rd_latency : NATURAL := 0;
+  CONSTANT c_axi4_lite_reg            : t_c_mem := (latency    => c_axi4_lite_reg_rd_latency,
+                                                    adr_w      => 1,
+                                                    dat_w      => 32,
+                                                    nof_dat    => 1,
+                                                    init_sl    => 'X');
+
+   CONSTANT c_axi4_lite_reg_init_w     : NATURAL := 1*256*32;  -- >= largest expected value of dat_w*nof_dat (256 * 32 bit = 1k byte)
+
+   CONSTANT c_mask_ones                : t_slv_32_arr(0 TO 0)  := (OTHERS => (OTHERS => '1'));
+   CONSTANT c_mask_zeros               : t_slv_32_arr(0 TO 0)  := (OTHERS => (OTHERS => '0'));
+
+   FUNCTION pad(str: STRING; width: NATURAL; pad_char: CHARACTER) RETURN STRING;
+   FUNCTION pad(str: STRING) RETURN STRING;
+   FUNCTION strip(str: STRING) RETURN STRING;
+
+   --PROCEDURE axi_lite_blockwrite (SIGNAL mm_clk   : IN STD_LOGIC;
+   --                               SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+   --                               SIGNAL axi_copi : OUT t_axi4_lite_copi;
+   --                               register_addr   : NATURAL;
+   --                               dataFileName    : STRING;
+   --                               name            : STRING := pad("");
+   --                               expected_fail   : BOOLEAN := false;
+   --                               fail_on_error   : BOOLEAN := false);
+
+   -- Multiple variants of the same function
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk : IN STD_LOGIC; SIGNAL axi_cipo : IN t_axi4_lite_cipo; SIGNAL axi_copi : OUT t_axi4_lite_copi; register_addr : NATURAL; write_reg : BOOLEAN; data : t_slv_32_arr; validate : boolean := false; mask : t_slv_32_arr := c_mask_zeros; expected_fail : BOOLEAN := false; fail_on_error : BOOLEAN := false);
+
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk : IN STD_LOGIC; SIGNAL axi_cipo : IN t_axi4_lite_cipo; SIGNAL axi_copi : OUT t_axi4_lite_copi; register_addr : t_register_address; write_reg : BOOLEAN; data : t_slv_32_arr; validate : boolean := false; mask : t_slv_32_arr := c_mask_zeros; expected_fail : BOOLEAN := false; fail_on_error : BOOLEAN := false);
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk : IN STD_LOGIC; SIGNAL axi_cipo : IN t_axi4_lite_cipo; SIGNAL axi_copi : OUT t_axi4_lite_copi; register_addr : t_register_address; write_reg : BOOLEAN; data : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0); validate : boolean := false; mask : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0) := (OTHERS => '0'); expected_fail : BOOLEAN := false; fail_on_error : BOOLEAN := false);
+
+   -- Base function
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk   : IN STD_LOGIC;
+                                   SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                   SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                   register_addr   : NATURAL;
+                                   write_reg       : BOOLEAN;
+                                   data            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0);
+                                   validate        : boolean := false;
+                                   mask            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0) := (OTHERS => '0');
+                                   offset          : NATURAL := 0;
+                                   width           : NATURAL := 32;
+                                   name            : STRING  := pad("");
+                                   expected_fail   : BOOLEAN := false;
+                                   fail_on_error   : BOOLEAN := false);
+
+   PROCEDURE axi_lite_init (SIGNAL mm_rst : IN STD_LOGIC; SIGNAL mm_clk : IN STD_LOGIC; SIGNAL axi_cipo : IN t_axi4_lite_cipo; SIGNAL axi_copi : OUT t_axi4_lite_copi);
+   --PROCEDURE axi_lite_wait (SIGNAL mm_clk : IN STD_LOGIC; SIGNAL axi_cipo : IN t_axi4_lite_cipo; SIGNAL axi_copi : OUT t_axi4_lite_copi; register_addr : t_register_address; data : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0); fail_on_error   : BOOLEAN := TRUE);
+
+
+END axi4_lite_pkg;
+
+PACKAGE BODY axi4_lite_pkg IS
+
+  FUNCTION pad(str: STRING; width: NATURAL; pad_char: CHARACTER) RETURN STRING IS
+    VARIABLE v_str : STRING(1 TO width) := (OTHERS => pad_char);
+  BEGIN
+    v_str(width-str'LENGTH+1 TO width) := str;
+    RETURN v_str;
+  END;
+
+  FUNCTION pad(str: STRING) RETURN STRING IS
+    VARIABLE v_str : STRING(1 TO c_max_string) := (OTHERS => ' ');
+  BEGIN
+    v_str(1 TO str'LENGTH) := str;
+    RETURN v_str;
+  END;
+
+  FUNCTION strip(str: STRING) RETURN STRING IS
+  BEGIN
+    FOR i IN str'REVERSE_RANGE LOOP
+      IF str(i) /= ' ' THEN
+         RETURN str(1 TO i);
+      END IF;
+    END LOOP;
+    RETURN str;
+  END;
+
+  -- Resize functions to fit an integer or an SLV in the corresponding t_axi4_lite_cipo or t_axi4_lite_copi field width
+  FUNCTION TO_AXI4_LITE_ADDRESS(n : INTEGER) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_UVEC(TO_SVEC(n, 32), c_axi4_lite_address_w);
+  END TO_AXI4_LITE_ADDRESS;
+
+  FUNCTION TO_AXI4_LITE_DATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN TO_AXI4_LITE_UDATA(n);
+  END TO_AXI4_LITE_DATA;
+
+  FUNCTION TO_AXI4_LITE_UDATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_UVEC(TO_SVEC(n, 32), c_axi4_lite_data_w);
+  END TO_AXI4_LITE_UDATA;
+
+  FUNCTION TO_AXI4_LITE_SDATA(n : INTEGER) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_SVEC(TO_SVEC(n, 32), c_axi4_lite_data_w);
+  END TO_AXI4_LITE_SDATA;
+
+  FUNCTION RESIZE_AXI4_LITE_ADDRESS(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_UVEC(vec, c_axi4_lite_address_w);
+  END RESIZE_AXI4_LITE_ADDRESS;
+
+  FUNCTION RESIZE_AXI4_LITE_DATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_AXI4_LITE_UDATA(vec);
+  END RESIZE_AXI4_LITE_DATA;
+
+  FUNCTION RESIZE_AXI4_LITE_UDATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_UVEC(vec, c_axi4_lite_data_w);
+  END RESIZE_AXI4_LITE_UDATA;
+
+  FUNCTION RESIZE_AXI4_LITE_SDATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN RESIZE_SVEC(vec, c_axi4_lite_data_w);
+  END RESIZE_AXI4_LITE_SDATA;
+
+   FUNCTION RESIZE_AXI4_LITE_XDATA(vec : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+      VARIABLE v_vec : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0) := (OTHERS=>'X');
+   BEGIN
+      v_vec(vec'LENGTH-1 DOWNTO 0) := vec;
+      RETURN v_vec;
+   END RESIZE_AXI4_LITE_XDATA;
+
+
+   ------------------------------------------------------------------------------
+   ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_init (SIGNAL mm_rst   : IN STD_LOGIC;
+                            SIGNAL mm_clk   : IN STD_LOGIC;
+                            SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                            SIGNAL axi_copi : OUT t_axi4_lite_copi) is
+
+   BEGIN
+
+      axi_copi <= c_axi4_lite_copi_rst;
+
+      -- wait for reset to be released
+      WAIT UNTIL to_x01(mm_rst) = '0';
+      WAIT UNTIL rising_edge(mm_clk);
+      WAIT UNTIL rising_edge(mm_clk);
+
+   END PROCEDURE;
+
+
+
+   ------------------------------------------------------------------------------
+   ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk   : IN STD_LOGIC;
+                                   SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                   SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                   register_addr   : NATURAL;
+                                   write_reg       : BOOLEAN;
+                                   data            : t_slv_32_arr;
+                                   validate        : BOOLEAN := false;
+                                   mask            : t_slv_32_arr := c_mask_zeros;
+                                   expected_fail   : BOOLEAN := false;
+                                   fail_on_error   : BOOLEAN := false) is
+
+         VARIABLE mask_unit         : STD_LOGIC_VECTOR(31 DOWNTO 0);
+   BEGIN
+      data_write_loop: FOR i IN data'RANGE LOOP
+
+         IF mask'LENGTH = data'LENGTH THEN
+            mask_unit := mask(i);
+         ELSE
+            mask_unit := mask(0);
+         END IF;
+
+         axi_lite_transaction(mm_clk, axi_cipo, axi_copi, register_addr+i, write_reg, data(i), validate, mask_unit, 0, 32,  pad(""), expected_fail, fail_on_error);
+      END LOOP;
+   END PROCEDURE;
+
+ ------------------------------------------------------------------------------
+ ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk   : IN STD_LOGIC;
+                                   SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                   SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                   register_addr   : t_register_address;
+                                   write_reg       : BOOLEAN;
+                                   data            : t_slv_32_arr;
+                                   validate        : BOOLEAN := false;
+                                   mask            : t_slv_32_arr := c_mask_zeros;
+                                   expected_fail   : BOOLEAN := false;
+                                   fail_on_error   : BOOLEAN := false) is
+
+         VARIABLE mask_unit         : STD_LOGIC_VECTOR(31 DOWNTO 0);
+   BEGIN
+      data_write_loop: FOR i IN data'RANGE LOOP
+
+         IF mask'LENGTH = data'LENGTH THEN
+            mask_unit := mask(i);
+         ELSE
+            mask_unit := mask(0);
+         END IF;
+
+         axi_lite_transaction(mm_clk, axi_cipo, axi_copi, register_addr.base_address+register_addr.address+i, write_reg, data(i), validate, mask_unit, register_addr.offset, register_addr.width, register_addr.name, expected_fail, fail_on_error);
+      END LOOP;
+   END PROCEDURE;
+
+   ------------------------------------------------------------------------------
+   ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk   : IN STD_LOGIC;
+                                   SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                   SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                   register_addr   : t_register_address;
+                                   write_reg       : BOOLEAN;
+                                   data            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0);
+                                   validate        : BOOLEAN := false;
+                                   mask            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0) := (OTHERS => '0');
+                                   expected_fail   : BOOLEAN := false;
+                                   fail_on_error   : BOOLEAN := false) is
+
+   BEGIN
+      axi_lite_transaction(mm_clk, axi_cipo, axi_copi, register_addr.base_address+register_addr.address, write_reg, data, validate, mask, register_addr.offset, register_addr.width, register_addr.name, expected_fail, fail_on_error);
+   END PROCEDURE;
+
+
+
+
+   ------------------------------------------------------------------------------
+   ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_transaction (SIGNAL mm_clk   : IN STD_LOGIC;
+                                   SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                   SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                   register_addr   : NATURAL;
+                                   write_reg       : BOOLEAN;
+                                   data            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0);
+                                   validate        : BOOLEAN := false;
+                                   mask            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0) := (OTHERS => '0');
+                                   offset          : NATURAL := 0;
+                                   width           : NATURAL := 32;
+                                   name            : STRING := pad("");
+                                   expected_fail   : BOOLEAN := false;
+                                   fail_on_error   : BOOLEAN := false) is
+
+         VARIABLE stdio             : line;
+         VARIABLE result            : STD_LOGIC_VECTOR(31 DOWNTO 0);
+   BEGIN
+
+      -- Start transaction
+      WAIT UNTIL rising_edge(mm_clk);
+
+      IF write_reg = false THEN
+         -- Setup read address
+         axi_copi.arvalid <= '1';
+         axi_copi.araddr <= std_logic_vector(to_unsigned(register_addr*4, 32));
+         axi_copi.rready <= '1';
+
+         read_address_wait: LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+            IF axi_cipo.arready = '1' THEN
+               axi_copi.arvalid <= '0';
+               axi_copi.araddr <= (OTHERS => '0');
+            END IF;
+
+            IF axi_cipo.rvalid = '1' THEN
+               EXIT;
+            END IF;
+         END LOOP;
+
+
+         write(stdio, string'("INFO: AXI Lite read of register "));
+         IF name(1) /= ' ' THEN
+            write(stdio, strip(name));
+         ELSE
+            write(stdio, (register_addr));
+         END IF;
+         write(stdio, string'(" returned "));
+
+         -- Read response
+         IF axi_cipo.rresp = "00" THEN
+            write(stdio, string'("OK "));
+         ELSIF axi_cipo.rresp = "01" THEN
+            write(stdio, string'("exclusive access error "));
+         ELSIF axi_cipo.rresp = "10" THEN
+            write(stdio, string'("peripheral error "));
+         ELSIF axi_cipo.rresp = "11" THEN
+            write(stdio, string'("address decode error "));
+         END IF;
+
+         write(stdio, string'("with data 0x"));
+         hwrite(stdio, axi_cipo.rdata(offset+width-1 DOWNTO offset));
+         writeline(output, stdio);
+
+         IF validate = TRUE THEN
+            IF (axi_cipo.rdata(offset+width-1 DOWNTO offset) AND mask(width-1 DOWNTO 0)) /= (data(width-1 DOWNTO 0) AND mask(width-1 DOWNTO 0) ) THEN
+               IF expected_fail THEN
+                  write(stdio, string'("INFO (Expected Error)"));
+               ELSE
+                  write(stdio, string'("ERROR"));
+               END IF;
+               write(stdio, string'(": Return data doesn't match mask"));
+               writeline(output, stdio);
+               ASSERT NOT(fail_on_error) REPORT "Return data doesn't match mask" SEVERITY ERROR;
+            END IF;
+         END IF;
+
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.rready <= '0';
+      ELSE
+
+         -- Needs to actually do a read first to perform a RMW on the shared fields
+         axi_copi.arvalid <= '1';
+         axi_copi.araddr <= std_logic_vector(to_unsigned(register_addr*4, 32));
+         axi_copi.rready <= '1';
+
+         rmw_address_wait: LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+            IF axi_cipo.arready = '1' THEN
+               axi_copi.arvalid <= '0';
+               axi_copi.araddr <= (OTHERS => '0');
+               EXIT;
+            END IF;
+         END LOOP;
+
+         rmw_response_wait: WHILE axi_cipo.rvalid = '0' LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+         END LOOP;
+
+         result := axi_cipo.rdata;
+
+         IF axi_cipo.rresp /= "00" THEN
+            IF expected_fail THEN
+               write(stdio, string'("INFO (Expected Error)"));
+            ELSE
+               write(stdio, string'("ERROR"));
+            END IF;
+            write(stdio, string'(": Failure to read during write of register "));
+            IF name(1) /= ' ' THEN
+               write(stdio, strip(name));
+            ELSE
+               write(stdio, (register_addr));
+            END IF;
+            write(stdio, string'(" got "));
+            write(stdio, to_integer(unsigned(axi_cipo.rresp)));
+            writeline(output, stdio);
+            ASSERT NOT(fail_on_error and not expected_fail) REPORT "Failure to read during write of register" SEVERITY ERROR;
+         END IF;
+
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.rready <= '0';
+
+         -- Setup write address, data, & reponse ready
+         axi_copi.awvalid <= '1';
+         axi_copi.awaddr <= std_logic_vector(to_unsigned(register_addr*4, 32));
+         axi_copi.bready <= '1';
+         axi_copi.wvalid <= '1';
+
+         FOR i IN 0 TO 31 LOOP
+            IF (i >= offset) and (i < (offset+width)) THEN
+               axi_copi.wdata(i) <= (result(i) AND mask(i-offset)) OR data(i-offset);
+            ELSE
+               axi_copi.wdata(i) <= result(i);
+            END IF;
+         END LOOP;
+
+         axi_copi.wstrb <= X"f";
+
+         write_address_wait: LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+
+            IF axi_cipo.wready = '1' THEN
+               axi_copi.wvalid <= '0';
+            END IF;
+
+            IF axi_cipo.awready = '1' THEN
+               axi_copi.awvalid <= '0';
+               axi_copi.awaddr <= (OTHERS => '0');
+            END IF;
+
+            IF axi_cipo.awready = '1' AND axi_cipo.wready  = '1' THEN
+               EXIT;
+            END IF;
+         END LOOP;
+
+         response_wait: WHILE axi_cipo.bvalid = '0' LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+         END LOOP;
+
+         IF axi_cipo.bresp = "00" THEN
+            write(stdio, string'("INFO"));
+         ELSE
+            IF expected_fail THEN
+               write(stdio, string'("INFO (Expected Error)"));
+            ELSE
+               write(stdio, string'("ERROR"));
+            END IF;
+         END IF;
+
+         write(stdio, string'(": AXI Lite write of register "));
+         IF name(1) /= ' ' THEN
+            write(stdio, strip(name));
+         ELSE
+            write(stdio, (register_addr));
+         END IF;
+         write(stdio, string'(" returned "));
+
+         -- Print response
+         IF axi_cipo.bresp = "00" THEN
+            write(stdio, string'("OK"));
+         ELSIF axi_cipo.bresp = "01" THEN
+            write(stdio, string'("exclusive access error "));
+            ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code exclusive access error" SEVERITY ERROR;
+         ELSIF axi_cipo.bresp = "10" THEN
+            write(stdio, string'("peripheral error "));
+            ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code peripheral error" SEVERITY ERROR;
+         ELSIF axi_cipo.bresp = "11" THEN
+            write(stdio, string'("address decode error "));
+            ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code address decode error" SEVERITY ERROR;
+         END IF;
+
+         writeline(output, stdio);
+
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.bready <= '0';
+      END IF;
+
+   END PROCEDURE;
+
+ ------------------------------------------------------------------------------
+ ------------------------------------------------------------------------------
+ -- Write a block of values from a file starting at a given address.
+   PROCEDURE axi_lite_blockwrite (SIGNAL mm_clk   : IN STD_LOGIC;
+                                  SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                                  SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                                  register_addr   : NATURAL;
+                                  dataFileName    : STRING;
+                                  name            : STRING := pad("");
+                                  expected_fail   : BOOLEAN := false;
+                                  fail_on_error   : BOOLEAN := false) is
+
+      VARIABLE stdio   : line;
+      VARIABLE result  : STD_LOGIC_VECTOR(31 DOWNTO 0);
+      variable wrData : std_logic_vector(31 downto 0);
+      variable wrCount : natural := 0; -- which word we are up to
+      file dataFile : TEXT;
+      variable lineIn : line;
+      variable good : boolean;
+      
+   BEGIN
+      
+      FILE_OPEN(dataFile,dataFileName,READ_MODE);
+      
+      while (not endfile(dataFile)) loop 
+         
+         -- Get the data to write
+         readline(dataFile, lineIn);
+         
+         while (not endfile(dataFile)) and ((lineIn'length = 0) or (lineIn(lineIn'left) = '#')) loop
+            readline(dataFile, lineIn);  -- skip empty lines or lines starting with a comment character ('#')
+         end loop;
+         if endfile(dataFile) and ((lineIn'length = 0) or (lineIn(lineIn'left) = '#')) then
+            exit;
+         end if;
+         hread(lineIn,wrData,good);
+         assert good
+            report "text IO Read error" severity ERROR;
+         
+         -- Start transaction
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.rready <= '0';
+    
+         -- Setup write address, data, & response ready
+         axi_copi.awvalid <= '1';
+         axi_copi.awaddr <= std_logic_vector(to_unsigned((register_addr + wrCount)*4, 32));
+         axi_copi.bready <= '1';
+         axi_copi.wvalid <= '1';
+         axi_copi.wdata <= wrData;
+         axi_copi.wstrb <= X"f";
+   
+         write_address_wait: LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+   
+            IF axi_cipo.wready = '1' THEN
+               axi_copi.wvalid <= '0';
+            END IF;
+   
+            IF axi_cipo.awready = '1' THEN
+               axi_copi.awvalid <= '0';
+               axi_copi.awaddr <= (OTHERS => '0');
+            END IF;
+   
+            IF axi_cipo.awready = '1' AND axi_cipo.wready  = '1' THEN
+               EXIT;
+            END IF;
+         END LOOP;
+   
+         response_wait: WHILE axi_cipo.bvalid = '0' LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+         END LOOP;
+   
+         IF axi_cipo.bresp /= "00" THEN
+
+            IF expected_fail THEN
+               write(stdio, string'("INFO (Expected Error)"));
+            ELSE
+               write(stdio, string'("ERROR"));
+            END IF;
+   
+            write(stdio, string'(": AXI Lite write of register "));
+            IF name(1) /= ' ' THEN
+               write(stdio, strip(name));
+            ELSE
+               write(stdio, (register_addr));
+            END IF;
+            write(stdio, string'(" returned "));
+       
+            -- Print response
+            IF axi_cipo.bresp = "00" THEN
+               write(stdio, string'("OK"));
+            ELSIF axi_cipo.bresp = "01" THEN
+               write(stdio, string'("exclusive access error "));
+               ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code exclusive access error" SEVERITY ERROR;
+            ELSIF axi_cipo.bresp = "10" THEN
+               write(stdio, string'("peripheral error "));
+               ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code peripheral error" SEVERITY ERROR;
+            ELSIF axi_cipo.bresp = "11" THEN
+               write(stdio, string'("address decode error "));
+               ASSERT NOT(fail_on_error and not expected_fail) REPORT "AXI LIte error code address decode error" SEVERITY ERROR;
+            END IF;
+       
+            writeline(output, stdio);
+             
+         end if;
+   
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.bready <= '0';
+         
+         wrCount := wrCount + 1;
+         
+      end loop;
+   
+   END PROCEDURE;   
+
+ ------------------------------------------------------------------------------
+ ------------------------------------------------------------------------------
+   PROCEDURE axi_lite_wait (SIGNAL mm_clk   : IN STD_LOGIC;
+                            SIGNAL axi_cipo : IN t_axi4_lite_cipo;
+                            SIGNAL axi_copi : OUT t_axi4_lite_copi;
+                            register_addr   : t_register_address;
+                            data            : STD_LOGIC_VECTOR(c_axi4_lite_data_w-1 DOWNTO 0);
+                            fail_on_error   : BOOLEAN := TRUE) is
+
+      VARIABLE response    : STD_LOGIC_VECTOR(1 DOWNTO 0);
+      VARIABLE stdio       : LINE;
+      VARIABLE timeout     : INTEGER := 100000;           -- 100K iteration limit, about 50M clocks
+   BEGIN
+
+      wait_loop: LOOP
+         -- Start transaction
+         WAIT UNTIL rising_edge(mm_clk);
+
+
+         -- Setup read address
+         axi_copi.arvalid <= '1';
+         axi_copi.araddr <= std_logic_vector(to_unsigned((register_addr.base_address+register_addr.address)*4, 32));
+         axi_copi.rready <= '1';
+
+         read_address_wait: LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+            IF axi_cipo.arready = '1' THEN
+               axi_copi.arvalid <= '0';
+               axi_copi.araddr <= (OTHERS => '0');
+            END IF;
+
+            IF axi_cipo.rvalid = '1' THEN
+               EXIT;
+            END IF;
+         END LOOP;
+
+         response := axi_cipo.rresp;
+
+         IF (axi_cipo.rdata(register_addr.offset+register_addr.width-1 DOWNTO register_addr.offset) = data(register_addr.width-1 DOWNTO 0)) OR response /= "00" THEN
+            EXIT;
+         END IF;
+
+         WAIT UNTIL rising_edge(mm_clk);
+         axi_copi.rready <= '0';
+
+         delay_loop: FOR i IN 0 TO 500 LOOP
+            WAIT UNTIL rising_edge(mm_clk);
+         END LOOP;
+         timeout := timeout - 1;
+         IF timeout = 0 THEN
+            EXIT;
+         END IF;
+      END LOOP;
+
+      IF timeout = 0 THEN
+         write(stdio, string'("ERROR: AXI Lite wait on register "));
+         write(stdio, strip(register_addr.name));
+         write(stdio, string'("failed"));
+         ASSERT not fail_on_error REPORT "AXI LIte wait timed out" SEVERITY ERROR;
+      ELSE
+
+         write(stdio, string'("INFO: AXI Lite wait on register "));
+         write(stdio, strip(register_addr.name));
+
+         write(stdio, string'(" completed with response "));
+
+         IF response = "00" THEN
+            write(stdio, string'("OK "));
+         ELSIF response = "01" THEN
+            write(stdio, string'("exclusive access error "));
+         ELSIF response = "10" THEN
+            write(stdio, string'("peripheral error "));
+         ELSIF response = "11" THEN
+            write(stdio, string'("address decode error "));
+         END IF;
+      END IF;
+
+
+      writeline(output, stdio);
+   END PROCEDURE;
+
+
+END axi4_lite_pkg;
diff --git a/libraries/base/axi4/src/vhdl/mem_to_axi4_lite.vhd b/libraries/base/axi4/src/vhdl/mem_to_axi4_lite.vhd
new file mode 100644
index 0000000000..5cb460b2f7
--- /dev/null
+++ b/libraries/base/axi4/src/vhdl/mem_to_axi4_lite.vhd
@@ -0,0 +1,213 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2023
+-- 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 : R vd Walle
+-- Purpose:  
+--   Translate AXI4-Lite to standard memory interface
+-- Description:
+--   Ported from:
+--   https://git.astron.nl/desp/gemini/-/blob/master/libraries/base/axi4/src/vhdl/mem_to_axi4_lite.vhd
+-------------------------------------------------------------------------------
+
+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 work.axi4_lite_pkg.ALL;
+
+ENTITY mem_to_axi4_lite IS
+   GENERIC (
+      g_adr_w     : NATURAL := 8;
+      g_dat_w     : NATURAL := 32;
+      g_timeout   : NATURAL := 6);        -- 2^clocks for transaction timeout. Needs to be longer than 3* slowest clock on AXI bus
+
+   PORT (
+      rst      : IN  STD_LOGIC;   -- reset synchronous with mm_clk
+      clk      : IN  STD_LOGIC;   -- memory-mapped bus clock
+
+      -- Memory Mapped Peripheral in mm_clk domain
+      axi4_lite_in   : IN  t_axi4_lite_copi;
+      axi4_lite_out  : OUT t_axi4_lite_cipo;
+
+      wren     : OUT STD_LOGIC;
+      rden     : OUT STD_LOGIC;
+
+      wr_adr   : OUT STD_LOGIC_VECTOR(g_adr_w-1 DOWNTO 0);
+      wr_dat   : OUT STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0);
+      rd_adr   : OUT STD_LOGIC_VECTOR(g_adr_w-1 DOWNTO 0);
+      rd_dat   : IN STD_LOGIC_VECTOR(g_dat_w-1 DOWNTO 0);
+      rd_busy  : IN STD_LOGIC;
+      rd_val   : IN STD_LOGIC;
+      wr_busy  : IN STD_LOGIC;
+      wr_val   : IN STD_LOGIC);
+END mem_to_axi4_lite;
+
+
+ARCHITECTURE str OF mem_to_axi4_lite IS
+
+   SIGNAL i_axi4_lite_out                 : t_axi4_lite_cipo;
+
+   SIGNAL write_pending             : STD_LOGIC := '0';
+   SIGNAL write_counter             : UNSIGNED(g_timeout-1 DOWNTO 0);
+   SIGNAL write_trans_valid         : STD_LOGIC;
+   SIGNAL i_wren_d                  : STD_LOGIC;
+   SIGNAL i_wren                    : STD_LOGIC;
+
+   SIGNAL read_pending              : STD_LOGIC := '0';
+   SIGNAL read_counter              : UNSIGNED(g_timeout-1 DOWNTO 0);
+   SIGNAL read_trans_valid          : STD_LOGIC;
+   SIGNAL i_rden                    : STD_LOGIC;
+   SIGNAL i_rden_d                  : STD_LOGIC;
+
+   SIGNAL rresp                     : STD_LOGIC_VECTOR(1 DOWNTO 0);
+   SIGNAL rresp_r                   : STD_LOGIC_VECTOR(1 DOWNTO 0);
+
+BEGIN
+
+
+   axi4_lite_out <= i_axi4_lite_out;
+
+
+---------------------------------------------------------------------------
+-- Write Channel --
+---------------------------------------------------------------------------
+
+
+   write_timeout: PROCESS(clk)
+   BEGIN
+      IF rising_edge(clk) THEN
+         i_wren_d <= i_wren;
+
+         IF write_pending = '0' THEN
+            IF axi4_lite_in.awvalid = '1' AND wr_busy = '0' THEN
+               write_counter <= (OTHERS => '1');
+               write_pending <= '1';
+            END IF;
+         ELSE
+            -- Once the whole transaction is complete release pending and allow next
+            IF i_axi4_lite_out.bvalid = '1' AND axi4_lite_in.bready = '1' THEN
+               write_pending <= '0';
+            ELSE
+               write_counter <= write_counter - 1;
+            END IF;
+         END IF;
+      END IF;
+   END PROCESS;
+
+   write_trans_valid <= '1' WHEN write_pending = '1' AND (write_counter = 0 OR wr_val = '1') ELSE '0';
+
+   -- Assert ready after the transaction has been (or should have been) acknowledged
+   i_axi4_lite_out.wready  <= write_trans_valid;
+   i_axi4_lite_out.awready <= write_trans_valid;
+
+   -- Write when data path and address path are valid (make it a single clock for neatness)
+   i_wren <= axi4_lite_in.wvalid AND axi4_lite_in.awvalid AND NOT wr_busy;
+   wren <= i_wren AND NOT i_wren_d;
+
+   wr_adr <= axi4_lite_in.awaddr(g_adr_w+1 DOWNTO 2);            -- Correct for byte addressing, ARSG uses dword addressing
+   wr_dat <= axi4_lite_in.wdata(g_dat_w-1 DOWNTO 0);
+
+   -- Need to latch response code in case ready is asserted on response bus
+   write_response_latch: PROCESS(CLK)
+   BEGIN
+      IF RISING_EDGE(CLK) THEN
+         IF RST = '1' THEN
+            i_axi4_lite_out.bvalid <= '0';
+         ELSE
+            IF i_axi4_lite_out.bvalid = '1' THEN
+               IF axi4_lite_in.bready = '1' THEN
+                  i_axi4_lite_out.bvalid <= '0';
+               END IF;
+            ELSE
+               IF write_trans_valid = '1' THEN
+                  i_axi4_lite_out.bvalid <= '1';
+                  IF wr_val = '1' THEN
+                     i_axi4_lite_out.bresp <= c_axi4_lite_resp_okay;
+                  ELSE
+                     i_axi4_lite_out.bresp <= c_axi4_lite_resp_slverr;
+                  END IF;
+               END IF;
+            END IF;
+         END IF;
+      END IF;
+   END PROCESS;
+
+---------------------------------------------------------------------------
+-- Read Channel --
+---------------------------------------------------------------------------
+
+   read_timeout: PROCESS(clk)
+   BEGIN
+      IF RISING_EDGE(clk) THEN
+         i_rden_d <= i_rden;
+
+         IF read_pending = '0' THEN
+            IF axi4_lite_in.arvalid = '1' AND rd_busy = '0' THEN
+               read_counter <= (OTHERS => '1');
+               read_pending <= '1';
+            END IF;
+         ELSE
+            IF read_trans_valid = '1' THEN
+               read_pending <= '0';
+            ELSE
+               read_counter <= read_counter - 1;
+            END IF;
+         END IF;
+      END IF;
+   END PROCESS;
+
+   read_trans_valid <= '1' WHEN read_pending = '1' and (read_counter = 0 or rd_val = '1') ELSE '0';
+
+   -- Acknowledge read when response is ready
+   i_axi4_lite_out.arready <= read_trans_valid;
+
+   -- Map read address bus
+   rd_adr  <= axi4_lite_in.araddr(g_adr_w+1 DOWNTO 2);
+
+   -- Read Enable when address is valid
+   i_rden <= read_pending;
+   rden <= i_rden and not(i_rden_d);
+
+   -- Assert data valid after the transaction has been (or should have been) acknowledged
+   i_axi4_lite_out.rvalid <= read_trans_valid;
+   i_axi4_lite_out.rdata(g_dat_w-1 DOWNTO 0) <= rd_dat;
+
+   -- If the address was decoded return OK otherwise error. Need to latch status as AXI clock
+   -- crossing IP for AXI4Lite assumes values are static after the valid is deasserted
+
+   rresp <= c_axi4_lite_resp_okay WHEN rd_val = '1' ELSE
+            c_axi4_lite_resp_slverr;
+
+   u_pipe_rresp : ENTITY common_lib.common_pipeline
+   GENERIC MAP (
+      g_pipeline    => 1,
+      g_in_dat_w    => 2,
+      g_out_dat_w   => 2)
+   PORT MAP (
+      clk         => clk,
+      clken       => read_trans_valid,
+      in_dat      => rresp,
+      out_dat     => rresp_r);
+
+      i_axi4_lite_out.rresp <= rresp WHEN read_trans_valid = '1' ELSE rresp_r;
+
+END str;
diff --git a/libraries/base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd b/libraries/base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd
new file mode 100644
index 0000000000..0200575c3d
--- /dev/null
+++ b/libraries/base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd
@@ -0,0 +1,191 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2023
+-- 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 : R vd Walle
+-- Purpose:  
+--   TB for testing mem_to_axi4_lite using common_ram_crw_crw
+-- Description:
+--   Ported from:
+--   https://git.astron.nl/desp/gemini/-/blob/master/libraries/base/axi4/tb/vhdl/tb_axi4_lite_ram.vhd
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE IEEE.std_logic_textio.ALL;
+USE STD.textio.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE work.axi4_lite_pkg.ALL;
+
+
+ENTITY tb_axi4_lite_ram IS
+END tb_axi4_lite_ram;
+
+ARCHITECTURE tb OF tb_axi4_lite_ram IS
+
+   CONSTANT mm_clk_period     : TIME := 40 ns;
+   CONSTANT usr_clk_period    : TIME := 10 ns;
+   CONSTANT c_reset_len       : NATURAL := 16;
+
+   CONSTANT dat_w             : INTEGER := 32;
+   CONSTANT adr_w             : INTEGER := 8;
+
+   CONSTANT c_mm_usr_ram      : t_c_mem :=   (latency     => 1,
+                                              adr_w	    => 5,
+                                              dat_w	    => 8,
+                                              nof_dat	    => 32,
+                                              init_sl     => '0');
+
+   CONSTANT ram_addr_base     : NATURAL := to_integer(shift_right(to_unsigned(0, 32), ceil_log2(c_mm_usr_ram.nof_dat))) ;
+
+   SIGNAL mm_rst              : STD_LOGIC;
+   SIGNAL mm_clk              : STD_LOGIC := '0';
+   SIGNAL usr_rst             : STD_LOGIC;
+   SIGNAL usr_clk             : STD_LOGIC := '0';
+   SIGNAL sim_finished        : STD_LOGIC := '0';
+   SIGNAL tb_end              : STD_LOGIC := '0';
+
+
+   SIGNAL rd_dat              : STD_LOGIC_VECTOR(dat_w-1 DOWNTO 0);
+   SIGNAL wr_dat              : STD_LOGIC_VECTOR(dat_w-1 DOWNTO 0);
+   SIGNAL wr_val              : STD_LOGIC;
+   SIGNAL rd_val              : STD_LOGIC;
+   SIGNAL reg_wren            : STD_LOGIC;
+   SIGNAL reg_rden            : STD_LOGIC;
+   SIGNAL wr_adr              : STD_LOGIC_VECTOR(adr_w-1 DOWNTO 0);
+   SIGNAL rd_adr              : STD_LOGIC_VECTOR(adr_w-1 DOWNTO 0);
+
+   SIGNAL ram_wr_en           : STD_LOGIC;
+   SIGNAL ram_rd_en           : STD_LOGIC;
+   SIGNAL ram_adr             : STD_LOGIC_VECTOR(c_mm_usr_ram.adr_w-1 DOWNTO 0);
+   SIGNAL ram_rd_dat          : STD_LOGIC_VECTOR(c_mm_usr_ram.dat_w-1 DOWNTO 0);
+
+   SIGNAL axi_copi            : t_axi4_lite_copi;
+   SIGNAL axi_cipo            : t_axi4_lite_cipo;
+
+BEGIN
+
+
+
+   mm_clk <= NOT mm_clk OR sim_finished  AFTER mm_clk_period/2;
+   mm_rst <= '1', '0'    AFTER mm_clk_period*c_reset_len;
+
+   usr_clk <= NOT usr_clk OR sim_finished  AFTER usr_clk_period/2;
+   usr_rst <= '1', '0'    AFTER usr_clk_period*c_reset_len;
+
+   u_mem_to_axi4_lite : ENTITY work.mem_to_axi4_lite
+   GENERIC MAP (
+      g_adr_w    => adr_w,
+      g_dat_w    => dat_w)
+   PORT MAP (
+      rst        => mm_rst,
+      clk        => mm_clk,
+      axi4_lite_in      => axi_copi,
+      axi4_lite_out     => axi_cipo,
+      wren       => reg_wren,
+      rden       => reg_rden,
+      wr_adr     => wr_adr,
+      wr_dat     => wr_dat,
+      wr_val     => wr_val,
+      wr_busy    => '0',
+      rd_adr     => rd_adr,
+      rd_dat     => rd_dat,
+      rd_busy    => '0',
+      rd_val     => rd_val);
+
+
+   ram_wr_en <= reg_wren AND is_true(ram_addr_base = unsigned(wr_adr(wr_adr'length-1 downto c_mm_usr_ram.adr_w)));
+   ram_rd_en <= reg_rden AND is_true(ram_addr_base = unsigned(rd_adr(rd_adr'length-1 downto c_mm_usr_ram.adr_w)));
+
+	ram_adr <= wr_adr(c_mm_usr_ram.adr_w-1 downto 0) WHEN ram_wr_en = '1' ELSE
+				  rd_adr(c_mm_usr_ram.adr_w-1 downto 0);
+
+   u_ram : ENTITY common_lib.common_ram_crw_crw
+   GENERIC MAP (
+      g_ram               => c_mm_usr_ram,
+      g_true_dual_port    => TRUE)
+   PORT MAP (
+      rst_a       => mm_rst,
+      rst_b       => usr_rst,
+      clk_a       => mm_clk,
+      clk_b       => usr_clk,
+      clken_a     => '1',
+      clken_b     => '1',
+      wr_en_a     => ram_wr_en,
+      wr_dat_a    => wr_dat(c_mm_usr_ram.dat_w-1 downto 0),
+      adr_a       => ram_adr,
+      rd_en_a     => ram_rd_en,
+      rd_dat_a    => ram_rd_dat,
+      rd_val_a    => rd_val,
+      wr_en_b     => '0',
+      wr_dat_b    => X"00",
+      adr_b       => "00000",
+      rd_en_b     => '0',
+      rd_dat_b    => OPEN,
+      rd_val_b    => OPEN
+   );
+
+    u_ram_wr_val : ENTITY common_lib.common_pipeline
+    GENERIC MAP (
+        g_pipeline   => c_mm_usr_ram.latency,
+        g_in_dat_w   => 1,
+        g_out_dat_w  => 1)
+    PORT MAP (
+        clk          => mm_clk,
+        clken        => '1',
+        in_dat(0)    => ram_wr_en,
+        out_dat(0)   => wr_val);
+
+   rd_dat <= "000000000000000000000000" & ram_rd_dat WHEN rd_val = '1' ELSE (OTHERS => '0');
+
+
+ --
+ tb : PROCESS
+
+      variable data_in     : t_slv_32_arr(0 TO 10);
+   BEGIN
+
+      axi_lite_init (mm_rst, mm_clk, axi_cipo, axi_copi);
+
+      -- Read and write a number of words to memory
+      for i in 0 to 10 loop
+         data_in(i) := std_logic_vector(to_unsigned(57+i, 32));
+      end loop;
+      axi_lite_transaction (mm_clk, axi_cipo, axi_copi, 0, true, data_in, mask => c_mask_zeros);
+
+
+      for i in 0 to 10 loop
+         data_in(i) := std_logic_vector(to_unsigned(57+i, 32));
+      end loop;
+      axi_lite_transaction (mm_clk, axi_cipo, axi_copi, 0, false, data_in, validate => true);
+
+
+
+      sim_finished <= '1';
+      tb_end <= '1';
+      wait for 1 us;
+      REPORT "Finished Simulation" SEVERITY FAILURE;
+      WAIT;
+ END PROCESS tb;
+
+
+END tb;
-- 
GitLab