作用:
一旦定义了时钟块,测试平台就可以采用@arbif.cb等待时钟,而不需要描述确切的时钟信号和边沿,即使改变了时钟块中的时钟或边沿,也不需要修改测试代码
应用:
将测试平台中的信号,都放在clocking 中,并指定方向(以测试平台为参考的方向)。并且在modprot test(clocking cb,
最完整的接口:
Interface arb_if(input bit clk); Logic[1:0] grant,request; Logic rst;
Clocking cb @(posedge clk); Output request; Input grant; Endclocking
Modport test (clocking cb, Output rst);
Modport dut (input clk, request,rst, Output grant); endinterface
变化:将request 和grant移动到时钟块中去了,test中没有使用了。
l 接口中的双向信号
Interface master_if(input bit clk); //在类中为了,不使用有符号数,常用bit[]定义变量 Wire [7:0] data;
Clocking cb@(posedge clk); Inout data;
Endclocking
Modport TEST(clocking cb); endinterface
program test(master_if mif); initial begin mif.cb.data <= ?z; @mif.cb;
$display(mif.cb.data); //总线中读数据 @mif.cb;
Mif.cb.data <= 8‘h5a; //驱动总线 @mif.cb;
Mif.cb.data <= ?z; //释放总线 注:
(1)interface 列表中clk 采用的是input bit clk;为什么要用bit?
(2)时钟块 clocking cb 中,一般将testbench中需要的信号,方向指定在这里; 而在modprot 指定test信号方向的时候,采用clocking cb。
(3)interface中信号,不一定都用logic,也可采用wire(双驱动);systemverilog 中如果采用C代码的风格(参数列表中方向和类型写一起),必须采用logic类型 (4)现在的风格,DUT 没才用clocking cb ,测试平台和DUT的时钟如何统一? l 激励时序
DUT和测试平台之间时序必须密切配合。
l 测试平台和设计间的竞争状态 好的风格:
使用非阻塞赋值可以减少竞争。
systemverilog验证中initial 中都采用<= 赋值,而等待延迟采用@arbif.cb等待一个周期来实现。
而verilog中采用的风格时,initial 中采用 =阻塞赋值,沿时可以采用#2,等实现。 因此时钟发生器,只能放在module 中,而不能放在program中
l Program中不能使用always块
测试平台可以使用initial 但不能使用always,使用always 模块不能正常工作。 原因:测试平台的执行过程是进过初始化、驱动和响应等步骤后结束仿真。
如果确实需要一个always块,可以使用initial forever 来完成。比如:在产生时钟时。 类
l 类中static变量 背景:
如果一个变量需要被其他对象所共享,如果没有OPP,就需要创建全局变量,这样会污染全局名字空间,导致你想定义局部变量,但变量对每个人都是可见的。
1)作用:
类中static变量,将被这个类的所有实例(对象)所共享,使用范围仅限于这个类。 例:class transaction; Static int count=0; Int id; Endclass
Trasaction tr1,tr2;
Id不是静态变量,所以每个trasaction对象都有自己的id;count 是静态变量,所有对象只有一个count变量。
如何用?
当你打算创建一个全局变量的时候,首先考虑创建一个类的静态变量。 2)static变量的引用 句柄或类名加::
4) static 变量的初始化
static变量通常在声明时初始化。不能在构造函数中初始化,因为每一个新的对象都会调用构造函数。
l 静态句柄:
背景:当类的每一个对象,都需要从同一个对象(另一个类)中获取信息的时候。如果定义成非静态句柄,则每个对象都会有一份copy,造成内存浪费。
l 静态方法 背景:
当使用更多静态变量的时候,操作他们的代码会很长。 作用:
可以在类中创建一个静态方法用于读写静态变量。 注:systemverilog不允许,静态方法读写非静态变量。 l 类之外的方法
背景:解决类太长的问题。类最好控制在一页内,如果方法很都很长。 l This
背景:如果在类很深的底层作用域,却想引用类一级的对象。在构造函数中最常见。 作用:this指向类一级变量 l 如何做类,类做多大? 上限:类不能太大
当类中存在多处相同的代码,你需要将这段代码做成当前类的一个成员函数或父类的成员函数。
下限:类不能太小 类太小,增加了层次。
方法:如果一个小类只被例化了一次,可以将它合并到父类中去。
l 动态对象
概念区分:方法中修改对象 和修改句柄 修改对象——将对象的变量重新赋值。 修改句柄——在任务中new()对象。
1) 当你将对象传递给方法
背景:句柄,new()后变成对象,在将其作为参数传递给方法。 实质和作用:
传递的是句柄。这个方法可以读取对象中的值;也以改变对象中的值
2) 修改标量变量的值
背景:在方法的参数中,前面加ref;(用ref传递,ref传递的是变量的地址)。
作用:
方法可以修改变量的值,并将修改的值,传递给主程序。 引申:
方法可以改变对象,即使没有使用ref 修饰句柄。
因为传递的是句柄,句柄是地址。不要将句柄和对象混为一谈,如果传递的是对象,对象是单向的,那方法以外也不能传递回来。可以这样理解吧。
读写对象中的值: 例:
Task transmit(Transcation t); Cbbus.rx_data <= t.data;
t.stats.startT = $time; //在任务中,改变了对象 endtask
trancation t; initilal beign t = new(); t.addr = 42; transmit(t); end
既然传递的是句柄,那数据就没传过去,如何读取值?
答:主程序中new()创建了一个对象,而句柄是指向对象的指针,传递的是句柄,transmit中也指向了对象,所以transmit中可以读写对象。
3) 在任务中修改句柄 背景:
在方法中,参数为句柄,前面加ref。 作用:
可以在方法中new()对象,并将初始化放在方法中;在主程序中仅仅调用。 注意:正确的事物发生器,参数是带ref的句柄