Wow! No TestbenchWow!

R

rickman

Guest
This is the first project I've done in Verilog in many years. With a
long history in VHDL I have a new perspective and am seeing Verilog in
a different way. I am finding some of the differences to be pretty
interesting actually.

I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this. Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!! I would love to have had this in Verilog. It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.

.... or did I miss something again?

Rick
 
rickman wrote:
This is the first project I've done in Verilog in many years. With a
long history in VHDL I have a new perspective and am seeing Verilog in
a different way. I am finding some of the differences to be pretty
interesting actually.

I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this. Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!! I would love to have had this in Verilog. It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.

... or did I miss something again?
Yep; VHDL 2008 added External Names (P1076-2008, section 8.7), which I believe are the
moral equivalent of Verilog hierarchical references. Google's first hit on them:

http://www.doulos.com/knowhow/vhdl_designers_guide/vhdl_2008/vhdl_200x_ease/#hierarchicalnames

--
Tim McBrayer
MathWorks
 
"rickman" wrote in message
news:f8d79600-a7d4-4c5b-b5d3-655122ad1124@k14g2000pre.googlegroups.com...
...
I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this. Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!! I would love to have had this in Verilog. It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.

.... or did I miss something again?

Yes, VHDL2008 supports hierarchical references (works fine in Modelsim
10.0), before that you had SignalSpy and many other custom solutions to this
issue.

<<signal .test2008_tb.u1.muxout : std_logic_vector(2 downto 0) >> <= force
"011"; -- inject error

Hans.
www.ht-lab.com
 
rickman wrote:

This is the first project I've done in Verilog in many years. With a
long history in VHDL I have a new perspective and am seeing Verilog in
a different way. I am finding some of the differences to be pretty
interesting actually.

I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this. Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!! I would love to have had this in Verilog. It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.

... or did I miss something again?
Yes! ;-)
Well, maybe....

For bringing up debug signals to the testbench you can use "global" signals
declared in a package. It is not synthesizable (but for debug signals that
would not be a problem). Also, the assignment to the global signal has to
take place at the location were the object to be observed is visible.

Example:

PACKAGE pkg IS
SIGNAL spy: std_logic_vector(7 DOWNTO 0);
END PACKAGE pkg;

In the architecture where you want to observe a signal:

USE work.pkg.ALL;
ARCHITECTURE arch OF design_block IS
BEGIN
...
spy <= observed_signal;
...
END ARCHITECTURE arch;

In your testbench:

USE work.pkg.ALL;
ARCHITECTURE arch OF tb IS
BEGIN
...
-- spy is visible here, due to the USE statement
IF spy = ... -- whatever
...
END ARCHITECTURE arch;


Another way to do all this is using the new VHDL-2008 feature
called "external names". Then you can peek into the DUV every which way you
want, without the need of changing DUV code.

Example (from the Ashenden/Lewis book: VHDL-2008 just the new stuff):

ASSERT <<SIGNAL .tb.duv.controller.state: std_logic_vector(0 TO 4)>>
/= "00000"
REPORT "Illegal controller state";

VHDL-2008 also includes FORCE and RELEASE assignments. This means that you
don't need simulator dependant commands anymore for forcing signals in the
DUT.

I have no idea if there already is a simulator that supports these
constructs.

--
Paul Uiterlinden
www.aimvalley.nl
e-mail addres: remove the not.
 
On Jan 27, 2:22 pm, rickman <gnu...@gmail.com> wrote:
This is the first project I've done in Verilog in many years.  With a
long history in VHDL I have a new perspective and am seeing Verilog in
a different way.  I am finding some of the differences to be pretty
interesting actually.

I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this.  Now I am learning how Verilog
allows hierarchical path references to signals for test benches.  This
is awesome!!!  I would love to have had this in Verilog.  It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.

... or did I miss something again?

Rick
I've always thought that it would be nice if FPGA synthesis tools
supported the hierarchical path names too.
i.e. if you wanted to debug a core with chipscope, you could do

assign trig0[0] = my_core.some_internal_block.troublesome_node;
 
rickman wrote:
This is the first project I've done in Verilog in many years. With a
long history in VHDL I have a new perspective and am seeing Verilog in
a different way. I am finding some of the differences to be pretty
interesting actually.

I've already commented on the lack of the wildcard sensitivity only to
find that VHDL has recently added this. Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!! I would love to have had this in Verilog. It is such a
PITA to have to bring every generic or debug signal to the top of a
design just to support a test bench.
If you're using ModelSim, there's a library "modelsim_lib" that has a
function called "SignalSpy". With that you can access any signal in your
design from a test bench. Use it like this:

