Skip to content
Snippets Groups Projects
Commit 478ef67f authored by Reinier van der Walle's avatar Reinier van der Walle
Browse files

ported axi4-lite components from gemini

parent 28bfcd09
Branches
No related tags found
2 merge requests!319Resolve HPR-87,!315Resolve HPR-87
Pipeline #46034 failed
......@@ -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]
......
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
-------------------------------------------------------------------------------
--
-- 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;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment