【SV】中$cast 的用法

常见$cast, 但对其功能(向下类型转换)和具体应用场景却一知半解, 现将一点心得记录在此。

  • 类型向下转换 :将父类句柄(father_handle) 赋给 扩展类(child_handle)
    child_handle = father_handle

  • 类型向上转换: 将扩展类句柄(child_handle) 赋给 基类(father_handle)
    father_handle = child_handle

下面插入SV绿皮书 8.3.1 中的代码, 又加入了print()来说明virtual 的用法。

class Transaction;
   rand bit [31:0] src;
   virtual function void display(input string prefix = "");
	   $display("%s Transaction : src=%0d", prefix,src);
   endfunction

   function void print();
	   $display("the function in base class of Transaction");
   endfunction
endclass

class BadTr extends Transaction;
   bit bad_csm = 1;
   virtual function void display(input string prefix = "");
	   $display("%s BadTr : bad_csm=%b", prefix,bad_csm);
	   super.display(prefix);
   endfunction

   function void print();
	   $display("the function in extended class of BadTr");
   endfunction

endclass

在这里插入图片描述

在这里插入图片描述

  • 类型向上转换 可以直接赋值(tr = bad),赋值后基类句柄(tr)指向了扩展类(BadTr),但是如果想通过次句柄(tr,现在指向了BadTr)直接引用仅存在扩展类中的变量(bad_csm),$display行会报错,见下面代码。
	Transaction tr;
	BadTr       bad;
	
	function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
       super.new(name, parent);
	   bad = new();
	   tr = bad;
	   $display("tr.bad_csm = %d",tr.bad_csm);
    endfunction
*E,NOTCLM : bad_csm is not a class item.

如果用$cast 做类型向上转换,同样会报错

	Transaction tr;
	BadTr       bad;
	
	function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
       super.new(name, parent);
	   bad = new();
	   $cast( tr , bad);
	   $display("tr.bad_csm = %d",tr.bad_csm);
    endfunction
*E,NOTCLM : bad_csm is not a class item.

通过tr.display(), 同时存在基类和扩展类中的virtual function。执行的是扩展类中的display().
通过tr.print(), 同时存在基类和扩展类中的非virtual function。执行的是基类中的print().

	Transaction tr;
	BadTr       bad;
	
	function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
       super.new(name, parent);
	   bad = new();
	   tr = bad;
	   tr.display();
	   tr.print();
   endfunction
 BadTr : bad_csm=1
 Transaction : src=0
the function in base class of Transaction

总结

类型向上转换,可以直接赋值,但即使基类句柄已经指向了扩展类,仍然不能通过基类句柄去引用扩展类中的变量(tr.BadTr),因为SV 在编译时,只检查句柄类型。

  • 类型向下转换
	Transaction tr;
	BadTr       bad, bad2;
 function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
      super.new(name, parent);
      bad = new();
      tr = bad;
	  bad2 = tr;
 endfunction
xmvlog: *E,TYCMPAT : assignment operator type check failed (expecting datatype compatible with 'class ecp_x_tb_env_pkg::BadTr' but found 'class ecp_x_tb_env_pkg::Transaction' instead).

直接赋值的方式,SV编译只检查句柄类型,会报错。类型向下转换需要使用cast,cast是检查对象类型(tr指向的对象是BadTr),而不是检查句柄类型。
这里只有当 tr 指向的对象是BadTr本身或者其扩展类时,$cast 才会成功,否则会失败。

 function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
      super.new(name, parent);
      bad = new();
      tr = bad;
	  $cast(bad2,tr);
      $display("bad2.bad_csm = %d",bad2.bad_csm);
 endfunction
//in irun.log 成功。
bad2.bad_csm = 1        

如果tr指向的是基类的对象,$cast会失败,

function new (string name = "er_ecp_agent_update" , uvm_component parent = null);
      super.new(name, parent);
      bad = new();
	  tr = new();
	  if($cast(bad2,tr))  begin
        $display("bad2.bad_csm = %d",bad2.bad_csm);
   	  end else begin
        $display("the cast is failed");
      end		
 endfunction
//in irun.log
the cast is failed

为什么要进行两部操作

  • 问题:为什么要进行两部操作,先把基类句柄指向扩展类,再做类型向下转换,为什么不在第一步直接使用扩展类句柄,有哪些应用场景?

应用场景一在这里插入图片描述
以IQC_X项目为例,在TC 中会发两种类型的IQC packet, CPRI or EBCOM。
用基类句柄指向扩展类,再做类型向下转换,可以简化操作,这里sequece 和driver(in e language 相当于UVM中的sequencer)之间只需要定义一种类型的trasaction。
如果如上图中直接使用扩展类句柄,需要两个 driver,实现iqc_driver的功能。

