最近吃了没文化的亏,想来就把interface好好看看。
在SV中常用interface连接端口,它的好处在于,方便了在sv中模块声明中不需要一个个的写端口,直接在端口中实例化一个interface即可。接口中还可以包含任务函数、断言等等。说多了咱也记不住,就说这点吧。不过我觉得最好用的还是第一点哈。
interface的推荐写法
interface
v_if
(
input bit clk
);
logic
[
31
:
0
]
data
;
.....
clocking cb@
(
posedge clk
)
//需要包含resetn
endclocking
:
cb
......
//需要几个写几个,个人觉得是有几个component写几个时钟块
modport TB
(
clocking cb
,
output resetn
);
......
//modport和clocking块对应
endinterface
下面就是个人的几点扯淡了...
1.为什么clk要写在括号里?
我的理解是,clk是在顶层testbench中驱动的,其他component只会使用clk作为input,这样可以减少不必要的接口层次。而且,虽然clk和interface中的其他端口定义的位置不一样,但是在仿真环境中还是可以使用<接口实例名>.clk。这个仍然代表着interface中的clk信号。但是除了对DUT模块使用上述clk信号,对于在testbench,不建议使用这个clk,要用时钟块的名称替换,这样做的好处是避免在仿真时发生竞争冒险,使得各个信号是时钟同步信号。但是需要注意的是,使用时钟块时,不再需要添加上升沿或者下降沿关键字,给时钟块中的变量赋值时应当使用<=而不是=。
2.为什么resetn需要定义两次?一次在时钟块中,一次在modport中?
为了做到异步复位,同步释放。因此resetn有效时应当直接使用<接口实例名>.resetn。释放时为<接口实例名>..resetn。
3.时钟块什么时候采样?
时钟块默认输入偏斜为1step,也就是在上一个时钟片的结束部分。换句话说,就是在紧接着时钟上升沿之前采样信号,或者说是本时钟片的preponed区域。
如果显示使用#0输入,则会在相应的时钟事件同步进行采样,但是是在observed区域采样,这样可以避免竞争情况。同样的,在re-NBA区域进行输出。忘了的,不懂的看这个SystemVerilog中scheduler(调度)
如下代码所示:
clocking cb_0
@(
posedge clk
);
input
#0 gnt;
endclocking
clocking cb_1
@(
posedge clk
);
input
#1step gnt;
endclocking
begin
@(
if0
.
cb_0
);
$display
(
"cb_0.gnt = 0x%0h"
,
if0
.
cb_0
.
gnt
);
end
begin
@(
if0
.
cb_1
);
$display
(
"cb_1.gnt = 0x%0h"
,
if0
.
cb_1
.
gnt
);
end
最终结果是不一样的: