在System verilog 中引入类(class)的概念,这是面向对象编程的基础。有了类的概念就会有对象(object), 继承(heritage), 封装(encapsulation), 多态(polymorphism)以及属性和方法等。类的主要目的是定义私有数据集合,并对该定义的数据规定了类的拥有者的数据备份以及对该数据的操作方法。本节及后续章节的内容将详细介绍与类相关的各项内容及类数据的操作方法。
1. 类的定义
-
类的基本定义
类是用户定义的数据类型,以及数据的操作方法。类的定义采用关键字class 引导,后跟类名。如:
class my_data; //关键字class引导, my_data是类名,以分号”;”结束首行。
bit [3:0] a; //类的数据a
bit [3:0] b; //类的数据b
bit [3:0] sum; //类的数据sum
bit cy; //类的数据cy
endclass //以endclass 完成类的定义。
在上面的类定义中,没有引入更多函数( function)或任务(task)等操作数据的方法,目的简化类的概念,由浅入深的逐步给大家介绍类的定义与使用。
- 类的声明
类的定义不能独立存在,一般是在模块(module),包(package)或接口(interface)中进行。在module中声明class与其他变量(wire, variable),function,task等的声明类似,如例1所示,
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: FII // Engineer: Tim // // Create Date: 2022/04/09 21:50:32 // Design Name: // Module Name: test_class1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module test_class1( input [7:0] a, input [7:0] b, output logic [7:0] sum, output logic cy ); class my_data; bit ci; bit [3:0] a; bit [3:0] b; bit [3:0] sum; bit cy; task add ; begin {cy,sum}=a+b+ci; end endtask endclass always@(*)begin static my_data mydata_low=new; static my_data mydata_high=new; mydata_low.ci=0; mydata_low.a=a[3:0]; mydata_low.b=b[3:0]; mydata_low.add; mydata_high.ci=mydata_low.cy; mydata_high.a=a[7:4]; mydata_high.b=b[7:4]; mydata_high.add; sum={mydata_high.sum,mydata_low.sum}; cy=mydata_high.cy; end endmodule
在例1中声明了类(class), 类的名称为mydata。其中a,b, sum,cy为类mydata的数据。add 是利用task 定义了类的方法,用来操作类mydata的数据。类内的数据又称为属性。类的声明与变量声明类似,也是全局的,即从声明的位置开始到模块结束之前都有效。
2. 句柄与对象(object)
对象是类的实现。那么什么是类(class), 什么是句柄?什么是对象呢?三者有何区别呢?
所谓类(class)是对某一类的事物或群体进行抽象的描述,往往使用属性(property)对事物或群体进行描述,并定义一组对属性或数据具有操作行为的函数(function)或任务(task),当然也可以只有属性没有方法的类。例如:
我们可以为人类或人作为一个群体进行抽象描述,可以用以下属性描述:
国籍,肤色,年龄,身高,体重,性别等来描述所有人的共同属性。并对人的行为定义一套或多套行为,如:学习,工作,睡觉,跑步等。
class people;
nationality;
color;
age;
height;
weight;
gender;
function runing;
function study;
task work
function sleeping;
…
endclass
那么是不是有了类就可以呢?根据上面的描述,我们知道类不过是一个抽象的描述,比如人这个类是一个抽象的描述,有身高的属性,但身高是多少呢?不知道,因为不知道具体是哪个人,因此不能确切知道身高到底是多少,但是一旦知道某个具体的个人,那么这些属性就变得有意义了。比如知道某个具体的人张三,那么其所有的属性都可以确定了。我们把类具体化,就生成了对象,如类people中张三就是一个对象。如何生成对象呢?用类名后面跟对象名即可,如:
people zhangsan=new; //zhangsan是类生成的对象。又是生成对象也称为例化。
此时zhangsan这个类的对象就拥有了people的所有属性和方法。如例1所示,首先定义了类:
class my_data;
bit ci;
bit [3:0] a;
bit [3:0] b;
bit [3:0] sum;
bit cy;
task add ;
begin
{cy,sum}=a+b+ci;
end
endtask
endclass
在类中有数据ci,a ,b ,sum ,cy以及由task实现数据的操作方法add。在其后的always过程中由类my_data生成了两个对象,mydata_low和mydata_high。
static my_data mydata_low=new;
static my_data mydata_high=new;
注意:只有对象才真正拥有类的的属性和方法。一般不能直接用类名对数据(属性)进行操作。
那么什么是句柄(handle)呢?
句柄就是对象的名称或指针。是操作该对象内容的依据。如:
my_data mydata_low; //生成了句柄mydata_low,此时mydata_low=NULL
mydata_low=new(); //生成了有血有肉的对象。因为通过new给mydata分配空间(综合时可以生成电路),对象可以通过对象的名称也就是句柄mydata_low来访 问对象的内容。
总之,对象是包含属性、方法等所有内容的整体,而句柄仅仅是对象名或引用对象的指针。例如:
zhangsam是例化生成的对象,有身高,体重等属性,也有工作,学习等方法, 但如何来使用这些属性呢?必须通过zhangsan这个名字来访问,此时zhangsan既是对象名又是对象的指针。
3. 对象数据和方法的引用
类生成了对象,就可以利用对象进行属性和方法的引用和修改。对象对属性的引用采用 “对象名.属性”的方式引用。如:
第一个对象mydata_low对属性和方法的引用:
mydata_low.ci=0;
mydata_low.a=a[3:0];
mydata_low.b=b[3:0];
mydata_low.add;
第二个对象mydata_high对属性和方法的引用:
mydata_high.ci=mydata_low.cy;
mydata_high.a=a[7:4];
mydata_high.b=b[7:4];
mydata_high.add;
由类生成的对象是可综合的电路,也只有在类生成对象时才会在综合时以具体电路的形式体现。如果只有类而不生成对象,则不会生成具体的电路,正像C++中的类生成对象后才在计算机内存分配具体的空间一样。
一个类可以生成多个对象,如例1就利用mydata 生成了mydata_low,mydata_high两个对象。那么这些对象是什么关系呢?到底是共享类的属性和方法,还是独立拥有自己的属性和方法呢?实际上每个对象都会从类中的数据和函数得到一个自己的备份,因此对象的属性和方法都有自己的备份,彼此是独立的。在系统综合生成电路时也是各自独立的,图1显示了例1在Vivado下经过RTL分析形成的逻辑电路示意图
图1
4.仿真
编写testbench对例1进行仿真, tb程序如下:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2022/04/09 22:07:42 // Design Name: // Module Name: tb_test_class1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module tb_test_class1( ); logic [7:0] a, b; logic [7:0] sum; logic cy; initial begin a=0; b=0; #10 a=100; b=105; #10 a=109; b=155; #10 a=255; b=255; #10 $stop; end test_class1 test_class1_inst ( .a(a), .b(b), .sum(sum), .cy(cy) ); endmodule
仿真波形如下:
图2
5. 总结:
下面引用CSDN部分内容作为本节内容的总结,如图3所示,
图3
如图2所示:
(1)类 class——相当于我们要盖房的图纸,有了这个图纸,我们就能盖出一堆结构类似的楼房;
(2)对象 object——可以理解为房子本身,这是我们根据图纸建起来的;
(3)句柄 handle——房子的地址,门牌号,一个门牌号对应着一个房子,你可以通过门牌号来找到你想找的房子。用专业的话讲,就是句柄是对象的指针,我们通过句柄来找到对应的对象;
(4)变量 properties(也叫属性)——是房子里面的东西。我们知道,房子里可以有很多东西,也就对应着对象可以有多个变量。比如灯就是房子里的东西之一。我们可以通过灯的开关来调节房子的亮暗。
(5)方法 methods——用来操作变量的一段代码。如果这个变量是灯的话,那么这段代码对应的就是开灯和关灯两个动作。
————————————————
版权声明:本文为CSDN博主「Hardworking_IC_boy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dinghj3/article/details/116583131
老师您好,在阅读本篇文章时,遇到一些问题,请老师解答:
1、在文章中出现了2中生成对象的方式分别为“static my_data mydata_low = new;”与“my_data mydata_low ; mydata_low = new()”,请问老师,这两种书写方式都可以吗?
2、在vivado下形成的逻辑电路示意图图1中,注意到,上下两个加法器的输出位宽不一致,图1下方高位[7:4]加法输出的O[4:0],上方低位[3:0]加法输出的O[3:0],出现位宽一样的原因是因为“mydata_high.ci=mydata_low.cy”(即:低位的进位cy给了高位的ci)的原因吗?
1. 都可以,后续课程会继续讲解, constructor的概念
2.原图有误,已修改