system verilog interface with function - system-verilog

How can I add a function in an interface? I am trying to implement half adder using interface having function to calculate sum and carry.Following is my code for the same. When trying without functions it as running by using complemented lines.
module top_ha_interface;
ha_interface nh1();
ha h1(nh1);
ha_tb h2(nh1);
endmodule
interface ha_interface;
logic sum,c_out;
logic a,b;
function summ(a,b,output sum,c_out);
sum=a^b;
c_out=a&b;
endfunction
endinterface
module ha(ha_interface nh1);
// assign nh1.sum=nh1.a^nh1.b;
// assign nh1.c_out=nh1.a&nh1.b;
nh1.summ(nh1.a,nh1.b);
endmodule
module ha_tb(ha_interface nh1);
initial
begin
nh1.a=1'b1;
nh1.b=1'b0;
#10 $display($time,"ns\t",nh1.sum,nh1.c_out);
nh1.a=1'b1;
nh1.b=1'b1;
#20 $display($time,"ns\t",nh1.sum,nh1.c_out);
nh1.a=1'b0;
nh1.b=1'b0;
#30 $display($time,"ns\t",nh1.sum,nh1.c_out);
end
endmodule

Function is synthesizable, but that must be used, within any procedural block of verilog. (Like always or initial)
Tasks and void functions are called as statements within procedural
blocks
So required modifications in your code :
module ha(ha_interface nh1);
// assign nh1.sum=nh1.a^nh1.b;
// assign nh1.c_out=nh1.a&nh1.b;
always # (*)
nh1.summ(nh1.a,nh1.b, nh1.sum, nh1.c_out);
endmodule

Related

SystemVerilog - dynamic types in non-procedural context error

