第一手:sequence内的方法调用(sequence或item的启动或发送)
下面的parent是指父节点。(以下内容参考uvm源码src/seq/uvm_sequence_base)
pre_body/start和post_body/start是只用于sequence方面的任务
在test_case这层启动sequence的2种方法:default_sequence和start。
采用start启动sequence会调用以下方法:(可以在test_case和sequence这两层使用start)
//| sub_seq.randomize(...); // optional
//| sub_seq.start(seqr, parent_seq, priority, call_pre_post)
//| sub_seq.pre_start() (task)
//| sub_seq.pre_body() (task) if call_pre_post==1
//| parent_seq.pre_do(0) (task) if parent_sequence!=null
//| parent_seq.mid_do(this) (func) if parent_sequence!=null
//| sub_seq.body (task) YOUR STIMULUS CODE
//| parent_seq.post_do(this) (func) if parent_sequence!=null
//| sub_seq.post_body() (task) if call_pre_post==1
//| sub_seq.post_start() (task)
采用`uvm_do系列宏启动sequence会调用以下方法:(但是不会执行pre/post_body任务;只能用于sequence内部,不能用于test_case那一层;)
//| `uvm_do_with_prior(seq_seq, { constraints }, priority)
//| sub_seq.pre_start() (task)
//| parent_seq.pre_do(0) (task)
//| parent_req.mid_do(sub_seq) (func)
//| sub_seq.body() (task)
//| parent_seq.post_do(sub_seq) (func)
//| sub_seq.post_start() (task)
采用`uvm_do系列宏发送item会调用以下方法:
//| `uvm_do_with_prior(item, constraints, priority)
以上等价于下面这种形式://| parent_seq.start_item(item, priority);
//| item.randomize(...) [with {constraints}];
//| parent_seq.finish_item(item);
//| sequencer.wait_for_grant(prior) (task) \ start_item \
//| parent_seq.pre_do(1) (task) / \
//| `uvm_do* macros
//| parent_seq.mid_do(item) (func) \ /
//| sequencer.send_request(item) (func) \finish_item /
//| sequencer.wait_for_item_done() (task) /
//| parent_seq.post_do(item) (func) /上面send_request(item)和wait_for_item_done()是sequence与seqr、driver之间的握手机制。
// Attempting to execute a sequence via <start_item>/<finish_item>
// will produce a run-time error.也就是说不能用 <start_item>/<finish_item>来启动一个sequence
default_sequence:启动sequence也是通过调用start任务来启动sequence的;白皮书P165
第二手:clone
a_class A1 ;
a_class A2 ;
assert( A1.randomize() with{ xx == 1; zz == 0; } );// 对所有变量随机
void'($cast(A2,A1.clone()));
assert( A2.randomize(a1,a2,a3) with{ } );// 只对a1/a2/a3随机,其他变量保持A1的随机结果
//在UVM中要注意,涉及到约束的成员都应该被注册到工厂,否则clone时不会复制上一个的随机结果,然后如果它又不在a1/a2/a3里面的话,它的结果就会是0,这样很可能会与constraint相冲突,导致随机失败;
第三式:活用PHASE
1、应该在reset_phase阶段,去等待rst_n的上升沿(完成复位)(rst_n下降沿:开始复位;rst_n上升沿:复位完成;)(这样才能允许进入下一个configure_phase)
2、对寄存器和表项的配置的seq一般指定在configure_phase执行;
3、对发送激励的seq一般指定在main_phase阶段执行;
4、某一个类的句柄在class中声明后,虽然没有去get,但是它在build phase阶段已经接收到了指针,那么它可以在run phase阶段被其他组件调用,而不会报错空指针错误,因为run phase在build phase 之后执行的;(全局接口 在xxx_tr里面声明了,在refm_mode(或env)的build phase中赋值了,而xxx_tr又set到cfg_seq,cfg_seq就可以在build phase后的任意一个phase阶段正常去访问xxx_tr.v_glb_intf.xx_signal)
1、设置后门路径:两部分构成:top_tb.dut.u_reg_xx + reg_model里面的add_hdl_path_slice;后门实际是通过force强制去修改值;
应该是有两种方式:一种是在env这层设置(如上图) ;另一种是在uvm_reg_block这层通过add_hdl_path(“TOP_TB.DUT”);(DUT实际上是你在top_tb里面例化RTL的名字,不一定是这里的这个名字)来设置,参考红皮书P419
调试:(加到run选项# run options)
(RUN_OPTS += +UVM_CONFIG_DB_TRACE +UVM_PHASE_TRACE #)
+UVM_DUMP_CMDLINE_ARGS:导出所有命令行参数,确认当前的命令行参数符合预期;
+UVM_CONFIG_DB_TRACE:打开访问config_db的追踪功能,可以看到config_db的set和get操作是在什么时间由什么组件发起的;(在log里面搜索:CFGDB/GET)
+UVM_PHASE_TRACE:可以看到不同phase的执行顺序;(在log里面搜索:PH/TRC/STRT,PH/TRC/DONE)
+UVM_OBJECTION_TRACE:打开objection相关活动的追踪功能,可以看到objection的运行状态;(在log里面搜索:OBJTN_TRC)
+RUN_OPTS += +UVM_VERBOSITY=UVM_HIGH:设置打印信息的冗余程度,有效控制不必要的层次信息;
UVM内置的打印函数:
uvm_root::print_topology():打印整个验证环境的组件拓扑结构;
uvm_component::print_config():打印当前组件可见视角下的所有配置资源信息;
后门访问:本质上就是通过层次化路径点到具体的reg或mem去赋值;
前门访问:本质上是模拟实际物理场景的总线接口访问;