• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

SOC/IP验证工程师

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

systemverilog中$cast类型向下转换和虚方法

当使用继承来扩展类的功能的时候,需要一些OOP技巧来控制对象和功能。
如果将子类句柄赋值给父类句柄时,编译器则认为赋值是合法的,但分别利用子类句柄和父类句柄调用相同对象的成员时,将可能有不同的表现。
例1:不加virtual的function

class Transaction;
	rand bit [31:0] src;
	function void display(input string = "");	//没有virtual
		$display(%s Transaction: src = %d", prefix, src);
	endfunction
endclass
class BadTr extends Transaction;
	bit [31:0] src;
	bit bad_crc;
	function void display(input string prefix = "");	//没有virtual
		$display("%s BadTr: bad_src = %b", prefix, bad_crc);
		super.display(prefix);
	endfunction
endclass
Transaction tr;
BadTr 	bad, bad2, bad3;
//第一种情况
bad = new();				//构建BadTr扩展对象
tr = bad;					//将子类句柄赋值给父类句柄
$display(tr.src);			//显示基类对象的成员变量
tr.display();				//Transaction::display()
tr.src;					//基类的src
bad.src						//子类的src
bad.bad_src;				//子类的bad_src
bad.super.src;				//基类的src

通过tr找不到子类的src,虽然tr指向子类的对象,但是tr能查找到的范围只是父类范围的对象;
当一个类被扩展时,所有的基类变量和方法被继承,所以整数变量src存在于扩展类对象中。

//第二种情况
tr = new();		//创建一个父类
bad2 = tr;		//编译错误
$display(bad2.bad_crc);	//bad_crc成员不在父类对象中

如果你将一个基类对象直接拷贝到一个扩展类的句柄中,会发生操作失败,因为有些属性仅仅存在于扩展类中,基类并不具备。systemverilog编译器对句柄类型做静态检查。

//此时可以通过\$cast实现,$cast(bad, tr),如下所示:
//第三种情况
bad = new();
tr = bad;
if(!$cast(bad2, tr))
  $display("cannot assign tr to bad2");
$display(bad2.bad_crc);	//bad2指向的对象包括bad_crc
bad2.display();	//子类的BadTr::display()

类型向下转换或者类型变换是指将一个指向基类的指针转换成一个指向派生类的指针。
1、将一个父类句柄赋值给一个子类句柄并不总是非法的;但是systemverilog编译器对这种直接赋值的做法是禁止的,也就是无论父类句柄是否真正指向一个子类对象,赋值给子类句柄时,编译(静态)都将出现错误。因此需要$cast(target, source)来实现句柄类型的动态转换;
2、$cast不仅仅检查句柄本身,还会检查句柄所指向的的对象类型。注意:指向父类对象的父类句柄不能转换为子类句柄,指向子类对象的父类句柄可以转换为子类句柄。
3、一旦源对象跟目的句柄是同一类型,或者是目的句柄的扩展类,$cast()函数执行成功会返回1,否则返回0。

虚方法:
例2: 加virtual的function函数
(将类中的子程序定义成virtual, 这样就可以在扩展类中重定义。这一点适用于所有的任务和函数,除了new函数。因为new函数在对象创建时调用,所以无法扩展。systemverilog始终基于句柄类型来调用new函数。)

class Transaction;
	rand bit [31:0] src, dst, data[8];
	bit [31:0] crc;
	virtual function void calc_crc();
		crc = src ^ dst ^ data.xor;
	endfunction
endclass : Transaction
class BadTr extends Transaction;
	rand bit bad_crc;
	virtual function void calc_crc;
		super.calc_crc();
		if (bad_crc) crc = ~ crc;
	endfunction
endclass :BadTr
Transaction 	tr;
BadTr 			bad;
initial begin
	tr = new();
	tr.calc_crc;	//调用Transaction::calc_crc
	bad = new();
	bad.calc_crc;	//调用BadTr::calc_crc
	tr = bad;		//基类句柄指向扩展对象
	tr.calc_crc;	//调用BadTr::calc_crc
end

当需要决定调用哪个虚方法时,systemverilog根据对象的类型,而不是句柄的类型来决定调用什么方法。如例2最后一句tr.calc_crc.
如果没有virtual修饰符,systemverilog会根据句柄的类型而不是对象的类型来决定调用什么方法。如例1 tr.display().

类型转换也算一种多态形式
从父类向子类的转换,称为向下类型转换。
子类向父类的转换称为向上类型转换。
向下类型转换是安全的,而反之则是不安全的。

class father;
  string m_name;
  function new (string name);
    m_name = name;
  endfunction
  function void print ();
    $display("Hello %s", m_name);
  endfunction
endclass : father

class child0 extends father;
  string car = "car";
  function new (string name);
    super.new(name);
  endfunction
endclass : child0

class child1 extends father;
  string plane = "plane";
  function new (string name);
    super.new(name);
  endfunction
endclass : child1
module top;
  father f;
  child0 c0;
  child1 c1;
  child1 c2;
  initial begin
    f = new("father");
    f.print();
    c0 = new("child0");
    f = c0;
    f.print();
    c1 = new("child1");
    f = c1;
    f.print();
    c1.plane="big_plane";
    $cast(c2,f);  
    f.print();
    $write(", has %s",c2.plane)
  end  
endmodule : top

只有当前父类指针指向的对象和待转换对象的类型一致时,cast才会成功。

posted on 2021-10-27 21:58  SOC验证工程师  阅读(355)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3