testbench question

S

Shannon

Guest
I'm a hardware guy so please excuse me if this question is too
simple. I working through writing my first non-synthesizeable (i.e.
testbench) code.

I have a process with no sensitivity list and a couple of wait
statements structurally like this:

process
begin
wait until Reset = '0';
while not endfile(commands) loop
... do stuff ...
wait until send_done;
end loop;
end process;

All works surprisingly well (to me) except for that last "wait until
send_done" part. I can't seem to get the process to notice send_done
has changed state. What am I doing wrong?

if you need to see more code I can post it but it will take a little
while for me to "sanitize" it for public release.

Shannon
 
Shannon wrote:

I have a process with no sensitivity list and a couple of wait
statements structurally like this:

process
begin
wait until Reset = '0';
while not endfile(commands) loop
... do stuff ...
wait until send_done;
end loop;
end process;
I do the same thing, but use synchronous delays.

All works surprisingly well (to me) except for that last "wait until
send_done" part. I can't seem to get the process to notice send_done
has changed state. What am I doing wrong?
I would run a sim to see what is happening.
Here's how I exit a testbench:

procedure coda is
begin
results;
done_s <= true;
tic;
wait;
end procedure coda;

details here:
http://mysite.verizon.net/miketreseler/test_uart.vhd

Good luck.

-- Mike Treseler
 
On Jul 13, 10:09 am, Shannon <sgo...@sbcglobal.net> wrote:
I'm a hardware guy so please excuse me if this question is too
simple.  I working through writing my first non-synthesizeable (i.e.
testbench) code.

I have a process with no sensitivity list and a couple of wait
statements structurally like this:

process
begin
wait until Reset = '0';
while not endfile(commands) loop
   ... do stuff ...
  wait until send_done;
end loop;
end process;

All works surprisingly well (to me) except for that last "wait until
send_done" part.  I can't seem to get the process to notice send_done
has changed state.  What am I doing wrong?

if you need to see more code I can post it but it will take a little
while for me to "sanitize" it for public release.

Shannon
AAAAAAARRRRRRRRRRRRRGGGGGGGGGGGGGGGGGHHHHHHHHH

ok. never mind. got it. I keep getting stuck on the concept that
signals won't update until the process suspends. It's just not
something I worry about with synth. code.

Shannon
 
On Mon, 13 Jul 2009 10:09:16 -0700 (PDT), Shannon wrote:

I'm a hardware guy
So am I; we won't hold it against you :)

I working through writing my first non-synthesizeable (i.e.
testbench) code.
And, presumably, first suffering the considerable mindset
shift that it brings; soon, though, you'll see what a
liberating step you've taken. The whole language to
play around in! No ringfences around the synthesisable
templates!

I have a process with no sensitivity list and a couple of wait
statements structurally like this:

process
begin
wait until Reset = '0';
while not endfile(commands) loop
... do stuff ...
wait until send_done;
end loop;
end process;

All works surprisingly well (to me) except for that last "wait until
send_done" part. I can't seem to get the process to notice send_done
has changed state. What am I doing wrong?
Obviously, see your self-reply; but there are a couple of
other things worth bearing in mind.

~~~~~~~~~~~~~~~~~~~~~~~~~~

First off, know your WAIT statement. It's a powerful beast.

wait on signalA, signalB... until <expression> for <time>;

All three pieces -= "on" sensitivity list, "until" and "for" -
are optional; you can have any combination of the three.

This statement:
a) puts itself to sleep - unconditionally, regardless of the
value of <expression> or <time> - for at least a delta cycle;
b) wakes up in any delta cycle on which there's an event
(value change) on any signal in the "on" list;
c) on each awakening, tests the <expression>; if false, goes back
to sleep until the next triggering of the "on" list; if true,
unblocks and allows execution to proceed;
d) on top of all the above, times-out and unblocks if the <time>
expires before the test comes true.

Note that the <expression> is tested only when the "on" list
trips. If you don't supply an "on" list, it's automatically
constructed from every signal that appears in the <expression>.

Note one special gotcha:

wait until <expression>; -- with no signals in <expression>

This will never wake up, because the automatically constructed
sensitivity list is empty. A favourite error is...

wait until NOW = 3 us; -- - OUCH - stops forever

The correct formulation is, of course,

wait for 3 us - NOW;

~~~~~~~~~~~~~~~~~~~~~~~~~~

Next, be aware of 'TRANSACTION for signalling between
processes. It's a slightly odd idea, but works well. In
your example, "send_done" must be driven FALSE and then back
TRUE again in order to release the WAIT. That's tiresome.
Instead, consider simply

