Skip to content

Commit

Permalink
Drive invalid in AXI VCs
Browse files Browse the repository at this point in the history
When the 'valid' signal is low for an AXI (-Lite) channel, the signals of the channel should be driven with 'X'.
They should not keep their value from the last time the channel was valid.
This can hide errors in the DUT if the DUT samples values in the wrong clock cycle.

This change is largely inspired by the AXI-Stream VCs.
  • Loading branch information
LukasVik committed Oct 10, 2023
1 parent 7d7ae16 commit 79da503
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 72 deletions.
163 changes: 105 additions & 58 deletions vunit/vhdl/verification_components/src/axi_lite_master.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ context work.com_context;
context work.vunit_context;

entity axi_lite_master is
generic(
bus_handle : bus_master_t
generic (
bus_handle : bus_master_t;
drive_invalid : boolean := true;
drive_invalid_val : std_logic := 'X'
);
port(
port (
aclk : in std_logic;

arready : in std_logic;
Expand All @@ -45,13 +47,15 @@ entity axi_lite_master is

bvalid : in std_logic;
bready : out std_logic := '0';
bresp : in axi_resp_t := axi_resp_okay);
bresp : in axi_resp_t := axi_resp_okay
);
end entity;

architecture a of axi_lite_master is
constant reply_queue, message_queue : queue_t := new_queue;
signal idle : boolean := true;
begin

main : process
variable request_msg : msg_t;
variable msg_type : msg_type_t;
Expand All @@ -73,77 +77,120 @@ begin

-- Use separate process to always align to rising edge of clock
bus_process : process
procedure drive_ar_invalid is
begin
if drive_invalid then
araddr <= (araddr'range => drive_invalid_val);
end if;
end procedure;

procedure drive_aw_invalid is
begin
if drive_invalid then
awaddr <= (awaddr'range => drive_invalid_val);
end if;
end procedure;

procedure drive_w_invalid is
begin
if drive_invalid then
wdata <= (wdata'range => drive_invalid_val);
wstrb <= (wstrb'range => drive_invalid_val);
end if;
end procedure;

variable request_msg : msg_t;
variable msg_type : msg_type_t;
variable w_done, aw_done : boolean;
variable expected_resp : axi_resp_t;
variable w_done, aw_done : boolean;

-- These variables are needed to keep the values for logging when transaction is fully done
variable addr_this_transaction : std_logic_vector(awaddr'range) := (others => '0');
variable wdata_this_transaction : std_logic_vector(wdata'range) := (others => '0');
begin
wait until rising_edge(aclk) and not is_empty(message_queue);
idle <= false;
wait for 0 ps;
-- Initialization
drive_ar_invalid;
drive_aw_invalid;
drive_w_invalid;

loop
wait until rising_edge(aclk) and not is_empty(message_queue);
idle <= false;
wait for 0 ps;

request_msg := pop(message_queue);
msg_type := message_type(request_msg);

if is_read(msg_type) then
addr_this_transaction := pop_std_ulogic_vector(request_msg);
expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay;
push(reply_queue, request_msg);

araddr <= addr_this_transaction;

arvalid <= '1';
wait until (arvalid and arready) = '1' and rising_edge(aclk);
arvalid <= '0';
drive_ar_invalid;

rready <= '1';
wait until (rvalid and rready) = '1' and rising_edge(aclk);
rready <= '0';
check_axi_resp(bus_handle, rresp, expected_resp, "rresp");

if is_visible(bus_handle.p_logger, debug) then
debug(bus_handle.p_logger,
"Read 0x" & to_hstring(rdata) &
" from address 0x" & to_hstring(addr_this_transaction));
end if;

request_msg := pop(message_queue);
msg_type := message_type(request_msg);
elsif is_write(msg_type) then
addr_this_transaction := pop_std_ulogic_vector(request_msg);
wdata_this_transaction := pop_std_ulogic_vector(request_msg);
wstrb <= pop_std_ulogic_vector(request_msg);
expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay;
delete(request_msg);

if is_read(msg_type) then
araddr <= pop_std_ulogic_vector(request_msg);
expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay;
push(reply_queue, request_msg);
awaddr <= addr_this_transaction;
wdata <= wdata_this_transaction;

arvalid <= '1';
wait until (arvalid and arready) = '1' and rising_edge(aclk);
arvalid <= '0';
wvalid <= '1';
awvalid <= '1';

rready <= '1';
wait until (rvalid and rready) = '1' and rising_edge(aclk);
rready <= '0';
check_axi_resp(bus_handle, rresp, expected_resp, "rresp");
w_done := false;
aw_done := false;
while not (w_done and aw_done) loop
wait until ((awvalid and awready) = '1' or (wvalid and wready) = '1') and rising_edge(aclk);

if is_visible(bus_handle.p_logger, debug) then
debug(bus_handle.p_logger,
"Read 0x" & to_hstring(rdata) &
" from address 0x" & to_hstring(araddr));
end if;
if (awvalid and awready) = '1' then
awvalid <= '0';
drive_aw_invalid;

elsif is_write(msg_type) then
awaddr <= pop_std_ulogic_vector(request_msg);
wdata <= pop_std_ulogic_vector(request_msg);
wstrb <= pop_std_ulogic_vector(request_msg);
expected_resp := pop_std_ulogic_vector(request_msg) when is_axi_lite_msg(msg_type) else axi_resp_okay;
delete(request_msg);
aw_done := true;
end if;

wvalid <= '1';
awvalid <= '1';
if (wvalid and wready) = '1' then
wvalid <= '0';
drive_w_invalid;

w_done := false;
aw_done := false;
while not (w_done and aw_done) loop
wait until ((awvalid and awready) = '1' or (wvalid and wready) = '1') and rising_edge(aclk);
w_done := true;
end if;
end loop;

if (awvalid and awready) = '1' then
awvalid <= '0';
aw_done := true;
end if;
bready <= '1';
wait until (bvalid and bready) = '1' and rising_edge(aclk);
bready <= '0';
check_axi_resp(bus_handle, bresp, expected_resp, "bresp");

if (wvalid and wready) = '1' then
wvalid <= '0';
w_done := true;
if is_visible(bus_handle.p_logger, debug) then
debug(bus_handle.p_logger,
"Wrote 0x" & to_hstring(wdata_this_transaction) &
" to address 0x" & to_hstring(addr_this_transaction));
end if;
end loop;

bready <= '1';
wait until (bvalid and bready) = '1' and rising_edge(aclk);
bready <= '0';
check_axi_resp(bus_handle, bresp, expected_resp, "bresp");

if is_visible(bus_handle.p_logger, debug) then
debug(bus_handle.p_logger,
"Wrote 0x" & to_hstring(wdata) &
" to address 0x" & to_hstring(awaddr));
end if;
end if;

idle <= true;
idle <= true;
end loop;
end process;

-- Reply in separate process do not destroy alignment with the clock
Expand Down
33 changes: 25 additions & 8 deletions vunit/vhdl/verification_components/src/axi_read_slave.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ context work.vc_context;

entity axi_read_slave is
generic (
axi_slave : axi_slave_t);
axi_slave : axi_slave_t;
drive_invalid : boolean := true;
drive_invalid_val : std_logic := 'X'
);
port (
aclk : in std_logic;

Expand All @@ -34,7 +37,7 @@ entity axi_read_slave is
rdata : out std_logic_vector;
rresp : out axi_resp_t;
rlast : out std_logic
);
);
end entity;

architecture a of axi_read_slave is
Expand All @@ -51,6 +54,17 @@ begin
end process;

axi_process : process

procedure drive_r_invalid is
begin
if drive_invalid then
rid <= (rid'range => drive_invalid_val);
rdata <= (rdata'range => drive_invalid_val);
rresp <= (rresp'range => drive_invalid_val);
rlast <= drive_invalid_val;
end if;
end procedure;

variable input_burst, burst : axi_burst_t;
variable address : integer;
variable idx : integer;
Expand All @@ -60,17 +74,17 @@ begin
variable has_response_time : boolean := false;
begin
assert arid'length = rid'length report "arid vs rid data width mismatch";

-- Initialization
rid <= (rid'range => '0');
rdata <= (rdata'range => '0');
rresp <= (rresp'range => '0');
rlast <= '0';
drive_r_invalid;

wait on initialized until initialized;

loop
if (rready and rvalid) = '1' then
rvalid <= '0';
drive_r_invalid;

beats := beats - 1;
end if;

Expand All @@ -90,19 +104,22 @@ begin
has_response_time := false;
burst := self.pop_burst;
beats := burst.length;
rid <= std_logic_vector(to_unsigned(burst.id, rid'length));
rresp <= axi_resp_okay;
address := burst.address;
end if;
end if;

if beats > 0 and (rvalid = '0' or rready = '1') and not self.should_stall_data then
rvalid <= '1';

rid <= std_logic_vector(to_unsigned(burst.id, rid'length));

for j in 0 to burst.size-1 loop
idx := (address + j) mod self.data_size;
rdata(8*idx+7 downto 8*idx) <= std_logic_vector(to_unsigned(read_byte(axi_slave.p_memory, address+j), 8));
end loop;

rresp <= axi_resp_okay;

if burst.burst_type = axi_burst_type_incr then
address := address + burst.size;
end if;
Expand Down
23 changes: 17 additions & 6 deletions vunit/vhdl/verification_components/src/axi_write_slave.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ context work.com_context;

entity axi_write_slave is
generic (
axi_slave : axi_slave_t);
axi_slave : axi_slave_t;
drive_invalid : boolean := true;
drive_invalid_val : std_logic := 'X'
);
port (
aclk : in std_logic;

Expand All @@ -41,7 +44,7 @@ entity axi_write_slave is
bready : in std_logic;
bid : out std_logic_vector;
bresp : out axi_resp_t
);
);
end entity;

architecture a of axi_write_slave is
Expand Down Expand Up @@ -98,6 +101,14 @@ begin

axi_process : process

procedure drive_b_invalid is
begin
if drive_invalid then
bid <= (bid'range => drive_invalid_val);
bresp <= (bresp'range => drive_invalid_val);
end if;
end procedure;

procedure record_input_data(variable input_data : inout burst_data_t;
address : natural; byte : natural) is
variable ignored : boolean;
Expand Down Expand Up @@ -132,19 +143,19 @@ begin
variable response_time : time;
variable has_response_time : boolean := false;
begin
-- Initialization
bid <= (bid'range => '0');
bresp <= (bresp'range => '0');

assert awid'length = bid'length report "arwid vs wid data width mismatch";
assert (awlen'length = 4 or
awlen'length = 8) report "awlen must be either 4 (AXI3) or 8 (AXI4)";

-- Initialization
drive_b_invalid;

wait on initialized until initialized;

loop
if bready = '1' then
bvalid <= '0';
drive_b_invalid;
end if;

if (awvalid and awready) = '1' then
Expand Down
24 changes: 24 additions & 0 deletions vunit/vhdl/verification_components/test/tb_axi_lite_master.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,30 @@ begin
end if;
end process;

check_not_valid : process
constant a_addr_invalid_value : std_logic_vector(araddr'range) := (others => 'X');
constant wdata_invalid_value : std_logic_vector(wdata'range) := (others => 'X');
constant wstrb_invalid_value : std_logic_vector(wstrb'range) := (others => 'X');
begin
wait until rising_edge(clk);

-- All signals should be driven with 'X' when the channel is not valid
-- (R and B channels have no outputs from the VC, except for handshake).

if not arvalid then
check_equal(araddr, a_addr_invalid_value, "ARADDR not X when ARVALID low");
end if;

if not awvalid then
check_equal(awaddr, a_addr_invalid_value, "AWADDR not X when AWVALID low");
end if;

if not wvalid then
check_equal(wdata, wdata_invalid_value, "WDATA not X when WVALID low");
check_equal(wstrb, wstrb_invalid_value, "WSTRB not X when WVALID low");
end if;
end process;

dut : entity work.axi_lite_master
generic map (
bus_handle => bus_handle)
Expand Down
Loading

0 comments on commit 79da503

Please sign in to comment.