Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/news.d/1240.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Improved behavior of AXI stream verification components on AXI stream reset:
- Any active transaction (`push`, `pop`, `check`, `wait_for_time`) is aborted asynchronously rather than synchronously.
- Pending messages in the VC inbox that relates to transactions on the stream can be either
left unaffected or be cancelled. This is controlled by the `reset_policy` parameter when calling
the VC `new` function.
- VC inbox messages related to VC configuration are never affected by the reset.
51 changes: 29 additions & 22 deletions vunit/vhdl/verification_components/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
from itertools import product
from vunit import VUnit

ROOT = Path(__file__).parent
root = Path(__file__).parent

UI = VUnit.from_argv()
UI.add_vhdl_builtins()
UI.add_random()
UI.add_verification_components()
ui = VUnit.from_argv()
ui.add_vhdl_builtins()
ui.add_random()
ui.add_verification_components()

LIB = UI.library("vunit_lib")
LIB.add_source_files(ROOT / "test" / "*.vhd")
lib = ui.library("vunit_lib")
lib.add_source_files(root / "test" / "*.vhd")


def encode(tb_cfg):
Expand Down Expand Up @@ -70,22 +70,22 @@ def gen_avalon_master_tests(obj, *args):
obj.add_config(name=config_name, generics=dict(encoded_tb_cfg=encode(tb_cfg)))


tb_avalon_slave = LIB.test_bench("tb_avalon_slave")
tb_avalon_slave = lib.test_bench("tb_avalon_slave")

for test in tb_avalon_slave.get_tests():
gen_avalon_tests(test, [32], [1, 2, 64], [1.0, 0.3], [0.0, 0.4])

tb_avalon_master = LIB.test_bench("tb_avalon_master")
tb_avalon_master = lib.test_bench("tb_avalon_master")

for test in tb_avalon_master.get_tests():
if test.name == "wr single rd single":
gen_avalon_master_tests(test, [1], [1.0], [0.0], [1.0], [1.0])
else:
gen_avalon_master_tests(test, [64], [1.0, 0.3], [0.0, 0.7], [1.0, 0.3], [1.0, 0.3])

TB_WISHBONE_SLAVE = LIB.test_bench("tb_wishbone_slave")
tb_wishbone_slave = lib.test_bench("tb_wishbone_slave")

for test in TB_WISHBONE_SLAVE.get_tests():
for test in tb_wishbone_slave.get_tests():
# TODO strobe_prob not implemented in slave tb
gen_wb_tests(
test,
Expand All @@ -100,9 +100,9 @@ def gen_avalon_master_tests(obj, *args):
)


TB_WISHBONE_MASTER = LIB.test_bench("tb_wishbone_master")
tb_wishbone_master = lib.test_bench("tb_wishbone_master")

for test in TB_WISHBONE_MASTER.get_tests():
for test in tb_wishbone_master.get_tests():
if test.name == "slave comb ack":
gen_wb_tests(
test,
Expand All @@ -129,13 +129,13 @@ def gen_avalon_master_tests(obj, *args):
)


TB_AXI_STREAM = LIB.test_bench("tb_axi_stream")
tb_axi_stream = lib.test_bench("tb_axi_stream")

for id_length in [0, 8]:
for dest_length in [0, 8]:
for user_length in [0, 8]:
for data_length in [8, 16]:
for test in TB_AXI_STREAM.get_tests("*check"):
for test in tb_axi_stream.get_tests("*check"):
test.add_config(
name=f"id_l={id_length} dest_l={dest_length} user_l={user_length} data_l={data_length}",
generics=dict(
Expand All @@ -146,22 +146,29 @@ def gen_avalon_master_tests(obj, *args):
),
)

TB_AXI_STREAM.test("test passing with no tkeep").set_generic("g_data_length", 16)
tb_axi_stream.test("test passing with no tkeep").set_generic("g_data_length", 16)

TB_AXI_STREAM_PROTOCOL_CHECKER = LIB.test_bench("tb_axi_stream_protocol_checker")
for reset_policy_value in [0, 1]:
tb_axi_stream.test("test reset of transactions").add_config(
name="abort_all_transactions" if reset_policy_value == 0 else "abort_active_transaction",
generics=dict(reset_policy_value=reset_policy_value),
)


tb_axi_stream_protocol_checker = lib.test_bench("tb_axi_stream_protocol_checker")

for data_length in [0, 8, 32]:
for test in TB_AXI_STREAM_PROTOCOL_CHECKER.get_tests("*passing*tdata*"):
for test in tb_axi_stream_protocol_checker.get_tests("*passing*tdata*"):
test.add_config(name="data_length=%d" % data_length, generics=dict(data_length=data_length))