send_done <= TRUE; -- over and over again

Now, clearly, if you do

wait until send_done;

you'll be stuck, because there is no event on send_done
(after the first one). But instead you can do

wait on send_done'TRANSACTION;

and ANY write to send_done, even one that doesn't change
its value, will release the wait. Neat - just one
signal assignment to indicate an event.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Finally an easy one: level-sensitive waiting.

if not send_done then wait until send_done; end if;

Falls through if send_done is already true, otherwise waits
until send_done becomes true.

Enjoy!
--
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.
 
Jonathan Bromley wrote:

Note one special gotcha:

wait until <expression>; -- with no signals in <expression

This will never wake up, because the automatically constructed
sensitivity list is empty. A favourite error is...

wait until NOW = 3 us; -- - OUCH - stops forever

The correct formulation is, of course,

wait for 3 us - NOW;
Excuse me for stumbling in into this newsgroup after a too long
absence, finding your much appreciated and informative (as always)
writing, and not being able to restrain my self of making the
following remark:

The correct formulation is, of course,

if now < 3 us then
wait for 3 us - now;
end if;

It avoids a run time error if the simulation time already passed
the 3 us mark (been there, done that).

Next, be aware of 'TRANSACTION for signalling between
processes. It's a slightly odd idea, but works well. In
your example, "send_done" must be driven FALSE and then back
TRUE again in order to release the WAIT. That's tiresome.
And that's why in such cases I always use a signal of type BIT and
toggle it. No need for initialisation, no need to put it back to an
inactive state and you can even generate events in consecutive delta
times.

--
Paul.
 
On Wed, 22 Jul 2009 00:02:33 +0200, Paul wrote:

not being able to restrain my self of making the
following remark:

The correct formulation is, of course,

if now < 3 us then
wait for 3 us - now;
end if;

It avoids a run time error if the simulation time already passed
the 3 us mark (been there, done that).
touché! I could not have been chided by a nicer chap...
Yes, you're right of course, and I too have "been there";
shame on me for forgetting to mention it.

Next, be aware of 'TRANSACTION for signalling between
processes. It's a slightly odd idea, but works well. In
your example, "send_done" must be driven FALSE and then back
TRUE again in order to release the WAIT. That's tiresome.

And that's why in such cases I always use a signal of type BIT and
toggle it. No need for initialisation, no need to put it back to an
inactive state and you can even generate events in consecutive delta
times.
Now that one is MUCH more interesting...

When I wrote the original comment, I was basically just
repeating our standard story about using 'TRANSACTION to
signal events from one part of a testbench to another.
Your response made me think about it some more. One
reason I like using 'TRANSACTION is that it seems to me
much more elegant to be able to write

done <= TRUE;

than

done <= not done;

But you could easily get around that by writing

procedure notify (signal flag: inout bit) is
begin
flag <= not flag;
end;

However, there's a more significant reason in favour of
using 'TRANSACTION: It works with resolved signals
that have multiple drivers. This means that you can
put a global signal in a package, and any part of your
testbench can notify using that global signal by
simply writing to it. That's not something you want
to do all the time, of course, but it can be useful.

Finally, an argument in favour of your version: You
can't detect 'TRANSACTION on procedure arguments. So,
for example, this doesn't work:

procedure wait_for_notify(signal s: in <something>);
begin
wait on s'transaction; -- illegal on procedure arg
end;

But of course you *can* do that with 'EVENT and so this
is fine - the receiving end of the "notify" procedure:

procedure wait_for_notify(signal flag: in bit);
begin
wait on flag;
end;

Thanks!
--
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 Jul 22, 2:59 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
However, there's a more significant reason in favour of
using 'TRANSACTION:  It works with resolved signals
that have multiple drivers.  This means that you can
put a global signal in a package, and any part of your
testbench can notify using that global signal by
simply writing to it.  That's not something you want
to do all the time, of course, but it can be useful.
You can grow your own resolved signal using an XOR resolution
function, such that toggling any driver (more specifically an odd
number of inputs) will toggle the resolved value, which can then be
detected with 'event. But then if an even number of drivers get
toggled in the same delta, you're hosed...

Andy
 
Jonathan Bromley wrote:

Next, be aware of 'TRANSACTION for signalling between
processes. It's a slightly odd idea, but works well. In
your example, "send_done" must be driven FALSE and then back
TRUE again in order to release the WAIT. That's tiresome.

