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中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...
随机推荐
- SAXParseException Content is not allowed in Prolog (前言中不允许有内容)
解析 XML 文件的时候,如 Mybatis 的 Mapper 文件,有时会出现 org.xml.sax.SAXParseException 前言中不允许有内容 的异常,英文就是 Content is ...
- mac下搭建http服务器(apache+php),使用homebrew升级php
新版mac依旧预装了 Apache ,但是已经不能在 「系统偏好设置」中的「Web 共享」来开启了,需要手动通过命令行开启. 启动Apache 启动:sudo apachectl start 停止:s ...
- 基于 webGL 的元素周期表 3D 交互展示
前言 之前在网上看到别人写的有关元素周期表的文章,深深的勾起了一波回忆,记忆里初中时期背的“氢氦锂铍硼,碳氮氧氟氖,钠镁铝硅磷,硫氯氩钾钙”.“养(氧)龟(硅)铝铁盖(钙),哪(钠)家(钾)没(镁)青 ...
- Jmeter 连接Redis获取数据集
公司开展了新的业务活动,需要配合其他部门做压测,由于脚本中的手机号和用户的uid需要参数化而且每次均不能重复,最初的考虑使用csv的方式来获取数据,比较头疼的问题是集群节点需要维护测试数据,所以我将所 ...
- Django 系列
Django基础 Django框架简介 Django 静态文件 Django request对象与ORM简介 Django路由系统 Django之视图层 Django之模板层 Django的setti ...
- linux设置服务器时间
在 Linux 机器上有两种时钟: 由内核维持的软件时钟(又称系统时钟)和在机器关机后记录时间的(电池供电的)硬件时钟. 启动的时候, 内核会把系统时钟与硬件时钟同步. 之后, 两个时钟各自独立运行. ...
- Linux分区工具-parted
parted用于操纵磁盘分区的程序,通常用于规则大小超过2T的分区,也可用于小分区的规划:它支持多种分区表格式,包括MS-DOS(MBR)和GPT:这对于为新操作系统创建空间,重新组织磁盘使用以及将数 ...
- Visual Studio Code中C/C++的环境配置
Visual Studio Code 的功能十分强大,但是对我这种小白不是很友好,它和其它的集成开发工具不同,Visual Studio Code (以下简称VS)自身其实仅仅是一个编辑器, 是不具备 ...
- MRAM技术进入汽车应用
在整个地址空间范围内读写各种类型的数据.通常MRAM的操作和时序类似于32位微控制器的规范和时序.与DLFASH相比,当今的非易失性存储器可以接受MRAM设备的性能和吞吐量. 与当今的DFLASH相比 ...
- 五种编程语言解释数据结构与算法——顺序表2(java与C++语言实现)
5.java实现方式: 5.1.顺序表的抽象结构 package com.xgp.顺序表; public interface MyList<T> { //1. initList(& ...