inout signal in an interface

A

Amal

Guest
How does one define an inout signal in an interface? I wrote the
following for a cpu bus interface and define two mod_ports as follows:

interface cpu_if( input bit reset, input bit clk );
logic [31:0] ad; // Address/Data
bit ale; // Address Latch Enable

modport cpu_mp( inout ad, output ale );
modport per_mp( inout ad, input ale );

endinterface : cpu_if

And connect this to a dut:

cpu_if if( .reset(reset), .clock(clock) );
slave dut( .reset(reset), .clock(clock), .ad(if.ad), .ale(if.ale) );

It compiles OK, but when I load a design that instantiates this
interface, I get the following error!

Illegal output or inout port connection (port 'ad').

How should I deal with bi-directional signals in an interface?
-- Amal
 
On Thu, 27 Mar 2008 07:51:32 -0700 (PDT), Amal <akhailtash@gmail.com>
wrote:

How does one define an inout signal in an interface? I wrote the
following for a cpu bus interface and define two mod_ports as follows:

interface cpu_if( input bit reset, input bit clk );
logic [31:0] ad; // Address/Data
bit ale; // Address Latch Enable

modport cpu_mp( inout ad, output ale );
modport per_mp( inout ad, input ale );

endinterface : cpu_if
Mostly good so far, except that you have a VARIABLE
for the inout port - that won't ever work, since
the things on both sides of an inout (whether it's
in a modport, or a real module port) must be NETS.

You could make the modport have "ref" direction,
but don't expect that to be synthesisable.

So let's suppose that you change the declaration
of "ad" to be

wire [31:0] ad;

And connect this to a dut:

cpu_if if( .reset(reset), .clock(clock) );
slave dut( .reset(reset), .clock(clock), .ad(if.ad), .ale(if.ale) );
This is the weird part. Why are you using an interface to do
the connection, when your DUT module doesn't have an interface port?
It means you're obliged to make cross-module references (if.ad, etc)
which won't work for synthesis.

It compiles OK, but when I load a design that instantiates this
interface, I get the following error!

Illegal output or inout port connection (port 'ad').

How should I deal with bi-directional signals in an interface?
As already pointed out: Make them nets. But I still don't see
why you're using an interface here - unless, perhaps, it's
part of a testbench and you want to take a virtual interface
reference to it?
--
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 Mar 27, 6:58 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Thu, 27 Mar 2008 07:51:32 -0700 (PDT), Amal <akhailt...@gmail.com
wrote:

How does one define an inout signal in an interface? I wrote the
following for a cpu bus interface and define two mod_ports as follows:

interface cpu_if( input bit reset, input bit clk );
logic [31:0] ad; // Address/Data
bit ale; // Address Latch Enable

modport cpu_mp( inout ad, output ale );
modport per_mp( inout ad, input ale );

endinterface : cpu_if

Mostly good so far, except that you have a VARIABLE
for the inout port - that won't ever work, since
the things on both sides of an inout (whether it's
in a modport, or a real module port) must be NETS.

You could make the modport have "ref" direction,
but don't expect that to be synthesisable.

So let's suppose that you change the declaration
of "ad" to be

wire [31:0] ad;

And connect this to a dut:

cpu_if if( .reset(reset), .clock(clock) );
slave dut( .reset(reset), .clock(clock), .ad(if.ad), .ale(if.ale) );

This is the weird part. Why are you using an interface to do
the connection, when your DUT module doesn't have an interface port?
It means you're obliged to make cross-module references (if.ad, etc)
which won't work for synthesis.

It compiles OK, but when I load a design that instantiates this
interface, I get the following error!

Illegal output or inout port connection (port 'ad').

How should I deal with bi-directional signals in an interface?

As already pointed out: Make them nets. But I still don't see
why you're using an interface here - unless, perhaps, it's
part of a testbench and you want to take a virtual interface
reference to it?
--
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.
Yes, this is part of a testbench and it will not be synthesized. The
problem is that if I make it wire, I cannot assign to it in a class
method; i.e., in a driver.

Maybe I should implement the tri-state functionality outside the
interface and driver?
-- Amal
 
On Thu, 27 Mar 2008 21:24:23 -0700 (PDT),
Amal <akhailtash@gmail.com> wrote:

Yes, this is part of a testbench and it will not be synthesized. The
problem is that if I make it wire, I cannot assign to it in a class
method; i.e., in a driver.
Ah, but you can!

Clocking blocks can have either nets or variables as outputs,
and you drive them both in exactly the same way. So the
correct scheme for your testbench-style interface is...