And that's why in such cases I always use a signal of type BIT and
toggle it. No need for initialisation, no need to put it back to an
inactive state and you can even generate events in consecutive delta
times.

Now that one is MUCH more interesting...

When I wrote the original comment, I was basically just
repeating our standard story about using 'TRANSACTION to
signal events from one part of a testbench to another.
Your response made me think about it some more. One
reason I like using 'TRANSACTION is that it seems to me
much more elegant to be able to write

done <= TRUE;
True. ;-)

than

done <= not done;

But you could easily get around that by writing

procedure notify (signal flag: inout bit) is
begin
flag <= not flag;
end;
Indeed. I use the perhaps not so elegant name "toggle" instead
of "notify". And I consistently use type bit for toggle signals. For
me that's clear enough.

However, there's a more significant reason in favour of
using 'TRANSACTION: It works with resolved signals
that have multiple drivers. This means that you can
put a global signal in a package, and any part of your
testbench can notify using that global signal by
simply writing to it. That's not something you want
to do all the time, of course, but it can be useful.
Ah, yes, that's a nice one.

Finally, an argument in favour of your version: You
can't detect 'TRANSACTION on procedure arguments. So,
for example, this doesn't work:

procedure wait_for_notify(signal s: in <something>);
begin
wait on s'transaction; -- illegal on procedure arg
end;
One of those silly limitation of VHDL...

But of course you *can* do that with 'EVENT and so this
is fine - the receiving end of the "notify" procedure:

procedure wait_for_notify(signal flag: in bit);
begin
wait on flag;
end;
Another reason not to use 'TRANSACTION: it gets lost with a signal
assignment. But the fact that it cannot be used in a procedure is the
main reason for me not to use it. Though I must admit, I had
forgotten that reason. Thanks for refreshing it.

--
Paul.
 
On Jul 22, 2:43 pm, Paul <pa...@sx4all.nl> wrote:
Jonathan Bromley wrote:
Next, be aware of 'TRANSACTION for signalling between
processes.  It's a slightly odd idea, but works well.  In
your example, "send_done" must be driven FALSE and then back
TRUE again in order to release the WAIT.  That's tiresome.

And that's why in such cases I always use a signal of type BIT and
toggle it. No need for initialisation, no need to put it back to an
inactive state and you can even generate events in consecutive delta
times.

Now that one is MUCH more interesting...

When I wrote the original comment, I was basically just
repeating our standard story about using 'TRANSACTION to
signal events from one part of a testbench to another.
Your response made me think about it some more.  One
reason I like using 'TRANSACTION is that it seems to me
much more elegant to be able to write

  done <= TRUE;

True.   ;-)



than

  done <= not done;

But you could easily get around that by writing

  procedure notify (signal flag: inout bit) is
  begin
    flag <= not flag;
  end;

Indeed. I use the perhaps not so elegant name "toggle" instead
of "notify". And I consistently use type bit for toggle signals. For
me that's clear enough.

However, there's a more significant reason in favour of
using 'TRANSACTION:  It works with resolved signals
that have multiple drivers.  This means that you can
put a global signal in a package, and any part of your
testbench can notify using that global signal by
simply writing to it.  That's not something you want
to do all the time, of course, but it can be useful.

Ah, yes, that's a nice one.

Finally, an argument in favour of your version:  You
can't detect 'TRANSACTION on procedure arguments.  So,
for example, this doesn't work:

  procedure wait_for_notify(signal s: in <something>);
  begin
    wait on s'transaction;  -- illegal on procedure arg
  end;

One of those silly limitation of VHDL...

But of course you *can* do that with 'EVENT and so this
is fine - the receiving end of the "notify" procedure:

  procedure wait_for_notify(signal flag: in bit);
  begin
    wait on flag;
  end;

Another reason not to use 'TRANSACTION: it gets lost with a signal
assignment. But the fact that it cannot be used in a procedure is the
main reason for me not to use it. Though I must admit, I had
forgotten that reason. Thanks for refreshing it.

--
Paul.
You know...it's threads like these that make me feel like I'm in my
garage working on the car. I ask my neighbor for some help tuning the
carburetor and the next thing you know half the neighborhood is in my
garage discussing the merits of fuel injection.

This is a great group. Thanks for the help and more. Oh and you are
right Jonathan, I was stressed about learning to write testbenches.
But now I'm finding it very liberating! Oh the junk I can get away
with now!

Shannon
 

Welcome to EDABoard.com

Sponsor

Back
Top