1
\$\begingroup\$

I have a testbench that exercises an AXI component by reading and writing registers, waiting for state changes in the middle.

It is somewhat easy to define timeouts for the entire testbench

process is begin wait for 1 ms; report "timeout" severity error; sys.env.finish; end process; 

and for individual waiting steps, but I'd like something that detects states that are obviously "stuck", like a long period with no bus access.

For that, I'd like to define a group of signals, where an event on any of them will reset a watchdog timer, and when the timer expires, an error is triggered.

The best I could come up with so far is

process(axi_arvalid,axi_awvalid) is begin watchdog <= '1', '0' after 10 us; end process; process(watchdog) is begin assert watchdog'last_event = 0 sec report "watchdog timeout" severity error; end process; 

Is there a cleaner way to write "if none of these signals change for x amount of time, flag an error"?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Starting with your code, I see two candidates for improvement. First, you can watch the signal changes using a single process:

process is begin wait on axi_arvalid, axi_awvalid for 10 us ; assert axi_arvalid'last_event = 0 sec or axi_awvalid'last_event = 0 sec report "watchdog timeout" severity failure ; end process ; 

Alternately, you can track time. This is similar and assumes that the timeout is big enough that even if XxValid changed right at 10 us, it is ok to fail anyway. This has a simpler check if you are trying to check all of the AXI channels at the same time.

process is variable WaitStartTime : time ; begin WaitStartTime := now ; wait on axi_arvalid, axi_awvalid for 10 us ; assert now - WaitStartTime < 10 us report "watchdog timeout" severity failure ; end process ; 

For the AXI interface, especially when the testbench is interacting with a subordinate, instead of checking for XxValid activity, I watch how long the valid signal high without a ready in response. As a result, I have one checker per AXI channel. The checks then work more like:

process begin wait until ArValid = '1' and rising_edge(Clk) ; if ArReady /= '1' then wait until ArReady = '1' and rising_edge(Clk) for TIMEOUT_PERIOD ; assert ArReady = '1' report "Timeout on ArReady" severity FAILURE ; end if ; end process ; 

When I have an Axi4Manager VC, I generally put these sort of stall checks inside the VC. This simplifies the above as the Axi4Manager is also the source of ArValid.

You could minimize your effort some by encapsulating the above into a subprogram:

procedure CheckAxiReady ( signal Clk : in std_logic ; signal Valid : in std_logic ; signal Ready : in std_logic ; constant Message : in string ; constant TimeOutPeriod : in time ) is begin loop wait until Valid = '1' and rising_edge(Clk) ; if Ready /= '1' then wait until Ready = '1' and rising_edge(Clk) for TimeOutPeriod ; assert Ready = '1' report Message severity FAILURE ; end if ; end loop ; end procedure CheckAxiReady ; 

Then call it concurrently:

CheckAxiReady ( Clk => Clk, Valid => ArValid, Ready => ArReady, Message => "AXI Read Address, Timeout on ArReady", TimeOutPeriod => 10 us ) ; 
\$\endgroup\$
1
  • \$\begingroup\$ The two process approach came out of a desire to not repeat the list of signals, so I can just add any signals that signify that the test is not stuck -- so any internal state that will make a component become active on the bus later. I like the idea of specifically monitoring valid->ready, that should give me more helpful error messages for some of the more problematic cases. \$\endgroup\$ Commented Apr 18, 2023 at 14:50
0
\$\begingroup\$

The best thing you can do in an HDL is define everything so that it can't decide for you. If you have a state machine define all the states. If you have an if then statement, define all the outcomes. Define all wires and defaults.

\$\endgroup\$
1
  • \$\begingroup\$ Yes, but even then I will have bugs like state transitions that are just wrong. I have simulations that take a lot of simulated time, so the global timeout for these is large. There is also always a future event scheduled because it has a clock, so it never terminates early. I'd like to detect cases where it is obvious that because of a bug, the entire system is stuck because two components are waiting for each other, so I'm monitoring a list of signals and expect at least one of these to change (i.e. either one of the components is processing data, or the bus is active). \$\endgroup\$ Commented Apr 19, 2023 at 1:41

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.