interface cpu_if( input bit reset, input bit clock );
//
wire [31:0] ad; // Address/Data
bit ale; // Address Latch Enable
//
// No point in providing a modport for the DUT,
// because it doesn't expect an interface connection.
//
clocking cpu_cb @(posedge clock);
inout ad; // creates both a clocking input
// and a clocking output for "ad"
output ale;
endclocking
//
// Give the TB synchronous access to what it needs;
// also give it asynchronous access to reset
modport for_testbench (input reset, clocking cpu_cb);
//
endinterface : cpu_if

module test_harness();
// clock and reset generators go here
...
cpu_if if( .reset(reset), .clock(clock) );
slave dut( .reset(reset), .clock(clock),
.ad(if.ad), .ale(if.ale) );
endmodule : test_harness

// This package and stimulus class can be written
// so that it is completely independent of the
// structure of the testbench in which it will be
// used. The stimulus generator merely knows that
// it will connect to *some* instance of a cpu_if.
//
package TB_pkg;
class stimulus_generator; // Many details missing.
//
// Virtual interface connection
protected virtual cpu_if.for_testbench hook;
//
function void connect_to_TB(
virtual cpu_if.for_testbench hook);
// Connect the virtual interface to the real one
this.hook = hook;
endfunction : connect_to_TB
//
task run();
// sync-up with the clock
@(hook.cpu_if);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
// wait until reset has gone away
do @(hook.cpu_cb); while (reset);
// run a bus cycle
hook.cpu_if.ad <= SOME_ADDRESS;
hook.cpu_if.ale <= 1;
@(hook.cpu_cb)
hook.cpu_if.ad <= 'z; // ready for the read data
hook.cpu_if.ale <= 0;
....
endtask : run
//
endclass : stimulus_generator
endpackage : TB_pkg

program simple_test;
import TB_pkg::*;
stimulus_generator sg;
initial begin
// Construct the stimulus generator
sg = new;
// Connect it to the appropriate point
sg.connect_to_TB(test_harness.cpu_if.for_testbench);
// And run the test:
sg.run();
end
endprogram : simple_test

The three-level references...
virtual_intf.clocking_blk.signal
are unfortunate, but there's nothing you can do about that.

Check out Chris Spear's book on SV verification - all this
stuff is nicely described there.
--
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 Fri, 28 Mar 2008 08:38:17 +0000, Jonathan Bromley wrote:

correct scheme for your testbench-style interface is...
Well, it was *nearly* correct.....

task run();
// sync-up with the clock
@(hook.cpu_if);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
......
endtask : run
Ouch! ALL the references to "hook.cpu_if." in this run() task
should instead be "hook.cpu_cb.". The idea is that you reach
through the virtual interface reference to find the
interface.modport, then you find the clocking block that's
exported through the modport, then hit a clockvar inside
the clocking block. So, as I correctly said somewhere else
in the post, you should be doing

virtual_intf_variable.clocking_block.signal <= ...

For example:

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_cb.ale <= 0;

Apologies for the goof.
--
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 Mar 28, 5:38 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Fri, 28 Mar 2008 08:38:17 +0000, Jonathan Bromley wrote:
correct scheme for your testbench-style interface is...

Well, it was *nearly* correct.....



task run();
// sync-up with the clock
@(hook.cpu_if);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
.....
endtask : run

Ouch! ALL the references to "hook.cpu_if." in this run() task
should instead be "hook.cpu_cb.". The idea is that you reach
through the virtual interface reference to find the
interface.modport, then you find the clocking block that's
exported through the modport, then hit a clockvar inside
the clocking block. So, as I correctly said somewhere else
in the post, you should be doing

virtual_intf_variable.clocking_block.signal <= ...

For example:

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_cb.ale <= 0;

Apologies for the goof.
--
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 for making it clear Jonathan. Great description. I will
give it a try.
-- Amal
 
On Fri, 28 Mar 2008 07:47:05 -0700 (PDT), Amal wrote:

Jonathan, it all seems good, except that how do I deal with
asynchronous reset in a clocking block?
Don't try. Pass the asynch reset by another path.
That's why I passed it through the "for_testbench"
modport separately, independently of the clocking.

The way you have coded this,
doesn't it infer a synchronous reset?
In this testbench code, I'm waiting synchronously
for the first clock after reset has gone away. I don't
think there's anything crazy about that, except that
I made one more mistake...

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
// wait until reset has gone away
do @(hook.cpu_cb); while (reset);
oops, that last line should have been:

do @(hook.cpu_cb); while (hook.reset);
Your problem would arise when *generating* an asynch reset.

If the reset had been a member of my clocking block,
I could only drive it synchronously, because

hook.cpu_cb.SOME_SIGNAL <= ...;

clocking drives *always* take effect synchronously
with a clock, even if you actually execute the drive
at a different time - its effect is postponed until
the next clocking event.