应用场景二
在这里插入图片描述我们先看一下: er_ecp_agent中的代码:

  er_ecp_monitor_base                 monitor;
  er_hsp_low_agent                    hsp_low_agent;
  er_eci_l1_agent                     eci_l1_agent;
  
  virtual function void build_phase(uvm_phase phase);
    case (config_obj.agent_kind)
      ECP_UNCONFIGURED: `uvm_error(get_type_name(), "You need to select what version of the agent you want to use: ECP_LP1B_RTL, ECP_CPRI")

      ECP_LP1B_RTL, ECP_CPRI: begin
        hsp_low_agent = er_hsp_low_agent::type_id::create("hsp_low_agent",this);
        monitor       = er_ecp_monitor_hsp_low::type_id::create("monitor_hsp_low", this);
      end
      ECP_ECI: begin
        eci_l1_agent = er_eci_l1_agent::type_id::create("eci_l1_agent", this);
        monitor      = er_ecp_monitor_eci::type_id::create("monitor_eci", this);
      end
      default: `uvm_error(get_type_name(), "Unspecified configuration")
    endcase
  endfunction

  virtual function void connect_handles();
    monitor.p_ecp_config   = config_obj;
    monitor.hsp_low_agent = hsp_low_agent;
    monitor.eci_l1_agent  = eci_l1_agent;
  endfunction

1,根据agent 类型实例化monitor, 用基类句柄指向扩展类。
2,实例化hsp_low_agent,eci_l1_agent。
3,给monitor 中的agent指针赋值。
如果这里monitor直接使用扩展类的句柄,需要声明多个句柄,同时connect_handles()也要改写为如下代码。

er_ecp_monitor_eci  monitor_eci;
er_ecp_monitor_hsp_low monitor_hsp_low;
  virtual function void connect_handles();
    monitor.p_ecp_config   = config_obj;
    monitor_hsp_low.hsp_low_agent = hsp_low_agent;
    monitor_eci.eci_l1_agent  = hsp_low_agent;
  endfunction

下面需要添加er_ecp_agent_update, 去增加ast 接口。

  er_ecp_monitor_base                 monitor;
  er_hsp_low_agent                    hsp_low_agent;
  er_eci_l1_agent                     eci_l1_agent;
  er_ast_agent                        ast_agent;                                      // new add
  
  virtual function void build_phase(uvm_phase phase);
    case (config_obj.agent_kind)
      ECP_UNCONFIGURED: `uvm_error(get_type_name(), "You need to select what version of the agent you want to use: ECP_LP1B_RTL, ECP_CPRI")

      ECP_LP1B_RTL, ECP_CPRI: begin
        hsp_low_agent = er_hsp_low_agent::type_id::create("hsp_low_agent",this);
        monitor       = er_ecp_monitor_hsp_low::type_id::create("monitor_hsp_low", this);
      end
      ECP_ECI: begin
        eci_l1_agent = er_eci_l1_agent::type_id::create("eci_l1_agent", this);
        monitor      = er_ecp_monitor_eci::type_id::create("monitor_eci", this);
      end
      ECP_AST begin
        ast_agent = er_ast_agent::type_id::create("ast_agent", this);                  // new add
        monitor      = er_ecp_monitor_ast::type_id::create("monitor_ast", this);       // new add
      end      
      default: `uvm_error(get_type_name(), "Unspecified configuration")
    endcase
  endfunction

  virtual function void connect_handles();
    monitor.p_ecp_config   = config_obj;
    monitor.hsp_low_agent = hsp_low_agent;
    monitor.eci_l1_agent  = hsp_low_agent;
    monitor.ast_agent     = ast_agent;            // notice 这里会报错,ast_agent is not a class item
  endfunction

monitor.ast_agent = ast_agent; 这行会报错,ast_agent is not a class item。compile 是根据句柄类型, er_ecp_monitor_base中没有ast_agent。所以这里也需要使用类型向下转换。monitor_update 只需要声明,它指向了er_ecp_monitor_ast的实例。

 er_ecp_monitor_base_update                 monitor_update;       // new add
 virtual function void connect_handles();
    monitor.p_ecp_config   = config_obj;
    monitor.hsp_low_agent = hsp_low_agent;
    monitor.eci_l1_agent  = hsp_low_agent;
    $cast(monitor_update, monitor);            // new add
    monitor_update.ast_agent  = ast_agent;     // new add    
  endfunction

$cast的魅力你get到了吗?

  • 22
    点赞
  • 144
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值