08-SV面向对象编程的高级技巧指南
1、原始类与扩展类
(1)原始类被称为父类或者超类,扩展类被称为派生类或者子类。扩展类可以直接访问原始类和其本身的所有变量,应该将原始类中的子程序定义成虚拟的,这样它们就可以在扩展类中重定义。new函数无法扩展,SV始终基于句柄类型来调用new函数。
(2)扩展类的构造函数
如果基类构造函数有参数,那么扩展类必须有一个构造函数而且必须在其构造函数的第一行调用基类的构造函数。
(3)关于句柄
指向基类(Transaction)的句柄也可以用来指向派生类(BadTr)的对象。当调用tr.calc_crc函数的时候,到底是调用基类中的虚拟函数,还是调用扩展类中扩展的calc_crc,这取决于tr中的对象类型:如果对象是Transaction类型,那么调用Transaction::calc_crc,否则调用BadTr::calc_crc。
(4)关于约束
如果在扩展类中定义了一个约束,并且扩展后的约束名和基类里的约束名相同,那么扩展类的约束会替代基类中的约束。
2、蓝图
先构建一个对象的蓝图,然后修改它的约束,甚至使用一个扩展对象替换它,然后当你随机化这个蓝图的时候,他就会具有你想赋予的随机值,接着复制这个对象,并将拷贝值发送给下游的事务处理器。此技术出色的地方在于:如果你改变了蓝图对象,你的发生器就会创建一个不同类型的对象。
3*、类型向下转换(downcasting)和虚方法
(1)句柄能够指向一个类的对象或者任何它的扩展类的对象,那么,当一个基类句柄指向一个扩展类对象的时候会发生什么?当你调用一个同时存在于基类和扩展类中的方法的时候将会发生什么?
(2)类型向下转换或者类型变换是指将一个指向基类的指针转换成一个指向派生类的指针。
(3)将一个基类对象拷贝到一个扩展类的句柄会失败,但是将一个基类句柄赋值给一个扩展类句柄并不总是非法的,当基类句柄确实指向一个派生类对象时是允许的,需要用$cast函数转换。
(4)当需要决定调用哪个虚方法的时候,SV根据对象的类型,而非句柄的类型来决定调用什么方法。但是对于非虚方法,SV会根据句柄的类型tr,而不是对象的类型。OOP中多个子程序使用一个共同的名字的现象叫做“多态”。使用虚方法的缺点:一旦定义了一个虚拟的子程序,所有带有该虚拟子程序的扩展类就必须使用相同的签名,例如相同类型和个数的参数。
4、合成、继承和其他替代方法
(1)合成就是在类中嵌套类,将几个小类组合成一个更大的类。
(2)将类层次化的经典OOP方法是根据功能将类划分成易于理解的小块。
(3)合成会导致层次结构变复杂,继承需要额外的代码和设计来处理所有的不同类,而且两者的创建和初始化都很困难。可以考虑创建一个单一的不分层的类,包含所有的变量和子程序,然后通过判别变量去采用不同的约束
5、对象的复制
(1)使用copy()方法:扩展类的虚函数必须跟基类的相匹配,包括所有的参数和返回类型
(2)使用copy_data()方法;
(3)指定复制的目标
6、抽象类和纯虚方法
(1)抽象类(virtual class):可以被扩展但是不能被直接实例化
(2)纯虚(pure virtual)方法:没有实体的方法原型
(3)一个由抽象类扩展而得来的类只有在所有虚方法都有实体的时候才能被例化,纯虚方法只能在抽象类中定义,但是抽象类中也可以定义非纯虚方法。
7、回调
(1)是指使用回调的方法,驱动器“回调”一个在顶层测试中定义的子程序。这项技术的好处在于这种回调子程序可以在每个测试中做不同的定义,这样就可以使用回调来为驱动器增加新的功能而不需要编辑Driver类。
(2)包括前回调(pre_callback)和后回调(post_callback)
(3)回调的一种常见用法是用来注入干扰,例如引起一个错误或者延迟。回调也可以用来向记分板发送数据或者收集功能覆盖率。
8、参数化的类
(1)SV的类参数化近似于C++中的模板。比如一个堆栈就可以引入类参数,使得它支持多种不同数据类型。
(2)在SV中,可以为类增加一个数据类型参数,并在声明类句柄的时候指定类型。例如:class Stack #(type T=int)
9、结论
(1)继承使得现有的类可以在原始类的基础上增加新的功能,并且与之前的设计保有兼容性
(2)可以通过“升级”现有的驱动器类来注入错误以创建一个新的测试,如果驱动器中已有的回调,无须对测试平台的架构做任何改变。
(3)使用OOP技术需要提前做好计划。通过使用虚拟子程序和提供足够的程序回调入口,测试可以在对代码不作任何改变的情况下更改平台的行为。
(4)使用参数化的类
10、示例程序
(1)类定义
package class_define;
//事务基类
class Transaction;
rand bit[:] src,dst,data[];
bit [:] crc; // 基类的构造函数
function new(input bit[:] src = );
this.src = src;
$display("construction function of base class");
endfunction // 基类复制函数
virtual function Transaction copy();
copy=new();
copy.src=src;
copy.dst=dst;
copy.data=data;
copy.crc=crc;
endfunction virtual function void calc_crc;
crc = src^dst^data.xor;
endfunction virtual function void display(input string prefix="");
$display("%sTr:src=%h,dst=%h,crc=%h",
prefix,src,dst,crc);
endfunction function void fun1();
$display("execute non-virtual Transaction::fun1");
endfunction
endclass //使用继承来增加一个约束
class Nearby extends Transaction;
constraint c_nearby{
dst inside {[src-:src+]};
}
endclass //扩展的Transaction类
class BadTr extends Transaction;
rand bit bad_crc;
bit badtr_var1; //扩展类的构造函数
function new(input bit[:] src = , bit badtr_var1 = );
super.new(src);
this.badtr_var1 = badtr_var1;
endfunction // 扩展类的复制函数
virtual function Transaction copy(); //返回类型和基类复制函数一样
BadTr bad;
bad=new();
bad.src=src;
bad.dst=dst;
bad.data=data;
bad.crc=crc;
bad.bad_crc=bad_crc;
return bad;
endfunction virtual function void calc_crc;
super.calc_crc(); //计算正确的CRC
if(bad_crc) crc = ~crc; //计算错误的CRC位
endfunction virtual function void display(input string prefix="");
$write("%sBadTr:bad_crc=%b,",prefix,bad_crc);
super.display();
endfunction function void fun1();
$display("execute non-virtual BadTr::fun1");
endfunction endclass // 发生器
class Generator;
mailbox gen2drv;
Transaction blueprint; //蓝图 function new(input mailbox gen2drv);
this.gen2drv = gen2drv;
blueprint = new();
endfunction task run;
Transaction tr;
forever begin
assert(blueprint.randomize);
tr=blueprint.copy();
gen2drv.put(tr);
end
endtask endclass //驱动类
class Driver;
mailbox gen2drv; function new(input mailbox gen2drv);
this.gen2drv = gen2drv;
endfunction task run;
Transaction tr;
forever begin
gen2drv.get(tr); //从发生器获得事务
tr.calc_crc(); //处理事务
//发送事务
end
endtask
endclass // 环境类
class Environment;
Generator gen;
Driver drv;
mailbox gen2drv; function void build(); //通过构建邮箱、发生器和驱动器来创建环境
gen2drv = new();
gen = new(gen2drv);
drv = new(gen2drv);
endfunction task run();
fork
gen.run();
drv.run();
join_none
endtask task wrap_up(); endtask endclass endpackage
(2)测试程序1
import class_define::*;
program test8;
initial begin
Transaction tr;
BadTr badtr,badtr2;
tr = new();
badtr = new(,);
tr.display(); // 调用 Transaction::display()
tr.fun1(); // 调用 Transaction::fun1()
//badtr = tr; // 错误,不能把基类对象赋给扩展类句柄
//badtr.display(); //错误
tr = badtr; // 正确,可以把扩展类对象赋给基类句柄
tr.display(); // tr指向扩展类对象,调用BadTr::display()
tr.fun1(); // 仍然调用 Transaction::fun1(),因为fun1为不是虚函数
//badtr2 = tr; // 报错,虽然tr指向了扩展类对象,需要用$cast
$cast(badtr2,tr); // 正确,cast函数会检查句柄所指向的对象类型,而不仅仅检查句柄本身
badtr2.display();
end endprogram
输出:
construction function of base class
# construction function of base class
# Tr:src=,dst=,crc=
# execute non-virtual Transaction::fun1
# BadTr:bad_crc=,Tr:src=,dst=,crc=
# execute non-virtual Transaction::fun1
# BadTr:bad_crc=,Tr:src=,dst=,crc=
(3)测试程序2
import class_define::*;
program test8_2;
Environment env;
initial begin
env = new();
env.build();
begin
BadTr bad = new(,); //以扩展类的bad对象取代蓝图
env.gen.blueprint=bad;
end
env.run();
env.wrap_up(); end endprogram
输出:
# construction function of base class
# construction function of base class
08-SV面向对象编程的高级技巧指南的更多相关文章
- 《JavaScript面向对象编程指南(第2版)》读书笔记(一)
目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(二)
<JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...
- Python面向对象编程指南
Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...
- 《JavaScript面向对象编程指南》读书笔记②
概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...
- 《JavaScript面向对象编程指南》读书笔记①
概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...
- 闭包初体验 -《JavaScript面向对象编程指南》
下面是我对闭包的理解:(把他们整理出来,整理的过程也是在梳理) 参考<JavaScript面向对象编程指南> 1.首先,在理解闭包之前: 我们首先应该清楚下作用域和作用域链 作用域:每个函 ...
- [Java入门笔记] 面向对象编程基础(一):类和对象
什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...
- [.net 面向对象编程基础] (14) 重构
[.net 面向对象编程基础] (14) 重构 通过面向对象三大特性:封装.继承.多态的学习,可以说我们已经掌握了面向对象的核心.接下来的学习就是如何让我们的代码更优雅.更高效.更易读.更易维护.当然 ...
- [.net 面向对象编程基础] (18) 泛型
[.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...
随机推荐
- Time-Frequency Networks For Audio Super-Resolution
论文题目:2018_用于音频超分辨率的时频网络 博客作者:凌逆战 博客地址:https://www.cnblogs.com/LXP-Never/p/12345950.html 摘要 音频超分辨率(即带 ...
- 《自拍教程17》Python调用命令
他山之石 何为他山之石,就是借助外界工具,来实现自己想要的功能. 命令行界面软件, 即各种命令,我们也叫命令行工具, 此类工具也是测试人员或者开发人员常用的工具的一种. 测试人员可以借助这类工具,快速 ...
- 云服务器centos系统安装python
1.查看python的版本 $ cd /usr/bin/$ ls python* $ ls -al python* //查看依赖关系 2.如果版本不合适可以卸载python再重新安装 # rpm -q ...
- Node.js的__dirname,__filename,process.cwd(),./的一些坑
参考博客:https://github.com/jawil/blog/issues/18
- Jenkins 插件使用国内镜像源-解决插件下载慢的问题
问题 我们在Jenkins里面经常会遇到安装插件很慢,这是由于我们使用的是更新中心镜像默认为国外的源.现在我们可以进行设置为国内镜像源,来解决安装插件慢的问题. 解决办法 安装插件localizati ...
- Linux学习Day3:新手必须掌握的Linux命令(二)
今天学习的命令都是运维工作中经常要用到的,非常实用,必须要用心学习,争取把这些命令烂熟于心,具体内容如下: 一.系统状态监测命令 1.ifconfig命令 用于获取网卡配置与网络状态等信息. [roo ...
- Linux学习Day2:安装RedHat Linux和新手必须掌握的命令
今天是Linux线上培训的第二天,主要是Linux环境的安装和几个常见命令的学习,具体如下: 一.RHEL7系统的安装 首先是VMware WorkStation 12.0软件的安装,然后是RHEL7 ...
- LeetCode 57. Insert Interval 插入区间 (C++/Java)
题目: Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if nec ...
- Orleans[NET Core 3.1] 学习笔记(四)( 2 )获取Grain的方式
简介 在这一节,我们将介绍如何在Silo和Client中获取Grain及调用Grain Grain获取方式 从Grain内部获取: //根据特定的Key值创建或获取指定的Grain IStudent ...
- vue2.0嵌套组件之间的通信($refs,props,$emit)
vue的一大特色就是组件化,所以组件之间的数据交互是非常重要,而我们经常使用组件之间的通信的方法有:props,$refs和emit. 初识组件之间的通信的属性和方法 props的使用 子组件使用父组 ...