1. 事务传递 在事务生成器中,每次新建transaction,随机化后发送给接收器,这样每次发送函数transmit(tr)发送的transaction都不同。避免了transmit程序需要多个cycle,后面的随机化将前面还没有发送完成的数据覆盖了。
下面这种写法是只创建一个transaction对象,对对象进行多次随机化,后面的随机化可能会覆盖前面没发送的数据。
class Generator; Transaction tr; task gen(int n); tr = new (); repeat (n) begin tr.randomize (); transmit(tr); end endtask endclass
每次新建新的对象,再随机化之后发送。
1 2 3 4 5 6 7 8 9 10 class Generator; Transaction tr; task gen(int n); repeat (n) begin tr = new (); tr.randomize (); transmit(tr); end endtask endclass
2. 继承 在子类的构造函数中,第一行先调用父类的构造函数,如果父类的构造函数有传入参数,那么子类构造也要有参数。
1 2 3 4 5 6 7 8 9 10 11 class Parent; int var ; function new (int n); var = n; endfunction endclass class Child extends Parent; function new (int n); super .new (n); endfunction endclass
3. blueprint模式 蓝图(blueprint)模式可以在不改变生成器代码的前提下,让生成器生成并发送不同的transaction。
在生成器中定义hook对象,也可以叫他蓝图对象,然后再testbench将这个对象通过层次引用的方式替换成需要的对象。
作用:不需要修改generator代码,就可以生成不同的transaction。
在第一节中,将事务创建和发送写在一起,避免覆盖的问题,但那也使得没法通过层次引用来替换generator中的对象,因为一创建对象,立马就随机化并发送了。所以必须营造这样的流程:
generator中的new函数创建transaction —->>> tb中在对象创建后将对象替换 —->>>generator中随机化对象,并发送对象的一个深拷贝(避免覆盖问题)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Generator; Transaction blueprint; mailbox mlb; function new (input mailbox mlb_i); mlb = mlb_i; blueprint = new (); endfunction task run(int n); repeat (n) begin blueprint.randomize (); mlb.put (blueprint.copy ()); end endtask endclass
在使用的时候,在testbench中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Env; Generator gen; Driver drv; mailbox mlb; ....endclass module tb; Env env; initial begin env=new (); env.build (); BadTr bad=new (); env.gen .blueprint =bad; env.run (); env.wrap_up (); end endmodule
4. 纯虚方法和抽象类 抽象类可以被扩展但不能被实例化,用virtual关键字定义。
纯虚方法没有实体,只有声明。只能定义在抽象类中,但抽象类中可以由其他非纯虚方法。****pure
由抽象类扩展的子类,必须将抽象类中的纯虚方法都重定义了才能用。
1 2 3 4 virtual class Base; pure virtual function void get(); pure virtual task cmp(int i);endclass
5.回调 callback 希望创建一个不需要修改就可以生成所有激励的验证平台。方法是在测试平台上提供一些hook,使得测试程序在不改变验证平台代码的前提下,向测试平台注入一些代码,改变发生器的行为,产生需要的激励。
通过继承 的方法来实现。
5.1 创建并使用回调 1)定义回调基类,可以定义为纯虚方法
1 2 3 4 5 6 7 8 virtual class CallBack; virtual task pre_tx(ref Transaction tr,ref bit drop); endtask virtual task post_tx(ref Transaction tr); endtask endclass
2)driver中使用回调基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Driver ; CallBack cbs[$]; Transaction tr; bit drop; mailbox mlb; function new (mailbox m);mlb=m;endfunction taks run(); forever begin drop=0 ; mlb.get (tr); foreach (cbs[i]) cbs.pre_tx (tr,drop); if (drop) continue ; transmit(tr); foreach (cbs[i]) cbs.post_tx (tr); end endtask endclass
此时cbs是空的,不会调用foreach语句。
3)定义回调类、回调函数。
回调类继承回调基类
1 2 3 4 5 6 class CallBack_drop extends CallBack; virtual task pre_tx(ref Transaction tr,ref bit drop); drop=($random (0 ,99 )==0 ); endtask endclass
4)在顶层注入回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 program automatic test; Env env; initial begin env=new (); env.gen_cfg (); env.build (); begin CallBack_drop cb=new (); env.drv .cbs .push_back (cb); end env.run (); env.wrap_up (); end endprogram
5.2 回调与记分板连接 在driver的回调中假如记分板,可以将driver发送的tr保存在记分板中。
1 2 3 4 5 6 7 8 9 class CallBack_scb extend CallBack; Scb scb; function new (Scb s); this .scb = s; endfunction virtual task pre_tx(ref Transaction tr,ref bit drop); scb.save_expected (tr); endtask endclass
也可以对覆盖率用回调,将发送的tr收集到覆盖率的类中。
使用的时候也是在tb上注入到Driver中。
6. 参数化的类 要用type 关键字定义类型。
要写一个参数化类,先要写一个非参数化的类,然后将他转换成参数化。
下面实现一个参数化的堆栈。
1 2 3 4 5 6 7 8 9 10 class Stack#(type T=int) ; local T stack[100 ]; local int top; function void push(T t); stack[top++]=t; endfunction function T pop(); pop=stack[top--]; endfunction endclass
1 2 3 4 5 initial begin Stack#(real) s; s.push (1 ,0 ); $display ("%f" ,s.pop ());end
7. 总结
使用blueprint模式和callback都可以生成不同的transaction,并且不需要改变tb代码。
记分板、覆盖率可以通过回调来收集transaction信息。
callback是一种思想——向验证环境中注入代码。至于实现什么功能,由你向其中注入的代码决定。