SystemVerilog -- 6.1 Interface ~ Introduction
SystemVerilog Interface Intro
SystemVerilog 允许我们将多个信号组合在一起,并将它们表示为单个端口。所有这些信号都可以在一个地方声明和维护,并且易于维护。Interface
中的信号由 Interface
实例句柄访问。
Syntax
Interface block
在 interface
和 endinterface
关键字中间定义和描述。它可以像带或不带 port
的 module
一样实例化。
interface [name] ([port_list]);
[list_of_signals]
endinterface
interface
还可以具有 functions
、tasks
、variables
、parameters
,使其更像 class template
。它还能够通过该结构定义不同的模块端口的方向信息策略,以及带有 clocking blocks
的 tb 同步功能。它还可以具有 assertions
、 coverage recording
记录和其他协议检查元素。最后但并非最不重要的一点是,它还可以包含 initial
、always
过程语句和 assign
连续赋值语句。
模块不能在接口中实例化!但是接口可以在模块中实例化。
SystemVerilog 现在作为 HDL 很流行,让我们看看两种情况,其中接口在 Verilog 和 SystemVerilog 中都使用相同的设计。为了在这个介绍性实例中保持简单,我们将创建一个简单的界面。
让我们看看如何在tb中使用接口,并通过端口列表连接到标准Verilog设计。
Interface with a Verilog Design
- 功能和指标定义:
up-down counter
load_en
有效,load
加载到countload_en
无效,down
有效,count倒计时load_en
无效,down
无效,count正常计时rollover
输出指示counter何时从max_value转换到0或0转换到max_value。
- RTL 编写
module counter_ud #(parameter WIDTH = 4) (
input clk,
input rstn,
input load_en,
input down,
input [WIDTH-1:0] load,
output rollover,
output reg [WIDTH-1:0] count
);
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
count <= 0;
end else begin
if (load_en) begin
count <= load;
end else begin
if (down) begin
count <= count - 1;
end else begin
count <= count + 1;
end
end
end
assign rollover = &count;
endmodule
下面声明了一个名为cnt_if
的接口,其中包含一个可参数化的值作为counter信号的宽度。此任务还有一个任务init ()
来分配值。
interface cnt_if #(parameter WIDTH = 4) (input bit clk);
logic rstn;
logic load_en;
logic down;
logic [WIDTH-1:0] load;
logic [WIDTH-1:0] count;
logic rollover;
endinterface
module tb;
// Design with a clock -> here half_period = 10ns => 50 MHZ
always #10 clk = ~clk;
cnt_if cnt_if0(clk);
counter_ud c0 (
.clk (cnt_if0.clk),
.rstn(cnt_if0.rstn),
.load(cnt_if0.load),
.load_en(cnt_if0.load_en),
.down(cnt_if0.down),
.count(cnt_if0.count),
.rollover(cnt_if0.rollover)
);
initial begin
bit load_en, down;
bit [3:0] load;
$monitor("[%0t] down=%0b load_en=%0b load=0x%0h count=0x%0h rollover=%0b", $time, cnt_if0.down, cnt_if0.load_en, cnt_if0.load, cnt_if0.rollover);
// Initialize testbench varibles
clk <= 0;
cnt_if0.rstn <= 0;
cnt_if0.load_en <= 0;
cnt_if0.load <= 0;
cnt_if0.down <= 0;
// Drive design out of reset after 5 clocks
repeat (5) @(posedge clk);
cnt_if0.rstn <= 1;
// Drive inputs after some random delay
for (int i = 0; i < 5; i++) begin
// Drive inputs after some random delay
int delay = $urandom_range (1,30);
#(delay);
// Randomize input values to be driven
std::randomize(load, load_en, down);
// Assign tb values to interface signals
cnt_if0.load <= load;
cnt_if0.load_en <= load_en;
cnt_if0.down <= down;
end
// Wait for 5 clocks and finish simulation
repeat(5) @ (posedge clk);
$finish;
end
endmodule
模拟日志
ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=0 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=1 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0
Interface with a SystemVerilog Design
现在让我们看看如何在tb中使用接口并连接到SystemVerilog设计模块。SystemVerilog允许接受接口作为端口列表,而不是单个信号。在下面显示的设计示例中,我们用用于定义设计功能的接口句柄替换了counter_ud
的端口列表。
`timescale 1ns/1ns
// This module accepts an interface object as the port list
module counter_ud #(parameter WIDTH = 4) (cnt_if _if);
always @(posedge _if.clk or negedge _if.rstn) begom
if (!_if.rstn) begin
_if.count <= 0;
end else begin
if (_if.load_en) begin
_if.count <= _if.load;
end else begin
if (_if.down) begin
_if.count <= _if.count - 1;
end else begin
_if.count <= _if.count + 1;
end
end
end
assign _if.rollover = &_if.count;
endmodule
设计实例传递一个名为cnt_if
的接口句柄,用于驱动来自tb的设计输入。如果需要,可以使用相同的接口句柄来监控设计的输出。
// Interface definition is the same as before
module tb
reg clk;
always #10 clk= ~clk;
cnt_if cnt_if0 (clk);
counter_ud c0 (cnt_if0);
endmodule
模拟日志
ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=0 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=1 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0
What makes it different from Verilog ?
Verilog 通过其模块端口在不同module
之间连接。对于大型设计,这种连接方法可能会变得更加耗时和重复。其中一些端口可能包括与总线协议(如 AXI/AHB)、时钟和复位引脚、进出RAM/memory和其它外围设备的信号相关的信号。
Using Verilog Ports
这是Verilog中传统的端口连接方式。
moduoe d_slave (
input clk,
reset,
enable,
// Many more input signals
output gnt,
irq,
// Many more output signals
);
// Some design functionality
endmodule
module d_top ( [top_level_ports] );
reg [`NUM_SLAVES-1:0] clk;
reg [`NUM_SLAVES-1:0] tb_reset;
// Other declarations
d_slave slave_0 (
.clk(d_clk[0]),
.rest(d_reset[0]),
...
.gnt(d_gnt[0]),
...
);
d_slave slave_1 ( ... );
d_slave slave_2 ( ... );
endmodule
让我们考虑一个场景,在上面显示的设计中有12个slaves。如果在d_slave
模块端口上进行了更改,则该更改也必须反映在d_top
中的所有12个slaves实例连接中。
Disadvantages
使用Verilog端口方法进行连接的一些缺点是:
- 跟踪、调试和维护繁琐
- 设计功能太容易成败
- 设计要求的变化可能需要对多个模块进行修改
- 需要在多个模块、通信协议和其他地方进行复制
Using SystemVerilog Interface
请注意,模块d_top
只是使用接口与slave实例连接,而不是像从前所示重复声明与slave模块的每个信号的连接。
interface slave_if (input logic clk, reset);
reg clk;
reg reset;
reg enable;
reg gnt;
// Declarations for other signals follow
endinterface
module d_slave (slave_if s_if);
always (s_if.enable & s_if.gnt) begin
// Some behavior
end
endmodule
module d_top (input clk, reset);
slave_if slave_if_inst (.clk(clk), .reset(reset));
d_slave slave_0 (.s_if (slave_if_inst));
d_slave slave_1 (.s_if (slave_if_inst));
d_slave slave_2 (.s_if (slave_if_inst));
endmodule
现在,如果接口中的某个信号发生更改,则会自动应用于所有实例。在SystemVerilog中,模块端口列表还可以具有inout
接口类型的端口,而不是通常的input
和output
。
Interface Array
在下面的示例中,在 top_tb模块中创建并实例化了一个名为myInterface的接口,该接口的端口列表为空。省略空端口列表的括号,用分号阶截断语句也是可以的。
// interface myInterface
interface myInterface ();
reg gnt;
reg ack;
reg [7:0] irq;
endinterface
module tb
// Single interface handle
myInterface if0 ();
// An array of interfaces
myInterface wb_if [3:0] ();
endmodule
可以实例化一个名为if0
的接口,并且应通过引用此句柄来访问此接口中的信号。然后,这可用于驱动和采样进入DUT的信号。
我们还可以有一个Interface Array
。这里这个数组用名称wb_if
引用,它有4个接口实例。
module myDesign (myInterface dut_if,
input logic clk);
always @(posedge clk) begin
if (dut_if.ack)
dut_if.gnt <= 1;
end
endmodule
module tb;
reg clk;
myInterface if0;
myDesign top (if0, clk);
myDesign md0 (wb_if[0], clk);
myDesign md0 (wb_if[1], clk);
myDesign md0 (wb_if[2], clk);
myDesign md0 (wb_if[3], clk);
endmodule
当接口被引用为端口时,假定其中的变量和网络分别具有和访问。如果在设计中使用相同的标识符作为接口实例名称和端口名称,则也可以使用隐式端口连接。
module tb;
reg clk;
myInterface dut_if();
// Can use implicit port connection when all port signals have same name
myDesign top (.*);
endmodule