call by reference in a task

  • Thread starter rajatkmitra@gmail.com
  • Start date
R

rajatkmitra@gmail.com

Guest
cant get this to work any thoughts or alternatives??


module transactor();

task foo;

input a;
input b;
input c;

begin
force a = 1'b1;
force b = 1'b0;

if(~c) $display("XOR Failure...");
end

endtask//foo

initial
begin
foo(`DUT.pin1 , `DUT.pin2, `DUTpin3);
end


endmodule //trans
 
On Thu, 16 Jul 2009 20:12:00 -0700 (PDT), <rajatkmitra@gmail.com>
wrote:

module transactor();

task foo;

input a;
input b;
input c;

begin
force a = 1'b1;
force b = 1'b0;

if(~c) $display("XOR Failure...");
end

endtask//foo

initial
begin
foo(`DUT.pin1 , `DUT.pin2, `DUTpin3);
end


endmodule //trans
1)
Don't use "force" unless you absolutely must. It's
hard work for the simulator, and confusing for the reader.
Just write to the signals you want to update.

2)
Your task is, I guess, testing an XOR whose inputs
are 'a' and 'b' and whose output is 'c'. If I'm right,
your code is very, very wrong in several ways.

Here's why:

Your subject line suggests you understand what "call
by reference" means. You also need to understand
that there is no such thing as call by reference in
standard Verilog (it has been added to SystemVerilog).
The arguments of a task are local variables of the
task, and are passed by copy-in, copy-out. Copy-in
works for input and inout arguments; copy-out for
output and inout.

So your "force" statements are only modifying the
task's argument locals, and are not affecting
your DUT signals.

OK, so let's make arguments a,b outputs:

task foo (input c, output a, output b); // Verilog-2001 syntax
begin
a = 1; b = 0; // drive stimulus - don't need force
if (~c) $display("error");
end
endtask

initial foo(`DUT.pin1, `DUT.pin2, `DUT.pin3);

This is still wrong. foo.a and foo.b are updated, but
DUT.pin1 and DUT.pin2 will not be updated until after
the task exits. Meanwhile, the value of foo.c was
copied in from DUT.pin3 at the moment the task was
called, so does NOT reflect the new values on a,b.

OK, so let's take the task out of it altogether
for clarity:

initial begin
`DUT.pin1 = 1;
`DUT.pin2 = 0;
#1; // give time for DUT output to settle
if (~`DUT.pin3) $display("error");
...

NOTE THE TIME DELAY; you can't expect the DUT
output to update by magic between writing to
its inputs and testing the output.

Problem is, of course, that the packaging of this
code is now dreadful. So how do we do it? Answer:
Package the transactor not as a task but as a
MODULE so that values can be passed through its
ports.

module test_transactor
( output reg a // will drive DUT inputs
, output reg b
, input c // will monitor DUT output
);

// A task you can call to create stimulus
task drive(input stim_a, stim_b);
begin
a = stim_a; // write to module-level variables
b = stim_b;
#1; // allow settling time
end
endtask

// A task you can call to check output
task check (input expected);
if (c !== expected) $display("error: bad output");
endtask

endmodule

Now build a testbench that connects your DUT to the tester:

module testbench;

// structural connections between tester and DUT
wire in0, in1, out;
test_transactor tester(.a(in1), .b(in2), .c(out));
DUT dut(.pin1(in1), .pin2(in2), .pin3(out));

And now, in that same module, you can use the test transactor
to manipulate the DUT:

initial begin
tester.drive(0,0);
tester.check(0);
tester.drive(0,1);
tester.check(1);
tester.drive(0, 1'bx);
tester.check(1'bx);
....

HTH
--
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 17, 3:52 am, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
On Thu, 16 Jul 2009 20:12:00 -0700 (PDT), <rajatkmi...@gmail.com
wrote:





module transactor();

task foo;

 input a;
 input b;
 input c;

begin
 force a = 1'b1;
 force b = 1'b0;

 if(~c) $display("XOR Failure...");
end

endtask//foo

initial
begin
  foo(`DUT.pin1 , `DUT.pin2, `DUTpin3);
end

endmodule //trans

1)
Don't use "force" unless you absolutely must.  It's
hard work for the simulator, and confusing for the reader.
Just write to the signals you want to update.

2)
Your task is, I guess, testing an XOR whose inputs
are 'a' and 'b' and whose output is 'c'.  If I'm right,
your code is very, very wrong in several ways.

Here's why:

Your subject line suggests you understand what "call
by reference" means.  You also need to understand
that there is no such thing as call by reference in
standard Verilog (it has been added to SystemVerilog).
The arguments of a task are local variables of the
task, and are passed by copy-in, copy-out.  Copy-in
works for input and inout arguments; copy-out for
output and inout.

So your "force" statements are only modifying the
task's argument locals, and are not affecting
your DUT signals.

OK, so let's make arguments a,b outputs:

  task foo (input c, output a, output b); // Verilog-2001 syntax
    begin
      a = 1; b = 0;  // drive stimulus - don't need force
      if (~c) $display("error");
    end
  endtask

  initial foo(`DUT.pin1, `DUT.pin2, `DUT.pin3);

This is still wrong.  foo.a and foo.b are updated, but
DUT.pin1 and DUT.pin2 will not be updated until after
the task exits.  Meanwhile, the value of foo.c was
copied in from DUT.pin3 at the moment the task was
called, so does NOT reflect the new values on a,b.

OK, so let's take the task out of it altogether
for clarity:

  initial begin
    `DUT.pin1 = 1;
    `DUT.pin2 = 0;
    #1;  // give time for DUT output to settle
    if (~`DUT.pin3) $display("error");
    ...

NOTE THE TIME DELAY; you can't expect the DUT
output to update by magic between writing to
its inputs and testing the output.

Problem is, of course, that the packaging of this
code is now dreadful.  So how do we do it?  Answer:
Package the transactor not as a task but as a
MODULE so that values can be passed through its
ports.

  module test_transactor
    ( output reg a   // will drive DUT inputs
    , output reg b
    , input c        // will monitor DUT output
    );

    // A task you can call to create stimulus
    task drive(input stim_a, stim_b);
      begin
        a = stim_a;  // write to module-level variables
        b = stim_b;
        #1;  // allow settling time
      end
    endtask

    // A task you can call to check output
    task check (input expected);
      if (c !== expected) $display("error: bad output");
    endtask

  endmodule

Now build a testbench that connects your DUT to the tester:

  module testbench;

    // structural connections between tester and DUT
    wire in0, in1, out;
    test_transactor tester(.a(in1), .b(in2), .c(out));
    DUT dut(.pin1(in1), .pin2(in2), .pin3(out));

And now, in that same module, you can use the test transactor
to manipulate the DUT:

    initial begin
      tester.drive(0,0);
      tester.check(0);
      tester.drive(0,1);
      tester.check(1);
      tester.drive(0, 1'bx);
      tester.check(1'bx);
      ....

HTH
--
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.- Hide quoted text -

- Show quoted text -
Hi Jonathan,
Your analysis is very to the point and extremely helpful. The
unfortunate part of my verification effort is that I have a test bench
that has several transactors that only interface to selected I/O on
the DUT. The specific test I am running is to determine the integrity
of the pad frame( much like a continuity test) and this requires me to
force signals inside the DUT to verify that the signals make it all
the way through the pad frame and eventually to the pad. So in my test
( that runs internally inside an ATE transactor ), I need to stimulate
the DUT's core control signals and see that the stimulus makes it all
the way to the Pad Frame and then to the Pad. Therefore the forces ( I
personally dont like doing it this way ) on hierarchial referenced
signals.
I understand that verilog tasks operate by copying arguments onto a
stack much like passing values to a function. I was wondering if there
was any way to assign values to hierarchial referenced signals that
you might be aware of..System Verilog appears to only allow passing
references of data structures ( arrays, lists etc ) but not
necessarily hierarchial referenced signals. Any thoughts here ??
Thanks,
Raj
 
Jonathan Bromley <jonathan.bromley@mycompany.com> wrote:

Your subject line suggests you understand what "call
by reference" means. You also need to understand
that there is no such thing as call by reference in
standard Verilog (it has been added to SystemVerilog).
The arguments of a task are local variables of the
task, and are passed by copy-in, copy-out. Copy-in
works for input and inout arguments; copy-out for
output and inout.
Call by value, call by result, and call by value result.

-- glen
 
On Fri, 17 Jul 2009 21:11:34 +0000 (UTC), glen herrmannsfeldt wrote:

Jonathan Bromley <jonathan.bromley@mycompany.com> wrote:
[...]
The arguments of a task are local variables of the
task, and are passed by copy-in, copy-out. Copy-in
works for input and inout arguments; copy-out for
output and inout.

Call by value, call by result, and call by value result.
Indeed so, but I find the "copy-in/copy-out" descriptions
clearer - particularly for "call by result" which seems
utterly opaque to me. In fact, I think you'll find
that Fortran (which, at least in its "classic" versions,
did the same) referred to it as "call by value-return".

Niklaus Wirth, he of Pascal fame, said (at least) once
when asked how to pronounce his name:

You can call me by name, in which case I'm
"veert", or call me by value, Worth.
--
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, 17 Jul 2009 13:59:59 -0700 (PDT),
<rajatkmitra@gmail.com> wrote:

Therefore the forces ( I
personally dont like doing it this way ) on hierarchial referenced
signals.
Sure - hence my comment about "only use forces if you really must".
Sorry, I think I perhaps misread the tone of your post. You had
kindly trimmed your example to a super-simplified form and I
incorrectly guessed that you were new to this game.

I understand that verilog tasks operate by copying arguments onto a
stack
careful! If the task is static (the default) then the args
are copied to/from static local variables. However, if you
declare

task automatic (...args...)

then you do indeed get a fresh set of locals and arguments
(probably on a stack, as you say) for each invocation.

any way to assign values to hierarchial referenced signals that
you might be aware of..System Verilog appears to only allow passing
references of data structures ( arrays, lists etc ) but not
necessarily hierarchial referenced signals. Any thoughts here ??
Well... you can have "ref" arguments in SV, so it would be OK
to do this...

task automatic do_one_XOR_test
( input [1:0] stimulus
, ref in0, ref in1, ref out
);
reg correct_xor;
correct_xor = ^stimulus;
{in1, in0} = stimulus;
#1 assert (out === correct_xor);
endtask

and then call the task, hooking it to hierarchically named signals:

initial begin
reg [1:0] stim;
stim = 0;
repeat (4) begin
do_one_XOR_test(stim, `DUT.in1, `DUT.in2, `DUT.out);
stim++;
end
end

But I'm not sure whether it's OK to "force" through a ref
argument - need to check the LRM... <rummage, rummage>
yes, it seems to be OK - BUT ONLY if the `DUT. signals
are variables (regs) - it doesn't work if they are nets,
because you can't pass a net through a ref argument.

I think there may be other approaches to this, borrowing
ideas from SV testbench methodologies like OVM and VMM.
Have you read Chris Spear's book on SV for verification?
It has some good advice on the use of virtual interfaces
to get connections into your DUT hierarchy. You may also
find some interesting hints in the paper my colleague
Doug Smith presented at SNUG earlier this year: you can
get it at www.snug-universal.org, or
http://www.doulos.com/knowhow/sysverilog/SNUG09_SanJose/

An interesting problem - 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.
 
Jonathan Bromley <jonathan.bromley@mycompany.com> wrote:
(snip, I wrote)

Call by value, call by result, and call by value result.

Indeed so, but I find the "copy-in/copy-out" descriptions
clearer - particularly for "call by result" which seems
utterly opaque to me. In fact, I think you'll find
that Fortran (which, at least in its "classic" versions,
did the same) referred to it as "call by value-return".
I think I have used that one, too.

Fortran allows either reference or value/result(or return).
It pretty much always has and still does.

The first ones I used, the OS/360 compilers, pass scalar
variables that way. Well, the called routine copies to a local
variable, and copies back before returning. For S/360, and possibly
many others, it is more efficient to access local variables in
both instructions and time.

-- glen
 
As Jonathan says, even with SystemVerilog ref arguments, you cannot
pass a net by reference.

There is a way you can effectively do it, using virtual interfaces.
Here is some untested code off the top of my head:

interface net_ref (inout wire w);
// Relying on port collapsing to make w the same as the net
connected to it.
// Otherwise you would have to use a separate alias statement to
alias the
// desired net to a net declared locally to the interface.
endinterface

module transactor;

net_ref pin1(`DUT.pin1), pin2(`DUT.pin2), pin3(`DUT.pin3);

task foo (input virtual net_ref a, b, c);
force a.w = 1'b0;
force b.w = 1'b1;
#1 // still need this delay!

if (~c.w) $display("XOR Failure...");
endtask

initial
begin
foo (pin1, pin2, pin3);
end

endmodule


Note that you still need a delay between forcing the inputs and
checking the outputs. Some simulators may evaluate fanout of a signal
immediately for writes to those signals, so that the output changes
are immediately visible, but you can't count on that. And it is even
less likely when the writing to the signals is done indirectly through
a virtual interface or ref argument, so that the fanout cannot be
statically inlined at compile time.
 
On Jul 18, 1:14 pm, sh...@cadence.com wrote:
As Jonathan says, even with SystemVerilog ref arguments, you cannot
pass a net by reference.

There is a way you can effectively do it, using virtual interfaces.
Here is some untested code off the top of my head:

interface net_ref (inout wire w);
  // Relying on port collapsing to make w the same as the net
connected to it.
  // Otherwise you would have to use a separate alias statement to
alias the
  // desired net to a net declared locally to the interface.
endinterface

module transactor;

net_ref pin1(`DUT.pin1), pin2(`DUT.pin2), pin3(`DUT.pin3);

task foo (input virtual net_ref a, b, c);
  force a.w = 1'b0;
  force b.w = 1'b1;
  #1 // still need this delay!

  if (~c.w) $display("XOR Failure...");
endtask

initial
begin
  foo (pin1, pin2, pin3);
end

endmodule

Note that you still need a delay between forcing the inputs and
checking the outputs.  Some simulators may evaluate fanout of a signal
immediately for writes to those signals, so that the output changes
are immediately visible, but you can't count on that.  And it is even
less likely when the writing to the signals is done indirectly through
a virtual interface or ref argument, so that the fanout cannot be
statically inlined at compile time.
Hi SH and Jonathan,
I will give this a try. Since this transactor is compiled as a part
of an AMS compile
NCSIM may not directly honor System Verilog in it. I will create a sub
transactor below
the main one and compile it as a part of the System Verilog code.
Thanks
Raj
 

Welcome to EDABoard.com

Sponsor

Back
Top