1.类和对象概述
1.1.OOP术语
1.类(class):包含变量和子程序的基本构建块。 Verilog中与之对应的是模块(module)。
2.对象(object): 类的一个实例 。 在Verilog中,你需要实例化一个模块才能使用它。
3.句柄(handle):指向对象的指针。 在Verilog中,你通过实例名在模块外部引用信号和方法。一个OOP句柄就像—个对象的地址,但是它保存在一个只能指向单一数据类型的指针中。4.属性(property) : 存储数据的变量。 在Verilog中,就是寄存器(reg)或者线网 (wire)类型的信号。
5.方法(method):任务或者函数中操作变量的程序性代码。 Verilog模块除了initial 和always块以外,还含有任务和函数。6.原型(prototype):程序的头.包括程序名、返回类型和参数列表。程序体则包含了执行代码。
在Verilog中,通过创建模块并且逐层例化,就可以得到一个复杂的设计。在OOP中创建类并且例化它们(创建对象),你可以得到一个相似的层次结构。
1.2.创建对象
Verilog和OOP都具有例化的概念,但是在细节方面却存在着一些区别。一个Verilog模块例如一个计数器,是在代码被编译的时候例化的。而一个System Verilog类,例如一个网络数据包,却是在运行中测试平台需要的时候才被创建。Verilog的例化是静态的,就像硬件一样在仿真的时候不会变化,只有信号值在改变。而System Verilog中,激励对象不断地被创建并且用来驱动DUT(动态的),检查结果。最后这些对象所占用的内存可以被释放, 以供新的对象使用。
OOP和Verilog之间的相似性也有一些例外。Verilog的顶层模块是不会被显式地例化的。但是System Verilog类在使用前必须先例化。另外.Verilog的实例名只可以指向 一个实例.而System Verilog句柄可以指向很多对象,当然一次只能指向一个。
//在创建对象时,注意什么是声明,什么是创建
Transaction tr; //声明句柄
tr =new(); //创建对象(开辟了新的内存空间, 用来存放新的成员变量和方法)
创建对象时,可以通过自定义构建函数(constructor)来完成变量的初始化和其它初始操作。
class Transaction;logic [31:0] addr, crc, data [8];
function new();
addr = 3;
foreach (data [i])
data[i] = 5;
endfunction
endclass
构建函数new ()是系统预定义函数, 不需要指定返回值, 函数会隐式地返回例化后的对象指针。
构建函数也可以定义多个参数作为初始化时外部传入数值的手段。
class Transaction;
logic [31:0] addr ='h10;
logic [31:0] crc, data[8] ;//开辟空间;
function new(logic [31:0] a=3, d=5);addr = a;
foreach (data[i])data[i]= d;
endfunction
endclass
initial begin
Transaction tr;//声明句柄
tr = new(10); // data uses default of 5end
1.3.句柄传递,销毁
在创建了对象之后, 该对象的空间位置不会更改, 而指向该空间的句柄可以有多个。
Transaction t1, t2; //声明句柄tl, t2
t1 =new(); //例化对象, 将其句柄赋予tl
t2 = t1; //将t1的值赋予t2, 即t1和t2指向同一个对象
t1 =new(); //例化第二个对象, 并将其句柄赋予t1
• 软件编程的灵活在于可以动态地开辟使用空间, 在资源闲置或者不再需要时, 可以回收空间, 这样使得内存空间保持在—个合理的区间。
• C++语言中的类除了有构建函数, 还有析构函数。 析构函数的作用即在于手动释放空间, 但这对编程人员的细心和经验提出了要求; Java和Python等后续面向对象语言则不再需要手动定义析构函数并且释放空间, 这意味着空间的回收利用也是自动的。
• SV也采用了自动回收空间的处理方式, 使得用户不再为软件空间的开销而烦恼。那么,自动回收空间的基本原理是什么呢?当—个对象, 在整个程序中没有任何—个地方再需要它时, 便会被销毁,即回收其空间。 这里需要的意思即指的是有句柄指向该对象。
class word;
byte nb[];
function new(int n);
nb = new[n];
endfunction
endclass
initial begin : initial_1
word wd;
for(int i=l; i<=4; i++) wd = new(i);
end
initial begin : initial_2
#lps
$display("How many Bytes are allocated for word instances?");
end
假设wd= new(l)所需要开辟1B的空间那么在initial_2的display语句处,需要为对象例化开辟多少空间呢?(4B)
C++
class word;
byte nb[];
function new(int n);
nb = new[n];
endfunction
endclass
initial begin : initial_1
automatic word wd; //声明为动态
for(int i=l; i<=4; i++) wd = new(i);
end
initial begin : initial_2
#lps
$display("How many Bytes are allocated for word instances?");
end
假设wd= new(l)所需要开辟1B的空间那么在initial_2的display语句处,需要为对象例化开辟多少空间呢?(0B)
1.4.句柄使用
句柄可以用来创建多个对象,也可以前后指向不同对象
Transaction t1, t2; //声明句柄
t1 =new(); //创建对象并将其指针赋予t1
t2 = new(); //创建对象并将其指针赋予t2
t1 =t2; //将t2的值赋予t1,t1和t2指向同一对象, t1之前指向的对象被释放
t2 = null; //将t2赋值为空即不指向任何对象, 此时指针悬空 , 悬空的指针很危险可以通过句柄来使用对象中的成员变量或者成员方法
Transaction t;//声明句柄
t =new(); //例化对象
t.addr = 32'h42; //对象的成员变量赋值t. display() ; / /调用对象的成员方法
1.5.静态变量/方法
与硬件域例如module, interface不同的是,在class中声明的变量其默认类型为动态变量,即其生命周期在仿真开始后的某时间点开始到某时间点结束。具体来讲,其声明周期始于对象创建, 终于对象销毁。
那么如果使用关键字static来声明class内的变量时,则其为静态变量根据之前课程对变量声明周期的描述,静态变量的生命开始于编译阶段,贯穿于整个仿真阶段。如果在类中声明了静态变量,那么可以直接引用该变量class: :var, 或者通过例化对象引用object.var, 类中的静态变量声明以后,无论例化多少个对象(O.. N) 只可以共享—个同名的静态变量,因此类的静态变量在使用时需要注意共享资源的保护。
class Transaction;
static int count = 0; // Number of objects created
int id; // Unique instance ID
function new();
id = count++; // Set ID, bump count
endfunction
endclass
Transaction t1, t2;
initial begin
t1 = new();//lst instance, id=O, count=1
t2 = new();// 2nd instance, id=l, count=2
$display("Second id=%d, count=%d",t2.id, t2.count);//或者通过Transaction::count索引
• 类似于静态变量, 在class中定义的方法默认类型是动态方法, 而我们也可以通过关键词static修改其类型为静态方法。
• 静态方法内可以声明并使用动态变量, 但是不能使用类的动态成员变量。 原因是因为在调用静态方法时, 可能并没有创建具体的对象 , 也因此没有为动态成员变量开辟空间, 因此在静态方法中 使用类的动态成员变量是禁止的, 可能会造成内存泄洞, 但是静态方法可以使用类的静态变量, 因为静态方法同静态变量一样在编译阶段就已经为其分配好了内存空间。
class Transaction;
static Config cfg;
static int count = O;
int id;
//通过静态方法来操作静态变量
static function void display_statics();
$display("Transaction cfg.mode=%s, coun七=%Od", cfg. mode. name() , count) ;
endfunction
endclass
Config cfg;
initial begin
cfg = new (MODE_ ON) ;
Transaction::cfg = cfg; //静态变墨赋值
Transaction::display_statics(); //调用静态方法
end