Doubt about SystemVerilog tasks in interfaces

A

Andreas Ehliar

Guest
I am playing around a little bit with SystemVerilog but
I have encountered a problem which I don't really know
how to solve.

My scenario is as follows:

I have created an interface for a certain bus.
Two modports, one for a bus master and one for a bus
slave.
There is also tasks in the interface that allows the
testbench to perform read and write transactions on
the bus.

My problem is that the assignments I have to make
procedurally inside the task in order to read/write
conflicts with continuous assignments made in another
part of the design (even though I never even use that
task anywhere).

The source code included below will give the following
error message in ModelSim 6.3c:

# ** Fatal: (vsim-3838) argh.sv(12): Variable '/argh2/thebus/adr' written by continuous and procedural assignments. See argh.sv(49).

Is this really the correct behavior? If so it seems
like tasks in interfaces aren't as useful as I
initially thought. Is there any sort of workaround
for my problem? I have looked at various tutorials
on the web but never seen this problem mentioned
or any ways around it.


/Andreas


---------------------------------------------
argh.sv follows below:
---------------------------------------------
`timescale 1ns / 10ps

module testmod3(
output reg [31:0] address_o);

assign address_o = 32'hdeadbeef;

endmodule // testmod3


module testmod2(bus.master thebus);
testmod3 foo(.address_o(thebus.adr));

endmodule // testmod2


module argh2;
reg clk;
bus thebus(clk);


testmod2 uut(.*);

endmodule // argh2

interface bus(input clk);

typedef reg [31:0] adr_t;
typedef reg [31:0] dat_t;

adr_t adr; // address bus
dat_t dat_o; // write data bus
dat_t dat_i; // read data bus
reg stb; // strobe
reg we; // indicates write transfer
reg ack; // normal termination

modport master(
output adr, dat_o, stb, we,
input clk, dat_i, ack);


modport slave(
input clk, adr, dat_o, stb, we,
output dat_i, ack);

task read(input adr_t radr, output dat_t rdat);
begin
adr = radr;
stb = 1'b1;
we = 1'b0;

while (!ack) begin
@(posedge clk);
end

stb = 1'b0;
we = 1'b0;

rdat = dat_i;
end
endtask


endinterface // bus
 
On Thu, 11 Oct 2007 10:07:22 +0000 (UTC), Andreas Ehliar
<ehliar-nospam@isy.liu.se> wrote:

I am playing around a little bit with SystemVerilog but
I have encountered a problem which I don't really know
how to solve.

My scenario is as follows:

I have created an interface for a certain bus.
Two modports, one for a bus master and one for a bus
slave.
There is also tasks in the interface that allows the
testbench to perform read and write transactions on
the bus.

My problem is that the assignments I have to make
procedurally inside the task in order to read/write
conflicts with continuous assignments made in another
part of the design (even though I never even use that
task anywhere).
Andreas, does the following work for you?

Make the modport drive a stub variable, which is
then copied on to the real "adr" variable:

interface foo;
// adr is the real thing, master_adr is a stub
reg [...] adr, master_adr;
modport slave (input adr);
modport master (output master_adr); // driven by master
// Procedural copy, will do nothing if master not connected
always @master_adr adr = master_adr;
// Drive from task, will do nothing if task not called
task set_adr(reg [...] A); adr = a; endtask
endinterface

I agree that it's a tiresome mess.
--
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 Andreas

I found that I could get round some of these problems by using
always_comb instead of assign, when dealing with interfaces containing
tasks. The semantics are slightly different, but might not worry you.

Jonathan and I had a longer discussion in the thread:
SystemVerilog Interface style (managing continuous and non-continuous
assignments)

A direct URL is below:
http://groups.google.co.uk/group/comp.lang.verilog/browse_thread/thread/7f0773cc87366b5e/b0bdc2f6bc4abac4?lnk=st&q=#b0bdc2f6bc4abac4

Regards

Andrew
 
On 2007-10-11, Jonathan Bromley <jonathan.bromley@MYCOMPANY.com> wrote:
Andreas, does the following work for you?

Make the modport drive a stub variable, which is
then copied on to the real "adr" variable:

interface foo;
// adr is the real thing, master_adr is a stub
reg [...] adr, master_adr;
modport slave (input adr);
modport master (output master_adr); // driven by master
// Procedural copy, will do nothing if master not connected
always @master_adr adr = master_adr;
// Drive from task, will do nothing if task not called
task set_adr(reg [...] A); adr = a; endtask
endinterface

I agree that it's a tiresome mess.
Hi, it seems to work for me. But as you said, it is
tiresome.

I have been thinking about this and have another
possible solution. I'm removing the task from the
interface altogether and putting the task into a
regular module instead:

module bus_tasks(bus.master thebus);
task read(input [31:0] radr, output reg [31:0] rdat);
begin
@(posedge thebus.clk);

thebus.adr <= radr;
thebus.stb <= 1'b1;
thebus.we <= 1'b0;

while (!thebus.ack) begin
@(posedge thebus.clk);
end

thebus.stb <= 1'b0;
thebus.we <= 1'b0;

rdat = thebus.dat_i;
end
endtask
endmodule


Inside my testbench I can then do something like this:

bus thebus(clk);
bus_tasks bt(thebus);

reg [31:0] dat;
initial begin
// reset stuff
...
...
bt.read(32'hfffffffc0,dat);
$display("Read %08x",dat);
end


What do you think about this solution? Does it have any
hidden pitfalls that I'm not aware of?


/Andreas
 
On 12 Oct, 07:30, Andreas Ehliar <ehliar-nos...@isy.liu.se> wrote:
On 2007-10-11, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com> wrote:



Andreas, does the following work for you?

Make the modport drive a stub variable, which is
then copied on to the real "adr" variable:

interface foo;
// adr is the real thing, master_adr is a stub
reg [...] adr, master_adr;
modport slave (input adr);
modport master (output master_adr); // driven by master
// Procedural copy, will do nothing if master not connected
always @master_adr adr = master_adr;
// Drive from task, will do nothing if task not called
task set_adr(reg [...] A); adr = a; endtask
endinterface

I agree that it's a tiresome mess.

Hi, it seems to work for me. But as you said, it is
tiresome.

I have been thinking about this and have another
possible solution. I'm removing the task from the
interface altogether and putting the task into a
regular module instead:

module bus_tasks(bus.master thebus);
task read(input [31:0] radr, output reg [31:0] rdat);
begin
@(posedge thebus.clk);

thebus.adr <= radr;
thebus.stb <= 1'b1;
thebus.we <= 1'b0;

while (!thebus.ack) begin
@(posedge thebus.clk);
end

thebus.stb <= 1'b0;
thebus.we <= 1'b0;

rdat = thebus.dat_i;
end
endtask
endmodule

Inside my testbench I can then do something like this:

bus thebus(clk);
bus_tasks bt(thebus);

reg [31:0] dat;
initial begin
// reset stuff
...
...
bt.read(32'hfffffffc0,dat);
$display("Read %08x",dat);
end

What do you think about this solution? Does it have any
hidden pitfalls that I'm not aware of?

/Andreas
Thanks all for the useful comments and link to the other thread. I
have been struggling with this one too recently, and was pleased to
see some suggested solutions.
 
On Oct 11, 3:07 am, Andreas Ehliar <ehliar-nos...@isy.liu.se> wrote:
I am playing around a little bit with SystemVerilog but
I have encountered a problem which I don't really know
how to solve.

My scenario is as follows:

I have created an interface for a certain bus.
Two modports, one for a bus master and one for a bus
slave.
There is also tasks in the interface that allows the
testbench to perform read and write transactions on
the bus.

My problem is that the assignments I have to make
procedurally inside the task in order to read/write
conflicts with continuous assignments made in another
part of the design (even though I never even use that
task anywhere).

The source code included below will give the following
error message in ModelSim 6.3c:

# ** Fatal: (vsim-3838) argh.sv(12): Variable '/argh2/thebus/adr' written by continuous and procedural assignments. See argh.sv(49).

Is this really the correct behavior? If so it seems
like tasks in interfaces aren't as useful as I
initially thought. Is there any sort of workaround
for my problem? I have looked at various tutorials
on the web but never seen this problem mentioned
or any ways around it.

/Andreas

---------------------------------------------
argh.sv follows below:
---------------------------------------------
`timescale 1ns / 10ps

