1. Interface 初识
接口是一种将信号封装到block中的方法。
语法
interface[name]([port_list]);
[list_of_signals]
endinterface
接口还可以具有函数、任务、变量和参数,使其更像一个类模板。它还可以通过 modport 结构定义不同模块端口的方向信息策略,以及带有时钟块的 testbench 同步功能。它还可以有断言、覆盖率记录和其他协议检查元素。它还可以包含initial过程和always过程以及连续赋值(assign)语句。
模块不能在接口中实例化! 但是接口可以在模块中实例化。
interface apb_if (input pclk);
logic[31:0] paddr;
logic[31:0] pwdata;
logic[31:0] prdata;
logic penable;
logic pwrite;
logic psel;
endinterface
1.如何定义端口方向?
接口信号可用于各种验证组件以及 DUT,使用 modport 来定义信号方向。不同的 modport 定义可以传递给不同的组件,这样我们就可以为每个组件定义不同的输入输出方向。
interface myBus (input clk);
logic[7:0] data;
logic enable;
// From TestBench perspective, 'data' is input and 'write' is output
modport TB (input data, clk,output enable);
// From DUT perspective, 'data' is output and 'enable' is input
modport DUT (output data,input enable, clk);
endinterface
2.如何连接interface与 DUT?
接口对象应该在实例化 DUT 的顶级 testbench 模块中创建,并传递给 DUT。确保为 DUT 分配正确的模型是非常重要的。
testbench模块中,接口对象在DUT前创建。
module dut (myBus busIf);
always @(posedge busIf.clk)
if(busIf.enable)
busIf.data <= busIf.data+1;
else
busIf.data <=0;
endmodule
// Filename : tb_top.sv
module tb_top;
bit clk;
// 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
initialbegin
busIf.enable <=0;
#10 busIf.enable <=1;
#40 busIf.enable <=0;
#20 busIf.enable <=1;
#100 $finish;
end
endmodule
2.有什么好处?
接口可以包含任务、函数、参数、变量、函数覆盖和断言(tasks, functions, parameters, variables, functional coverage, and assertions.)。这使我们能够通过该块中的接口监视和记录事务。由于信息封装在一个接口中,不管它有多少个端口,连接到设计也变得更加容易。
3.如何参数化接口?
接口定义可以使用与模块定义相同的方式利用参数和参数重定义。下面的示例演示如何在接口定义中使用参数
interface myBus #(parameter D_WIDTH=31)(input clk);
logic [D_WIDTH-1:0] data;
logic enable;
endinterface
interface simple_bus #(AWIDTH = 8, DWIDTH = 8) (input logic clk); // Define the interface
logic req, gnt;
logic [AWIDTH-1:0] addr;
logic [DWIDTH-1:0] data;
logic [1:0] mode;
logic start, rdy;
endinterface : simple_bus
clocking block
时钟块内指定的信号将相对于该时钟进行采样/驱动。在一个接口中可以有多个时钟块。
注意,这是用于 testbench 相关信号。控制 TB 何时驱动信号,何时从 DUT 采样信号。 解决了竞争条件的一部分,但不是全部。倾斜值(skew values)可以参数化。
interface my_int (inputbit clk);
// Rest of interface code
clocking cb_clk @(posedge clk);
default input #3ns output #2ns;
input enable;
output data;
endclocking
endinterface
在上面的例子中,指定默认情况下,input应该在 clk 的posedge 之前3ns 采样,output应该在clk 的posedge 之后2ns 被驱动。
// To wait for posedge of clock
@busIf.cb_clk;
// To use clocking block signals
busIf.cb_clk.enable =1;
在enable 赋值1之前,您不必等待 clk 的posedge。 这样,您可以确保在下一个posedge clk 后2ns 驱动enable。
clocking
它是与特定时钟同步的信号集合,有助于指定时钟与信号之间的定时要求。
这将使测试编写人员能够更多地关注事务,而不用担心信号什么时候会与时钟交互。一个 testbench 可以有许多时钟块,但是每个时钟只有一个时钟块。
语法
[default] clocking [identifier_name] @ [event_or_identifier]
default input #[delay_or_edge] output #[delay_or_edge]
[list of signals]
endclocking
delay_value表示一个信号被采样或驱动时离时钟事件有多少个时间单位的偏移。如果没有指定默认偏移,那么在指定的事件之后,所有的输入信号将采样 # 1步,输出信号驱动0ns。
2. interface intro
在 Verilog 和 SystemVerilog 中使用相同设计的接口。
Verilog 设计的interface
如何在测试平台中使用接口,并使用端口列表连接到标准的 Verilog 设计。下面显示的代码是 Verilog 编写的上下计数器的设计。这个模块接受一个参数来决定计数器的宽度。它还接受一个输入加载值 load,该加载值仅在 load_en 为1时加载到计数器中。
当输入向下为1时,计数器开始向下计数,否则向上计数。翻转输出指示计数器何时从最大值转换为0或从0转换为最大值。
module counter_ud #(parameter WIDTH =4)(
input clk,
input rstn,
inputwire[WIDTH-1:0] load,
input load_en,
input down,
output rollover,
outputreg[WIDTH-1:0] count
);
always @(posedge clk ornegedge rstn)begin
if(!rstn)
count <=0;
elseif(load_en)
count <= load;
else begin
if(down)
count <= count -1;
else
count <= count +1;
end
end
assign rollover =&count;
endmodule
下面声明了一个名为 cnt_if 的接口,并使用可参数化的值作为计数器信号的宽度。 这个任务还有一个task init() 来赋值。
interface cnt_if #(parameter WIDTH = 4) (input bit clk);
logic rstn;
logic load_en;
logic [WIDTH-1:0] load;
logic [WIDTH-1:0] count;
logic down;
logic rollover;
endinterface
module tb;
reg clk;
// TB Clock Generator us