I need to bypass programming a bunch of registers in different blocks, the basic infrastructure is something like shown below. This gives me two types of errors:
Dynamic type in non-procedural context
Illegal reference in force/proc assign
Both of these are for line:
force top.design0.register_block.in = in;
Is there any quick solution short of writing an FSM that goes over all register_values?
logic [31:0] register_values[2:0] = {'habcd, 'hbcde, 'hcdef };
class Injector;
task automatic run();
foreach (register_values[i]) force_reg(register_values[i]);
endtask
task automatic force_reg(input logic [31:0] in);
#(negedge top.design0.register_block.clk);
force top.design0.register_block.in = in;
#(negedge top.design0.register_block.clk);
endtask
endclass
module register_block(input logic clk,
input logic[31:0] in);
endmodule
task force_registers();
Injector injector = new();
injector.run();
endtask
module design(input logic clk);
logic[31:0] in;
register_block register_block(clk, in);
endmodule
module top();
logic clk;
design design0(clk);
initial force_registers();
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial #200 $finish;
endmodule
Tried the tasks without the 'automatic' but that gives same error.
Tasks defined in class are automatic by default. The tool complains about in which is a task argument. Since the task is automatic, the argument variable is considered to be automatic as well.
The way around it is to declare the task static:
task static force_reg(input logic [31:0] in);
Assuming, that there is only a single instance of the task 'run' at any time, it should work.

Parameterizing the Bit Widths of fields in a packed struct so that modules can infer bit width if used in port map

Also discussed at:
https://verificationacademy.com/forums/systemverilog/parameterizing-bit-widths-fields-packed-struct-so-modules-can-infer-bit-width-if-used-port-map-virtual-interface-interface-compile-time-configured-struct-bit-width
https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678
I am having trouble accomplishing my intent in SystemVerilog trying to use the latest language features to make my code more elegant and less verbose. For synthesis**
I would like to accomplish the following:
be able to parameterize the bit widths of fields in a packed struct that I want to define ... I have attempted to accomplish this using a parameterized interface construct
I would like for modules with that parameterized interface as an INPUT to the module to be able to infer the bit width of a field inside that packed struct defined inside the interface
I have been mostly successful in past experiments but I have run into an issue.
Please see the following simple interface definition:
interface MyInterface #(int DATA_W, ADDR_W) () ;
typedef struct packed
{ logic valid
; logic [ADDR_W-1:0] addr
; logic [DATA_W-1:0] data
; } SimpleStruct;
SimpleStruct bus;
logic ready;
modport SNK (input bus, output ready);
modport SRC (output bus, input ready);
endinterface
It is easy enough to instantiate an interface and use it at the input of a simple module in my Top module for this example:
module TopTest
( input wire Clock
, input wire Reset
, input wire [31:0] In
, output wire dummyOut
) ;
MyInterface # ( 32, 3 ) my_interface ();
assign my_interface.bus.data = In ;
assign my_interface.bus.addr = 3'h3 ;
InnerTest inst_mod_inner_test
( .Clock( Clock )
, .Reset( Reset )
, .Sink( my_interface )
) ;
assign dummyOut = my_interface.ready ;
endmodule
The problem that I am running into is that I do not want to parameterize the actual module with field bit widths, because I believe that at compile time the bit widths of the fields should be already established and accessible. This seems to not be the case, and I am wondering if there is anything I can do to accomplish inferring the bit width of the packed struct in the interface (remember that is the case because I want it parameterized, I know it is easy to get $bits of a field of a struct that is not defined in an interface but instead defined in a package or module)
module InnerTest
( input wire Clock
, input wire Reset
, MyInterface.SNK Sink
) ;
localparam BIT_WIDTH_SINK_DATA = $bits( Sink.bus.data ) // this line errors out b/c sink is 'virtual'
RAM # ( .DATA_WIDTH( BIT_WIDTH_SINK_DATA ) ) ram ( ... // etc
... other code to drive output ready of interface ...
endmodule
There are many reasons why a designer would want to make a module "parameterizable" and I have taken that approach in the past, but I am very interested in not duplicating information. If I were to take the easy approach, I would simply parameterize my inner test module so that I provided it DATA_WIDTH, but I would then have two numbers to update and a lot of parameters that I feel I do not need. I think it would be most elegant if I could simply infer characteristics of the parameterized struct somehow. The information I am looking for is truly known at compile time in my opinion. I just can't seem to access it, or this is another shortfall of SystemVerilog.
Follow up Q, in Simulation
The workaround mentioned by Dave was very useful when using QuestaSim, but now running into different issue in QuestaSim:
parameter reference "sink.bus.data" through interface port "sink" is not valid when the actual interface in the instance is an arrayed instance element or below a generate construct
What is the workaround for this, I don't understand why simply being in a generate statement would impact things way downstream. In this case i use a generate statement to choose between different FIFO implementations, a few layers above the line of code where the error happens.
typedef sink.bus.data questasim_workaround;
localparam width = $bits(questasim_workaround);
Follow Up Experiment
I have experimented with passing in type instead of restricting myself to passing in DATA_W.
interface MyInterface #(int ADDR_W, type DATA_TYPE) () ;
typedef struct packed
{ logic valid
; logic [ADDR_W-1:0] addr
; DATA_TYPE data
; } SimpleStruct;
SimpleStruct bus;
logic ready;
modport SNK (input bus, output ready);
modport SRC (output bus, input ready);
endinterface
This allows for more flexibility. I have observed that Vivado Simulator and Synthesis tools can handle an example like this without issue.
module SomeModule
( MyInterface myInt
blah...
);
localparam SOMETHING = $bits(myInt.DATA_TYPE);
// or equivalently
localparam SOMETHING_ELSE = $bits(myInt.data);
// or even this, for needs of a internal signal for pipelined processing steps
MyInterface # ($bits(myInt.addr), myInt.DATA_TYPE) internal_0 () ;
In place of this in QUestaSim we have had to implement Dave's work around:
module SomeModule
( MyInterface myInt
blah...
);
// this gets less elegant :/
typedef myInt.data questasim_workaround_data;
typedef myInt.addr questasim_workaround_addr;
localparam SOMETHING = $bits(questasim_workaround_data);
// or equivalently
localparam SOMETHING_ELSE = $bits(questasim_workaround_data);
// or even this, for needs of a internal signal for pipelined processing steps
MyInterface # ($bits(questasim_workaround_addr), questasim_workaround_data) internal_0 () ;
The current SystemVerilog BNF does not allow any dotted "." names in a parameter initialization. But you can get around this by using a typedef instead
interface MyInterface #(int DATA_W=0, ADDR_W=0) () ;
typedef logic [DATA_W-1:0] data_t;
...
endinterface
module InnerTest
( input wire Clock
, input wire Reset
, MyInterface.SNK Sink
) ;
typedef Sink.data_t data_t;
localparam BIT_WIDTH_SINK_DATA = $bits( data_t );
...
endmodule
Whenever you see an error that is unexpected in Vivado around "[Synth 8-27] scoped/hierarchical type name not supported" ... check to see if the instantiation port map matches all the names of the actual module definitions portmap. That was the issue in Vivado only with this code. The spelling didn't match and instead of a "[Synth 8-448] named port connection 'clkkkk' does not exist" I got a "[Synth 8-27] scoped/hierarchical type name not supported" error
As explained in: https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678

how to generate array of interfaces having different connections?

I have declared following interface:
interface data_x #(parameter g_DataWidth = 8)
(input ckrs_t ClkRs_ix);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
always_comb begin
ClkRs = ClkRs_ix;
end
endinterface
The interface has data bus and the data enable, and it is as well associated with the clock and reset signal, which is a typedef ckrs_t.
I have a module, which accepts as an argument array of those interfaces:
module fourmclinks
(...
data_x packet_ox[NUMBER_OF_GBT_LINKS-1:0],
data_x packet_ix[NUMBER_OF_GBT_LINKS-1:0],
...
);
The problem I have is, that I need to declare in top-level entity an array of those data_x interfaces, but each time use different ClkRs_ix input clock. (It is used in the gbts, where each receiver has its own clock and reset signal).
I tried many things, including this one:
ckrs_t txclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
data_x #(.g_DataWidth(g_FrameSize)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++) begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs_ix.clk =
GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs_ix.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
Hence making empty/virtual/unassigned/... interface array declaration, and then in generate loop assign correct signals to it. This simulates, but quartus does not compile it claiming
value cannot be assigned to input "ClkRs_ix".
How to correctly generate array of interfaces, each having different input connection? Please help
I'm bit smarter now, so here is the solution to the problem. But first issues:
it is not possible just to remove 'input' direction from the port declaration in the data_x interface declaration above. If this is done, one has to then manually assign clock and reset lines for every instance of the data_x object. This is indeed possible, but one loses all the beauty of having the clock and reset automatically assigned during the instantiation of the interface
it is not possible either in this particular case to make a virtual interface, and connect the signals in the for loop. Root cause of this is the presence of always_comb, which takes in the input reset/clock and assigns it to the internal signals. So this assignment, together with manual assignment of reset and clock in the top-level entity results in driving those signals from two sources, which Quartus will not digest
So the only possible way, which I found is following:
Declare the data_x interface to generate the always_comb on demand:
interface data_x #(
parameter g_hasClock = 1,
parameter g_DataWidth = 8)
(
input ckrs_t ClkRs_ix
);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
generate
if(g_hasClock) begin
always_comb begin
ClkRs = ClkRs_ix;
end
end
endgenerate
endinterface // data_x
Instantiate the interface with unbound ClkRs_ix. Note the usage of g_hasClock, which instantiates the data_x interface without always_comb block, hence Quartus stops complaining about multiple drivers:
data_x #(.g_DataWidth(g_FrameSize),
.g_hasClock(0)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
And then generate interface with different clocks:
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++)
begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs.clk = GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
This works. It is not so nice because we have to do it manually. Just for sake of completeness: if the clocks for all interfaces is the same, all that code above boils down to this snippet:
ckrs_t txclock_x, rxclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
I'm sure this is not the best solution ever, but it is compilable and gives the result needed.

