Skip to content

Commit

Permalink
add possibility to configure random stalls for axi_stream (#557)
Browse files Browse the repository at this point in the history
* Added possibility to configure random stalls

At creation of master/slave one can provide a configuration which tells
how often a stall should occure, and if it occures what the
minimal/maximal stall length of such a stall shall be.

* Added lines for linter

* Removed whitespaces and added check for zero stall activity where not expected

* replaced configuration functions by constants configured by generic

* uncommented disabled checks

* fixed ordering of the signals in if statement and used VHDL 2008

* Fixed order of generics to be backwards compatible

based on pull request comment of 1138-4EB

* Change of stall generics to provide [0-100]% stall

* replaces stat signals with records

* only include RandomPType with use instead of all

* made random variable privat to process

* moved procedures that are private to a private package

* give meaningful name to generics to avoid comment

* fixed return value for p_actor in new_axi_stream_slave

Changed new_actor to p_actor

* added procedure drive_invalid_output

* replaced for loop with direct call to test case

* introduced new_stall_config function

* unified random stall test cases for axi_stream

* removed explicit numbers in test case

this makes it more general and percentage numbers can be varied through
the generics without having to manual change the vhdl code.

* replaced if statement for min/max evaluation by min/max functions

* reformated using black

* fixed typo

* fixed formating issues

* Fixed year of license text in axi_stream_private_pkg.vhd

* uppercased test bench variable
  • Loading branch information
dstadelm authored Feb 10, 2020
1 parent 1bc9c37 commit a7f2bbe
Show file tree
Hide file tree
Showing 6 changed files with 373 additions and 122 deletions.
7 changes: 7 additions & 0 deletions vunit/vhdl/verification_components/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,12 @@ def gen_avalon_master_tests(obj, *args):
name="max_waits=%d" % max_waits, generics=dict(max_waits=max_waits)
)

TB_AXI_STREAM.test("test random stall on master").add_config(
name="stall_master", generics=dict(g_stall_percentage_master=30)
)

TB_AXI_STREAM.test("test random stall on slave").add_config(
name="stall_slave", generics=dict(g_stall_percentage_slave=30)
)

ui.main()
141 changes: 82 additions & 59 deletions vunit/vhdl/verification_components/src/axi_stream_master.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ context work.vunit_context;
context work.com_context;
use work.stream_master_pkg.all;
use work.axi_stream_pkg.all;
use work.axi_stream_private_pkg.all;
use work.queue_pkg.all;
use work.sync_pkg.all;

library osvvm;
use osvvm.RandomPkg.RandomPType;

entity axi_stream_master is
generic (
master : axi_stream_master_t;
Expand Down Expand Up @@ -42,6 +46,22 @@ architecture a of axi_stream_master is
constant message_queue : queue_t := new_queue;
signal notify_bus_process_done : std_logic := '0';

procedure drive_invalid_output(signal l_tdata : out std_logic_vector(data_length(master)-1 downto 0);
signal l_tkeep : out std_logic_vector(data_length(master)/8-1 downto 0);
signal l_tstrb : out std_logic_vector(data_length(master)/8-1 downto 0);
signal l_tid : out std_logic_vector(id_length(master)-1 downto 0);
signal l_tdest : out std_logic_vector(dest_length(master)-1 downto 0);
signal l_tuser : out std_logic_vector(user_length(master)-1 downto 0))
is
begin
l_tdata <= (others => drive_invalid_val);
l_tkeep <= (others => drive_invalid_val);
l_tstrb <= (others => drive_invalid_val);
l_tid <= (others => drive_invalid_val);
l_tdest <= (others => drive_invalid_val);
l_tuser <= (others => drive_invalid_val_user);
end procedure;

begin

main : process
Expand Down Expand Up @@ -69,68 +89,71 @@ begin
bus_process : process
variable msg : msg_t;
variable msg_type : msg_type_t;
variable rnd : RandomPType;
begin
if drive_invalid then
tdata <= (others => drive_invalid_val);
tkeep <= (others => drive_invalid_val);
tstrb <= (others => drive_invalid_val);
tid <= (others => drive_invalid_val);
tdest <= (others => drive_invalid_val);
tuser <= (others => drive_invalid_val_user);
end if;

-- Wait for messages to arrive on the queue, posted by the process above
wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0');

if (areset_n = '0') then
tvalid <= '0';
else
while not is_empty(message_queue) 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);
elsif msg_type = notify_request_msg then
-- Ignore this message, but expect it
elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then
tvalid <= '1';
tdata <= pop_std_ulogic_vector(msg);
if msg_type = push_axi_stream_msg then
tlast <= pop_std_ulogic(msg);
tkeep <= pop_std_ulogic_vector(msg);
tstrb <= pop_std_ulogic_vector(msg);
tid <= pop_std_ulogic_vector(msg);
tdest <= pop_std_ulogic_vector(msg);
tuser <= pop_std_ulogic_vector(msg);
else
if pop_boolean(msg) then
tlast <= '1';
rnd.InitSeed(rnd'instance_name);
loop
if drive_invalid then
drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser);
end if;