module testmod3(
output reg [31:0] address_o);

assign address_o = 32'hdeadbeef;

endmodule // testmod3

module testmod2(bus.master thebus);
testmod3 foo(.address_o(thebus.adr));

endmodule // testmod2

module argh2;
reg clk;
bus thebus(clk);

testmod2 uut(.*);

endmodule // argh2

interface bus(input clk);

typedef reg [31:0] adr_t;
typedef reg [31:0] dat_t;

adr_t adr; // address bus
dat_t dat_o; // write data bus
dat_t dat_i; // read data bus
reg stb; // strobe
reg we; // indicates write transfer
reg ack; // normal termination

modport master(
output adr, dat_o, stb, we,
input clk, dat_i, ack);

modport slave(
input clk, adr, dat_o, stb, we,
output dat_i, ack);

task read(input adr_t radr, output dat_t rdat);
begin
adr = radr;
stb = 1'b1;
we = 1'b0;

while (!ack) begin
@(posedge clk);
end

stb = 1'b0;
we = 1'b0;

rdat = dat_i;
end
endtask

endinterface // bus
Hi Andreas,

This is a classic Verilog bidirectional bus problem. If one driver is
continuously driving

assign address_o = 32'hdeadbeef;

How do expect any other assignment to effect those bus bits? The
simulator doesn't know that the read task is never called. It's there
because someone expect to be able to call it. You really have two
drivers on one bus. Then you need to define adr as a wire and have
have each drive assign 'z when it is not driving. So your read task
will look like

interface bus(input clk);

typedef reg [31:0] adr_t;
typedef reg [31:0] dat_t;

wire adr_t adr; // address bus as a wire
var adr_t adr_reg='z; // address_bus as a reg
assign adr = adr_reg; // has no effect until someone removes the z's
dat_t dat_o; // write data bus
dat_t dat_i; // read data bus
reg stb; // strobe
reg we; // indicates write transfer
reg ack; // normal termination

modport master(
output adr, dat_o, stb, we,
input clk, dat_i, ack);

modport slave(
input clk, adr, dat_o, stb, we,
output dat_i, ack);

task read(input adr_t radr, output dat_t rdat);
begin
adr_reg = radr; // start driving
stb = 1'b1;
we = 1'b0;

while (!ack) begin
@(posedge clk);
end

stb = 1'b0;
we = 1'b0;

rdat = dat_i;
adr_reg = 'z; // disable this driver
end
endtask

endinterface // bus


Your testmod3 will also need to be modified to drive 'z when someone
else wants to drive.


Dave
 

Welcome to EDABoard.com

Sponsor

Back
Top