第五章. 面向对象编程基础

5.1 OOP概述

        OOP:面向对象编程语言(Object Oriented Programming)

Verilog 属于过程性编程语言(代码逐行执行,无数据结构,类似C语言),Verilog 中没有结构,只有位向量和数组。而在对总线事务(bus transaction)建模时往往需要数据结构,使用过程性语言不够便利。

SV属于面向对象编程语言(Object Oriented Programming,OOP),OOP 所有的功能都是基于类来实现的,类中可以封装成员变量和成员方法,这极大提高了建模的效率。OOP 的基本单元是类(class)和对象(object),通过这些基础的单元来实现 OOP 编程语言的三个特性,封装(encapsulation),继承(iheritance),多态(polymorphism)。因此,可以简单的说:OOP=类+对象+封装+继承+多态。

5.2 考虑名词,而非动词

测试平台目标:给一个设计施加激励,检查其结果是否正确。测试平应该分成诺干个板块(block)然后定义它们相互之间如何通信。

5.3.1 类(class)

        包含成员变量和成员方法。类不是对象,类描述了实例化对象的规则,定义了实例化对象中包含哪些成员变量和成员方法,可以简单理解类是图纸,对象是通过图纸构建的实体。

5.3.2 对象(object)

类的一个实例。类是抽象的,而对象是由抽象的类具体化的一个实体。在实例化对象时,必须先定义类,否则SV无法实例化对象,因为SV不知道如何创建对象。

5.3.3 封装

        SV中使用类来实现封装。类中封装了成员变量和成员方法。

5.3.4 继承

        允许通过现有的类去得到一个新类。现有的类称为父类或基类,得到的新类称为子类或派生类。子类拥有父类成员变量和成员方法。

5.3.5 多态

多态的实现基于继承概念。将父类中的方法声明为virtual,并在子类中实现该方法当父类句柄指向子类对象时,通过父类句柄调用该方法,方法会依据父类句柄实际指向的对象选择调用子类中的方法,而不是父类中的方法。这种父类/子类有相同的方法名称,但能够依据对象准确调用的特性称为多态。

5.3.6 句柄(handle)

指向对象的指针,像一个对象的地址,但只能指向单一数据类型的指针中。

5.3.7 属性(property)

类中存储数据的变量,在Verilog 中i就是寄存器(reg)或者线网(wire)类型的信号,其实就是成员变量

5.3.8 原型(prototype)

原型即程序的头,包含程序名,返回类型和参数列表。与程序头相对应的为程序体,包含了该函数要执行的代码。

5.4 在哪里定义类

定义在 program、module、package 中,或者在这些快之外的任何地方,类可以在程序和模块中使用。

5.5 OOP 术语的比喻

        类(房子的蓝图,可构建房子的各个部分,也就是可创建多个对象)-- 对象(实际的房子)-- 句柄(房子的地址)-- 类中的变量(用来保存数值,灯的开或者关)-- 子程序(用来控制这些数值,使灯开或者关),一个房子的类可能具有很多盏灯。

5.6 创建新对象

Verilog 和 SV 都具有例化的概念,但是在细节方面存在一些区别。Verilog 的例化是静态的(编译的时候例化),就像硬件一样在仿真的时候不会变化,只有信号值在改变。而 SV 中例化可以理解为动态的(运行的时候例化和释放内存),激励对象不断地被创建并且用来驱动 DUT,检查结果,最后这些对象所占用的内存可以被释放,以供新的对象的使用。Verilog 的顶层模块是不会被显式的例化的,SV 类在使用前必须先例化。另外,Verilog 的实例名只可以指向一个实例,而 SV句柄可以指向很多对象,当然一次只能指向一个。

5.6.1 没有消息就是好消息

例 5.2

 //声明和使用句柄
Transaction tr;            //声明句柄
tr = new();                //为tr分配内存空间

在声明句柄时,初始化为特殊值null,然后调用new()函数创建Transaction对象。new函数为Transaction分配空间,将变量初始化为默认值(二值逻辑默认值为0,四值逻辑默认值为X)new为构造函数。

5.6.2 什么是实例化

实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。实例化过程中一般由类名 对象名 = new 类名(参数1,参数2...参数n)构成。

多数语言中,实例化一个对象就是为对象开辟内存空间,或者是不用声明,直接使用new 构造函数名,建立一个临时对象

5.6.3 定制构造函数(Constructor)

new()函数称为构造函数,默认情况下构造函数会分配内存,初始化变量。new()函数不能有返回值,因为构造函数总是返回一个指向类对象的句柄,其类型就是类本身。SV怎么知道该调用哪个new()函数呢?这取决于赋值操作符左边的句柄类型。

class Transaction;logic [31:0] addr, crc, data[8];function new;addr = 3;foreach(data[i]) data[i] = 5;endfunction
endclass

5.6.4 将声明和创建分开

应该将声明和创建分开,避免在声明一个句柄的时候调用构造函数。虽然在语法上合法,但是这会引起顺序问题。因为构造函数在第一条过程语句之前就被调用了。

5.6.5 new()和 new[ ] 的区别

new()用来创建对象,可以包含参数;new[ ]用来为数组分配内存,只可以包含数组的大小。

5.7对象的解除分配

垃圾回收就是一种自动释放不再被引用的对象的过程。SV 分辨对象不再被引用的方法就是记住指向它的句柄的数量,当最后一个句柄不再引用某个对象了,SV 就释放该对象的空间。SV不能回收一个被句柄引用的对象,可以通过给指针赋值nul,清除句柄。如果对象包含从一个线程派生出来的程序,那么只要该线程仍在运行,这个对象的空间就不会被释放。

例5.7 创建多个对象

Transaction t;//  创建一个句柄
t = new();    // 分配一个新的 Transaction
t = new();   //分配第二个,并且释放第一个 t;
t = null;    //解除分配第二个

5.8 使用对象

Transaction t;        //声明Transaction类型句柄
t = new();            //创建对象
t.addr = 32'h42;      //设置变量值
t.display();          //调用子程序

        严格的OOP规定,只能通过对象的公有方法访问对象的变量,如get()和put()。此规定保证了无法通过类实例进行外部赋值来改变类成员变量的值,保证类的对外部的封装性。在创建测试平台中,目标是最大限度的控制所有变量,以产生最广泛的激励,所以不可能实现严格的OOP规定。

5.9 静态变量和全局变量

全局变量:关键字static,将变量定义为全局变量。

静态变量(在加载之后就有这个变量了):使用关键词 static 来声明 class 内的变量时,则其为静态变量。

静态变量通常在声明时初始化,而不是在类的new函数中初始化。在类中定义静态变量,该变量属于类所有,即通过该类实例化的对象共享同一个变量,通过类名+类作用域操作符(::)+变量名方法访问class::parameter类的静态变量。

5.10 类的方法

类中的程序也称为方法,也就是在类的作用域内定义的内部 task 或者 function。

静态方法:方法名称前加入 static 关键字。SV 中可以在类中创建一个静态方法读写静态变量的值,SV 不允许静态方法读写非静态变量。

在类外定义方法:在类中声明方法时在方法名称前添加 extern 关键字。然后将整个方法移至类的外部,并在方法名前加上类名和类作用域操作符::。类中的方法默认使用自动存储。

class transaction;
extern function void display;
endclassfunction void transaction::display;
...
endfunction

5.11 作用域规则

作用域是一个代码块,例如模块,程序,任务,函数,类或者begin-end块。

作用域可以相对于当前作用域,也可以使用绝对作用域,绝对作用域$root开始。

//名字作用域
int limit;                          //$root.limitprogram automatic p;int limit;                     //$root.p.limitclass Foo;int limit, array[];            //$root.p.Foo.limit//$root.p.Foo.print.limitfunction void print (int limit);for(int i=0; i<limit; i++)begin$display("%m: array[%0d] = %0d", i, array[i]);endendfunctionendclass
endprogram

5.12 this 是什么

tihs: 当你使用一个变量名的时候,SV 将首先在当前作用域内寻找,接着在上一级作用域内寻找,直到该找到改变量为止。当你想引用类一级的对象,可以使用 this 明确地指明变量的作用域为当前类。

5.13 在一个类内使用另一个类

通过使用指向对象的句柄,一个类内部可以包括另一个类的实例。

5.14 编译顺序的问题

        如果需要编译一个类,而这个类包含一个尚未定义的类 。声明这个被包含的类的句柄会引起错误,因编译器还不认识这个新的数据类型。可以使用typedef语句声明一个类名。

//使用typedef class语句声明statistics是一个类
typedef class Statistics;//定义低级别类class Transaction;Statistics status;//使用Statistics类...
endclassclass Statistics;//定义Statistics类...
endclass

5.15 理解动态对象

静态分配内存的语言中,每一块数据都有一个变量与之关联,而在OOP语言中,不存在这种一一对于关系。可能有很多对象,但是只定义了少量的句柄。(都是这个小区的地址,但是这个小区里面有很多住户)

5.16 将对象传递给方法

        当调用方法的的时候,传递的是对象的句柄,而非对象本身。

5.17 在任务中修改句柄
        一个常见的编码错误是当你想修改参数的值的时候,忘记在方法的参数前加 ref 关键词,尤其是句柄

5.18 在程序中修改对象

在测试平台中,一个常见的错误是忘记为每个事务创建一个事务的对象

5.19 句柄数组

在写测试平台的时候,可能需要保存并且引用许多对象。你可以创建句柄数组,数组的每一个元素指向一个对象。

5.20 对象复制

复制分为浅复制(shallow copy)和深复制(deep copy)。

如果拷贝对象里的元素只有值,没有句柄,浅拷贝和深拷贝没有差别。都会将原对象复制一份,产生一个新对象,对新对象的值进行修改不会影响原有的对象。

如果拷贝的对象里的元素包含句柄,则深拷贝和浅拷贝是不同的,浅拷贝复制的是原句柄,其指向与原句柄相同,使用浅拷贝新对象对句柄进行修改会改变原对象句柄指向值。深拷贝会复制原对象句柄指向的对象,产生一个新的对象,对深拷贝产生的新句柄修改不会影响原句柄的值。简单来说就是浅拷贝只是复制了句柄,并没有对句柄指向对象进行复制,浅拷贝复制的句柄与原句柄指向同一个对象。而深拷贝是将句柄指向的对象复制。

示例

//使用new复制一个对象,创建了一个新的对象,并且复制了现有对象的所有变量。new复制属于浅复制。
Transaction src, dst;
initial
beginsrc = new;            //创建第一个对象dst = new src;        //使用new操作符进行浅复制
end

编写copy函数:copy函数属于深复制(注意复制对象中含有句柄的变量,需要调用句柄类型的copy函数,重新分配空间,指向一个新的对象)。

5.21 公有和私有

在一个类中,数据默认被定义为私有,这样防止了其他类对内部数据成员的随意访问。
        SV 中类似其 OOP 语言,成员变量的访问权限有以下三种 local(类似C++中的private),protected,public,访问权限依次扩大。

local:只有该类中函数可以访问,子类和外部类都无权访问。

protected:该类和其子类中可以访问,类外部无权访问。

public:类中,子类,类外均可访问。

SV 中定的类中,数据默认被定义为 local 类型。

5.22 建立一个测试平台

图中的 Generator、Agent、Driver、Monitor、Checker 和 Scoreboard 都是类,被建模成事务处理器(transactor)。它们在 Environment 类内部例化。

功能覆盖(Function Coverage)

SV绿皮书提炼笔记(五)相关推荐

  1. SV,class学习笔记五、参数化类(parameterized class)

    1.改变类中传递的参数 //定义一个参数化类 class packet #(int size = 1);...... endclass//在创建句柄handle时改变参数 packet #(.size ...

  2. SV绿皮书笔记(九)暂时完结

    第九章. 功能覆盖率 9.1覆盖率类型 功能覆盖率:功能覆盖率和设计意图是紧密相连的.用来衡量哪些设计特性已经被测试程序测试过的一个指标. 代码覆盖率:包括行覆盖率,路径覆盖率,翻转覆盖率,状态机覆盖 ...

  3. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  4. Ethernet/IP 学习笔记五

    Ethernet/IP 学习笔记五 Accessing data within a device using a non-time critical message (an explicit mess ...

  5. StackExchange.Redis学习笔记(五) 发布和订阅

    StackExchange.Redis学习笔记(五) 发布和订阅 原文:StackExchange.Redis学习笔记(五) 发布和订阅 Redis命令中的Pub/Sub Redis在 2.0之后的版 ...

  6. 吴恩达《机器学习》学习笔记五——逻辑回归

    吴恩达<机器学习>学习笔记五--逻辑回归 一. 分类(classification) 1.定义 2.阈值 二. 逻辑(logistic)回归假设函数 1.假设的表达式 2.假设表达式的意义 ...

  7. 好程序员教程分析Vue学习笔记五

    好程序员教程分析Vue学习笔记五,上次我们学习了Vue的组件,这次我们来学习一下路由的使用.在Vue中,所谓的路由其实跟其他的框架中的路由的概念差不多,即指跳转的路径. 注意:在Vue中,要使用路由, ...

  8. 【AngularJs学习笔记五】AngularJS从构建项目开始

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# AngularJs学习笔记 [AngularJs学习笔记一]Bower解决js的依赖管理 [AngularJs学习笔 ...

  9. ROS学习笔记五:理解ROS topics

    ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...

最新文章

  1. 循环神经网络实现文本情感分类之使用LSTM完成文本情感分类
  2. Android的消息机制简单总结
  3. gis地图和普通地图的区别_GIS之如何添加WMTS地图
  4. Uva 247 - Calling Circles(传递闭包 / 强连通分量)
  5. 为什么深度学习有效?(why deep learning works)
  6. tomcat优化实例
  7. 打造自己的Android源码学习环境之六:运行Android模拟器
  8. Python—2022 |已有文章汇总 | 持续更新,直接看这篇就够了
  9. 【小程序合集】来一组适合你的表情包-表情包大全
  10. 分享scratch转exe可执行文件scratch2exe-ch-se
  11. 深度学习笔记 摘抄笔记
  12. Data Recovery Strategy Determines Backup Strategy【每日一译】--2012-11-11
  13. js插件---画图软件wePaint如何使用(插入背景图片,保存图片,上传图片)
  14. WINRAR密码去除/破解工具
  15. 权限提升+权限维持+痕迹清理
  16. 高效搭建基于dnsmasq通过webui管理的dns服务器
  17. 移动硬盘装Ubuntu系统小记
  18. 打开CAD的dwg文件时提示:许可检出超时,您要执行什么操作?AutoCAD将关闭。
  19. tiup uninstall
  20. excel从入门到忘记 学习心得分享(含百度网盘分享)

热门文章

  1. 很不错实用的前端工具
  2. 100多个国外市场APP推广渠道网站 提交地址
  3. 王传福:华为手机大部分是比亚迪造!小米们造车亏钱是小事,浪费时间是大事...
  4. 工作第十二天(一篇喜欢的文章)
  5. 遥感原理与技术(绪论、遥感物理基础)
  6. linux没有mail命令,linux metamail命令负责处理非文字E-mail的程序
  7. auto.js实现自动更新,带进度条
  8. 关于电商平台目前环境的思考
  9. wins 服务器的介绍
  10. 【算法:数学】计算几何-多边形的重心