How do you code synch vs asynch reset in this task?
My example assumed that reset was *generated* in the
test harness, and only *observed* in the test class.
However, by making reset an output of the test-access
modport, I could generate it in the test class:

task run();
// starting at time zero...
#1 hook.reset = 1; // asynchronous drive at t=1
#4 hook.reset = 0; // asynchronous drive at t=5
@(hook.cpu_cb); // wait for first clock after end of reset
hook.cpu_cb.ale <= 1; // synchronously drive ALE

Not perfect, but it does the job.

Asynch resets are difficult to handle in any synchronous
methodology. They cause serious trouble for assertions too,
because SV assertions need a clock for sampling. What clock
do I use to sample a reset signal? Here's a property that
tries to check that a register goes to zero on reset:

property Q_asynch_reset;
reset |=> (Q==0);
endproperty

How do I check that property? Here are a few attempts:


assert property (@(posedge clock) Q_asynch_reset);

No, that's no good - it waits until the first clock
after reset before checking Q==0. And if reset pulses
entirely within one clock, so that you never see reset==1
on a clock edge, then the property is never tested.


assert property (@(posedge reset) Q_asynch_reset);

No, that's no good either. Assertions see the sampled
value of the signals they check, so we would be testing
the signals "reset" and "Q" as they were JUST BEFORE the
rising edge of reset. Obviously "reset" would be false
at that point, so the assertion would never fire.


assert property (@(negedge reset) Q_asynch_reset);

That's a bit better. On the trailing edge of reset we will
sample reset==1, so the assertion fires; but by that time
(the end of reset) Q will have gone to zero if all is well,
so the test will really work. But it's leaving it very late
to check the effect of reset!


Here's another possibility...

wire #1 delayed_reset = reset;
assert property (@(posedge delayed_reset) Q == 0);

This one is genuinely useful, because it waits until
1 time unit after the assertion of reset before checking
that Q has gone to zero. But the #1 delay looks hacky,
doesn't it?
--
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 Mar 28, 8:31 am, Amal <akhailt...@gmail.com> wrote:
On Mar 28, 5:38 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com
wrote:



On Fri, 28 Mar 2008 08:38:17 +0000, Jonathan Bromley wrote:
correct scheme for your testbench-style interface is...

Well, it was *nearly* correct.....

task run();
// sync-up with the clock
@(hook.cpu_if);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
.....
endtask : run

Ouch! ALL the references to "hook.cpu_if." in this run() task
should instead be "hook.cpu_cb.". The idea is that you reach
through the virtual interface reference to find the
interface.modport, then you find the clocking block that's
exported through the modport, then hit a clockvar inside
the clocking block. So, as I correctly said somewhere else
in the post, you should be doing

virtual_intf_variable.clocking_block.signal <= ...

For example:

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_cb.ale <= 0;

Apologies for the goof.
--
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 for making it clear Jonathan. Great description. I will
give it a try.
-- Amal
Jonathan, it all seems good, except that how do I deal with
asynchronous reset in a clocking block? The way you have coded this,
doesn't it infer a synchronous reset?

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
// wait until reset has gone away
do @(hook.cpu_cb); while (reset);
// run a bus cycle
hook.cpu_if.ad <= SOME_ADDRESS;
hook.cpu_if.ale <= 1;
@(hook.cpu_cb)
How do you code synch vs asynch reset in this task? Correct me if I
am wrong, but the above code looks to me as a synch reset not asynch
reset.
-- Amal
 
On Mar 28, 11:19 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Fri, 28 Mar 2008 07:47:05 -0700 (PDT), Amal wrote:
Jonathan, it all seems good, except that how do I deal with
asynchronous reset in a clocking block?

Don't try. Pass the asynch reset by another path.
That's why I passed it through the "for_testbench"
modport separately, independently of the clocking.

The way you have coded this,
doesn't it infer a synchronous reset?

In this testbench code, I'm waiting synchronously
for the first clock after reset has gone away. I don't
think there's anything crazy about that, except that
I made one more mistake...

task run();
// sync-up with the clock
@(hook.cpu_cb);
// clear down all the signals - NOTE "<=" clocking drive
hook.cpu_if.ale <= 0;
hook.cpu_if.ad <= 'z;
// wait until reset has gone away
do @(hook.cpu_cb); while (reset);

oops, that last line should have been:

do @(hook.cpu_cb); while (hook.reset);

Your problem would arise when *generating* an asynch reset.

If the reset had been a member of my clocking block,
I could only drive it synchronously, because

hook.cpu_cb.SOME_SIGNAL <= ...;

clocking drives *always* take effect synchronously
with a clock, even if you actually execute the drive
at a different time - its effect is postponed until
the next clocking event.

How do you code synch vs asynch reset in this task?

My example assumed that reset was *generated* in the
test harness, and only *observed* in the test class.
However, by making reset an output of the test-access
modport, I could generate it in the test class:

task run();
// starting at time zero...
#1 hook.reset = 1; // asynchronous drive at t=1
#4 hook.reset = 0; // asynchronous drive at t=5
@(hook.cpu_cb); // wait for first clock after end of reset
hook.cpu_cb.ale <= 1; // synchronously drive ALE

Not perfect, but it does the job.

Asynch resets are difficult to handle in any synchronous
methodology. They cause serious trouble for assertions too,
because SV assertions need a clock for sampling. What clock
do I use to sample a reset signal? Here's a property that
tries to check that a register goes to zero on reset:

property Q_asynch_reset;
reset |=> (Q==0);
endproperty

How do I check that property? Here are a few attempts:

assert property (@(posedge clock) Q_asynch_reset);

No, that's no good - it waits until the first clock
after reset before checking Q==0. And if reset pulses
entirely within one clock, so that you never see reset==1
on a clock edge, then the property is never tested.

assert property (@(posedge reset) Q_asynch_reset);

No, that's no good either. Assertions see the sampled
value of the signals they check, so we would be testing
the signals "reset" and "Q" as they were JUST BEFORE the
rising edge of reset. Obviously "reset" would be false
at that point, so the assertion would never fire.

assert property (@(negedge reset) Q_asynch_reset);

That's a bit better. On the trailing edge of reset we will
sample reset==1, so the assertion fires; but by that time
(the end of reset) Q will have gone to zero if all is well,
so the test will really work. But it's leaving it very late
to check the effect of reset!

Here's another possibility...

wire #1 delayed_reset = reset;
assert property (@(posedge delayed_reset) Q == 0);

This one is genuinely useful, because it waits until
1 time unit after the assertion of reset before checking
that Q has gone to zero. But the #1 delay looks hacky,
doesn't it?
--
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.
Quite interesting.

What do you think about coding a single interface; e.g, for two sides
of a data path. Say a TX and RX path for a UART. Would you code a
single interface putting signals for both TX and RX in the same
interface? Or a hierarchical interface, embedding and interface for
TX and another for RX. Or even two separate interfaces?

Right now, I am more interested in testbench, driver/monitor aspects,
but considering synthesis as well. Preferably, I would like to write
an interface block in a way that can be used for both synthesis and
simulation. Providing features for both worlds if possible, without
duplicating code/files.

On another note, clocking blocks are only for simulation, right?
-- Amal
 
On Fri, 28 Mar 2008 10:07:50 -0700 (PDT), Amal wrote:

What do you think about coding a single interface; e.g, for two sides
of a data path. Say a TX and RX path for a UART. Would you code a
single interface putting signals for both TX and RX in the same
interface? Or a hierarchical interface, embedding and interface for
TX and another for RX. Or even two separate interfaces?
For verification, I see an interface as relating to a self-
contained set of signals that could reasonably be stimulated
and/or monitored by a single testbench component or subsystem.
Since the Tx and Rx connections on a UART look essentially
identical (data line, handshakes) I would expect to have two
identical interfaces, one for each, with the testbench
playing different roles (via different modports) for the
two directions. In many situations, though, it might be
a fine judgment. For example, some full-duplex serial
protocols have tight coupling between transmit and receive
sides of the channel, and it would make more sense to have
a common interface and a common verification component for
the two sides.

Right now, I am more interested in testbench, driver/monitor aspects,
but considering synthesis as well. Preferably, I would like to write
an interface block in a way that can be used for both synthesis and
simulation. Providing features for both worlds if possible, without
duplicating code/files.
I sympathise, but experience suggests that it's better not to
try; there are too many things that get in the way. We've had
a few discussions about that here. Unfortunately, the semantics
of modport connection to interfaces is not as well specified as
I would wish, and there are some areas where you may get unexpected
behaviour or differences between simulators if you try to do
clever things with interfaces that combine synthesis-style modports
with testbench-style modports (and other features such as clocking
blocks and BFM tasks). I like to use interfaces both for synthesis
and for testbenches, but I've got into the habit of making the
two sorts completely distinct.

On another note, clocking blocks are only for simulation, right?
Indeed. I ought to know what happens when you give a synthesis
tool an interface containing clocking blocks, but I'm ashamed to
say that I've never tried it. One might hope that the tool
would ignore the clocking... but in fact I don't think it's
that simple, because clocking blocks can imply structures that
would affect synthesis. For example, if you have a clocking
that has a net as one of its outputs, then the clocking block
automatically constructs a continuous assignment to the net.
I can easily imagine that causing trouble for synthesis.
--
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.
 

Welcome to EDABoard.com

Sponsor

Back
Top