top的program中与case的class中。
1 导入UVM的方法
在top的program内部,import uvm_pkg::*,在Makefile的vcs中,添加-ntb_opts uvm-1.1选项
2 top的timeformat
系统函数,参数为:时间精度,时间小数,后缀,显示位宽
3 top的vcs选项
控制使用哪一个case(top下的case) +UVM_TESTCASE= 控制uvm_info的显示等级 +UVM_VERBOSITY=
4 生成vpd波形文件
+vcs+vpdpluson的vcs选项,生成vpd文件。使用dve打开。
+UVM_TR_RECORD将transaction的信息存入vpd文件中。
+UVM_LOG_RECORD将uvm_info的信息存入vpd文件中,其中用req.sprint方式输出更直观。
5 uvm类树继承关系
6 打印拓扑关系、注册在工厂的类,以及配置的重载信息
依次如下
uvm_top.print_topology() factory.print() uvm_top.print()
UVM内部部分
1 uvm_info展开
内部有几个函数,主要利用uvm_report_enabled、 uvm_report_info实现。
2 \$size的sv函数
$size(param1, param2) param1是被测量的变量,param2是测量的变量维度。1为最外层维度。
3 is_active的类型
在uvm_agent中,其类型为uvm_active_passive_enum
4 scb中的类顺序比较函数
uvm_in_order_class_comparator的使用。
最终report的过程中,m_matches、m_mismatches分别输出匹配和不匹配的个数。
为了在16路的交换机上进行包的对比,对应的tr也需要进行按照顺序的方式对比。
如果只有一路,直接将定义的uvm_anapysis_export给到uvm_in_order_class_comparator的before_export和after_export上,这样就接收后,是直接传入比较类中,无需imp了。
用了imp以后,imp将da进行识别,传入不同的比较类。
`ifndef _SCOREBOARD_SV_ `define _SCOREBOARD_SV_ `uvm_analysis_imp_decl(_before) `uvm_analysis_imp_decl(_after) class scoreboard extends uvm_scoreboard; // data or class properties `uvm_component_utils(scoreboard) uvm_analysis_imp_before#(packet, scoreboard) export_before; uvm_analysis_imp_after#(packet, scoreboard) export_after; uvm_in_order_class_comparator#(packet) comparator[16]; // initialization function new(string name="scoreboard", uvm_component parent); super.new(name, parent); endfunction : new virtual function void build_phase(uvm_phase phase); export_before = new("export_before", this); export_after = new("export_after", this); for (int i = 0; i < 16; i++) begin comparator[i] = uvm_in_order_class_comparator#(packet)::type_id::create( $sformatf("comparator[%0d]", i), this); end endfunction: build_phase virtual function void write_before(packet tr); `uvm_info($sformatf("SB_INPUT_GET%0d", tr.da), "scoreboard get a packet", UVM_LOW) comparator[tr.da].before_export.write(tr); endfunction: write_before virtual function void write_after(packet tr); `uvm_info($sformatf("SB_OUTPUT_GET%0d", tr.da), "scoreboard get a packet", UVM_LOW) comparator[tr.da].after_export.write(tr); endfunction: write_after virtual function void report_phase(uvm_phase phase); for (int i = 0; i < 16; i++) begin `uvm_info($sformatf("SB_REPORT[%0d]",i), $sformatf("Match %0d, MisMatch %0d", comparator[i].m_matches, comparator[i].m_mismatches[i]), UVM_LOW) end endfunction: report_phase endclass : scoreboard `endif
5 phase的顺序
https://blog.csdn.net/qq_37884273/article/details/114239843
一般在report这个phase的顶层输出点信息。比如上面的scb。
6 设置drain_time
在发送端,给seq的body上的starting_phase设置。
1 获取objection:starting_phase.get_objection();
2 设置drain_time:objection.set_drain_time(this, 1us);
7 uvm_sequence_library说明
在上一段的第5个,说明了它是继承了seq的,因此设置它到seqr的default_sequence上,类型还是uvm_object_wrapper(uvm_config_db函数用到)。
1. 注册:
使用uvm_object_utils和`uvm_sequence_library_utils,还需要init_sequence_library;在new函数中。
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/packet_seq_lib_pkg.sv
2. 配置:
在case中配置时,先定义和实例化uvm_sequence_library_cfg,然后传给seq_lib的config中
seq_cfg = new("seq_cfg", UVM_SEQ_LIB_RAND, 2, 2); uvm_config_db#(uvm_sequence_library_cfg)::set( this, "env.i_agt[*].seqr.main_phase", "default_sequence.config", seq_cfg);
3. 添加:
使用静态方法添加:packet_seq_lib是一个class,使用add_typewide_sequence添加seq到seq_lib中。
packet_seq_lib::add_typewide_sequence(packet_sequence::get_type());
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/test_collection.sv#L80
4. 修改:
seq_lib下的seq需要说明它的starting_phase不是seqr的,因此为null,它的starting_phase是seq_lib的,因此需要以下的get_parent_sequence。
同样参考继承关系,seq_lib和seq都是seq_base的,定义出的parent就是一个基类。
uvm_sequence_base parent = get_parent_sequence(); if (parent != null) begin starting_phase = parent.starting_phase; end
RAL设定
主工程代码:https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/
0:不使用ral方式,
和传统一样
定义agent、seqr、seq、drv,然后seq初始化,通过seqr发给drv,drv驱动获取数据(drv打印出来返回结果就行了)。
交互主要seq和drv。
1 ralf文件定义和生成:
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/host.ralf
其中的空格不可省去。the blank between id and bracket can't be ignored.
register HOST_ID { field REV_ID { bits 8; access ro; reset 'h03; } field CHIP_ID { bits 8; access ro; reset 'h5A; } } register PORT_LOCK { field LOCK { bits 16; access w1c; reset 'hffff; } } register REG_ARRAY { field USER_REG { bits 16; access rw; reset 'h0; } } memory RAM { size 4k; bits 16; access rw; } block host_regmodel { bytes 2; register HOST_ID (host_id) @'h0000; register PORT_LOCK (lock) @'h0100; register REG_ARRAY[256] (host_reg[%d]) @'h1000; # array must specify HDL index memory RAM (ram) @'h4000; }
2. 通过该ralf文件生成regmodel
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/Makefile#L31-L32
最终通过RALFILE文件生成的regmodel文件为:ral_RALPOSTFIX.sv。其中会有ral_block_RALPOSTFIX类。是uvm_reg_block的regmodel。
ralgen -uvm -t $(RALPOSTFIX) $(RALFILE)
3. 有了regmodel后,设置组件连接
定义好agent中指定的seqr、adapter、seq。
说明:
1.组件连接关系和数据流
其中的seqr就是普通的seqr,但是不再通过default_sequence带入seq,发送给drv。
seqr、drv、adapter、seq。
由上层的agent(case)的task_phase中创建seq,并赋予seq的regmodel值,然后发送(通过start)。
virtual task configure_phase(uvm_phase phase); super.configure_phase(phase); `uvm_info("RAL_HOST_AGENT", $sformatf("%m"), UVM_MEDIUM) seq = ral_host_sequence::type_id::create("host_seq", this); seq.regmodel = this.regmodel; phase.raise_objection(this); seq.start(null); phase.drop_objection(this);
其中的regmodel需要实例化和配置(注意seqr的设置):
regmodel中需要接入seqr和adapter,因此这里同时实例了seqr、adapter。
virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // switch to ral model uvm_config_db#(uvm_object_wrapper)::set(this, "seqr.configure_phase", "default_sequence", null); // start. regmodel = ral_block_host_regmodel::type_id::create("ral_block_host_regmodel", this); adapter = reg_adapter::type_id::create("reg_adapter", this); uvm_config_db#(string)::get(this, "", "hdl_path", hdl_path); regmodel.build(); regmodel.lock_model(); regmodel.set_hdl_path_root(hdl_path); endfunction: build_phase
在connect_phase接入:
virtual function void connect_phase(uvm_phase phase); super.connect_phase(phase); regmodel.default_map.set_sequencer(seqr, adapter); endfunction: connect_phase
2. adapter的新增的
继承uvm_reg_adapter,不是component。其中的reg2bus和bus2reg实现好。
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/reg_adapter.sv
3. seq是特定的,也算新增了
继承uvm_reg_sequence,后者的继承关系需要自己定,默认继承于sequence即可。
seq的内部body中,需要由外层传入的regmodel,实现regmodel的读写功能。此外需要数据、状态,用于控制。
https://github.com/baimengwei/5pS-5Yiw566A5Y6G5LiK55qE56ys5LiA5Liq/blob/main/ral_host_sequence.sv
virtual task body(); uvm_status_e status; uvm_reg_data_t data; if (starting_phase!=null) begin starting_phase.raise_objection(this); end regmodel.PORT_LOCK.read(.status(status), .value(data), .path(UVM_FRONTDOOR), .parent(this)); `uvm_info("RAL_READ", $sformatf("read_port_lock %0h", data), UVM_MEDIUM) regmodel.PORT_LOCK.write(.status(status), .value('1), .path(UVM_FRONTDOOR), .parent(this)); `uvm_info("RAL_WRITE", $sformatf("read_port_lock %0h", data), UVM_MEDIUM) regmodel.PORT_LOCK.read(.status(status), .value(data), .path(UVM_FRONTDOOR), .parent(this)); `uvm_info("RAL_READ", $sformatf("read_port_lock %0h", data), UVM_MEDIUM) if (starting_phase!=null) begin starting_phase.drop_objection(this); end endtask: body