How to bind an interface to multiple ports without duplicating code? - interface

In this example, how do I create a single interface bind statement that can be reused for both ports of the module:
module adderSubtractor2(
input clk,
input [7:0] a0,
input [7:0] b0,
input doAdd0, // if this is 1, add; else subtract
output reg [8:0] result0
`ifdef HAS_UNIT_2
,
input [7:0] a1,
input [7:0] b1,
input doAdd1, // if this is 1, add; else subtract
output reg [8:0] result1
`endif
);
// ...
endmodule
interface adderSubtractor_if(
input bit clk,
input [7:0] a,
input [7:0] b,
input doAdd,
input [8:0] result
);
// ...
endinterface: adderSubtractor_if
// BIND STATEMENT(S) HERE
// The test that will be run on the DUT
program automatic test(adderSubtractor_if addSub);
initial begin
// do stuff with interface
end
endprogram // test
// The top level testbench.
module testbench;
reg clk;
adderSubtractor2 dut(.clk (clk));
test test0(dut.adderSubtractor_if0);
`ifdef HAS_UNIT_2
test test1(dut.adderSubtractor_if1);
`endif
// ...
endmodule // testbench

I believe that what you're looking for is parametrizable interface.
In general, masking ports with `ifdef is very risky, and you must have very good reasons to do this. There has already been a discussion on this topic here.
I don't see any reason to use `ifdef in your case. You can:
define a parameter NUM_OF_INSTANCES
define all (except clk and rst) the ports of your module as packed arrays. i.e.
input [1:NUM_OF_INSTANCES][7:0] a;
use "generate for" statement inside the module to instantiate multiple adders
Use parametrizable interface and bind it to the ports of the module in the usual way.
Hope this helps.

You can use macros:
`define BIND_ADD_SUB(INDEX) \
bind adderSubtractor2 adderSubtractor_if adderSubtractor_if``INDEX``( \
.clk(clk), \
.a(a``INDEX``), \
.b(b``INDEX``), \
.doAdd(doAdd``INDEX``), \
.result(result``INDEX``) \
); \
`BIND_ADD_SUB(0)
`ifdef HAS_UNIT_2
`BIND_ADD_SUB(1)
`endif
Then pass dut.adderSubtractor_if0 and dut.adderSubtractor_if1 to your testbench.

Related

Combinatorial assignment to "composite" wire in always block

This is not a blocker that I'm dealing with, just looking for possibly a more elegant way of doing it.
module Ram(RamClk, CKE, CAS, RAS, WE, DQM, BA, A, DQ,
OpEnable, Addr, RdData, WrData, Ready);
input RamClk;
output CKE;
output logic CAS, RAS, WE;
output [1:0] DQM;
output logic [1:0] BA;
output logic [11:0] A;
inout [15:0] DQ;
input OpEnable;
output [23:0] Addr; //24 bits = 16 Mb
output [15:0] RdData;
input [15:0] WrData;
output Ready;
// code omitted here
wire CMD = {CAS,RAS,WE};
// code omitted here
//multiplexers
always #*
begin
if (InitDone == 0)
begin
CMD = InitCmd;
A = InitA;
BA = InitBA;
end
else
begin
//todo: assign the output of the main CMD, A and BA multiplexers
CMD = CMD_NOP;
A = 0;
BA = 0;
end
end
//rest of the module
I have 3 outputs of the module RAS, CAS, WE that I'm combining into a single wire for convenience so that I can assign command values to them as a whole.
Now I need to connect a multiplexer to CMD and I'm doing it in an always block for convenience.
I could do it in separate assign statements in this case of only 2 inputs to the multiplexer using the ternary expression, but let's imagine the multiplexer has more inputs, for more inputs it looks more convenient to use multiple if/else or a case statement which I cannot use with assign as far as I understand.
Now I have an issue that I have to have a reg on the LHS in the always block although it is combinatorial logic in this case and it will not infer a register in this case.
But if I change CMD to reg then I cannot use the {} notation to combine CAS, RAS and WE.
I could replace the CMD with a task or combine/split CAS, RAS and WE outside the module and have them as a single CMD module parameter.
Any more elegant way of expressing this?
When you have have multiple variables that you would like to assign at once, you can use the concatenation operator on the LHS of an expression:
output logic CAS, RAS, WE;
...
logic [2:0] CMD;
assign {CAS, RAS, WE} = CMD;
...
CMD = 3'b011; // Will assign CAS = 0, RAS = 1, WE = 1
...
You can also just not use CMD at all and just use {CAS, RAS, WE} in its place; depending on your style and what makes sense for your project. A side note, ideally, you should be using always_comb over always #(*) and logic over wire and reg (mostly) if you are in a SystemVerilog environment as you seem to be.

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.

assign statement for RTL readability in an interface causes assignments or a buffer in synthesis

We have an interface with modports connectin gmodules that looks something like this:
interface test_interface (clk, in1, out1);
input logic in1;
output logic out1;
input logic clk;
logic mid1;
logic aliased_signal;
modport a_to_b (
input in1,
input clk,
output mid1,
input aliased_signal
);
modport b_to_a (
input clk,
input mid1,
output out1,
output aliased_signal
);
endinterface : test_interface
module top(clock, inpad, outpad);
input logic clock;
input logic inpad;
output logic outpad;
test_interface test_if(.clk(clock), .in1(inpad), .out1(outpad));
a A0(.a2b(test_if));
b B0(.b2a(test_if));
endmodule
module a ( test_interface.a_to_b a2b);
always_ff #(posedge a2b.clk) begin
a2b.mid1 <= a2b.in1 & a2b.aliased_signal;
end
endmodule
module b (test_interface.b_to_a b2a);
assign b2a.aliased_signal = b2a.out1;
always_ff #(posedge b2a.clk) begin
b2a.out1 <= ~b2a.mid1;
end
endmodule
This is a trivial example, but demonstrates the problem. In the real design, we have, for example 32-bit outputs and 8 bits of that may go to one place and 8 bits to another, etc. In the destination, they would like to use names that are more meaningful in the destination module, so are using assignments to create those names in the interface so that the destination code isn't using just part of the bits of an obfuscated bus name in the interface.
While the above simulates fine, the result is assignment statements during synthesis (even if using always_comb obviously) and while we can have the synthesis tool insert buffers to make APR happy, this is not desired when these signals are just used as convenient aliases basically.
Is there an RTL coding style with SV interfaces that would allow such "aliasing" without creating complications downstream in synthesis/APR tools?
Below is a closer example to what I'm trying to do. The "obfuscated_name" signal is an output of the a module because some modules use the name directly from the interface (this could be cleaned up to only do what the a->b connection is doing), but I get the same errors from IUS and DC if I connect to mid2 a->c instead of using the obfuscated_name signal directly.
interface test_interface(clk, in1, out1, out2);
input logic [7:0] in1;
output logic [3:0] out1;
output logic [3:0] out2;
input logic clk;
logic [7:0] obfuscated_name;
logic [3:0] mid1;
logic [3:0] mid2;
modport a_mp (
input clk,
input in1,
output obfuscated_name
);
modport a_to_b (
input clk,
input .mid1(obfuscated_name[3:0]),
output out1
);
modport a_to_c (
input clk,
input obfuscated_name,
output out2
);
endinterface : test_interface
module a ( test_interface.a_mp a_intf);
always_ff #(posedge a_intf.clk) begin
a_intf.obfuscated_name <= ~a_intf.in1;
end
endmodule
module b (test_interface.a_to_b b_intf);
always_ff #(posedge b_intf.clk) begin
b_intf.out1 <= b_intf.mid1;
end
endmodule
module c (test_interface.a_to_c c_intf);
always_ff #(posedge c_intf.clk) begin
c_intf.out2 <= ~c_intf.obfuscated_name[7:4];
end
endmodule
module top( input logic clock,
input logic [7:0] inpad,
output logic [3:0] outpad1,
output logic [3:0] outpad2 );
test_interface test_if(.clk(clock), .in1(inpad), .out1(outpad1), .out2(outpad2));
a A0(.a_intf(test_if));
b B0(.b_intf(test_if));
c C0(.c_intf(test_if));
endmodule
The error I get from IUS is:
ncvlog: *E,MODPXE (test_interface.sv,18|18): Unsupported modport
expression for port identifier 'mid1'.
and DC gives this error:
Error: ./b.sv:1: The construct 'b_intf.mid1 (modport expression without a modport from parent)' is not supported in synthesis. (VER-700)
I'm hoping I'm doing something ignorant here and that this is possible what I'm trying to do.
There is a feature in Verilog called a port expression .name_of_port(expression_to_be_connected_to_port) that most people recognize in a module instance port list that they don't realize can also be used in the module deceleration port list header. This declares the name of the port, and the expression that gets connected to the port. This way a smaller part select of a larger internal bus can be made into a port.
module DUT(input clk, inout .data(bus[7:0]) );
wire [31:0] bus;
endmodule
module top;
reg clock;
wire [7:0] mydata;
DUT d1(.data(mydata), .clk(clock) );
endmodule
In SystemVerilog you can do the same thing with a modport expression.
interface test_interface (clk, in1, out1);
input logic in1;
output logic out1;
input logic clk;
logic mid1;
logic aliased_signal;
modport a_to_b (
input in1,
input clk,
output mid1,
input .aliased_signal(out1) // modport expression
);
modport b_to_a (
input clk,
input mid1,
output out1
);
endinterface : test_interface
module top( input logic clock,
input logic inpad,
output logic outpad );
test_interface test_if(.clk(clock), .in1(inpad), .out1(outpad));
a A0(.a2b(test_if));
b B0(.b2a(test_if));
endmodule
module a ( test_interface.a_to_b a2b);
always_ff #(posedge a2b.clk) begin
a2b.mid1 <= a2b.in1 & a2b.aliased_signal; // port reference to alias
end
endmodule
module b (test_interface.b_to_a b2a);
always_ff #(posedge b2a.clk) begin
b2a.out1 <= ~b2a.mid1;
end
endmodule

Is Reading of a clocking block output in system verilog is allowed?

module input2 (output [3:0] out1, out2, input [3:0] in1, input clk);
clocking c_clk #(posedge clk);
output #2ns out1, temp = in1[1:0];
input in1;
endclocking
clocking d_clk #(posedge clk);
output out2;
input #2ns svar = in1[3:2];
endclocking
assign out1 = c_clk.temp ^ 4'b1101;
assign out2 = d_clk.svar + in1;
endmodule
my tool is giving an error that "Reading of a clocking block output (c_clk.temp ) is not allowed." I have not found any standard for this statement.
Thanks in advance for your help.
The 1800-2012 standard says in 14.3 Clocking block declaration
It shall be illegal to read the value of any clockvar whose
clocking_direction is output.
The reason is an output has no defined sampling semantics. An inout does.