-- Wait for messages to arrive on the queue, posted by the process above
wait until rising_edge(aclk) and (not is_empty(message_queue) or areset_n = '0');

if (areset_n = '0') then
tvalid <= '0';
else
while not is_empty(message_queue) 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);
elsif msg_type = notify_request_msg then
-- Ignore this message, but expect it
elsif msg_type = stream_push_msg or msg_type = push_axi_stream_msg then
drive_invalid_output(tdata, tkeep, tstrb, tid, tdest, tuser);
-- stall according to probability configuration
probability_stall_axi_stream(aclk, master, rnd);

tvalid <= '1';
tdata <= pop_std_ulogic_vector(msg);
if msg_type = push_axi_stream_msg then
tlast <= pop_std_ulogic(msg);
tkeep <= pop_std_ulogic_vector(msg);
tstrb <= pop_std_ulogic_vector(msg);
tid <= pop_std_ulogic_vector(msg);
tdest <= pop_std_ulogic_vector(msg);
tuser <= pop_std_ulogic_vector(msg);
else
tlast <= '0';
if pop_boolean(msg) then
tlast <= '1';
else
tlast <= '0';
end if;
tkeep <= (others => '1');
tstrb <= (others => '1');
tid <= (others => '0');
tdest <= (others => '0');
tuser <= (others => '0');
end if;
tkeep <= (others => '1');
tstrb <= (others => '1');
tid <= (others => '0');
tdest <= (others => '0');
tuser <= (others => '0');
wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk);
tvalid <= '0';
tlast <= '0';
else
unexpected_msg_type(msg_type);
end if;
wait until ((tvalid and tready) = '1' or areset_n = '0') and rising_edge(aclk);
tvalid <= '0';
tlast <= '0';
else
unexpected_msg_type(msg_type);
end if;

delete(msg);
end loop;

notify_bus_process_done <= '1';
wait until notify_bus_process_done = '1';
notify_bus_process_done <= '0';
end if;

delete(msg);
end loop;

notify_bus_process_done <= '1';
wait until notify_bus_process_done = '1';
notify_bus_process_done <= '0';
end if;
end loop;
end process;

axi_stream_monitor_generate : if master.p_monitor /= null_axi_stream_monitor generate
Expand Down Expand Up @@ -171,4 +194,4 @@ begin
);
end generate axi_stream_protocol_checker_generate;

end architecture;
end architecture;
41 changes: 40 additions & 1 deletion vunit/vhdl/verification_components/src/axi_stream_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ context work.data_types_context;

package axi_stream_pkg is

type stall_config_t is record
stall_probability : real range 0.0 to 1.0;
min_stall_cycles : natural;
max_stall_cycles : natural;
end record;

constant null_stall_config : stall_config_t := (
stall_probability => 0.0,
min_stall_cycles => 0,
max_stall_cycles => 0
);

type axi_stream_component_type_t is (null_component, default_component, custom_component);

type axi_stream_protocol_checker_t is record
Expand Down Expand Up @@ -99,6 +111,7 @@ package axi_stream_pkg is
p_id_length : natural;
p_dest_length : natural;
p_user_length : natural;
p_stall_config : stall_config_t;
p_logger : logger_t;
p_monitor : axi_stream_monitor_t;
p_protocol_checker : axi_stream_protocol_checker_t;
Expand All @@ -110,6 +123,7 @@ package axi_stream_pkg is
p_id_length : natural;
p_dest_length : natural;
p_user_length : natural;
p_stall_config : stall_config_t;
p_logger : logger_t;
p_monitor : axi_stream_monitor_t;
p_protocol_checker : axi_stream_protocol_checker_t;
Expand All @@ -123,6 +137,7 @@ package axi_stream_pkg is
id_length : natural := 0;
dest_length : natural := 0;
user_length : natural := 0;
stall_config : stall_config_t := null_stall_config;
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
Expand All @@ -134,6 +149,7 @@ package axi_stream_pkg is
id_length : natural := 0;
dest_length : natural := 0;
user_length : natural := 0;
stall_config : stall_config_t := null_stall_config;
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
Expand Down Expand Up @@ -285,6 +301,12 @@ package axi_stream_pkg is
variable msg : inout msg_t;
variable axi_transaction : out axi_stream_transaction_t);