Creating a Verilog wrapper for System Verilog DUT that contains an interface

Basically, I have a system verilog design that I need to integrate in a simulation framework that's in verilog. So I need to create a wrapper in order to interface with the DUT but am having issues trying to do so.
The interface to the DUT has an interface block as well as other inputs and outputs. The interface to the DUT is seen below
interface ifc(input clk, input rst);
logic [`DATA_WIDTH-1:0] data;
logic valid;
modport in(
input data,
input valid
)
modport out(
output data,
output valid
)
endinterface
The DUT uses the interface as such
module DUT(
ifc.in in0,
ifc.in in1,
ifc.out out0,
ifc.out out1,
output error);
....
endmodule
I created an instance of the interface block and assigned the corresponding signals to the interface and passed the interface into the DUT.
module sim(input clk, input rst,
input in0, input in1,
input ivalid0, input ivalid1,
output out0, output out1
output ovalid0, output ovalid1 );
ifc if0(.clk(clk), .rst(rst));
ifc if1(.clk(clk), .rst(rst));
assign if0.data = in0;
assign if1.data = in1;
assign if0.valid = ivalid0;
assign if1.valid = ivalid1;
assign out0 = if0.data;
assign out1 = if1.data;
assign ovalid0 = if0.valid ;
assign ovalid1 = if1.valid ;
DUT dut(
.in0(if0.in),
.in1(if1.in),
.out0(if0.out),
.out1(if1.out)
);
endmodule
When I try and simulate in Modelsim, I get an error that This or another usage of 'if0.in' inconsistent with 'modport' object. In all the examples I have seen, they use a SystemVerilog wrapper for a Verilog DUT, but I have not seen any the other way around. Is it even possible to do it the other way around? How would I go about instantiating a systemverilog module with an interface in a Verilog wrapper.
Thanks for any help.
Your DUT instantiation is incorrect syntax. When you instantiate a module with interfaces, you just connect the interface. You don't include the modport. Try this:
DUT dut(
.in0(if0),
.in1(if1),
.out0(if0),
.out1(if1)
);

SystemVerilog Clocking Blocks in Bi-Directional Interface

Let's say I have a bi-directional interface. I want the TB to be able to receive data from the DUT and I want the TB to be able to drive data to the DUT. I need to put in a clocking block in my code because I have race-condition issues. I can fix these issues by putting in a little #1 in the right spot, but I know that a clocking block is the correct solution. The part I'm having difficulty with is the bi-directional part. If it was one direction I might be OK, but the syntax for bi-directional interface is tripping me up. Is the right solution to make 2 clocking blocks, or 2 modports, or something else entirely?
interface MyInterface
(input bit i_Clk);
logic [15:0] r_Data;
logic r_DV = 1'b0;
clocking CB #(posedge i_Clk);
default input #1step output #1step;
endclocking : CB
task t_Clock_Cycles(int N);
repeat (N) #(posedge i_Clk);
endtask : t_Clock_Cycles
modport Driver (clocking CB, output r_Data, r_DV);
modport Receiver (clocking CB, input r_Data, r_DV);
endinterface : MyInterface
package MyPackage;
class MyDriver;
virtual MyInterface.Driver hook;
function new(virtual MyInterface.Driver hook);
this.hook = hook;
endfunction : new
task t_Drive(input [15:0] i_Data);
forever
begin
hook.CB.r_Data = i_Data;
hook.CB.r_DV = 1'b1;
hook.CB.t_Clock_Cycles(1);
end
endtask : t_Drive
endclass : MyDriver
endpackage : MyPackage
module MyModule;
import MyPackage::*;
logic r_Clk = 1'b0;
MyInterface hook(.i_Clk(r_Clk));
always #5 r_Clk = ~r_Clk;
MyDriver d1 = new(hook.Driver);
initial
begin
d1.t_Drive(16'hABCD);
end
endmodule // MyModule
The whole point of using a clocking block is to declare which signals you want to access synchronously. You should add your signals to the clocking block:
clocking CB #(posedge i_Clk);
default input #1step output #1step;
inout r_Data;
inout r_DV;
endclocking : CB
Since you also want to have different access permissions for your driver and receiver, this means you'll need two different clocking blocks:
clocking CB_driver #(posedge i_Clk);
default input #1step output #1step;
output r_Data;
output_DV;
endclocking : CB_driver
// ... direction reversed for CB_receiver
Unfortunately, it's not possible to say that you have a reference to a certain clocking block inside your driver/receiver classes:
class Driver
virtual MyInterface.CB_driver hook; // !!! Not allowed
endclass
If you want to restrict your driver to only be able to drive through CB_driver, you can use a modport:
interface MyInterface;
modport Driver(CB_driver);
endinterface
class Driver;
virtual MyInterface.Driver hook;
endclass
This way you can reference hook.CB_driver when driving your signals. The same goes for your receiver.