Conditional wait statement in a verilog testbench

K

kb33

Guest
Hi,
While running a simulation, I am supposed to get a feedback signal
from the design under test. However, this feedback may or may not be
received (depending upon the internal logic). If I receive this signal
from the DUT within a certain time limit, I shall execute task X; but
if I don't receive the signal, I wish to continue with the original
simulation flow.
I am not sure how to insert a conditional wait statement in my test
bench.

Thanks
Kanchan
 
On 17 Sep, 19:04, kb33 <kanchan.devarako...@gmail.com> wrote:
Hi,
While running a simulation, I am supposed to get a feedback signal
from the design under test. However, this feedback may or may not be
received (depending upon the internal logic). If I receive this signal
from the DUT within a certain time limit, I shall execute task X; but
if I don't receive the signal, I wish to continue with the original
simulation flow.
I am not sure how to insert a conditional wait statement in my test
bench.

Thanks
Kanchan
I was wrote a quick reply to this and tested it in QuestaSim, and
found it didn't do what I expect, so maybe a SystemVerilog expert
could help out...

In Verilog you can do this using fork/join and disable. The following
code will wait for an event on 'flag' and continue if this doesn't
occur in 1000 time units:

fork
begin : TIMEOUT
#1000;
$display("Timeout %t", $time);
disable EVENT;
end

begin : EVENT
@(flag);
$display("Event %t", $time);
disable TIMEOUT;
end
join


In SystemVerilog it should be possible (and neater) to do this with
fork/join_any:

fork
begin
#1000;
$display("Timeout %t", $time);
end

begin
@(flag);
$display("Event %t", $time);
end
join_any

However, I tried this in QuestaSim, and while the Verilog version
works fine, the SystemVerilog version does not do what I expect. A
full testbench and output is given below:


`timescale 1ns/1ps

module test;
reg flag;

initial
$timeformat(-9, 0, "ns", 0);

always
begin
$display("Start %t", $time);

`ifdef SYSTEMVERILOG
fork
begin
#1000;
$display("Timeout %t", $time);
end

begin
@(flag);
$display("Event %t", $time);
end
join_any
`else
fork
begin : TIMEOUT
#1000;
$display("Timeout %t", $time);
disable EVENT;
end

begin : EVENT
@(flag);
$display("Event %t", $time);
disable TIMEOUT;
end
join
`endif

$display("End %t", $time);
end

initial
begin
#1005;
flag = 1;
#20;
flag = 0;
#1005;
$finish;
end

endmodule



The output of the Verilog version is:

# Start 0ns
# Timeout 1000ns
# End 1000ns
# Start 1000ns
# Event 1005ns
# End 1005ns
# Start 1005ns
# Event 1025ns
# End 1025ns
# Start 1025ns
# Timeout 2025ns
# End 2025ns
# Start 2025ns

The SystemVerilog version does this...

# Start 0ns
# Timeout 1000ns
# End 1000ns
# Start 1000ns
# Event 1005ns
# End 1005ns
# Start 1005ns
# Event 1005ns
# Event 1025ns
# End 1025ns
# Start 1025ns
# Timeout 2000ns
# Timeout 2005ns
# Timeout 2025ns
# End 2025ns
# Start 2025ns

Why are the two different?

Thanks,

Mark
 
On Mon, 17 Sep 2007 18:04:55 -0000, kb33
<kanchan.devarakonda@gmail.com> wrote:

Hi,
While running a simulation, I am supposed to get a feedback signal
from the design under test. However, this feedback may or may not be
received (depending upon the internal logic). If I receive this signal
from the DUT within a certain time limit, I shall execute task X; but
if I don't receive the signal, I wish to continue with the original
simulation flow.
I am not sure how to insert a conditional wait statement in my test
bench.
Wait with timeout, originally posted by Jonathan Bromley:

// Example 1: Wait, with timeout, for signal Done to go true
// (useful in testbenches, if device under test is not proven)
fork: WaitWithTimeout
wait (Done) disable WaitWithTimeout;
#(Timeout) disable WaitWithTimeout;
join
// now test Done to see whether you timed out

[Is this the only *really* useful thing you can do with fork/join?]
 
On Tue, 18 Sep 2007 09:13:26 -0000,
"mjl296@hotmail.com" wrote:

