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中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...
随机推荐
- Hexo部署到Gitee/Coding常见的错误
全网最全小白搭建Hexo+Gitee/Coding 全网最全小白搭建Hexo+Gitee/Coding 本站内容已全部转移到https://www.myyuns.ltd,具体请移步到www.myyun ...
- Django框架的初使用-2
目录 Django框架的初使用-1 1 Django MVT回顾 2 模型M 2.1 ORM框架 2.2 模型设计 3 视图V 3.1 定义视图函数 3.2 配置URLconf 3.3 视图-匹配过程 ...
- c语言标准I/O
头文件 <stdio.h> 打开/关闭文件 FILE *fopen(char *filename, char *mode); 如果正常打开返回FILE指针,否则返回NULL mode常用值 ...
- JS高阶编程技巧--compose函数
先看代码: let fn1 = function (x) { return x + 10; }; let fn2 = function (x) { return x * 10; }; let fn3 ...
- MySQL手工注入学习-1
MySQL手工注入学习 SQLi-labs 手工注入学习 以下是通过SLQi-labs平台的部分简单例题的手工注入过程 Less-1:union联合查询注入 页面提示:Please input the ...
- 关于Redis缓存预热的思考
系统上线时,提前将相关的缓存数据直接加载到缓存系统.避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题. 这里我考虑2个问题: A.哪些数据需要预热? B.如何预热? 关于问题A,根据不同的业 ...
- 搭建Linux(Ubuntu)系统下的Differential Datalog运行环境
DDlog is a bottom-up, incremental, in-memory, typed Datalog engine. It is well suited for writing pr ...
- uniapp-使用心得
<view class="cu-item flex-sub" :class="index==TabCur?'text-orange cur':''" v- ...
- 剑指offer-字符的所有组合,复制复杂链表,二叉树中和为某一值的路径
字符的所有组合 描述: 输入一个字符串,求这个字符串中的字符的所有组合.如:"abc",组合为"a" "b" c" "a ...
- List泛型
.Net自从2.0以后开始支持泛型. 泛型的作用:可以创建独立于被包含类型的类和方法.泛型类使用泛型类型,并可以根据需要使用特定的类型替换泛型类型.这就保证了类型安全性:如果某个类型不支持泛型类,编译 ...