for test in TB_AXI_STREAM_PROTOCOL_CHECKER.get_tests("*failing*tid width*"):
for test in tb_axi_stream_protocol_checker.get_tests("*failing*tid width*"):
test.add_config(name="dest_length=25", generics=dict(dest_length=25))
test.add_config(name="id_length=8 dest_length=17", generics=dict(id_length=8, dest_length=17))

TEST_FAILING_MAX_WAITS = TB_AXI_STREAM_PROTOCOL_CHECKER.test(
test_failing_max_waits = tb_axi_stream_protocol_checker.test(
"Test failing check of that tready comes within max_waits after valid"
)
for max_waits in [0, 8]:
TEST_FAILING_MAX_WAITS.add_config(name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits))
test_failing_max_waits.add_config(name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits))

UI.main()
ui.main()
52 changes: 45 additions & 7 deletions vunit/vhdl/verification_components/src/axi_stream_master.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ begin
variable inactive_bus_policy : inactive_bus_policy_t;
variable axi_stream_signal : axi_stream_signal_t;
variable stall_config : integer_vector_ptr_t;
variable delay : delay_length;

impure function get_inactive_axi_stream_policy(master : axi_stream_master_t) return inactive_axi_stream_policy_t is
impure function to_inactive_axi_stream_policy(vec : integer_vector_ptr_t) return inactive_axi_stream_policy_t is
Expand Down Expand Up @@ -140,6 +141,11 @@ begin
return p_to_stall_config(to_integer_vector_ptr(get(master.p_config, p_stall_config_idx)));
end;

impure function get_reset_policy(master : axi_stream_master_t) return axi_stream_reset_policy_t is
begin
return axi_stream_reset_policy_t'val(get(master.p_config, p_reset_policy_idx));
end;

procedure drive_inactive(
signal l_tdata : out std_logic_vector(data_length(master)-1 downto 0);
signal l_tlast : out std_logic;
Expand Down Expand Up @@ -192,27 +198,57 @@ begin
drive_policy(l_tuser, inactive_axi_stream_policy(work.axi_stream_pkg.tuser));
end procedure;

procedure flush_pending_transactions(queue : queue_t) is
constant total_length : natural := length(queue);
variable consumed_length : natural := 0;
variable before_pop_length : natural;
variable msg : msg_t;
variable msg_type : msg_type_t;
begin
while consumed_length < total_length loop
before_pop_length := length(queue);
msg := pop(queue);
consumed_length := consumed_length + (before_pop_length - length(queue));

-- Messages to keep are pushed back into the queue.
msg_type := message_type(msg);
if msg_type = stream_push_msg or msg_type = push_axi_stream_msg or msg_type = wait_for_time_msg then
null;
else
push(message_queue, msg);
end if;
end loop;
end;

begin
rnd.InitSeed(rnd'instance_name);
loop
drive_inactive(tdata, tlast, tkeep, tstrb, tid, tdest, tuser);
if areset_n = '0' then
tvalid <= '0';
wait until areset_n = '1' and rising_edge(aclk);
if get_reset_policy(master) = abort_all_transactions then
flush_pending_transactions(message_queue);
end if;
else
if is_empty(message_queue) then
-- Wait for messages to arrive on the queue, posted by the process above
wait until (not is_empty(message_queue) or areset_n = '0') and rising_edge(aclk);
wait until areset_n = '0' or (not is_empty(message_queue) and rising_edge(aclk));
end if;

while not is_empty(message_queue) loop
while not is_empty(message_queue) and areset_n = '1' loop
msg := pop(message_queue);
msg_type := message_type(msg);

if msg_type = wait_for_time_msg then
handle_sync_message(net, msg_type, msg);
-- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment.
wait until rising_edge(aclk);
handle_message(msg_type);
delay := pop_time(msg);
wait until areset_n = '0' for delay;

if areset_n /= '0' then
-- Re-align with the clock when a wait for time message was handled, because this breaks edge alignment.
wait until rising_edge(aclk);
end if;

elsif msg_type = notify_request_msg then
-- Ignore this message, but expect it
Expand All @@ -238,7 +274,7 @@ begin
tdest <= (others => '0');
tuser <= (others => '0');
end if;
wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk);
wait until areset_n = '0' or ((tvalid and tready) = '1' and rising_edge(aclk));
tvalid <= '0';

elsif msg_type = set_inactive_axi_stream_policy_msg then
Expand Down Expand Up @@ -271,7 +307,9 @@ begin
delete(msg);
end loop;

notify(bus_process_done);
if is_empty(message_queue) then
notify(bus_process_done);
end if;
end if;
end loop;
end process;
Expand Down
27 changes: 19 additions & 8 deletions vunit/vhdl/verification_components/src/axi_stream_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@ package axi_stream_pkg is
type inactive_axi_stream_policy_t is array (tdata to tuser) of inactive_bus_policy_t;

-- The standard protocol checker requires tuser to be a known value when the reset is released
constant default_axi_stream_policy : inactive_axi_stream_policy_t := (tuser => '0', others => 'X');
constant default_inactive_axi_stream_policy : inactive_axi_stream_policy_t := (tuser => '0', others => 'X');
constant all_0_policy : inactive_axi_stream_policy_t := (others => '0');
constant all_1_policy : inactive_axi_stream_policy_t := (others => '1');
constant all_x_policy : inactive_axi_stream_policy_t := (others => 'X');
constant all_hold_policy : inactive_axi_stream_policy_t := (others => hold);

type axi_stream_component_type_t is (null_component, default_component, custom_component);
-- abort_all_transactions will abort any active transaction and any pending transaction in the VC
-- inbox. Note that only pending messages related to bus transactions are affected. Other messages,
-- such as those related to VC configurations, will not be deleted.
type axi_stream_reset_policy_t is (abort_all_transactions, abort_active_transaction);

type axi_stream_component_type_t is (null_component, default_component, custom_component);

type axi_stream_protocol_checker_t is record
p_type : axi_stream_component_type_t;
Expand Down Expand Up @@ -184,7 +188,8 @@ package axi_stream_pkg is
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker;
inactive_policy : inactive_axi_stream_policy_t := default_axi_stream_policy
inactive_policy : inactive_axi_stream_policy_t := default_inactive_axi_stream_policy;
reset_policy : axi_stream_reset_policy_t := abort_all_transactions
) return axi_stream_master_t;