I was wrote a quick reply to this and tested it in QuestaSim, and
found it didn't do what I expect, so maybe a SystemVerilog expert
could help out...
I suspect you are misunderstanding what fork...join_any does
when it terminates. What's more, I'm fairly certain (must
go and check!) that the defined behaviour of join_any changed
at some point in the evolution of SV - hence, perhaps, your
surprise I know that a diagram in our training had to be
changed to deal with this, and I *think* it was due to a change
in the language definition rather than our mistake...

fork...join_any spawns a bunch of threads and then waits
for the first of them to complete. At that point it proceeds
(unblocks), BUT the remaining threads continue to run - hence
you get multiple threads responding to the event at t=1005, for
example.

You could probably get a better handle on this by logging not
only the end but the start time of each thread...

fork
begin : timeout_thread
time t = $time;
#1000;
$display(
"timeout_thread started at %t timed-out at %t",
t, $time);
end

begin : wait_thread
time t = $time;
@(flag);
$display(
"wait_thread started at %t got event at %t",
t, $time);
end
join_any

You can do "disable fork" after the join_any if you want
to kill the slower threads. But beware that "disable fork"
kills *all* child threads, not only those spawned by the
immediately previous fork...join block, so care is needed!
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Hi all,

I suppose this discussion has taken on a different thread (please
continue, if you guys wish!)..but just wanted to thank all...I tried
Jonathan's method (as suggested by Evan), and with a bit of tinkering,
it worked. I hope I had been working with SystemVerilog so that I
could pitch in with the current discussion.

thanks again..
kanchan
 
Jonathan Bromley wrote:
I suspect you are misunderstanding what fork...join_any does
when it terminates. What's more, I'm fairly certain (must
go and check!) that the defined behaviour of join_any changed
at some point in the evolution of SV - hence, perhaps, your
surprise
I'm not aware of a change, but I wasn't watching that closely at the
time.

You can do "disable fork" after the join_any if you want
to kill the slower threads. But beware that "disable fork"
kills *all* child threads, not only those spawned by the
immediately previous fork...join block, so care is needed!
The usual solution is to wrap the entire thing inside a fork..join, as
a single subprocess of it. Then the disable fork won't affect
anything created outside the extra fork..join. It is a rather
expensive thing to have to do just to protect the other children of
the outer process from the disable fork, but there isn't much choice
if you want safety.

BTW, I don't know that I have ever seen an actual use of
fork..join_any that wasn't followed by a disable fork. That suggests
that your presumed earlier definition would have been a more
convenient one. The separate join_any and disable fork are more
flexible, but if nobody ever uses join_any in any other way, then that
was wasted.
 
On Thu, 20 Sep 2007 10:35:44 -0700, sharp@cadence.com wrote:

Jonathan Bromley wrote:

I suspect you are misunderstanding what fork...join_any does
when it terminates. What's more, I'm fairly certain (must
go and check!) that the defined behaviour of join_any changed
at some point in the evolution of SV - hence, perhaps, your
surprise

I'm not aware of a change, but I wasn't watching that closely at the
time.
You're right. I trawled back: fork...joinX was introduced in
SV 3.1, and join_any already had the current functionality at
that time. It seems that we mis-read the 3.1 spec when we
wrote some very early material :-( although, in our defence,
it wasn't exactly crystal-clear in that version of the LRM.

Anyway, I'm happy to report that our tutorial material has
been fixed (on this count at least) for quite a while now!
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On 20 Sep, 21:28, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Thu, 20 Sep 2007 10:35:44 -0700, sh...@cadence.com wrote:

Jonathan Bromley wrote:

I suspect you are misunderstanding what fork...join_any does
when it terminates. What's more, I'm fairly certain (must
go and check!) that the defined behaviour of join_any changed
at some point in the evolution of SV - hence, perhaps, your
surprise

I'm not aware of a change, but I wasn't watching that closely at the
time.

You're right. I trawled back: fork...joinX was introduced in
SV 3.1, and join_any already had the current functionality at
that time. It seems that we mis-read the 3.1 spec when we
wrote some very early material :-( although, in our defence,
it wasn't exactly crystal-clear in that version of the LRM.

Anyway, I'm happy to report that our tutorial material has
been fixed (on this count at least) for quite a while now!
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.brom...@MYCOMPANY.comhttp://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Thanks guys for the detailed explaination. I must admit that I was the
victim of assumption, rather than having read an old version of the
standard. Adding a timeout to a process represents about 90% of the
fork-join usage that I have come across, so I incorrectly assumed that
fork-join_any was added to tidy up this common case.
 

Welcome to EDABoard.com

Sponsor

Back
Top