UVM基础之---------uvm report 机制分析
uvm 中的信息报告机制相对来说比较简单,功能上来说主要分为两部分:
第一通过ID对component的信息报告冗余级别进行控制,针对每个冗余级别进行不同的行为控制。这部分工作主要由uvm_report_hander来实现:
主要涉及到的方法有get_report_verbosity_level(severity, id)/get_report_action(severity,id) == uvm_action'(UVM_NO_ACTION)
第二是对message进行格式化的输出,这部分工作主要由uvm_report_server来实现
主要涉及到的方法有message_compose
我们通过一个`uvm_error宏的执行进行分析,
`uvm_error(“driver”, “the config object is null”)
这是一个在TB中调用的uvm宏,主要接收两个参数 ID和要打印的MSG,这个宏在 uvm_message_defines.svh中进行定义,具体定义如下:
140 `define uvm_error(ID,MSG) \
141 begin \
142 if (uvm_report_enabled(UVM_NONE,UVM_ERROR,ID)) \
143 uvm_report_error (ID, MSG, UVM_NONE, `uvm_file, `uvm_line); \
144 end
首先调用的是uvm_report_enabled函数,主要目的是为了检查这个message是否允许输出。这个函数有两个版本,假如是在一个 component中调用uvm_error 宏的话,那么使用的是这个 component自己的函数,而这个函数是在uvm_report_object中定义的:
447 function int uvm_report_enabled(int verbosity,
448 uvm_severity severity=UVM_INFO, string id="");
449 if (get_report_verbosity_level(severity, id) < verbosity || //检查对应severity,id的verbosity(冗余级别)是否大于允许输出的verbosity
450 get_report_action(severity,id) == uvm_action'(UVM_NO_ACTION)) //检查对应severity,id的action是否是UVM_NO_ACTION
451 return 0;
452 else
453 return 1;
454 endfunction
假如是在一个 sequence 中或者是一个派生自 uvm_object 的类中使用这个宏的话,调用的是全局的函数
115 function bit uvm_report_enabled (int verbosity,
116 uvm_severity severity=UVM_INFO, string id="");
117 uvm_root top;
118 top = uvm_root::get();
119 return top.uvm_report_enabled(verbosity,severity,id);
120 endfunction
get_report_verbosity_level 的定义如下: m_rh 是一个 uvm_report_handler 类型的变量,它会在每个 component 实例化的
时候被实例化,也就是说,每个 component 对应一个 m_rh,此变量用于记录这个component 的一些报告信息,如是否单独对此 component 设置了报告冗余度级别
(verbosity_lever) 。 get_report_verbosity_level 这个函数最终调用的是uvm_report_handler的get_verbosity_level函数。
81 class uvm_report_object extends uvm_object;
82
83 uvm_report_handler m_rh;
…
90 function new(string name = "");
91 super.new(name);
92 m_rh = new();
93 endfunction
…
413 function int get_report_verbosity_level(uvm_severity severity=UVM_INFO, string id="");
414 return m_rh.get_verbosity_level(severity, id);
415 endfunction
…
539 endclas
下面简单的分析一下在uvm_report_handler中用到的get_verbosity_level函数,我们可以从中学到一些对数组的控制方式:
55 class uvm_report_handler;
…
236 function int get_verbosity_level(uvm_severity severity=UVM_INFO, string id="" );
237
238 uvm_id_verbosities_array array;
239 if(severity_id_verbosities.exists(severity)) begin //首先判断severity_id_verbosities(联合数组,索引就是severity)中是否存在与serverity对应的记录
240 array = severity_id_verbosities[severity]; //这个联合数组返回的是一个uvm_id_verbosities_array,而这个array是uvm_pool的一个实例,
241 if(array.exists(id)) begin //这个uvm_id_verbosities_array的索引就是对应的message_id,而内容则是这个message_id的冗余整数
242 return array.get(id); //现在的问题是,这个severity_id_verbosities在哪里被写入??
243 end
244 end
245
246 if(id_verbosities.exists(id)) begin //id_verbosities是一个uvm_id_verbosities_array类型的变量
247 return id_verbosities.get(id); //索引就是对应的message_id,而内容则是这个message_id的冗余整数
248 end //同样的问题,这个id_verbosities在何时被写入??
249
250 return m_max_verbosity_level;
251
252 endfunction
…
622 endclass : uvm_report_handle
对uvm_action和file handle的处理跟get_verbosity_level相类似,这里就不再进行分析,现在我们分析在上面代码分析中提到的两个问题:
1. 这个severity_id_verbosities在哪里被写入?我们直接分析uvm_report_handler的code,因为前面提到了,这部分工作是有handler来实现和管理的:
function void set_severity_id_verbosity(uvm_severity severity,
string id,
int verbosity);
if(!severity_id_verbosities.exists(severity))
severity_id_verbosities[severity] = new;
severity_id_verbosities[severity].add(id,verbosity);
endfunction
2. id_verbosities在何时被写入?
function void set_id_verbosity(input string id, input int verbosity);
id_verbosities.add(id, verbosity);
endfunction
分析上面的代码可以知道这里面set_severity_id_verbosity 要比set_id_verbosity的优先级要高。
我们前面说过,对message进行格式化的输出,这部分工作主要由uvm_report_server来实现,所以uvm_report_error经过传递,最终会调用uvm_report_server中的report函数,这里面需要提一下的是uvm_report_server是单实例的,在这里重申一下uvm_report机制几个类之间的关系,report—object和report_handler的关系是一 一对应的,当然也可以多个report_object对应一个report_handler(set_report_handler).handler到server的关系是多对一。
243 virtual function void report(
244 uvm_severity severity,
245 string name,
246 string id,
247 string message,
248 int verbosity_level,
249 string filename,
250 int line,
251 uvm_report_object client
252 );
253 string m;
254 uvm_action a;
255 UVM_FILE f;
256 bit report_ok;
257 uvm_report_handler rh;
258
259 rh = client.get_report_handler(); // 再次拿到uvm_component的uvm_report_hander的实例指针,目的是为了后面再次进行uvm_report_enabled检查,以及拿到在uvm_report_handler中的file_handle
260
261 // filter based on verbosity level
262
273 f = rh.get_file_handle(severity, id);
274
275 // The hooks can do additional filtering. If the hook function
276 // return 1 then continue processing the report. If the hook
277 // returns 0 then skip processing the report.
278
279 if(a & UVM_CALL_HOOK)
280 report_ok = rh.run_hooks(client, severity, id,
281 message, verbosity_level, filename, line); //调用run_hooks函数再次确认是否进行打印信息,run_hooks在uvm_report_handler中进行定义,调用uvm_report_object中定义的report_info_hook/report_warning_hook/report_error_hook/report_fatal_hook函数,可以在继承uvm_component的时候对这些hook进行重载,今儿实现对message的控制。
282 else
283 report_ok = 1;
285 if(report_ok)
286 report_ok = uvm_report_catcher::process_all_report_catchers( //这是一个uvm_callback类,它的主要用处就是在真正的打印信息之前可以再次控制要打印的信息,每个uvm_catcher都要实现一个catch的函数!
287 this, client, severity, name, id, message,
288 verbosity_level, a, filename, line);
289
290 if(report_ok) begin
291 m = compose_message(severity, name, id, message, filename, line);
292 process_report(severity, name, id, message, a, f, filename,
这个是在uvm_catcher中的process_report_catcher函数:
local function int process_report_catcher();
action_e act;
act = this.catch();
if(act == UNKNOWN_ACTION)
this.uvm_report_error("RPTCTHR", {"uvm_report_this.catch() in catcher instance ", this.get_name(), " must return THROW or CAUGHT"}, UVM_NONE, `uvm_file, `uvm_line);
if(m_debug_flags & DO_NOT_MODIFY) begin
m_modified_severity = m_orig_severity;
m_modified_id = m_orig_id;
m_modified_verbosity = m_orig_verbosity;
m_modified_action = m_orig_action;
m_modified_message = m_orig_message;
end
if(act == CAUGHT && !(m_debug_flags & DO_NOT_CATCH)) begin
return 0;
end
return 1;
endfunction