impure function new_axi_stream_slave(
Expand All @@ -196,7 +201,8 @@ package axi_stream_pkg is
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker;
reset_policy : axi_stream_reset_policy_t := abort_all_transactions
) return axi_stream_slave_t;

impure function new_axi_stream_monitor(
Expand Down Expand Up @@ -400,6 +406,7 @@ package axi_stream_pkg is
-- Private
constant p_stall_config_idx : natural := 0;
constant p_inactive_policy_idx : natural := 1;
constant p_reset_policy_idx : natural := 2;
impure function p_to_stall_config(vec : integer_vector_ptr_t) return stall_config_t;

end package;
Expand Down Expand Up @@ -529,7 +536,8 @@ package body axi_stream_pkg is
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker;
inactive_policy : inactive_axi_stream_policy_t := default_axi_stream_policy
inactive_policy : inactive_axi_stream_policy_t := default_inactive_axi_stream_policy;
reset_policy : axi_stream_reset_policy_t := abort_all_transactions
) return axi_stream_master_t is
variable p_actor : actor_t;
variable p_monitor : axi_stream_monitor_t;
Expand All @@ -552,11 +560,12 @@ package body axi_stream_pkg is
p_logger => logger,
p_monitor => p_monitor,
p_protocol_checker => p_protocol_checker,
p_config => new_integer_vector_ptr(p_inactive_policy_idx + 1)
p_config => new_integer_vector_ptr(p_reset_policy_idx + 1)
);

set(handle.p_config, p_stall_config_idx, to_integer(to_integer_vector_ptr(stall_config)));
set(handle.p_config, p_inactive_policy_idx, to_integer(to_integer_vector_ptr(inactive_policy)));
set(handle.p_config, p_reset_policy_idx, axi_stream_reset_policy_t'pos(reset_policy));

return handle;
end;
Expand All @@ -570,7 +579,8 @@ package body axi_stream_pkg is
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker
protocol_checker : axi_stream_protocol_checker_t := null_axi_stream_protocol_checker;
reset_policy : axi_stream_reset_policy_t := abort_all_transactions
) return axi_stream_slave_t is
variable p_actor : actor_t;
variable p_monitor : axi_stream_monitor_t;
Expand All @@ -593,9 +603,10 @@ package body axi_stream_pkg is
p_logger => logger,
p_monitor => p_monitor,
p_protocol_checker => p_protocol_checker,
p_config => new_integer_vector_ptr(p_stall_config_idx + 1));
p_config => new_integer_vector_ptr(p_reset_policy_idx + 1));

set(handle.p_config, p_stall_config_idx, to_integer(to_integer_vector_ptr(stall_config)));
set(handle.p_config, p_reset_policy_idx, axi_stream_reset_policy_t'pos(reset_policy));

return handle;
end;
Expand Down
Loading
Loading