systemverilog 的 OOP
2016-08-29 12:49
459 查看
基类:
基类基本类中的方法,需标记为virtual,这样扩展类中才可以重新定义。扩展类中函数,和基类中函数名一样时,定义时i前面也要加virtual,通过supper.函数名,调用基类中函数。Systemverilog中不允许supper.supper.new方式经行多层调用.
当然也要记住,SV不支持多继承。。
如果基类构造函数new()有参数,那么扩展类,必须有一个构造函数,并在其构造函数的第一行调用基类的构造函数。
下面这段代码没啥实际功能,只是为了清楚说明面向对象的基本用法。
class Base; //基类
rand bit[31:0] src,dst,data[8];
bit[31:0] crc;
virtual function void calc_crc;
//虚函数
crc=src^dst^data;
endfunction
virtual function void display;
//虚函数
$display("Src=%h,Dst=%h,Crc=%h",src,dst,crc);
endfunction
function new(input bit[31:0]
crc);//带参构造
this.crc = crc;
endfunction
endclass
////////////////////////////////////////////
class Ext extends Base;
//扩展类
rand bit bad_crc;
constraint c{
//看到没?扩展类可以增加约束。这个蛮有用。
dst
inside{[src-100:src+100]};
}
virtual function void
calc_crc;
super.calc_crc();
if( bad_crc)
crc=~crc;
//注意这里crc没有用到分层标志,扩展类可以直接访问基类(crc)和其本身( bad_crc) 的所有变量。因为SV
默认是public继承以保证测试平台具有很高的权限进行对象操作。
endfunction
virtual function void display;
$display("Bad_crc=%b",bad_crc);
super.display();
endfunction
function
new(input bit[31:0] crc);
super.new(crc); //必须是构造函数的第一行
//其他行为
endfunction
endclass
怎么调用:
OPP规则指出:基类的句柄,也可以指向扩展类的对象。也就是说,
如果定义一个基类
Base Btr;
再定义一个扩展类
Ext Etr;
那么就能执行如下语句:
Etr=new();
//初始化一个Etr对象。
Btr=Etr;
//将刚生成并初始化的扩展类对象赋值给基类对象、反过来就不可以了。关于这点C++ 类和面向对象
那篇文章的最后也提到。
不过用
$cast( Etr , Btr
);可以进行强制向下转换,这个后面会用到。
program automatic
test1;
Base Btr;
Ext Etr;
initial begin
Btr=new();
Btr.calc_crc();
//调用Btr::calc_crc();
Etr=new();
Etr.calc_crc(); //调用Etr::calc_crc();
Btr=Etr;
Btr.calc_crc(); //在这里其实用的是扩展类中定义的Etr::calc_crc();函数,因为实际上这里的对象是扩展类对象。这就是多态。如果不用virtual,
这里看到定义的对象时Base类就用Base类中定义的calc_crc()函数了。这显然不是我们想要的。
蓝图模式:
那这里就说一下,为啥用蓝图
首先构建一个对象蓝图(一般是基类对象),然后修改它的约束,甚至可以用扩展对象替换它,随机化这个蓝图时,就得到想赋予的随机值;然后复制这个对象,将copy发给下游。
蓝图:是一个钩子,允许你改变发生器类的行为而无需修改其类代码。蓝图对象在一个地方构建(new()),在另一个地方(任务run)使用
class Generator;
mailbox gen2drv;
Base Blueprint; //蓝图(以基类定义)
//初始化一个Generator类的对象,就要初始化两个参数,一个是
mailbox 对象,另一个是蓝图对象。
function new(input mailbox gen2drv);
this.gen2drv=gen2drv;
Blueprint=new();
endfunction
task run;
Base Btr; //
创建局部变量指向Base的对象
forever begin
assert(Blueprint.randomize());
//使用蓝图
Btr= Blueprint.copy();
//对象值 复制,具体怎么copy下面再讲。
//上面这两句合起来算是对Btr进行了初始化。用蓝图的好处就是可以用扩展的功能来随机化蓝图对象然后赋值给Btr
gen2drv.put(Btr);
Btr.calc_crc();
end
endtask
endclass
program automatic test;
Generator gen;
initial begin
gen=new(); //初始化两个参数,一个是 mailbox
对象,另一个是蓝图对象。
Ext Etr=new(); //新建初始化一个扩展类。
gen.Blueprint = Etr;
//基类的句柄,也可以指向扩展类的对象。以Ert对象取代以基类定义的蓝图,这样,对于Generator类中的那句 Btr= Blueprint.copy();
就相当于用派生类给基类初始化,这是完全可以的。
gen.run();
//调用类的任务,其中task中定义的那句 Btr.calc_crc();
在这里其实用的是扩展类中定义的calc_crc()函数,因为实际上这里的对象是扩展类对象。这就是多态。如果不用virtual,
这里看到定义的对象时Base类就用Base类中定义的calc_crc()函数了。这显然不是我们想要的。
end
endprogram
刚刚上面用到了一个copy 函数,
Btr= Blueprint.copy();
这个函数应该在Base基类里的虚函数。Ext扩展类可以继承的。
那我们把上面的基类和扩展类里面分别添加一个copy
虚函数。
class Base;
//基类
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
virtual function Base copy();
copy=new();
copy.src=src;
//复制数据域
copy.dst=dst;
copy.data=data;
copy.crc=crc;
endfunction
........
endclass
////////////////////////////////////////////
class
Ext extends Base;
//扩展类
rand bit
bad_crc;
virtual function Base copy();
Ext
Etr; //临时变量,用于存值。
Etr=new();
Etr.src=src;
//复制数据域
Etr.dst=dst;
Etr.data=data;
Etr.crc=crc;
Etr.bad_crc= bad_crc;
return Etr;
//扩展类要返回一个Base类指向的对象,这样才能用于
Btr= Blueprint.copy();
endfunction
endclass
/////////////////////////////
优化:在扩展类中再一次定义
Etr.src=src;
Etr.dst=dst;
Etr.data=data;
Etr.crc=crc;
感觉有点多余吧。就跟构造函数一样,属于基类的部分直接调用基类函数就行了。我们把上面的copy函数拆分成两个,重新定义如下:
class Base;
//基类
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
virtual function void
copy_data(input Base Tr)
Tr=new();
Tr.src=src;
//复制数据域,这边跟教材有出入,我认为教材错了。
Tr.dst=dst;
Trdata=data;
Tr.crc=crc;
endfunction
virtual function Base
copy();
copy =
new();
copy_data(copy);
endfunction
........
endclass
class
Ext extends Base;
//扩展类
rand bit
bad_crc;
virtual function void copy_data(input
Base Tr)
//这里用Tr, 表明多态,既可以是基类,也可以是扩展类。
这边跟教材有出入,我认为教材错了。
super.copy_data(Tr); //其实这句话拷贝了扩展类对象中的基类部分。
Tr.bad_crc=bad_crc;
//派生类数据的复制。
endfunction
virtual function Base
copy();
Ext Etr;
Etr=new();
copy_data(Etr);
return Etr;
endfunction
endclass
/////////////////////////////////////////////////////////////////////////////////////////////////////////
纯虚方法和抽象类
virtual class BaseTr;
//前面加virtual,抽象类。抽象类可以被扩展,但不能被例化
static int
count;
int id;
function new();
id=count++;
endfunction
pure
virtual function BaseTr copy(input BaseTr
to);
//纯虚函数,仅仅是原型声明,没有定义功能,甚至连空函数也不是,所以不能直接例化基类。
//纯虚方法只能在抽象类中定义。
virtual function void
display;
//抽象类中也可以定义不是纯虚的函数。
$display("id=%d",id);
endfunction
endclass
扩展类
class Base extends
BaseTr;
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
extern
virtual function BaseTr
copy(input BaseTr to);
.........
............. //
在这里定义。
endfunction
//在扩展类中定义纯虚函数的具体功能。
virtual function void display;
super.display(); //显示id
$display("Src=%h,Dst=%h,Crc=%h",src,dst,crc);
endfunction
endclass
当一个扩展类的将基类中声明的所有纯虚函数实现定义以后,才能在测试平台中使用它。。
知道了抽象类,我们就说一种很有用的方法: 回调。
背景:测试平台目的:创建一个不做任何修改就能在所有测试中使用的验证环境。要做到这点的关键是测试平台使用钩子,(什么是钩子?)钩子作用,在不修改原始类的情况下注入新的代码。回调基类是一个抽象类,使用前必须先进行扩展。
作用:回调就是一个钩子,在不修改原始类的情况下注入新的代码。
实现:回调任务在顶层中创建,在最低级即驱动器中调用。这样驱动器不需要知道测试的任何信息,它只需要使用一个可以在测试中扩展的通用类。
1) 使用回调注入干扰
回调的一个常见用法就是注入干扰,例如引入一个错误或者延迟。下面测试平台使用回调对象,随机地丢弃数据包。
回调基类是抽象类,在扩展的回调类中加入错误注入,而drive驱动类中,是回调基类的数据队列,在环境中将扩展类句柄让入驱动器类,回调基类的数据队列中。
virtual class Driver_callback;
//抽象类
virtual task
pre_tx( ref Base, ref bit drop);
//默认情况下不做动作。但注意不是纯虚方法。因为下面的程序中需要默认例化这个基类。
endtask
endclass
class Driver;
Driver_callback Cb[$];
//定义一个队列,不断地处理回调对象。
task run();
bit drop=0; //默认是0
.................
forever begin
foreach(Cb[i])
//由于队列先进先出,后进后出,所以最新(后)注入的错误是最后出来的,给drop赋值
Cb[i].pre_tx(tr,drop); //默认的回调对象啥事都不做
if(!drop) continue;
//若drop=0, 跳出当前循环,不执行
transimit(tr),继续下一个forever;若为1, 发送 transimit
transimit(tr);
end
endtask
endclass
class Driver_callback_drop extends Driver_callback;
virtual task pre_tx( ref
Base, ref bit drop);
drop=($urandom(0,99)==0); //正常情况下drop为0
endtask
endclass
program automaitc test;
......
....
initial begin
Driver drv=new();
begin
Driver_callback_drop dcd=new();
drv.Cb.push_back(dcd); //将扩展过带有错误注入的回调对象放入队列的头。
end
endprogram
回调函数中的重要点就在 先定义一个基类方法
virtual task
,啥都不干(但不是纯虚,可以例化)。在测试的时候可以扩展定义然后使用。
只有在测试中碰到回调对象时才“开火”
基类基本类中的方法,需标记为virtual,这样扩展类中才可以重新定义。扩展类中函数,和基类中函数名一样时,定义时i前面也要加virtual,通过supper.函数名,调用基类中函数。Systemverilog中不允许supper.supper.new方式经行多层调用.
当然也要记住,SV不支持多继承。。
如果基类构造函数new()有参数,那么扩展类,必须有一个构造函数,并在其构造函数的第一行调用基类的构造函数。
下面这段代码没啥实际功能,只是为了清楚说明面向对象的基本用法。
class Base; //基类
rand bit[31:0] src,dst,data[8];
bit[31:0] crc;
virtual function void calc_crc;
//虚函数
crc=src^dst^data;
endfunction
virtual function void display;
//虚函数
$display("Src=%h,Dst=%h,Crc=%h",src,dst,crc);
endfunction
function new(input bit[31:0]
crc);//带参构造
this.crc = crc;
endfunction
endclass
////////////////////////////////////////////
class Ext extends Base;
//扩展类
rand bit bad_crc;
constraint c{
//看到没?扩展类可以增加约束。这个蛮有用。
dst
inside{[src-100:src+100]};
}
virtual function void
calc_crc;
super.calc_crc();
if( bad_crc)
crc=~crc;
//注意这里crc没有用到分层标志,扩展类可以直接访问基类(crc)和其本身( bad_crc) 的所有变量。因为SV
默认是public继承以保证测试平台具有很高的权限进行对象操作。
endfunction
virtual function void display;
$display("Bad_crc=%b",bad_crc);
super.display();
endfunction
function
new(input bit[31:0] crc);
super.new(crc); //必须是构造函数的第一行
//其他行为
endfunction
endclass
怎么调用:
OPP规则指出:基类的句柄,也可以指向扩展类的对象。也就是说,
如果定义一个基类
Base Btr;
再定义一个扩展类
Ext Etr;
那么就能执行如下语句:
Etr=new();
//初始化一个Etr对象。
Btr=Etr;
//将刚生成并初始化的扩展类对象赋值给基类对象、反过来就不可以了。关于这点C++ 类和面向对象
那篇文章的最后也提到。
不过用
$cast( Etr , Btr
);可以进行强制向下转换,这个后面会用到。
program automatic
test1;
Base Btr;
Ext Etr;
initial begin
Btr=new();
Btr.calc_crc();
//调用Btr::calc_crc();
Etr=new();
Etr.calc_crc(); //调用Etr::calc_crc();
Btr=Etr;
Btr.calc_crc(); //在这里其实用的是扩展类中定义的Etr::calc_crc();函数,因为实际上这里的对象是扩展类对象。这就是多态。如果不用virtual,
这里看到定义的对象时Base类就用Base类中定义的calc_crc()函数了。这显然不是我们想要的。
蓝图模式:
那这里就说一下,为啥用蓝图
首先构建一个对象蓝图(一般是基类对象),然后修改它的约束,甚至可以用扩展对象替换它,随机化这个蓝图时,就得到想赋予的随机值;然后复制这个对象,将copy发给下游。
蓝图:是一个钩子,允许你改变发生器类的行为而无需修改其类代码。蓝图对象在一个地方构建(new()),在另一个地方(任务run)使用
class Generator;
mailbox gen2drv;
Base Blueprint; //蓝图(以基类定义)
//初始化一个Generator类的对象,就要初始化两个参数,一个是
mailbox 对象,另一个是蓝图对象。
function new(input mailbox gen2drv);
this.gen2drv=gen2drv;
Blueprint=new();
endfunction
task run;
Base Btr; //
创建局部变量指向Base的对象
forever begin
assert(Blueprint.randomize());
//使用蓝图
Btr= Blueprint.copy();
//对象值 复制,具体怎么copy下面再讲。
//上面这两句合起来算是对Btr进行了初始化。用蓝图的好处就是可以用扩展的功能来随机化蓝图对象然后赋值给Btr
gen2drv.put(Btr);
Btr.calc_crc();
end
endtask
endclass
program automatic test;
Generator gen;
initial begin
gen=new(); //初始化两个参数,一个是 mailbox
对象,另一个是蓝图对象。
Ext Etr=new(); //新建初始化一个扩展类。
gen.Blueprint = Etr;
//基类的句柄,也可以指向扩展类的对象。以Ert对象取代以基类定义的蓝图,这样,对于Generator类中的那句 Btr= Blueprint.copy();
就相当于用派生类给基类初始化,这是完全可以的。
gen.run();
//调用类的任务,其中task中定义的那句 Btr.calc_crc();
在这里其实用的是扩展类中定义的calc_crc()函数,因为实际上这里的对象是扩展类对象。这就是多态。如果不用virtual,
这里看到定义的对象时Base类就用Base类中定义的calc_crc()函数了。这显然不是我们想要的。
end
endprogram
刚刚上面用到了一个copy 函数,
Btr= Blueprint.copy();
这个函数应该在Base基类里的虚函数。Ext扩展类可以继承的。
那我们把上面的基类和扩展类里面分别添加一个copy
虚函数。
class Base;
//基类
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
virtual function Base copy();
copy=new();
copy.src=src;
//复制数据域
copy.dst=dst;
copy.data=data;
copy.crc=crc;
endfunction
........
endclass
////////////////////////////////////////////
class
Ext extends Base;
//扩展类
rand bit
bad_crc;
virtual function Base copy();
Ext
Etr; //临时变量,用于存值。
Etr=new();
Etr.src=src;
//复制数据域
Etr.dst=dst;
Etr.data=data;
Etr.crc=crc;
Etr.bad_crc= bad_crc;
return Etr;
//扩展类要返回一个Base类指向的对象,这样才能用于
Btr= Blueprint.copy();
endfunction
endclass
/////////////////////////////
优化:在扩展类中再一次定义
Etr.src=src;
Etr.dst=dst;
Etr.data=data;
Etr.crc=crc;
感觉有点多余吧。就跟构造函数一样,属于基类的部分直接调用基类函数就行了。我们把上面的copy函数拆分成两个,重新定义如下:
class Base;
//基类
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
virtual function void
copy_data(input Base Tr)
Tr=new();
Tr.src=src;
//复制数据域,这边跟教材有出入,我认为教材错了。
Tr.dst=dst;
Trdata=data;
Tr.crc=crc;
endfunction
virtual function Base
copy();
copy =
new();
copy_data(copy);
endfunction
........
endclass
class
Ext extends Base;
//扩展类
rand bit
bad_crc;
virtual function void copy_data(input
Base Tr)
//这里用Tr, 表明多态,既可以是基类,也可以是扩展类。
这边跟教材有出入,我认为教材错了。
super.copy_data(Tr); //其实这句话拷贝了扩展类对象中的基类部分。
Tr.bad_crc=bad_crc;
//派生类数据的复制。
endfunction
virtual function Base
copy();
Ext Etr;
Etr=new();
copy_data(Etr);
return Etr;
endfunction
endclass
/////////////////////////////////////////////////////////////////////////////////////////////////////////
纯虚方法和抽象类
virtual class BaseTr;
//前面加virtual,抽象类。抽象类可以被扩展,但不能被例化
static int
count;
int id;
function new();
id=count++;
endfunction
pure
virtual function BaseTr copy(input BaseTr
to);
//纯虚函数,仅仅是原型声明,没有定义功能,甚至连空函数也不是,所以不能直接例化基类。
//纯虚方法只能在抽象类中定义。
virtual function void
display;
//抽象类中也可以定义不是纯虚的函数。
$display("id=%d",id);
endfunction
endclass
扩展类
class Base extends
BaseTr;
rand bit[31:0]
src,dst,data[8];
bit[31:0]
crc;
extern
virtual function BaseTr
copy(input BaseTr to);
.........
............. //
在这里定义。
endfunction
//在扩展类中定义纯虚函数的具体功能。
virtual function void display;
super.display(); //显示id
$display("Src=%h,Dst=%h,Crc=%h",src,dst,crc);
endfunction
endclass
当一个扩展类的将基类中声明的所有纯虚函数实现定义以后,才能在测试平台中使用它。。
知道了抽象类,我们就说一种很有用的方法: 回调。
背景:测试平台目的:创建一个不做任何修改就能在所有测试中使用的验证环境。要做到这点的关键是测试平台使用钩子,(什么是钩子?)钩子作用,在不修改原始类的情况下注入新的代码。回调基类是一个抽象类,使用前必须先进行扩展。
作用:回调就是一个钩子,在不修改原始类的情况下注入新的代码。
实现:回调任务在顶层中创建,在最低级即驱动器中调用。这样驱动器不需要知道测试的任何信息,它只需要使用一个可以在测试中扩展的通用类。
1) 使用回调注入干扰
回调的一个常见用法就是注入干扰,例如引入一个错误或者延迟。下面测试平台使用回调对象,随机地丢弃数据包。
回调基类是抽象类,在扩展的回调类中加入错误注入,而drive驱动类中,是回调基类的数据队列,在环境中将扩展类句柄让入驱动器类,回调基类的数据队列中。
virtual class Driver_callback;
//抽象类
virtual task
pre_tx( ref Base, ref bit drop);
//默认情况下不做动作。但注意不是纯虚方法。因为下面的程序中需要默认例化这个基类。
endtask
endclass
class Driver;
Driver_callback Cb[$];
//定义一个队列,不断地处理回调对象。
task run();
bit drop=0; //默认是0
.................
forever begin
foreach(Cb[i])
//由于队列先进先出,后进后出,所以最新(后)注入的错误是最后出来的,给drop赋值
Cb[i].pre_tx(tr,drop); //默认的回调对象啥事都不做
if(!drop) continue;
//若drop=0, 跳出当前循环,不执行
transimit(tr),继续下一个forever;若为1, 发送 transimit
transimit(tr);
end
endtask
endclass
class Driver_callback_drop extends Driver_callback;
virtual task pre_tx( ref
Base, ref bit drop);
drop=($urandom(0,99)==0); //正常情况下drop为0
endtask
endclass
program automaitc test;
......
....
initial begin
Driver drv=new();
begin
Driver_callback_drop dcd=new();
drv.Cb.push_back(dcd); //将扩展过带有错误注入的回调对象放入队列的头。
end
endprogram
回调函数中的重要点就在 先定义一个基类方法
virtual task
,啥都不干(但不是纯虚,可以例化)。在测试的时候可以扩展定义然后使用。
只有在测试中碰到回调对象时才“开火”
相关文章推荐
- Access数据库出现"Selected collating sequence not supported by the operating system."错误
- "System.Data.OleDb.OleDbException: 无法从指定的数据表中删除"的解决办法
- SystemVerilog VMM Workshop Lab Guide(LG)学习笔记-Lab1 VMM Environment & Message Service
- In Admin going to System > Magen…
- "面向切面(AOP)"与"面向对象(OOP)"
- 把 映射到xml中本来就该使用&#160来代替
- 奇怪的编码,奇怪的显示——一个关于 的故事
- 'Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception.
- js的replace方法将字符串中的 全部替换为空字符串
- HTML字符实体(关于 ><等)
- &NBSP:“Non-Breaking Space” 的缩写
- &nbsp
-
- Use Windows Authentication in ASP.NET 2.0/ASP.NET 2.0 中的 Windows 身份验证
- XML 问题: 超越DOM(轻松使用 DOM 的技巧和诀窍)
- HTML字符实体(关于 ><等)
- POJ 1018 Communication System
- SystemVerilog VMM Workshop Lab Guide(LG)学习笔记-Lab2 VMM Transaction & Atomic Generator
- <cf>System of Equations(水题)
- 'alter system switch logfile' 和 'alter system archive log current'的区别