system verilog中的interface

system verilog中的interface是为了把测试平台和DUT分割开来而提出来的类型。

interface结构

一个常用的interface主要结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface bus(input logic clk);
logic vld;
logic[7:0] data;
logic ready;

clocking drvc @(posedge clk);
default input #1ns output #1ns;
output vld;
output data;
input ready;
endclocking
clocking monc @(posedge clk);
default input #1ns output #1ns;
input vld;
input data;
input ready;
endclocking
modport drvm(clocking drvc);
modport monm(clocking monc);
endinterface

可以看出,一个interface一般包括三个部分:

  1. 定义信号列表:

    列出所有信号及位宽,一般都用logic,不指定方向。

  2. clocking:

    指定同步时钟,确定同步信号相当于时钟的时序。

    定义信号对于敏感列表的延迟。

    规定信号的方向,站在平台的角度看的。input表示需采集端口上的信号,该信号为tb的输入;output表示tb需要驱动该信号,为tb的输出。

  3. modport

    对信号进行分组,并指定一组中各信号的方向。

如果不写clocking和modport可不可以?

也可以,但是如果不写,就没有了输入输出信息和延迟时间。输入输出信息在编译的时候可以帮我们检查是否出现错误。延迟信息在仿真网表时非常有用,在仿RTL代码时没有delay,即使是始终上升沿采样对齐也不会有什么问题,但是在网表仿真时,有的地方就会出现采样错误的情况,加上延迟信息可以解决这些问题,更加利于移植与修改。

如果不写clocking的时候,我们在driver和monitor里面需要这么写

1
2
3
@(posedge bus.clk);
bus.vld <= 1;
bus.data <= data;

在驱动和采样的时候,这些代码肯定出现不止一次,若需要增加延迟,需要修改多个地方。

而如果加了clocking的时候,代码会变成

1
2
3
@bus.drvm.drvc;
bus.drvm.drvc <= 1;
bus.drvm.drvc < data;

这样,即使需要修改延迟,只要修改interface里面的就可以了。

因此还是建议加上clocking和modport信息。

interface中的default_skew

default_skew:
​ input clocking_skew
​ | output clocking_skew
​ | input clocking_skew output clocking_skew

输入时滞隐含是负的,它总是指向时钟之前的一个时间,采样时刻提前。

输出时滞隐含是正的,指向时钟之后的一个时间,会晚于时钟沿输出,当前时钟沿不会采集到cb的输出信号变化,避免了时序的竞争。

skew表述:

input #1ps //按ps

input #1 //按默认时序单位

实例

假设有interface代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
interface test_if(input clk);
logic[7:0] in;
logic[7:0] out;
clocking cbi @(posedge clk);
default input #1;
input in;
endclocking
clocking cbo(@posedge clk);
default outpu #1;
output out;
endclocking
endinterface

而使用代码如下:

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
34
35
36
37
38
test_if ti(clk);

initial begin
forever begin
@ti.cbi;
ticbiin = ti.cbi.in;
end
end
initial begin
forever begin
@ti.cbi;
ticbiin_noblock <= ti.cbi.in;
end
end
initial begin
forever begin
@(posedge ti.clk);
ticlkin = ti.in;
end
end
initial begin
forever begin
@ti.cbo;
ti.cbo.out <= data;
end
end
initial begin
forever begin
@ti.cbo;
ti.out = data;
end
end
initial begin
forever begin
@ti.cbo;
ticboout = ti.out;
end
end

其中:

  1. ticbiin和ticbiin_noblock都提前1ns。说明阻塞和非阻塞这里一样。
  2. ticlkin会和时钟对齐。
  3. ti.cbo.out会晚1ns输出,这里必须用非阻塞,不然会报错。
  4. ti.out会和时钟对齐。
  5. ticboout也会和时钟对齐,即使用的是@ti.cbo。

说明:

  1. 延时信息,是由ti.cb.xx起作用,而不是@ti.cb起作用。
  2. 阻塞和非阻塞并没有太大区别,但是建议用非阻塞赋值<=。