不枉初心,砥砺前行

皮皮祥的博客

欢迎留言,评论

导航

SV学习(6)——类的继承、句柄的使

SV学习(6)——类的继承、句柄的使用

1. 类的成员访问类型

  • public:如果没有指明访问类型,默认是public,子类和外部均可以访问成员
  • protected:只有该类或者子类可以访问成员,外部不可访问
  • local:只有该类可以访问成员,子类和外部均无法访问

2. 类的继承 extends

class cat;
	protected color_t color;	// 类和子类可以访问
	local bit is_good;			// 类可以访问
	
	function set_good(bit s);
		this.is_good = s;
	endfunction
endclass

class black_cat extends cat;
	function new();
		this.color = BLACK;
	endfunction
endclass

class white_cat extends cat;
	function new();
		this.color = WHITE;
	endfunction
endclass

black_cat bk;
white_cat wt;

initial	begin
	bk = new();
	wt = new();
	bk.set_good(1);
	wt.set_good(1);
end

【Q】
下面对于黑猫白猫类的说法那些是正确 的?
A:可以通过外部修改黑猫的颜色使其变为白猫
B:黑猫可以自己在初始化时使用this.is_good = 1,夸自己是好猫
C:外部可以通过访问黑猫的is_good属性得知它是不是好猫
D:至于白猫是不是一只“大脸猫”,无从得知

【An】
A:color是protected类型,类或者子类可以访问,外部不可访问
B:is_good是local类型,类可以访问,子类和外部均无法访问
C:外部不可访问local

3. 子类索引父类的同名函数 super

class basic_test;
	int def = 100;
	int fin;
	
	task test(stm_ini ini);
		$display("basic_test::test");
	endtask
	
	function new(int val);
		...
	endfunction

endclass

class test_wr extends basic_test;
	function new();
		super.new(def);
		$display("test_wr::new");
	endfunction
	
	task test(stm_ini ini);
		super.test(ini);
		$display("test_wr::test");
	endtask

endclass

class test_rd extends basic_test;
	function new();
		super.new(def);
		$display("test_rd::new");
	endfunction
	
	task test(stm_ini ini);
		super.test(ini);
		$display("test_rd::test");
	endtask

endclass
  • 子类可以继承父类的成员变量和方法,也可以定义新的成员变量和方法
  • 子类中的方法和父类本没有任何关联,是通过super才继承的
  • 如果子类中没有调用super.new(),那就不会执行父类new函数初始化部分【因为上述父类的new函数有参数

new():1. 系统看到new会开辟空间;2. 进入new完成变量的初始化;3. 返回句柄;


用一个代码例子体会一下,

class basic_test;
	int def = 100;
	int fin;
	
	task test(int ini);
		$display("basic_test::test");
	endtask
	
	function new();
		$display("basic_test::new");
	endfunction

endclass

class test_wr extends basic_test;
  int def = 200;

	function new();
		super.new();
		$display("test_wr::new");
	endfunction
	
	task test(int ini);
		$display("test_wr::test");
	endtask

endclass

class test_rd extends basic_test;
  int def = 200;

	function new();
		$display("test_rd::new");
//    super.new(); // err
	endfunction
	
	task test(int ini);
		super.test(ini);
		$display("test_rd::test");
    	super.test(ini);
	endtask

endclass


module test;
  initial begin
    test_wr wr1;
    test_rd rd1;

    wr1 = new();
    wr1.test(1);

    rd1 = new();
    rd1.test(1);
  end

endmodule

在这里插入图片描述

蓝色标记说明:子类在定义new函数时,首先应该调用父类的new函数即super.new()。如果父类的new函数没有参数,子类也可以省略调用,而系统会在编译时自动添加super.new()。


总结:

  • new函数必须要完成继承,若父类new没有参数,就默认调用;若父类new有参数,就必须显式调用super.new(参数列表);
  • 除了new函数,其他函数需要显式调用才能执行;
  • 父类的函数可以在子类的不同名函数中调用,

4. 成员覆盖

在父类和子类里,可以定义相同名称的成员变量和方法(形式参数和返回类型也应该相同),而在引用时,也将按照句柄类型来确定作用域。

class basic_test;
	int def = 100;
	int fin;
	
	task test(stm_ini ini);
		$display("basic_test::test");
	endtask
	
	function new(int val);
		...
	endfunction

endclass

