SV中的interface

  我们在学习verilog时,测试rtl代码的正确与否,直接创建一个top module然后将rtl代码在里面例化,使rtl模块直接与测试模块里的变量直接相连。再对rtl模块发激励进行验证。

  然而到了验证人员这里,DUT(rtl)是要与TB分开的。两者正是通过interface相连接。如下图。

![](https://img2022.cnblogs.com/blog/2860980/202205/2860980-20220518205621758-1450367134.png)

  那么interface要包含哪些内容,怎么使用呢?

答:1.interface中可以包括端口(输入,输出,双向),任务,函数,过程块(initial)。

  2.举例 

 1 interface arb_if(input bit clk,rst); //input通常会传入clk与rst信号 
 2     logic [1:0] grant, request;  
 3     logic reset;//指明interface中所有的信号
 4 
 5     clocking cb @(posedge clk); //在使用clocking block时,是为了而使得发送或者接收信号都是在clk时钟下同步
 6         input grant;  
 7     endclocking
 8 
 9     modport TEST (clocking cb, output reset);//将部分信号包起来给不同的模块使用,需要指明方向
10     modport DUT (input request, reset, output grant)  
11 endinterface

  3.在clocking block中加入 default input #1ns output #1ns;来设置建立时间和保持时间,避免了竞争冒险的发生。

如果不设置默认读取时钟跳变前的值。

  4.为了说明interface的用法,简易表示一下验证环境结构

 1 module top;
 2     bit clk,rst;
 3 
 4     arb_if intf(clk,rst);
 5     arb D1(intf.DUT);
 6     test T1(intf.TEST);
 7 
 8 
 9 endmodule
10 
11 module arb(arb_if.DUT arbif);
12     ...
13 endmodule
14 
15 program test(arb_if.TEST tif);
16     @(tif.cb)
17     tif.grant<=1'b1;
18     ...
19 endmodule
20 
21 interface arb_if(input bit clk rst);  
22       logic [1:0] grant, request;  
23       logic reset;
24   
25       clocking cb @(posedge clk);
26           input grant;  
27       endclocking
28   
29       modport TEST (clocking cb, output rst);
30       modport DUT (input request, rst, output grant);
31 endinterface

  5.优点: 便于设计重用

       可以替代原来需要在模块或者程序中重复声明并且位于代码内部的一系列信号,减少连接 错误的可能性

       要增加一个新信号时, 只需要在接口中声明一次, 不需要在更高层的模块层声明, 进一步 减少错误;

          modport允许一个模块将接口的一系列信号捆绑到一起, 为信号指定方向;

          缺点: 对于点对点的连接, 使用modport跟使用信号列表的端口一样冗余;

      必须同时使用信号名和接口名,会使模块变得更加冗长

      如果连接两个模块使用的是一个不会被重用的专用协议,使用接口需要做比端口连线更多 的工作;

      连接两个不同的接口很困难

      一个新的接口可能包含了现有接口的所有信号并新增了信号, 需要拆分独立的信号并正确的驱动

  6.接口同步(发送与接受)

先思考一下

1 @bus.cb;                 //clock模块中时钟的有效沿触发
2 repeat(4) @bus.cb;  //等待4个有效时钟沿
3 @bus.cb.grant;        //监测grant信号的变化(上升,下降)
4 @(posedge bus.cb.grant);    //监测grant信号的上升沿
5 @(negedge bus.cb.grant);    //监测grant信号的下降沿
6 wait (bus.cb.grant==1);    // 监测一个电平值, 若监测不到, 则一直等待
7 @(posedge bus.cb.grant or negedge bus.reset); //思考:这个是等待什么?

  7.接口信号采样

当在DUT arb中驱动arbif.grant: test对clocking cb中的输入信号grant进行采样。 采样输出为arbif.cb.grant

 1 clocking cb @(posedge clk); 
 2     output request;
 3     input grant;  
 4 endclocking
 5 
 6 module arb(arb_if.DUT arbif);  
 7 initial begin
 8     arbif.grant = 1; // @ 0ns
 9     #12 arbif.grant = 2; // @ 12ns
10     #18 arbif.grant = 3; // @ 30ns  
11 end
12 endmodule
13 
14 program test(arb_if.TEST arbif);  
15 initial begin
16     $monitor("@%0d: grant=%h", $time, arbif.cb.grant);
17     #50;
18 end  
19 endprogram

DUT的信号输出grant,clock block的cb.grant输出如波形所示。

在30ns处,DUT的grant的值在时钟沿跳变,同时TEST的cb.grant也在该时钟沿 触发输出,但此时它采样的值还是该时刻之前的值2。

  8.接口信号驱动

在test中驱动同步信号arbif.cb.request ,在DUT中监测arbif.request的输出

 1 program test(arb_if.TEST arbif);  
 2 initial begin
 3     # 7 arbif.cb.request <= 3; // @ 7ns
 4     #10 arbif.cb.request <= 2; // @ 17ns
 5     #13 arbif.cb.request <= 1; // @ 30ns
 6     #15 $finish;  
 7 end  endprogram
 8 
 9 module arb(arb_if.DUT arbif);  
10 initial begin
11     $monitor("@%0t: req=%h", $time, arbif.request);  
12 end
13 endmodule

test中arb.cb.request,DUT中request的波形如图

注意:30ns处arb.cb.request的变化立刻在上升沿被传送到arb.request上。因为 在clocking block中输出信号的默认延迟为#0,软件在编译时, clocking block中 的输出信号要晚于被采样信号的变化,但在波形上观察不出来。

总结: 在clocking block中,采样(input)和驱动(output)信号遵循如下原则: 同步后的采样信号,都是前一个状态的值。 同步后的驱动信号,都是当前状态的值。

  9.接口中的双向信号

 1 interface master_if(input bit clk);  
 2     wire[7:0] data;
 3     clocking cb@(posedge clk);  
 4         inout data;
 5     endclocking
 6     modport TEST(clocking cb);  
 7 endinterface
 8 
 9 program test(master_if mif);  
10 initial begin
11     mif.cb.data <='z;
12     @mif.cb;
13     $display(mif.cb.data);    //从总线读取
14     @mif.cb;
15     mif.cb.data <=7'h5a;
16     @mif.cb;  mif.cb.data <='z;
17 end 
18 endprogram   

由于SV的用户参考手册中没有明确定义如何驱动接口中的异步双向信号, 故可用以下两种方法:1.连续赋值语句2.虚接口

  10.时钟发生器

program bad_generator (output bit clk, out_sig);  
    bit clk=0, out_sig=0;
    initial
        forever #5 clk <= ~clk;  
    initial
        forever @(posedge clk)  out_sig <= ~out_sig;
endprogram
/*上例将时钟的产生定义到了program中,这样就会产生速度问题,时钟的翻转和 信号变化同时的,应该判定谁的变化在前?可能会引起竞争状态。    所以应该在module或者class中声明clk*/
module clock_generator (output bit clk);  
    bit clk = 1;
    always #5 clk = ~clk; // Use blocking assignment  
endmodule
        

 

posted @ 2022-05-18 22:39  NBI  阅读(1528)  评论(0)    收藏  举报