ch6-Interrface

2019-10-31

SystemVerilog Interface

What is an Interface ?

An Interface is a way to encapsulate signals into a block. All related signals are grouped together to form an interface block so that the same interface can be re-used for other projects. Also it becomes easier to connect with the DUT and other verification components.

Example

APB bus protocol signals are put together in the given interface. Note that signals are declared within interface and endinterface.

1
2
3
4
5
6
7
8
9
interface apb_if (input pclk);
logic [31:0] paddr;
logic [31:0] pwdata;
logic [31:0] prdata;
logic penable;
logic pwrite;
logic psel;
endinterface

Why are signals declared logic ?

logic is a new data type that lets you drive signals of this type via assign statements and in a procedural block. Remember that in verilog, you could drive a reg only in procedural block and a wire only in assign statement. But this is only one reason.

Signals connected to the DUT should support 4-states so that X/Z values can be caught. If these signals were bit then the X/Z would have shown up as 0, and you would have missed that DUT had a X/Z value.

How to define port directions ?

​ Interface signals can be used within various verification components as well as the DUT, and hence modport is used to define signal directions. Different modport definitions can be passed to different components that allows us to define different input-output directions for each component.

modport中不需要指定port width

1
2
3
4
5
6
7
8
9
10
11
12
// Filename : myBus.sv
interface myBus (input clk);
logic [7:0] data;
logic enable;

// From TestBench perspective, 'data' is input and 'write' is output
modport TB (input data, output enable);

// From DUT perspective, 'data' is output and 'enable' is input
modport DUT (output data, input enable);
endinterface

Click here to read more on SystemVerilog modports !

How to connect an interface with DUT ?

​ An interface object should be created in the top testbench module where DUT is instantiated, and passed to DUT. It is essential to ensure that the correct modport is assigned to DUT.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Filename : dut.sv
module dut (myBus.DUT busIf);
always @ (posedge clk)
if (busIf.enable)
busIf.data <= busIf.data+1;
else
busIf.data <= 0;
endmodule


// Filename : tb_top.sv
module tb_top;
bit clk;
bit enable;

// Create a clock
always #10 clk = ~clk;

// Create an interface object
myBus busIf (clk);

// Instantiate the DUT; pass modport DUT of busIf
dut dut0 (busIf.DUT);

// Testbench code : let's wiggle enable
initial begin
enable <= 0;
#10 enable <= 1;
#40 enable <= 0;
#20 enable <= 1;
end
endmodule

What are the advantages ?

​ Interfaces can contain tasks, functions, parameters, variables, functional coverage, and assertions. This enables us to monitor and record the transactions via the interface within this block. It also becomes easier to connect to design regardless of the number of ports it has since that information is encapsulated in an interface.

1
2
3
4
5
6
7
8
9
//Before interface
dut dut0 (.data (data),
.enable (enable),
// all other signals
);

// With interface - higher level of abstraction possible
dut dut0 (busIf.DUT);

How to parameterize an interface ?

The same way you would do for a module.

1
2
3
4
5
interface myBus #(parameter D_WIDTH=31) (input clk);
logic [D_WIDTH-1:0] data;
logic enable;
endinterface

What are clocking blocks ?

Signals that are specified inside a clocking block will be sampled/driven with respect to that clock. There can be mulitple clocking blocks in an interface. Note that this is for testbench related signals. You want to control when the TB drives and samples signals from DUT. Solves some part of the race condition, but not entirely. You can also parameterize the skew values.

1
2
3
4
5
6
7
8
9
10
interface my_int (input bit clk);
// Rest of interface code

clocking cb_clk @(posedge clk);
default input #3ns output #2ns;
input enable;
output data;
endclocking
endinterface

In the above example, we have specified that by default, input should be sampled 3ns before posedge of clk, and output should be driven 2ns after posedge of clk.

驱动延时(output,如2ps)或者采样延时(input,如10ps)的意思;驱动延时是正值,表示在时钟沿后2ps驱动信号,采样延时是负值,表示在时钟沿前10ps采样

How to use a clocking block ?

1
2
3
4
5
6
// To wait for posedge of clock
@busIf.cb_clk;

// To use clocking block signals
busIf.cb_clk.enable = 1;

As you can see, you don’t have to wait for the posedge of clk, before you assign 1 to enable. This way, you are assured that enable will be driven 2ns after the next posedge clk.


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!