class test_wr extends basic_test;
	int def = 200;
	function new();
		super.new(def);
		$display("test_wr::new");
		$display("test_wr::super.def = %0d", super.def);	// 父类 def = 100
		$display("test_wr::this.def = %0d", this.def);		// 子类 def = 200
	endfunction

endclass

【Q】
下面代码的wr.def和t.def分别是多少?

module tb;
	...
	basic_test t;
	test_wr wr;
	
	initial	begin
		wr = new();
		t = wr;
		$display("wr.def = %0d", wr.def);
		$display("t.def = %0d", t.def);
	end
endmodule

在这里插入图片描述

【An】
子类句柄赋值给父类之后,被赋值的父类句柄只能指向整个子类中属于父类的部分,这样对访问来说是安全,相当于缩小了访问范围,所以t.def = 100,wr.def = 200;

  • 子类句柄可以赋值给父类,反之父类的句柄不能赋值给子类;

  • 若wr = t,编译报错,相当于扩大了访问范围;

  • 如果想将父类句柄(该父类句柄要指向子类的对象)赋值给子类,用$cast();【没遇到过这样的代码,以后补充】【遇到了】

  • 若子类中没有同名的def变量,wr.def优先在子类成员中找def,找不到,扩大范围,找到父类中的def,wr.def = 100;


补充一个有趣的代码

class packet;
	integer	i = 1;
	integer m = 2;
	function new(int val);
		i = val + 1;
	endfunction
	function shift();
		i = i << 1;
	endfunction
	function test(int x);
		$display("haha");
	endfunction
	
endclass

class linkedpacket extends packet;
	integer i = 3;
	integer k = 5;
	function new(int val);
		super.new(val);
		if(val >= 2)
			i = val;
	endfunction
	function shift();
		super.shift();
		super.test(6);
//		i = super.i << 2;
		i = i << 2;
	endfunction
endclass

module tb;
	initial	begin
		packet p = new(3);
		linkedpacket lp = new(1);
		packet tmp;
		tmp = lp;
		$display("p.i = %0d", p.i);
		$display("lp.i = %0d", lp.i);	// child class i = 3, parent class i = 2
//		$display("lp.super.i = %0d", lp.super.i);	// err: unexpected "SystemVerilog keyword 'super'",super用在类内部
		$display("tmp.m = %0d", tmp.m);	// m = 2, index to parent scope
		$display("tmp.i = %0d", tmp.i);	// i = 2, index to parent scope
//		$display("tmp.k = %0d", tmp.k);	// err: not found k,父类只能索引父类域中的对象
		
		//-- p.shift();
		//-- $display("after shift, p.i = %0d", p.i);
		//-- lp.shift();
		//-- $display("after shift, lp.i = %0d", lp.i);
	end
endmodule 

在这里插入图片描述


5. 句柄的传递

  • 句柄可以作为形式参数通过方法来完成对象指针的传递,从外部传入方法内部;

  • 传递的是对象指针,句柄,不是对象;

    task generator;
    	Transaction t;
    	t = new();
    	transmit(t);
    endtask
    
    task transmit (Transaction t);
    	...
    endtask
    
  • 句柄可以在方法内部首先完成修改,而后再由外部完成使用;

    function void create(Transaction tr);
    	tr = new();
    	tr.addr = 100;
    	... 
    endfunction 
    
    Transaction t;
    
    initial begin
    	create(t);
    	t.addr = 10;
    	$display(t.addr);
    end
    

【Q】
最后显示的t.addr是多少?

【An】
句柄创建后默认悬空null,create函数并没有返回值,函数执行完t还是悬空,t.addr会报错
修改:默认参数传递方向是input,改为inout或ref

6. 句柄的动态修改

  • 程序在执行时,可以在任何时刻为句柄创建新对象,并将新的指针赋给句柄;

    task generator_trans();
    	Transaction t;
    	Transaction fifo[$];	// 队列
    	t = new();
    	for(int i = 0; i < 3; i++) begin
    		t.addr = i << 2;
    		fifo.push_back(t);
    	end
    	t = fifo.pop_front();
    endtask
    

【Q】
t.addr的数值是多少?
A:0
B:4
C:8
D:12

【An】
选C。
声明句柄t后,总共只创建了一个对象,所以每次在队尾push_back入队的时候,都是这一个对象地址(句柄),循环中的t.addr索引就是这个对象的addr,相当于addr赋值了三次,分别是0、4、8,最后从队头pop_front取出的就是最后一次写入的值
在这里插入图片描述

posted on 2022-10-04 23:21  皮皮祥  阅读(393)  评论(0编辑  收藏  举报