文章目录
前言
本文结合具体的例子和代码,介绍在UVM中加入寄存器模型的方法步骤。
0. uvm环境中加入寄存器模型的步骤
- 保证前门访问正常。
- 编写ralf文件。
- 生成UVM格式的寄存器模型。
- 编写UVM寄存器模型的适配器。
- 将寄存器模型加入到验证环境中。
- 编写并执行能够访问寄存器模型的sequence。
- 加入镜像和预测功能(可选)。
- 运行内建的自检测试(可选)。
- 寄存器模型覆盖率查看。
1. 保证前门访问正常
在没有寄存器模型的情况下,先保证通过寄存器总线,能够正常访问寄存器。
编写host_sequence_base
class host_sequence_base extends uvm_sequence #(host_data);
`uvm_object_utils(host_sequence_base)
virtual host_io vif;
uvm_sequencer_base p_sqr;
function new(string name = "host_sequence_base");
super.new(name);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
`ifdef UVM_POST_VERSION_1_1
set_automatic_phase_objection(1);
`endif
endfunction: new
virtual task pre_start();
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
p_sqr = get_sequencer();
`ifdef UVM_VERSION_1_1
if ((get_parent_sequence() == null) && (starting_phase != null)) begin
starting_phase.raise_objection(this);
end
`endif
if (uvm_config_db#(virtual host_io)::get(p_sqr.get_parent(), "", "vif", vif)) begin
`uvm_info("HOST_SEQ_CFG", "Has access to host interface", UVM_HIGH);
end
endtask: pre_start
`ifdef UVM_VERSION_1_1
virtual task post_start();
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
if ((get_parent_sequence() == null) && (starting_phase != null)) begin
starting_phase.drop_objection(this);
end
endtask: post_start
`endif
endclass: host_sequence_base
编写host_bfm_sequence
//
// The host_bfm_sequence class is designed to test the DUT registers and memory
// using the host_driver without using RAL.
//
class host_bfm_sequence extends host_sequence_base;
`uvm_object_utils(host_bfm_sequence)
function new(string name = "host_bfm_sequence");
super.new(name);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual task body();
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
`uvm_do_with(req, {addr == 'h0; kind == UVM_READ;});
if (req.data != 'h5a03) begin
`uvm_fatal("BFM_ERR", $sformatf("HOST_ID is %4h instead of 'h5a03", req.data));
end else begin
`uvm_info("BFM_TEST", $sformatf("HOST_ID is %4h the expected value is 'h5a03", req.data), UVM_MEDIUM);
end
`uvm_do_with(req, {addr == 'h100; kind == UVM_READ;});
if (req.data != '1) begin
`uvm_fatal("BFM_ERR", $sformatf("LOCK is %4h instead of 'hffff", req.data));
end
`uvm_do_with(req, {addr == 'h100; data == '1; kind == UVM_WRITE;});
`uvm_do_with(req, {addr == 'h100; kind == UVM_READ;});
if (req.data != '0) begin
`uvm_fatal("BFM_ERR", $sformatf("LOCK is %4h instead of 'h0000", req.data));
end else begin
`uvm_info("BFM_TEST", $sformatf("LOCK is %4h the expected value is 'h0000", req.data), UVM_MEDIUM);
end
for (int i=0; i<256; i++) begin
`uvm_do_with(req, {addr == 'h1000+i; data == (i ^ (i >> 1)); kind == UVM_WRITE;});
end
for (int i=0; i<256; i++) begin
`uvm_do_with(req, {addr == 'h1000+i; kind == UVM_READ;});
if (req.data != (i ^ (i >> 1))) begin
`uvm_fatal("BFM_ERR", $sformatf("R_ARRAY is %4h instead of %4h", req.data, i ^ (i >> 1)));
end
end
`uvm_info("BFM_TEST", "R_ARRAY contains the expected values", UVM_MEDIUM);
for (int i=0; i<4096; i++) begin
`uvm_do_with(req, {addr == 'h4000+i; data == 16'b1 << i%16; kind == UVM_WRITE;});
end
for (int i=0; i<4096; i++) begin
`uvm_do_with(req, {addr == 'h4000+i; kind == UVM_READ;});
if (req.data != 16'b1 << i%16) begin
`uvm_fatal("BFM_ERR", $sformatf("RAM[%4h] is %4h instead of %4h", i, req.data, 16'b1 << i%16));
end
end
`uvm_info("BFM_TEST", "RAM contains the expected values", UVM_MEDIUM);
endtask: body
endclass: host_bfm_sequence
编写test_base 测试用例
class test_base extends uvm_test;
`uvm_component_utils(test_base)
// For convenience, access to the command line processor is done for you
uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();
router_env env;
virtual router_io router_vif;
virtual reset_io reset_vif;
// Lab 6 - The declaration of the host virtual interface is done for you.
virtual host_io host_vif;
top_reset_sequencer top_reset_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
env = router_env::type_id::create("env", this);
uvm_resource_db#(virtual router_io)::read_by_type("router_vif", router_vif, this);
uvm_resource_db#(virtual reset_io)::read_by_type("reset_vif", reset_vif, this);
uvm_config_db#(virtual router_io)::set(this, "env.i_agt[*]", "vif", router_vif);
uvm_config_db#(virtual router_io)::set(this, "env.o_agt[*]", "vif", router_vif);
uvm_config_db#(virtual reset_io)::set(this, "env.r_agt", "vif", reset_vif);
top_reset_sqr = top_reset_sequencer::type_id::create("top_reset_sqr", this);
// The test is responsible for picking it up and configure the agent with the interface.
uvm_resource_db#(virtual host_io)::read_by_type("host_vif", host_vif, this);
uvm_config_db#(virtual host_io)::set(this, "env.h_agt", "vif", host_vif);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.sqr.reset_phase", "default_sequence", null);
uvm_config_db #(uvm_object_wrapper)::set(this, "top_reset_sqr.reset_phase", "default_sequence", top_reset_sequence::get_type());
uvm_config_db #(string)::set(this, "env", "hdl_path", "router_test_top.dut");
set_type_override_by_type(scoreboard::get_type(),ms_scoreboard::get_type());
endfunction: build_phase
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
foreach (env.i_agt[i]) begin
top_reset_sqr.pkt_sqr.push_back(env.i_agt[i].sqr);
end
top_reset_sqr.r_sqr = env.r_agt.sqr;
top_reset_sqr.h_sqr = env.h_agt.sqr;
endfunction: connect_phase
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
env.regmodel.set_coverage(UVM_CVR_ALL);
endfunction: end_of_elaboration_phase
virtual task shutdown_phase(uvm_phase phase);
super.shutdown_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
phase.raise_objection(this);
env.sb.wait_for_done();
phase.drop_objection(this);
endtask: shutdown_phase
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
`uvm_info("SB_REPORT", {"\n", env.sb.convert2string()}, UVM_MEDIUM);
endfunction: report_phase
virtual function void final_phase(uvm_phase phase);
super.final_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "TOPOLOGY")) begin
uvm_root::get().print_topology();
end
if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "FACTORY")) begin
uvm_factory::get().print();
end
endfunction: final_phase
endclass: test_base
编写test_host_bfm测试用例
class test_host_bfm extends test_base;
`uvm_component_utils(test_host_bfm)
function new(string name, uvm_component parent);
super.new(name, parent);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.configure_phase", "default_sequence", null);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.main_phase", "default_sequence", null);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.h_agt.sqr.main_phase", "default_sequence", host_bfm_sequence::get_type());
endfunction: build_phase
endclass: test_host_bfm
2. 编写ralf文件
按照一定的格式,在ralf这个文件中输入寄存器的基本信息,后面会利用ralgen脚本加载这个文件,生成对应的UVM格式的寄存器模型。
DUT中的寄存器和RAM如下图描述所示。
编写的host.ralf文件如下所示:
# This file contains the DUT register and memory definitions
register HOST_ID {
field REV_ID {
bits 8;
access ro;
reset 'h03;
}
field CHIP_ID {
bits 8;
access ro;
reset 'h5A;
}
}
register LOCK {
field LOCK {
bits 16;
access w1c;
reset 'hffff;
}
}
register R_ARRAY {
field H_REG {
bits 16;
access rw;
reset 'h0000;
}
}
memory RAM {
size 4k;
bits 16;
access rw;
}
#
# The block level declaration is done for you. This include the hdl_path name for
# the signals in the DUT. Later on, the hdl_path will be in RAL backdoor access.
#
block host_regmodel {
bytes 2;
register HOST_ID (host_id) @'h0000;
register LOCK (lock) @'h0100;
register R_ARRAY[256] (host_reg[%d]) @'h1000; # array must specify HDL index
memory RAM (ram) @'h4000;
}
3. 生成UVM格式的寄存器模型
利用synopsys自带的ralgen脚本,加载第二步中写好的ralf文件,生成寄存器模型。
编写的makefile文件如下所示。
ral:
ralgen -full64 -uvm -t host_regmodel host.ralf
ral_cov:
ralgen -full64 -uvm -c b -t host_regmodel host.ralf
通过执行make ral或者make ral_cov,可生成寄存器仿真模型。其中make ral生成的寄存器模型没有添加覆盖率,make ral_cov生成的寄存器模型添加了覆盖率。
hefei@ubuntu:~/Desktop/ces_uvm-1.2_2016.06/labs/lab6$ make ral
ralgen -full64 -uvm -t host_regmodel host.ralf
Synopsys UVM Register Abstraction Layer Code Generator
Copyright (c) 1991-2018 by Synopsys Inc.
All Rights Reserved.
生成的寄存器模型文件ral_host_regmodel.sv如下所示:
`ifndef RAL_HOST_REGMODEL
`define RAL_HOST_REGMODEL
import uvm_pkg::*;
class ral_reg_HOST_ID extends uvm_reg;
uvm_reg_field REV_ID;
uvm_reg_field CHIP_ID;
function new(string name = "HOST_ID");
super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
endfunction: new
virtual function void build();
this.REV_ID = uvm_reg_field::type_id::create("REV_ID",,get_full_name());
this.REV_ID.configure(this, 8, 0, "RO", 0, 8'h03, 1, 0, 1);
this.CHIP_ID = uvm_reg_field::type_id::create("CHIP_ID",,get_full_name());
this.CHIP_ID.configure(this, 8, 8, "RO", 0, 8'h5A, 1, 0, 1);
endfunction: build
`uvm_object_utils(ral_reg_HOST_ID)
endclass : ral_reg_HOST_ID
class ral_reg_LOCK extends uvm_reg;
rand uvm_reg_field LOCK;
function new(string name = "LOCK");
super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
endfunction: new
virtual function void build();
this.LOCK = uvm_reg_field::type_id::create("LOCK",,get_full_name());
this.LOCK.configure(this, 16, 0, "W1C", 0, 16'hffff, 1, 0, 1);
endfunction: build
`uvm_object_utils(ral_reg_LOCK)
endclass : ral_reg_LOCK
class ral_reg_R_ARRAY extends uvm_reg;
rand uvm_reg_field H_REG;
function new(string name = "R_ARRAY");
super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
endfunction: new
virtual function void build();
this.H_REG = uvm_reg_field::type_id::create("H_REG",,get_full_name());
this.H_REG.configure(this, 16, 0, "RW", 0, 16'h0000, 1, 0, 1);
endfunction: build
`uvm_object_utils(ral_reg_R_ARRAY)
endclass : ral_reg_R_ARRAY
class ral_mem_RAM extends uvm_mem;
function new(string name = "RAM");
super.new(name, `UVM_REG_ADDR_WIDTH'h1000, 16, "RW", build_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
endfunction: build
`uvm_object_utils(ral_mem_RAM)
endclass : ral_mem_RAM
class ral_block_host_regmodel extends uvm_reg_block;
rand ral_reg_HOST_ID HOST_ID;
rand ral_reg_LOCK LOCK;
rand ral_reg_R_ARRAY R_ARRAY[256];
rand ral_mem_RAM RAM;
uvm_reg_field HOST_ID_REV_ID;
uvm_reg_field REV_ID;
uvm_reg_field HOST_ID_CHIP_ID;
uvm_reg_field CHIP_ID;
rand uvm_reg_field LOCK_LOCK;
rand uvm_reg_field R_ARRAY_H_REG[256];
rand uvm_reg_field H_REG[256];
function new(string name = "host_regmodel");
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction: new
virtual function void build();
this.default_map = create_map("", 0, 2, UVM_LITTLE_ENDIAN, 0);
this.HOST_ID = ral_reg_HOST_ID::type_id::create("HOST_ID",,get_full_name());
this.HOST_ID.configure(this, null, "");
this.HOST_ID.build();
this.HOST_ID.add_hdl_path('{
'{"host_id", -1, -1}
});
this.default_map.add_reg(this.HOST_ID, `UVM_REG_ADDR_WIDTH'h0, "RO", 0);
this.HOST_ID_REV_ID = this.HOST_ID.REV_ID;
this.REV_ID = this.HOST_ID.REV_ID;
this.HOST_ID_CHIP_ID = this.HOST_ID.CHIP_ID;
this.CHIP_ID = this.HOST_ID.CHIP_ID;
this.LOCK = ral_reg_LOCK::type_id::create("LOCK",,get_full_name());
this.LOCK.configure(this, null, "");
this.LOCK.build();
this.LOCK.add_hdl_path('{
'{"lock", -1, -1}
});
this.default_map.add_reg(this.LOCK, `UVM_REG_ADDR_WIDTH'h100, "RW", 0);
this.LOCK_LOCK = this.LOCK.LOCK;
foreach (this.R_ARRAY[i]) begin
int J = i;
this.R_ARRAY[J] = ral_reg_R_ARRAY::type_id::create($psprintf("R_ARRAY[%0d]",J),,get_full_name());
this.R_ARRAY[J].configure(this, null, "");
this.R_ARRAY[J].build();
this.R_ARRAY[J].add_hdl_path('{
'{$psprintf("host_reg[%0d]", J), -1, -1}
});
this.default_map.add_reg(this.R_ARRAY[J], `UVM_REG_ADDR_WIDTH'h1000+J*`UVM_REG_ADDR_WIDTH'h1, "RW", 0);
this.R_ARRAY_H_REG[J] = this.R_ARRAY[J].H_REG;
this.H_REG[J] = this.R_ARRAY[J].H_REG;
end
this.RAM = ral_mem_RAM::type_id::create("RAM",,get_full_name());
this.RAM.configure(this, "ram");
this.RAM.build();
this.default_map.add_mem(this.RAM, `UVM_REG_ADDR_WIDTH'h4000, "RW", 0);
endfunction : build
`uvm_object_utils(ral_block_host_regmodel)
endclass : ral_block_host_regmodel
`endif
4. 编写UVM寄存器模型的适配器
UVM寄存器模型有一套自己的总线,这里需要根据自己的寄存器访问总线,编写一个转换器,实现这两套总线的适配。
生成的适配器如下所示:
//
// For the sake of minimizing the number of files in each lab directory, you will be
// creating the translator class in the same file as the host_data class.
//
class reg_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg_adapter)
function new(string name="reg_adapter");
super.new(name);
`uvm_info("Trace", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
host_data tr;
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
tr = host_data::type_id::create("tr");
tr.addr = rw.addr;
tr.data = rw.data;
tr.kind = rw.kind;
return tr;
endfunction: reg2bus
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
host_data tr;
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
if (!$cast(tr, bus_item)) begin
`uvm_fatal("NOT_HOST_REG_TYPE", "bus_item is not correct type");
end
rw.addr = tr.addr;
rw.data = tr.data;
rw.kind = tr.kind;
rw.status = tr.status;
endfunction: bus2reg
endclass: reg_adapter
5. 将寄存器模型加入到验证环境中
适配器和寄存器模型都要加入到验证环境中,再在connect_phase中,将对应的接口连接上。
具体的代码如下所示
class router_env extends uvm_env;
`uvm_component_utils(router_env)
reset_agent r_agt;
input_agent i_agt[16];
output_agent o_agt[16];
scoreboard sb;
host_agent h_agt;
ral_block_host_regmodel regmodel;
reg_adapter adapter;
typedef uvm_reg_predictor #(host_data) hreg_predictor;
hreg_predictor hreg_predict;
function new(string name, uvm_component parent);
super.new(name, parent);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
r_agt = reset_agent::type_id::create("r_agt", this);
uvm_config_db #(uvm_object_wrapper)::set(this, "r_agt.sqr.reset_phase", "default_sequence", reset_sequence::get_type());
foreach (i_agt[i]) begin
i_agt[i] = input_agent::type_id::create($sformatf("i_agt[%0d]", i), this);
uvm_config_db #(int)::set(this, i_agt[i].get_name(), "port_id", i);
uvm_config_db #(uvm_object_wrapper)::set(this, {i_agt[i].get_name(), ".", "sqr.reset_phase"}, "default_sequence", router_input_port_reset_sequence::get_type());
uvm_config_db #(uvm_object_wrapper)::set(this, {i_agt[i].get_name(), ".", "sqr.main_phase"}, "default_sequence", packet_sequence::get_type());
end
sb = scoreboard::type_id::create("sb", this);
foreach (o_agt[i]) begin
o_agt[i] = output_agent::type_id::create($sformatf("o_agt[%0d]", i), this);
uvm_config_db #(int)::set(this, o_agt[i].get_name(), "port_id", i);
end
h_agt = host_agent::type_id::create("h_agt", this);
adapter = reg_adapter::type_id::create("adapter", this);
uvm_config_db #(ral_block_host_regmodel)::get(this, "", "regmodel", regmodel);
if (regmodel == null) begin
string hdl_path;
`uvm_info("HOST_CFG", "Self constructing regmodel", UVM_MEDIUM);
if (!uvm_config_db #(string)::get(this, "", "hdl_path", hdl_path)) begin
`uvm_warning("HOST_CFG", "HDL path for DPI backdoor not set!");
end
regmodel = ral_block_host_regmodel::type_id::create("regmodel", this);
regmodel.build();
regmodel.lock_model();
regmodel.reset();
regmodel.set_hdl_path_root(hdl_path);
end
uvm_config_db #(ral_block_host_regmodel)::set(this, h_agt.get_name(), "regmodel", regmodel);
uvm_config_db #(uvm_object_wrapper)::set(this, {h_agt.get_name(), ".", "sqr.configure_phase"}, "default_sequence", ral_port_unlock_sequence::get_type());
hreg_predict = hreg_predictor::type_id::create("h_reg_predict", this);
endfunction: build_phase
virtual function void connect_phase(uvm_phase phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
foreach (i_agt[i]) begin
i_agt[i].analysis_port.connect(sb.before_export);
end
foreach (o_agt[i]) begin
o_agt[i].analysis_port.connect(sb.after_export);
end
regmodel.default_map.set_sequencer(h_agt.sqr, adapter);
regmodel.default_map.set_auto_predict(0);
hreg_predict.map = regmodel.get_default_map();
hreg_predict.adapter = adapter;
h_agt.analysis_port.connect(hreg_predict.bus_in);
endfunction: connect_phase
endclass: router_env
6. 编写并执行能够访问寄存器模型的sequence
编写host_ral_sequence_base:
//
// The following is the RAL configuration sequence base. It contains the
// regmodel that the RAL sequences will need.
//
class host_ral_sequence_base extends uvm_reg_sequence #(host_sequence_base);
`uvm_object_utils(host_ral_sequence_base)
ral_block_host_regmodel regmodel;
function new(string name = "host_ral_sequence_base");
super.new(name);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual task pre_start();
super.pre_start();
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
if (!uvm_config_db#(ral_block_host_regmodel)::get(p_sqr.get_parent(), "", "regmodel", regmodel)) begin
`uvm_info("RAL_CFG", "regmodel not set through configuration. Make sure it is set by other mechanisms", UVM_MEDIUM);
end
if (regmodel == null) begin
`uvm_fatal("RAL_CFG", "regmodel not set");
end
endtask: pre_start
endclass: host_ral_sequence_base
编写host_ral_test_sequence:
//
// This is the RAL test sequence.
//
class host_ral_test_sequence extends host_ral_sequence_base;
`uvm_object_utils(host_ral_test_sequence)
function new(string name = "host_ral_test_sequence");
super.new(name);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual task body();
uvm_status_e status;
uvm_reg_data_t data;
regmodel.HOST_ID.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));
if (data != 'h5a03) begin
`uvm_fatal("RAL_ERR", $sformatf("HOST_ID is %4h instead of 'h5a03", data));
end else begin
`uvm_info("RAL_TEST", $sformatf("HOST_ID is %4h the expected value is 'h5a03", data), UVM_MEDIUM);
end
regmodel.LOCK.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));
if (data != 'hffff) begin
`uvm_fatal("RAL_ERR", $sformatf("LOCK is %4h instead of 'hffff", data));
end
regmodel.LOCK.write(.status(status), .value('1), .path(UVM_FRONTDOOR), .parent(this));
regmodel.LOCK.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));
if (data != '0) begin
`uvm_fatal("RAL_ERR", $sformatf("LOCK is %4h instead of 'h0000", data));
end else begin
`uvm_info("RAL_TEST", $sformatf("LOCK is %4h the expected value is 'h0000", data), UVM_MEDIUM);
end
for (int i=0; i<256; i++) begin
regmodel.R_ARRAY[i].write(.status(status), .value(i ^ (i >> 1)), .path(UVM_FRONTDOOR), .parent(this));
end
for (int i=0; i<256; i++) begin
regmodel.R_ARRAY[i].read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));
if (data != (i ^ (i >> 1))) begin
`uvm_fatal("RAL_ERR", $sformatf("R_ARRAY is %4h instead of %4h", data, i ^ (i >> 1)));
end
end
`uvm_info("RAL_TEST", "R_ARRAY contains the expected values", UVM_MEDIUM);
for (int i=0; i<4096; i++) begin
regmodel.RAM.write(.status(status), .offset(i), .value(16'b1 << i%16), .path(UVM_FRONTDOOR), .parent(this));
end
for (int i=0; i<4096; i++) begin
regmodel.RAM.read(.status(status), .offset(i), .value(data), .path(UVM_BACKDOOR), .parent(this));
if (data != 16'b1 << i%16) begin
`uvm_fatal("RAL_ERR", $sformatf("RAM[%4h] is %4h instead of %4h", i, data, 16'b1 << i%16));
end
end
`uvm_info("RAL_TEST", "RAM contains the expected values", UVM_MEDIUM);
endtask: body
endclass: host_ral_test_sequence
编写test_host_ral测试用例:
class test_host_ral extends test_base;
`uvm_component_utils(test_host_ral)
function new(string name, uvm_component parent);
super.new(name, parent);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.configure_phase", "default_sequence", null);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.main_phase", "default_sequence", null);
uvm_config_db #(uvm_object_wrapper)::set(this, "env.h_agt.sqr.main_phase", "default_sequence", host_ral_test_sequence::get_type());
endfunction: build_phase
endclass: test_host_ral
7. 加入镜像和预测功能(可选)
镜像和预测功能在上述第5步的代码中已经有体现。
8. 运行内建的自检测试(可选)
测试用例的代码如下所示:
// For the ral self test:
// turn off all sequencer execution.
// Explicitely execute the virtual reset sequence.
// Then explicitely execute the uvm_reg_bit_bash_seq.
class test_ral_selftest extends test_base;
`uvm_component_utils(test_ral_selftest)
string seq_name="uvm_reg_bit_bash_seq";
uvm_reg_sequence selftest_seq;
top_reset_sequence top_reset_seq;
function new(string name, uvm_component parent);
super.new(name, parent);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
endfunction: new
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
uvm_config_db #(uvm_object_wrapper)::set(this,"*","default_sequence",null);
endfunction: build_phase
virtual task run_phase(uvm_phase phase);
`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
phase.raise_objection(this, "Starting reset tests");
top_reset_seq = top_reset_sequence::type_id::create("top_reset_seq", this);
top_reset_seq.start(top_reset_sqr);
clp.get_arg_value("+seq=", seq_name);
$cast(selftest_seq, uvm_factory::get().create_object_by_name(seq_name));
selftest_seq.model = env.regmodel;
selftest_seq.start(env.h_agt.sqr);
phase.drop_objection(this, "Done with register tests");
endtask: run_phase
endclass: test_ral_selftest
其他内建sequence。
不同内建sequence,通过test_base中的clp,获取命令行中+seq=后边的参数传递进去。
9. 查看寄存器模型的功能覆盖率
查看寄存器的功能覆盖率需要满足以下条件:
- 保证寄存器模型中带有功能覆盖率统计代码。这个比较简单,在利用ralgen生成寄存器模型的时候,把覆盖率选项的开关打开。
ralgen -full64 -uvm -c b -c a -c f -t host_regmodel host.ralf
#-c表示覆盖率coverage,a、b、f依次表示“Address Map” “Register Bits” “Field Values”功能覆盖率模型。
- 打开regmodel的功能覆盖率。在test_base的end_of_elaboration_phase方法中,打开regmodel的覆盖率开关。
env.regmodel.set_coverage(UVM_CVR_ALL);
- 打开RAL覆盖率。在testbench的initial模块中,打开RAL的覆盖率开关。
initial
uvm_reg::include_coverage("*", UVM_CVR_ALL);
- 执行仿真,查看覆盖率。
Makefile
cover:
urg -dir ./output/*/simv.vdb; firefox urgReport/groups.html &
总结
本文结合具体的测试用例以及代码,具体介绍了UVM中加入寄存器模型的方法步骤,以及如何查看寄存器的功能覆盖率。
参考文献
具体的实现可以参考synopsys的讲义,链接如下:
Synopsys_uvm1.2_user_guide1
Synopsys_uvm1.2_user_guide2
Synopsys_uvm1.2_lab
Synopsys_uvm1.2_lab_guide