function new_stall_config(
stall_probability : real range 0.0 to 1.0;
min_stall_cycles : natural;
max_stall_cycles : natural
) return stall_config_t;

end package;

package body axi_stream_pkg is
Expand Down Expand Up @@ -343,6 +365,7 @@ package body axi_stream_pkg is
id_length : natural := 0;
dest_length : natural := 0;
user_length : natural := 0;
stall_config : stall_config_t := null_stall_config;
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
Expand All @@ -361,6 +384,7 @@ package body axi_stream_pkg is
p_id_length => id_length,
p_dest_length => dest_length,
p_user_length => user_length,
p_stall_config => stall_config,
p_logger => logger,
p_monitor => p_monitor,
p_protocol_checker => p_protocol_checker);
Expand All @@ -371,6 +395,7 @@ package body axi_stream_pkg is
id_length : natural := 0;
dest_length : natural := 0;
user_length : natural := 0;
stall_config : stall_config_t := null_stall_config;
logger : logger_t := axi_stream_logger;
actor : actor_t := null_actor;
monitor : axi_stream_monitor_t := null_axi_stream_monitor;
Expand All @@ -384,11 +409,12 @@ package body axi_stream_pkg is
p_actor := actor when actor /= null_actor else new_actor;
p_protocol_checker := get_valid_protocol_checker(data_length, id_length, dest_length, user_length, logger, actor, protocol_checker, "slave");

return (p_actor => new_actor,
return (p_actor => p_actor,
p_data_length => data_length,
p_id_length => id_length,
p_dest_length => dest_length,
p_user_length => user_length,
p_stall_config => stall_config,
p_logger => logger,
p_monitor => p_monitor,
p_protocol_checker => p_protocol_checker);
Expand Down Expand Up @@ -780,4 +806,17 @@ package body axi_stream_pkg is
end if;
end;

function new_stall_config(
stall_probability : real range 0.0 to 1.0;
min_stall_cycles : natural;
max_stall_cycles : natural) return stall_config_t is
variable stall_config : stall_config_t;
begin
stall_config := (
stall_probability => stall_probability,
min_stall_cycles => min_stall_cycles,
max_stall_cycles => max_stall_cycles);
return stall_config;
end;

end package body;
68 changes: 68 additions & 0 deletions vunit/vhdl/verification_components/src/axi_stream_private_pkg.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this file,
-- You can obtain one at http://mozilla.org/MPL/2.0/.
--
-- Copyright (c) 2014-2020, Lars Asplund [email protected]

library ieee;
use ieee.std_logic_1164.all;

library osvvm;
use osvvm.RandomPkg.RandomPType;

use work.axi_stream_pkg.all;

package axi_stream_private_pkg is
procedure probability_stall_axi_stream(
signal aclk : in std_logic;
axi_stream : in axi_stream_slave_t;
rnd : inout RandomPType
);

procedure probability_stall_axi_stream(
signal aclk : in std_logic;
axi_stream : in axi_stream_master_t;
rnd : inout RandomPType
);

procedure probability_stall_axi_stream(
signal aclk : in std_logic;
stall_config : in stall_config_t;
rnd : inout RandomPType
);

end package;

package body axi_stream_private_pkg is

procedure probability_stall_axi_stream(
signal aclk : in std_logic;
axi_stream : in axi_stream_master_t;
rnd : inout RandomPType) is
begin
probability_stall_axi_stream(aclk, axi_stream.p_stall_config, rnd);
end procedure;

procedure probability_stall_axi_stream(
signal aclk : in std_logic;
axi_stream : in axi_stream_slave_t;
rnd : inout RandomPType) is
begin
probability_stall_axi_stream(aclk, axi_stream.p_stall_config, rnd);
end procedure;

procedure probability_stall_axi_stream(
signal aclk : in std_logic;
stall_config : in stall_config_t;
rnd : inout RandomPType) is
variable num_stall_cycles : natural := 0;
begin
if rnd.Uniform(0.0, 1.0) < stall_config.stall_probability then
num_stall_cycles := rnd.FavorSmall(stall_config.min_stall_cycles, stall_config.max_stall_cycles);
end if;
for stall in 0 to num_stall_cycles-1 loop
wait until rising_edge(aclk);
end loop;
end procedure;

end package body;
Loading

0 comments on commit a7f2bbe

Please sign in to comment.