常见$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到了吗?