验证架构
- 最基本的框架如上图所示,上一节中搭建了红色的lvc部分的架构
- 本节搭建rkv的顶层环境:
- 在uvm文件夹下创建env文件夹存放顶层环境的组件、创建cov文件夹存放收集覆盖率的文件、创建cfg文件夹存放顶层的配置文件,新建rgm文件夹存放顶层vip的寄存器模型文件以及adapter文件,新建test文件夹存放test文件,新建seq_lib存放各种测试情况的sequence(在其下新建elem_seqs存放基本的sequence)
- env文件夹中:
- rkv_ahbram_pkg:在上一节中以及简单提及并在其中导入了lvc的包,本节新添加rkv环境下的组件
- rkv_ahbram_env:顶层的环境组件
- rkv_ahbram_virtual_sequencer:virtual sequencer,由于本项目只有lvc_ahb_master_sequencer,所以其中只有一个sequencer,在rkv_ahbram_env中连接
- rkv_ahbram_subscriber:负责监听monitor的数据
- rkv_ahbram_scoreboard:继承于rkv_ahbram_subscriber
- cov文件夹中
- rkv_ahbram_cov:收集覆盖率
- cfg文件夹中
- rkv_ahbram_config:顶层的配置文件
- rgm文件夹中
- rkv_ahbram_rgm:寄存器模型
- rkv_ahbram_adapter:
- test文件夹中
- rkv_ahbram_base_test:base test文件
- seq_lib文件夹中
- rkv_ahbram_base_virtual_sequence
- rkv_ahbram_seq_lib
configuration:在其中定义检查的count变量,并在其中例化lvc的configuration
// config set in env
`ifndef RKV_AHBRAM_CONFIG_SV
`define RKV_AHBRAM_CONFIG_SV
class rkv_ahbram_config extends uvm_object;
int seq_check_count;
int seq_check_error;
int scb_check_count;
int scb_check_error;
bit enable_cov = 1;
bit enable_scb = 1;
// dut addr width 16, limit
bit [31:0] addr_start;
bit [31:0] addr_end;
int data_width;
lvc_ahb_agent_configuration ahb_cfg; // import low layer agent config
virtual rkv_ahbram_if vif; // interface
rkv_ahbram_rgm rgm; // regist model
`uvm_object_utils(rkv_ahbram_config)
// USER to specify the config item
function new(string name = "rkv_ahbram_config");
super.new(name);
ahb_cfg = lvc_ahb_agent_configuration::type_id::create("ahb_cfg");
endfunction : new
endclass
`endif
覆盖率收集rkv_ahbram_cov、寄存器模型的rkv_ahbram_rgm、rkv_ahbram_adapter搭建基本框架即可:
`ifndef RKV_AHBRAM_COV_SV
`define RKV_AHBRAM_COV_SV
class rkv_ahbram_cov extends rkv_ahbram_subscriber;
`uvm_component_utils(rkv_ahbram_cov)
function new(string name = "rkv_ahbram_cov", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
endclass
`endif
`ifndef RKV_AHBRAM_REG_SV
`define RKV_AHBRAM_REG_SV
class rkv_ahbram_rgm extends uvm_reg_block;
`uvm_object_utils(rkv_ahbram_rgm)
uvm_reg_map map;
function new(string name = "rkv_ahbram_rgm");
super.new(name,UVM_NO_COVERAGE);
endfunction
virtual function build();
map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
lock_model();
endfunction
endclass
`endif
`ifndef RKV_AHBRAM_REG_ADAPTER_SV
`define RKV_AHBRAM_REG_ADAPTER_SV
class rkv_ahbram_reg_adapter extends uvm_reg_adapter;
`uvm_object_utils(rkv_ahbram_reg_adapter)
function new(string name = "rkv_ahbram_reg_adapter");
super.new(name);
provides_responses = 1;
endfunction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
endfunction
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
endfunction
endclass
`endif
更新的rkv_ahbram_pkg:新include进来的都为rkv的组件。
`ifndef RKV_AHBRAM_PKG_SV
`define RKV_AHBRAM_PKG_SV
package rkv_ahbram_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
import lvc_ahb_pkg::*;
`include "../reg/rkv_ahbram_rgm.sv"
`include "../reg/rkv_ahbram_reg_adapter.sv"
`include "../cfg/rkv_ahbram_config.sv"
`include "rkv_ahbram_virtual_sequencer.sv"
`include "rkv_ahbram_subscriber.sv"
`include "rkv_ahbram_scoreboard.sv"
`include "rkv_ahbram_cov.sv"
`include "rkv_ahbram_env.sv"
`include "rkv_ahbram_seq_lib.svh"
`include "rkv_ahbram_base_virtual_sequence.sv"
`include "rkv_ahbram_base_test.sv"
endpackage
`endif
virtual_sequencer:声明指向lvc_ahb_master_sequencer的句柄
`ifndef RKV_AHBRAM_VIRTUAL_SEQUENCER_SV
`define RKV_AHBRAM_VIRTUAL_SEQUENCER_SV
class rkv_ahbram_virtual_sequencer extends uvm_sequencer;
// add sub-instances' sqr handles below for routing把各个Sequencer添加在下面
rkv_ahbram_config cfg; //顶层的配置文件
lvc_ahb_master_sequencer ahb_mst_sqr;
`uvm_component_utils(rkv_ahbram_virtual_sequencer)
function new (string name = "rkv_ahbram_virtual_sequencer", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Get configuration from test layer //获取配置
if(!uvm_config_db#(rkv_ahbram_config)::get(this,"","cfg", cfg)) begin
`uvm_fatal("GETCFG","cannot get config object from config DB")
end
endfunction
endclass
`endif
subscriber:在其中需要声明uvm_analysis_imp是monitor中广播的一个对象,并在build_phase中为其开辟空间。
`ifndef RKV_AHBRAM_SUBSCRIBER_SV
`define RKV_AHBRAM_SUBSCRIBER_SV
class rkv_ahbram_subscriber extends uvm_component;
// analysis import
uvm_analysis_imp #(lvc_ahb_transaction, rkv_ahbram_subscriber) ahb_trans_observed_imp;
// events delcared
protected uvm_event_pool _ep;
rkv_ahbram_config cfg;
virtual rkv_ahbram_if vif;
`uvm_component_utils(rkv_ahbram_subscriber)
function new (string name = "rkv_ahbram_subscriber", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ahb_trans_observed_imp = new("ahb_trans_observed_imp", this);//例化import
// Get configuration from test layer
if(!uvm_config_db#(rkv_ahbram_config)::get(this,"","cfg", cfg)) begin
`uvm_fatal("GETCFG","cannot get config object from config DB")
end
vif = cfg.vif;//
// Local event pool and events creation
_ep = new("_ep");
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_events_trigger();
do_listen_events();
endtask
virtual function void write(lvc_ahb_transaction tr);
endfunction
virtual task do_events_trigger();
endtask
virtual task do_listen_events();
endtask
endclass
`endif
scoreboard 继承与subscriber,用来比对读回的值是否与写入的值一致,功能的比对组件:
`ifndef RKV_AHBRAM_SCOREBOARD_SV
`define RKV_AHBRAM_SCOREBOARD_SV
class rkv_ahbram_scoreboard extends rkv_ahbram_subscriber;
// events of scoreboard
// typedef enum {CHECK_LOADCOUNTER} check_type_e;
`uvm_component_utils(rkv_ahbram_scoreboard)
function new (string name = "rkv_ahbram_scoreboard", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_data_check();
endtask
task do_listen_events();
endtask
virtual task do_data_check();
endtask
endclass
`endif
最后为环境env,需要例化整个验证环境中的组件:master_agent、rkv_config、virtual_sequencer、rgm、adapter、predictor、cov、scoreboard。并将在env中从上一级set下来的配置文件cfg再set到其所例化的组件中去(cov、scb:后续比对过程中对比对的count以及错误的count次数需要加一修改、virt_sqr)。还要在connent_phase中进行连接。将virtual_sequencer中例化的lvc的sequencer连接起来(lvc的master_sequencer已经开辟好空间,virtual中声明句柄,需要连接),寄存器模型的连接(vip中的架构,此验证中不涉及到寄存器模型,不用太在意);将monitor的输出的port与需要数据的地方的port连接起来;打印最后的比对报告。
`ifndef RKV_AHBRAM_ENV_SV
`define RKV_AHBRAM_ENV_SV
class rkv_ahbram_env extends uvm_env;
lvc_ahb_master_agent ahb_mst;
rkv_ahbram_config cfg;
rkv_ahbram_virtual_sequencer virt_sqr;
rkv_ahbram_rgm rgm;
rkv_ahbram_reg_adapter adapter;
uvm_reg_predictor #(lvc_ahb_transaction) predictor;
rkv_ahbram_cov cov;
rkv_ahbram_scoreboard scb;
`uvm_component_utils(rkv_ahbram_env)
function new (string name = "rkv_ahbram_env", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Get configuration from test layer
if(!uvm_config_db#(rkv_ahbram_config)::get(this,"","cfg", cfg)) begin
`uvm_fatal("GETCFG","cannot get config object from config DB")
end
uvm_config_db#(rkv_ahbram_config)::set(this, "virt_sqr", "cfg", cfg);
uvm_config_db#(rkv_ahbram_config)::set(this, "cov", "cfg", cfg);
uvm_config_db#(rkv_ahbram_config)::set(this, "scb", "cfg", cfg);
uvm_config_db#(lvc_ahb_agent_configuration)::set(this, "ahb_mst", "cfg", cfg.ahb_cfg);//config的set都在env
ahb_mst = lvc_ahb_master_agent::type_id::create("ahb_mst", this);
virt_sqr = rkv_ahbram_virtual_sequencer::type_id::create("virt_sqr", this);
if(!uvm_config_db#(rkv_ahbram_rgm)::get(this,"","rgm", rgm)) begin
rgm = rkv_ahbram_rgm::type_id::create("rgm", this);
rgm.build();
end
uvm_config_db#(rkv_ahbram_rgm)::set(this,"*","rgm", rgm);//set rgm?
adapter = rkv_ahbram_reg_adapter::type_id::create("adapter", this);
predictor = uvm_reg_predictor#(lvc_ahb_transaction)::type_id::create("predictor", this);
cov = rkv_ahbram_cov::type_id::create("cov", this);
scb = rkv_ahbram_scoreboard::type_id::create("scb", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
virt_sqr.ahb_mst_sqr = ahb_mst.sequencer;
rgm.map.set_sequencer(ahb_mst.sequencer, adapter);//??
ahb_mst.monitor.item_observed_port.connect(predictor.bus_in);
predictor.map = rgm.map;
predictor.adapter = adapter;
ahb_mst.monitor.item_observed_port.connect(cov.ahb_trans_observed_imp);
ahb_mst.monitor.item_observed_port.connect(scb.ahb_trans_observed_imp);
endfunction
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
endfunction
function void report_phase(uvm_phase phase);
string reports = "\n";
super.report_phase(phase);
reports = {reports, $sformatf("=============================================== \n")};
reports = {reports, $sformatf("CURRENT TEST SUMMARY \n")};
reports = {reports, $sformatf("SEQUENCE CHECK COUNT : %0d \n", cfg.seq_check_count)};
reports = {reports, $sformatf("SEQUENCE CHECK ERROR : %0d \n", cfg.seq_check_error)};
reports = {reports, $sformatf("SCOREBOARD CHECK COUNT : %0d \n", cfg.scb_check_count)};
reports = {reports, $sformatf("SCOREBOARD CHECK ERROR : %0d \n", cfg.scb_check_error)};
reports = {reports, $sformatf("=============================================== \n")};
`uvm_info("TEST_SUMMARY", reports, UVM_LOW)
endfunction
endclass
`endif // RKV_AHBRAM_ENV_SV
完善lvc_master_agent:创建monitor、sequencer、driver等组件
`ifndef LCV_AHB_MASTRE_AGENT_SV
`define LCV_AHB_MASTRE_AGENT_SV
class lvc_ahb_master_agent extends uvm_agent;
`uvm_component_utils(lvc_ahb_master_agent);
// 创建组件
lvc_ahb_agent_configuration cfg;
lvc_ahb_master_driver driver;
lvc_ahb_master_monitor monitor;
lvc_ahb_master_sequencer sequencer;
virtual lvc_ahb_if vif;
function new(string name = "lvc_ahb_master_agent", uvm_component parent);
super.new(name,parent);
endfunction
// cfg 来自env中的set,得到的是lvc的cfg,需要is_avtive信号判断是否需要例化driver和sequencer
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(lvc_ahb_agent_configuration)::get(this,"","cfg",cfg)) begin
`uvm_fatal("GETCFG","cannot get config object fraom config DB");
end
if(!uvm_config_db#(virtual lvc_ahb_if)::get(this,"","vif", vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
monitor = lvc_ahb_master_monitor::type_id::create("monitor",this);
if(cfg.is_active) begin
driver = lvc_ahb_master_driver::type_id::create("driver",this);
sequencer = lvc_ahb_master_sequencer::type_id::create("sequencer",this);
end
endfunction
// 将driver与sequencer连接起来,并将interface与monitor的driver的sequencer的interface连接起来
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
monitor.vif = vif;
if(cfg.is_active) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
driver.vif = vif;
sequencer.vif = vif;
end
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
endtask
endclass
`endif
最后还需要seq以及test文件:seq_lib文件夹下创建rkv_ahbram_base_virtual_sequence,这是一个virtual sequence,是sequence的组合,其中声明了compare函数后续可以做比对,并声明了p_sequencer为rkv_ahbram_virtual_sequencer,说明后续产生的sequence会挂载到virtual_sequencer上,而由于在env的connet_phase中将virahb_master_sequencer与ahb_master_sequencer连接到了一起,所以sequence会发送到lvc的sequencer上去,最后被driver get到。
`ifndef RKV_AHBRAM_BASE_VIRTUAL_SEQUENCE_SV
`define RKV_AHBRAM_BASE_VIRTUAL_SEQUENCE_SV
class rkv_ahbram_base_virtual_sequence extends uvm_sequence;
rkv_ahbram_config cfg;
virtual rkv_ahbram_if vif;
rkv_ahbram_rgm rgm;
bit[31:0] wr_val, rd_val;
uvm_status_e status;//?
`uvm_object_utils(rkv_ahbram_base_virtual_sequence)
`uvm_declare_p_sequencer(rkv_ahbram_virtual_sequencer)
function new (string name = "rkv_ahbram_base_virtual_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_info("body", "Entered...", UVM_LOW)
// get cfg from p_sequencer
cfg = p_sequencer.cfg;//?
vif = cfg.vif;
rgm = cfg.rgm;
// TODO in sub-class
`uvm_info("body", "Exiting...", UVM_LOW)
endtask
virtual function void compare_data(logic[31:0] val1, logic[31:0] val2);//?
cfg.seq_check_count++;
if(val1 === val2)
`uvm_info("CMPSUC", $sformatf("val1 'h%0x === val2 'h%0x", val1, val2), UVM_LOW)
else begin
cfg.seq_check_error++;
`uvm_error("CMPERR", $sformatf("val1 'h%0x !== val2 'h%0x", val1, val2))
end
endfunction
task wait_reset_signal_assertted();
@(posedge vif.rstn);
endtask
task wait_reset_signal_released();
@(negedge vif.rstn);
endtask
endclass
`endif
在seq_lib文件夹下的 rkv_ahbram_seq_lib中存放sequence的lib
`ifndef RKV_AHBRAM_SEQ_LIB_SVH
`define RKV_AHBRAM_SEQ_LIB_SVH
`include "rkv_ahbram_base_virtual_sequence.sv"
`endif
base_test文件:
`ifndef RKV_AHBRAM_BASE_TEST_SV
`define RKV_AHBRAM_BASE_TEST_SV
virtual class rkv_ahbram_base_test extends uvm_test;
rkv_ahbram_config cfg;
rkv_ahbram_env env;
rkv_ahbram_rgm rgm;
function new (string name = "rkv_ahbram_base_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
rgm = rkv_ahbram_rgm::type_id::create("rgm");//rgm的创建在test中
rgm.build();
uvm_config_db#(rkv_ahbram_rgm)::set(this, "env", "rgm", rgm);
cfg = rkv_ahbram_config::type_id::create("cfg");//config的创建
cfg.rgm = rgm;
if(!uvm_config_db#(virtual rkv_ahbram_if)::get(this,"","vif", cfg.vif))
`uvm_fatal("GETCFG","cannot get virtual interface from config DB")
uvm_config_db#(rkv_ahbram_config)::set(this, "env", "cfg", cfg);
env = rkv_ahbram_env::type_id::create("env", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.phase_done.set_drain_time(this, 1us);
phase.raise_objection(this);
phase.drop_objection(this);
endtask
endclass
`endif
到此为止,整个最基本的框架就已经搭建好了。后续对框架内的内容进行完善。