library modelsim_lib;
use modelsim_lib.util.all;

-- entity, architecture, signal declarations skipped

-----------------------------------------------------------------------------
-- spy process
-----------------------------------------------------------------------------
sig_spy : process is
begin
init_signal_spy("/DUT/submodule1/submodule2/interesting_signal",
"tb_sig", 1);
wait;
end process sig_spy;

This connects "interesting_signal" to your test bench signal "tb_sig".

This is not synthesizable and you have to consider ModelSim's built in
optimization, which might optimize away the signal you want to look at
during elaboration, but it's a start and works with older VHDL releases.
Doesn't work for GENERICs, though...

HTH,
Sean
 
I've always thought that it would be nice if FPGA synthesis tools
supported the hierarchical path names too.
i.e. if you wanted to debug a core with chipscope, you could do

assign trig0[0] = my_core.some_internal_block.troublesome_node;
VHDL:
I've successfully used signals in packages (global signals) in
synthesis (synplify).
Declare a signal in a package (I used std_ulogic to try to catch
multiple drivers
at compile time, but the error(s) came at elaboration...).

So at the top entity you "use debug_pkg" and get access to the
debug_signal.
Just drive it to your output, i.e.
debug_pin <= debug_signal;

In the lower level entity, also "use debug_pkg" and send your
troublesome_node to the debug_signal:
debug_signal <= troublesome_node;

HTH -- Pont
 
On Thu, 27 Jan 2011 06:22:19 -0800 (PST), rickman wrote:

Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!!
Not as awesome as the ability to call tasks
(procedures) in a module, from another module.
That's just the neatest thing ever, for
stimulus generation. This little example
should give you a flavour of what you can do:

