1、测试平台的构建
发生器(generator):创建事务并且将它们传给下一级
驱动器(drive):与设计进行会话
监视器(monitor):捕获设计返回的事务
计分板(scoreboard):将捕获的结果跟预期的结果进行对比
测试平台应该分成若干个块(block),然后定义它们相互之间如何通信。
2、OOP(Object-Oriented Program)
类(class)、对象(object)、句柄(object)、属性(property)、方法(method)、原型(prototype)
3、类定义的位置
类应当在program或者module外的package中定义,将临时变量在测试平台最内部的某处定义。
4、创建对象
方法:创建一个指向目标类对象的句柄,然后调用new()函数。
对象和句柄:句柄可以在不同的时间指向不同的对象
5、对象的接触分配
SV分辨对象不再被引用的最好办法就是记住指向它的句柄的数量,当最后一个句柄不再引用某个对象了,SV就释放该对象的空间。
6、使用对象
方法:用“.”操作符
7、静态变量和全局变量
每个对象都有自己的局部变量,这些变量不和任何其他对象共享。而使用静态变量就可以被所以对象所共享。
8、静态函数
没有创建类对象就可以执行,不允许读写非静态变量。调用静态函数:类名::方法名
9、类的方法
在类的作用域内定义的内部task或者function。SV会根据句柄的类型调用正确的display()方法。
10、将对象传递给方法
如果要在任务或者函数中修改句柄,则必须将该任务或函数声明为automatic,并且对象加ref
11、对象的复制
可以使用new操作符,但这样是简易复制(shallow copy),类似于原对象的一个影印本,原对象的值被盲目地抄写到目的对象中。如果类中包含一个指向另一个类的句柄,那么只有最高一级的对象被new操作符复制,下层的对象都不回被复制。更推荐的做法是编写自己的复制函数:为每个类构建复制函数,并在其中继续调用对象成员的复制函数。
12、公有和私有
在SV中,所以成员都是公有的,除非标记为local或者protected。应该尽量使用默认值,以保证对DUT行为的最大程度的控制
13、建立一个测试平台
图中的Generator、Agent、Drive、Monitor、Checker和Scoreboard都是类,被建模成事务处理器(transactor)。事务处理器由一个简单的循环构成,这个循环从前面的块接受事务对象,经过变换后送给后续块。
14、示例代码
package,包含类的定义
package class_package; typedef class Statistics; class Transaction; Statistics stats; //对象成员 bit [31:0] addr,crc,data[8]; //默认公有 static int count = 0; //静态变量 int id; function new(logic [31:0] a=3,d=5); addr = a; foreach (data[i]) data[i] = d; id = count++; stats = new(); // 调用对象成员的构造函数 endfunction:new function Transaction copy; // 复制函数 copy = new(); copy.addr=addr; copy.crc=crc; copy.data=data; copy.stats=stats.copy(); //调用对象成员的复制函数 endfunction function void display(); $display("addr:%h",addr); endfunction:display static function void static_f(); $display("count:%h",count); endfunction:static_f endclass:Transaction class Statistics; time startT; function new(); startT = 1; endfunction:new function Statistics copy(); //复制函数 copy=new(); copy.startT=startT; endfunction endclass:Statistics endpackage
program:
import class_package::*; //将对象传递给方法 function automatic void create(ref Transaction tr); //在方法中改变了句柄,因此必须加ref tr = new(); tr.addr = 42; endfunction program test; initial begin Transaction tr1,tr2,tr3,src,dst; tr1 = new(); tr2 = new(); create(tr3); //将对象传递给方法 tr1.display(); tr2.display(); tr3.display(); $display("Second object id = %0d,count = %0d",tr2.id,Transaction::count); Transaction::static_f(); //使用new复制对象 src = new(); src.stats.startT = 42; dst = new src; dst.stats.startT = 96; $display("========= dst = new src =========="); $display("shallow copy - src.stats.startT:%0d",src.stats.startT); //96 因为src和dst的stats都指向同一个对象
$display("shallow copy - dst.stats.startT:%0d",dst.stats.startT); //96 $display("shallow copy - src.id:%0d",src.id); // 3 $display("shallow copy - dst.id:%0d",dst.id); // 3 //使用copy函数复制对象 dst = src.copy; dst.stats.startT = 48; $display("========= dst = src.copy =========="); $display("shallow copy - src.stats.startT:%0d",src.stats.startT); //96,没有被改成48 $display("shallow copy - dst.stats.startT:%0d",dst.stats.startT); //48 $display("shallow copy - src.id:%0d",src.id); // 3 $display("shallow copy - dst.id:%0d",dst.id); // 4 #50; $display("End of test"); end endprogram
输出:
# addr:00000003
# addr:00000003
# addr:0000002a
# Second object id = 1,count = 3
# count:00000003
# ========= dst = new src ==========
# shallow copy - src.stats.startT:96
# shallow copy - dst.stats.startT:96
# shallow copy - src.id:3
# shallow copy - dst.id:3
# ========= dst = src.copy ==========
# shallow copy - src.stats.startT:96
# shallow copy - dst.stats.startT:48
# shallow copy - src.id:3
# shallow copy - dst.id:4
# End of test