system verilog面向对象的编程(2)

面向对象的编程基础(下)

类的方法

类中的程序也称为方法,也就是在类的作用域内定义的内部task或者function。下例为类Transaction定义了display()方法。System Verilog会根据句柄的类型调用正确的display()方法。

class Transaction;
	bit[31:0]addr,crc,data[8];
	function void display();
		$display("@%0t:TR addr=8h,crc=sh",Stime,addr,crc);
		$write("\tdata[0-7]=");
		foreach(data[i])$write(data[i]);
		$display();
	endfunction 
endclass

initial begin 
	t=new();//创建一个Transaction对象
	t.display();//调用Transaction的方法
end

类中的方法默认使用自动存储,所以并不需要担心忘记使用automatic修饰符。

在类之外定义方法

在System Verilog中可以将方法的原型定义(方法名和参数)放在类的内部,而方法的程序体(过程代码)放在类的后面定义。
下面是一个如何创建一个块外声明的例子。复制该方法的第一行,包描方法名和参数,然后在开始处添加关键词extern.然后将整个方法移至类定义的后面,并在方法名前加上类名和两个冒号(::作用域操作符)。上例中的类可以如下定义。

//
class Transaction; 
	bit[31:0]addr,crc,data[8]; 
	extern function void display(); 
endclass 

function void Transaction::display(); 
	$display("@% 0t:Transaction addr=%h,crc=8h", Stime,addr,crc); 
	$write("\tdata[o-7]="); 
	foreach (data[i])Swrite(data[i]);
	$display();
endfunction

注:常见两个错误:
      1、方法的原型定义跟内容不相匹配是一个常见的编码错误。System Verilog要求除了多一个类名和作用域操作符之外,原型定义跟块外的方法定义一致。
      2、另一个常见错误是在类的外部声明方法时忘记写类名。这样做的结果是它的作用范围高了一级,当某个任务试图访问类一级的变量和方法的时候,编译器就会报错。

作用域规则

作用域是一个代码块,例如:一个模块,一个程序、任务、函数、类或着begin-end块for 和foreach 循环自动创建一个块,所以下标变重可以作为该循环作用域的局部变量来声明和创建。
SystemVerilog中新增的特性是可以在一个没有名子的begin-end块中声明变量,例如for循环内定义索引变量
类应当在program或者module外的package中定义。这应当是所有测试平台都该遵守的,可以将临时变量在测试平台最内部的某处定义。
如果在一个块内使用了一个未声明的查量,碰巧在程序块中有一个、同名的变量,那么类就会使用程序块中的变量,不会给出任何的警告。

下例例子将类移到一个package中,那么类就看不到程序一级的变量了,由此就不会无意调用到它了。

//将类移入package来查找程序错误
package Mistake;
	class Bad;
		logic[31:0]data[];
		
		//未定义i,不会被编译
		function void display;
			for(i=0;i< data.size();i++)
			$display("data[%0d]=%x",i,data[i]);
		endfunction 
	endclass 
endpackage

this是什么

当使用一个变量名的时候,SystemVerilog将先在当前作用域内寻找,接着在上一级作用域内寻找,直到找到该变量为止。但是如果在类的很深的底层作用域,却想明确地引用类一级的对象呢。下例中“this”将oname赋给类一级变量oname

//使用this指针指向类一级变量
class Scoping;
	string oname;

	function new(string oname);
		this.oname=oname;//类变量oname=局部变量oname 
	endfunction 
endclass

在一个类中使用另一个类

通过使用指向对象的句柄,一个类内部可以包含另一个类的实例。这如同在Verilog中,在一个模块内部包含另一个模块的实例,以建立设计的层次结构。这样包含的目的通常是重用和控制复杂度。
system verilog面向对象的编程(2)

理解动态对象

将对象传递给方法

将对象传递给一个方法的时候可能需要读取对象中的值、修改对象的值,不管是哪一种情形,调用方法的时候,传递的是对象的句柄而非对象
当调用一个带有标量变量(不是数组,也不是对象)的方法并且使用ref关键词的时候,SystemVerilog传递该标量的地址,所以方法也可以修改标量变量的值。如果不使用ref关键词,System Verilog将该标量的值复制到参数变量中,对该参数变量的任何改变不会影响原变量的值。

//将包传送到一个32位总线上
task transmit(Transactiont);
	CBbus.rx data<=t.data;
	t.stats.startT=$time;
	...
endtask 

Transaction t;
initial begin 
	t=new();		//为对象分配空间
	t.addr=42;		//初始化数值
	transmit(t);	//将对象传递给任务
end

在上例中,初始化块先产生一个Transaction对象,并且调用transmit任务,transmit任务的参数是指向该对象的句柄。通过使用句柄,transmit可以读写对象中的值。但是,如果transmit试图改变句柄,初始化块将不会得到结果、因为参数上没有使用ref修饰符。(就如同ref传递其他函数参数一样)

这篇笔记参考《system verilog验证》绿皮书整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

上一篇:FLASK简单入门


下一篇:@Transaction 事务