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