`timescale 1ns/1ns

module simulatedUartTransmitter(output reg TxD);
time bitTime;
//
task setBitTime(input time newBitTime);
bitTime = newBitTime;
endtask

task sendChar(input [7:0] char);
begin
// send start bit
TxD = 0;
// send eight data bits, LSB first
repeat (8) begin
#(bitTime) TxD = char[0];
char = char >> 1;
end
// send stop bit
#(bitTime) TxD = 1;
#(bitTime);
end
endtask
//
initial TxD = 1; // line idles in "Mark" state
//
endmodule

module justTryThisOne;
// connections
wire serial_TxD;
// stimulus generator instance
simulatedUartTransmitter txGenerator(.TxD(serial_TxD));
//
// There's no DUT in this example, but you can still
// see the signal generator at work.
//
// code to generate some stimulus
initial begin
txGenerator.setBitTime(104000); // 9600Bd, roughly
#1_000_000; // idle awhile before starting
txGenerator.sendChar("h"); // ask the sig-gen...
txGenerator.sendChar("i"); // ...to send some data
txGenerator.sendChar("!"); // ...at our request
#1_000_000; // idle awhile at the end
end
endmodule

Utterly fantastic when you want to do stuff like
mimicking the behaviour of a CPU in your testbench.
Just write a module that can generate read or write
cycles on a bus, then connect an instance of it to
your DUT and get it to do accesses in the same way
you'd expect your CPU to behave.

Apologies if this is stuff you've seen already.
It's so useful that I couldn't resist sharing
the example (again).
--
Jonathan Bromley
 
I am new to system verilog and came across this hierarchical
referencing recently. I believe it is called out of module referncing
(OOMR). It is extremely usefull for "back door" access to modules, eg
setting a registers contents without having to waste simulator time
doing a proper bus transaction.

It works when the source and destination is verilog and can traverse
through vhdl layers.

I'll also add that I have used this technique to pass a record from a
test task into a module using a function call as previously shown, and
have the function split apart the record into destination module
signals. This allows the tests to keep data in nice high level
objects.

I have a copy of "SystemVerilog for verification" by Chris Spear where
I found the briefest of mentions of this method (section 4.7 Program-
Module Interaction). It seems to me to be a most useful technique
that warrants more discussion and examples. It makes me wonder if the
SV gurus may not like it. I wounder if there are any gotcha's?

Darrin
 
Dal <darrin.nagy@gmail.com> writes:

I wounder if there are any gotcha's?
It will usually not work on a gate level netlist, it will usually not
work on a netlist/design where the hierarchy has been modified by
ATPG/BIST tools.

//Petter
--
..sig removed by request.
 
On Fri, 28 Jan 2011 19:59:56 -0800 (PST), Dal <darrin.nagy@gmail.com>
wrote:

I am new to system verilog and came across this hierarchical
referencing recently. I believe it is called out of module referncing
(OOMR).
Or sometimes "cross-module reference" (XMR).

It is extremely usefull for "back door" access to modules, eg
setting a registers contents without having to waste simulator time
doing a proper bus transaction.
Among many other things, yes.

It works when the source and destination is verilog and can traverse
through vhdl layers.
That's a tool issue. I agree that all major simulators allow it,
but there are always caveats. For example, what happens about
VHDL's case insensitivity? Differences in escaped-name conventions?
Needs a lot of care.

I'll also add that I have used this technique to pass a record from a
test task into a module using a function call as previously shown, and
have the function split apart the record into destination module
signals. This allows the tests to keep data in nice high level
objects.
Right. This is somewhat the same kind of hiding of detail
that I was trying to show, in a different way, for the UART
send-a-character example. Subprograms (and data structures,
if you're using SystemVerilog) allow your testbench to work
in terms of "transactions" - meaningful blocks of activity
such as an Ethernet packet, a burst access on a CPU bus,
or whatever - instead of individual pin-wiggles.

I have a copy of "SystemVerilog for verification" by Chris
Spear where I found the briefest of mentions of this method
(section 4.7 Program-Module Interaction). It seems to me
to be a most useful technique that warrants more discussion
and examples. It makes me wonder if the SV gurus may not
like it.
We love it ;-) But that book lays the ground for
people to use SV classes in their verification
efforts, and focuses on that set of problems.
The Bergeron book "Writing Testbenches", which
I mentioned in an earlier thread, goes into a
lot more detail about pre-SV techniques.

I wonder if there are any gotcha's?
Oooh, there are plenty!

Apart from the inherent complexity of the rules
about how to make an XMR reference (the references
can go both up and down the hierarchy, in a cunning
and subtle way), there is also a methodology
problem. Suppose your testbench has a hard-coded
XMR to snoop some signal:

// testbench detects change on an important register
always @(harness.DUT.arbiter_subsys.regbank.collision)
begin
// report new register value to other parts of TB
end

Cool stuff. BUT what happens when your DUT is used as
part of a larger system? The path now changes and you
have NO WAY to carry the old code across to a new
verification environment. You are forced to hand-edit
the XMR. You can work around this with macros:

`define PATH_TO_ARBITER harness.DUT.arbiter_subsys
...
always @(`PATH_TO_ARBITER.regbank.collision)...

Now you can move the code simply by redefining the
macro. Not bad, but not perfect - what happens if
you end up with two instances of that arbiter?
That's why serious SV verification people prefer to
use classes and/or virtual interfaces, which allow
the code to be largely decoupled from details of
the precise XMR path to any given module. Hookup
of the verification code to a specific place in
the hierarchy can then happen at run time, in one
single place (a top-level configuration module).
These more advanced techniques enable the creation
of verification IP that can be re-deployed in a
new environment without having to rewrite any
core verification code.

But that's a discussion for another day :)
--
Jonathan Bromley
 
Jonathan Bromley wrote:

On Thu, 27 Jan 2011 06:22:19 -0800 (PST), rickman wrote:

Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!!

Not as awesome as the ability to call tasks
(procedures) in a module, from another module.
That's just the neatest thing ever, for
stimulus generation. This little example
should give you a flavour of what you can do:

`timescale 1ns/1ns

module simulatedUartTransmitter(output reg TxD);
time bitTime;
//
task setBitTime(input time newBitTime);
bitTime = newBitTime;
endtask

task sendChar(input [7:0] char);
begin
// send start bit
TxD = 0;
// send eight data bits, LSB first
repeat (8) begin
#(bitTime) TxD = char[0];
char = char >> 1;
end
// send stop bit
#(bitTime) TxD = 1;
#(bitTime);
end
endtask
//
initial TxD = 1; // line idles in "Mark" state
//
endmodule

module justTryThisOne;
// connections
wire serial_TxD;
// stimulus generator instance
simulatedUartTransmitter txGenerator(.TxD(serial_TxD));
//
// There's no DUT in this example, but you can still
// see the signal generator at work.
//
// code to generate some stimulus
initial begin
txGenerator.setBitTime(104000); // 9600Bd, roughly
#1_000_000; // idle awhile before starting
txGenerator.sendChar("h"); // ask the sig-gen...
txGenerator.sendChar("i"); // ...to send some data
txGenerator.sendChar("!"); // ...at our request
#1_000_000; // idle awhile at the end
end
endmodule

Utterly fantastic when you want to do stuff like
mimicking the behaviour of a CPU in your testbench.
Just write a module that can generate read or write
cycles on a bus, then connect an instance of it to
your DUT and get it to do accesses in the same way
you'd expect your CPU to behave.

Apologies if this is stuff you've seen already.
It's so useful that I couldn't resist sharing
the example (again).
Thanks for sharing. I've kept this for reference, as I don't use Verilog
normally but want to keep up to date as much as possible. It is
fantastically more simple than the hoops and loops you must go through when
implementing this in VHDL. Been there, done that (or rather: doing that).
And I am saying this as a VHDL aficionado.

One question though: if the task sendChar is called concurrently from
different procedural blocks in a way that the calls are overlapping, I
think the result would be a great mess (I am saying this as a not so great
lover of how Verilog works).

Is there a simple way to deal with collisions like that? Or will the
simplicity be lost then for the most part?

--
Paul Uiterlinden
www.aimvalley.nl
e-mail addres: remove the not.
 
On Thu, 03 Mar 2011 22:56:49 +0100, Paul Uiterlinden
<puiterl@notaimvalley.nl> wrote:

Jonathan Bromley wrote:

On Thu, 27 Jan 2011 06:22:19 -0800 (PST), rickman wrote:

Now I am learning how Verilog
allows hierarchical path references to signals for test benches. This
is awesome!!!

Not as awesome as the ability to call tasks
(procedures) in a module, from another module.
That's just the neatest thing ever, for
stimulus generation. This little example
should give you a flavour of what you can do:
<snip much>

Thanks for sharing. I've kept this for reference, as I don't use Verilog
normally but want to keep up to date as much as possible. It is
fantastically more simple than the hoops and loops you must go through when
implementing this in VHDL. Been there, done that (or rather: doing that).
And I am saying this as a VHDL aficionado.

One question though: if the task sendChar is called concurrently from
different procedural blocks in a way that the calls are overlapping, I
think the result would be a great mess (I am saying this as a not so great
lover of how Verilog works).
Right, it certainly is. In my simple example you would probably
argue that any concurrent call represents bad testcase design,
but it's a real problem in more complex situations.

Is there a simple way to deal with collisions like that? Or will the
simplicity be lost then for the most part?
No, it can be dealt with - easier in SystemVerilog,
but OK even in vanilla Verilog.

First point: make sure the task is declared "automatic";
if you don't do that, all its variables are static.
I forgot to do that in my post, but would normally
regard it as mandatory.

Second, consider what behaviour you actually want
in the event of a collision. Should the second
caller be locked out until the first is done? Or
should there be some real overlapping? Again
this depends on the problem specifics.

The standard solution is to use a home-grown
semaphore on the task, throwing an error if
there's a concurrent call:

reg Lock; // outside the task so it's static
task automatic doSomethingTimeConsuming;
begin
if (Lock) begin
$display("FATAL collision"); $finish();
end
Lock = 1;
// do the work of the task
Lock = 0;
end
endtask

Queueing requests until other callers have finished
is not as easy as it looks unless you have
SystemVerilog's built-in semaphores and mailboxes.
This doesn't work correctly:

task automatic BadLockout;
begin
wait (Lock !== 1'b1);
Lock = 1;
// do the work
Lock = 0;
end
endtask

It's fine if you have just two callers that might
collide, but breaks if there are more than two.
Suppose one caller is running the task, and TWO
other callers both make a call during that time;
then BOTH the later callers are sitting at the
wait() timing control, and BOTH will be released
when the first call completes. Ouch.

Strictly speaking, even the first (error on collision)
isn't 100% safe because of the way Verilog can allow
arbitrary interleaving of execution. In practice,
though, it catches all my goofs well enough.
--
Jonathan Bromley
 
Jonathan Bromley wrote:

On Thu, 03 Mar 2011 22:56:49 +0100, Paul Uiterlinden

Is there a simple way to deal with collisions like that? Or will the
simplicity be lost then for the most part?

No, it can be dealt with - easier in SystemVerilog,
but OK even in vanilla Verilog.

First point: make sure the task is declared "automatic";
if you don't do that, all its variables are static.
I forgot to do that in my post, but would normally
regard it as mandatory.
Ah yes, that's a nice gotcha! Thanks.

Second, consider what behaviour you actually want
in the event of a collision. Should the second
caller be locked out until the first is done? Or
should there be some real overlapping? Again
this depends on the problem specifics.

The standard solution is to use a home-grown
semaphore on the task, throwing an error if
there's a concurrent call:

reg Lock; // outside the task so it's static
task automatic doSomethingTimeConsuming;
begin
if (Lock) begin
$display("FATAL collision"); $finish();
end
Lock = 1;
// do the work of the task
Lock = 0;
end
endtask

Queueing requests until other callers have finished
is not as easy as it looks unless you have
SystemVerilog's built-in semaphores and mailboxes.
This doesn't work correctly:

task automatic BadLockout;
begin
wait (Lock !== 1'b1);
Lock = 1;
// do the work
Lock = 0;
end
endtask

It's fine if you have just two callers that might
collide, but breaks if there are more than two.
Suppose one caller is running the task, and TWO
other callers both make a call during that time;
then BOTH the later callers are sitting at the
wait() timing control, and BOTH will be released
when the first call completes. Ouch.

Strictly speaking, even the first (error on collision)
isn't 100% safe
What is in Verilog? :p

because of the way Verilog can allow
arbitrary interleaving of execution. In practice,
though, it catches all my goofs well enough.
Thanks for your clear explanations and insights.

I guess if you really want to be able to allow concurrent callers you would
need to introduce priorities. Only if two equal priorities collide you
would need to throw a fatal. Also, the callers should be well behaved, in
that they first check on any ongoing transaction.

I have done all that in VHDL using a signal and resolution function. A bit
complicated to create and grasp initially, but once done it works very
well. Two or more colliding priorities would cause undeterminism (yes, that
can be done in VHDL as well), so I flag that with a fatal, ending the
simulation.

--
Paul Uiterlinden
www.aimvalley.nl
e-mail addres: remove the not.
 
Jonathan,

I found it strange that you mentioned "hiding" as an advantage to the
XMR capability. While the overhead of implementing the interface is
hidden (if not avoided entirely), the entire implementation of the
module is exposed by XMR.

I cannot help but wonder if this XMR capability (in any language) is a
wolf in sheep's clothing. As long as I, the writer of a module, force
you to go through a mutually agreed upon interface to verify my
module's functionality, I'm free to change the implementation without
fear that it will break someone's verification code, so long as I have
not changed the behavior at the agreed upon interface. Even if I use
global signals in a package (VHDL), I am still declaring (and
protecting) a public interface to which I am implicitly promising (and
more importantly, limiting) fidelity. If you have a need to interface
with a module, whether it is for verification or not, that interface
must be agreed upon by all parties (with its limitations documented,
even if only in code), instead of taken for granted by the
verification engineer that it will continue to work in the future.

Is there any way to limit the tasks or objects that may be accessed
externally?

Andy
 
On Mon, 14 Mar 2011 15:41:22 -0700 (PDT), Andy wrote:

I found it strange that you mentioned "hiding" as an advantage to the
XMR capability. While the overhead of implementing the interface is
hidden (if not avoided entirely), the entire implementation of the
module is exposed by XMR.
Undoubtedly true; I should have said "encapsulation" rather
than "hiding", I suppose. In vanilla Verilog EVERYTHING is
both static and public, with some justification (think VPI
and debug...), whether you like it or not. The language
doesn't allow the writer of a module to enforce a contract.

The packaging I described isn't bad, though, as you
must surely admit. A Verilog user with some measure of
self-discipline (and buy-in from colleagues) can maintain
appropriate separation between modules. Like you, I wish
the situation were somewhat different, but it ain't.

I cannot help but wonder if this XMR capability (in any language) is a
wolf in sheep's clothing. As long as I, the writer of a module, force
you to go through a mutually agreed upon interface to verify my
module's functionality, I'm free to change the implementation without
fear that it will break someone's verification code, so long as I have
not changed the behavior at the agreed upon interface.
Not just verification, but for any use whatever, the
fear is always there. Sure. Verilog believes that you
know best, and rarely offers you a safety rail at the
edge of the cliff.

Even if I use
global signals in a package (VHDL), I am still declaring (and
protecting) a public interface to which I am implicitly promising (and
more importantly, limiting) fidelity. If you have a need to interface
with a module, whether it is for verification or not, that interface
must be agreed upon by all parties (with its limitations documented,
even if only in code), instead of taken for granted by the
verification engineer that it will continue to work in the future.
Ah, contract at the interface. Not a shining feature of Verilog.

Even for RTL design, where synthesis tools (not the language) forbid
you from accessing the functionality of a module by any means other
than through its ports, the ports themselves provide no contract
about the functionality they implement - they're just the names
and directions of signals. And those directions aren't very
rigidly enforced by Verilog either. Ho hum.

Is there any way to limit the tasks or objects that may be accessed
externally?
No, not in regular Verilog. Of course, in SystemVerilog you
can write classes and give them local and protected members;
doesn't help for RTL design, though.
--
Jonathan Bromley
 

Welcome to EDABoard.com

Sponsor

Back
Top