SV——OOP技巧

1. 事务传递

​ 在事务生成器中,每次新建transaction,随机化后发送给接收器,这样每次发送函数transmit(tr)发送的transaction都不同。避免了transmit程序需要多个cycle,后面的随机化将前面还没有发送完成的数据覆盖了。

​ 下面这种写法是只创建一个transaction对象,对对象进行多次随机化,后面的随机化可能会覆盖前面没发送的数据。

1
2
3
4
5
6
7
8
9
10
class Generator;
Transaction tr; //声明句柄
task gen(int n);
tr = new(); // 新建一个对象
repeat(n) begin
tr.randomize(); //随机化transaction
transmit(tr); //发送transaction
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(); //随机化transaction
transmit(tr); //发送transaction
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(); // 在new函数中新建一个对象
endfunction
task run(int n);
repeat(n) begin
blueprint.randomize(); //随机化blueprint
mlb.put(blueprint.copy()); //发送blueprint的深拷贝
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; //验证平台对象创建后,将新事务替换blueprint,那么发送的就是bad
env.run(); //运行平台(发送接受tr)
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; //结束这一次循环,不发送这个tr。
transmit(tr);//发送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);
// 没100个tr取消一个
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);// 保存发送的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. 总结

  1. 使用blueprint模式和callback都可以生成不同的transaction,并且不需要改变tb代码。
  2. 记分板、覆盖率可以通过回调来收集transaction信息。
  3. callback是一种思想——向验证环境中注入代码。至于实现什么功能,由你向其中注入的代码决定。