概述
apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)文章目录
文章目录
- apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)文章目录
- 前言
- 一、apb直接访问
- 1.sequence侧代码
- 2.test侧代码
- 3.仿真运行结果
- 二、寄存器模型访问
- 1.引入registermodel
- 2.adapter
- 3.regacc sequence
- 4.运行结果
- 三、集成模式测式
- 总结——当前验证结构框图
前言
承接上文,本文将继续apb watchdog模块的验证工作,在上次搭建的验证骨架下完成apb总线侧的激励发送。直接访问的方式就是调用vip中的apb2测试序列对总线进行访问以完成寄存器的配置,而register model访问的方式则是在验证环境中创建一个寄存器模型,通过adapter将其与总线连接起来,用户可以通过寄存器模型来配置硬件寄存器的值。本文将介绍二者在watchdog验证中的具体实现方法。
一、apb直接访问
apb直接访问就是直接在对应的seq中调用apb_seq,挂载到env中与apb_mst_sequencer连接好的sequencer上,进行激励的发送。需要创建对应的seq与test文件,并将它们加入seq_lib与test_lib的编译文件中。
对于apb直接访问,我们可以直接读取部分寄存器reset之后的数据,如下代码读取的是WDOGPERIPHID0到WDOGPERIPHID4的数据,其地址与reset之后的信号值如下:reset信号应分别为24,b8,1b与b0(本设计文件中此复位值与文件中不同)。
1.sequence侧代码
apbacc_test的代码如下:
`ifndef RKV_WATCHDOG_APBACC_VIRT_SEQ_SV
`define RKV_WATCHDOG_APBACC_VIRT_SEQ_SV
class rkv_watchdog_apbacc_virt_seq extends rkv_watchdog_base_virtual_sequence;
`uvm_object_utils(rkv_watchdog_apbacc_virt_seq)
function new (string name = "rkv_watchdog_apbacc_virt_seq");
super.new(name);
endfunction
virtual task body();
int rd_val;
super.body();
`uvm_info("body", "Entered...", UVM_LOW)
// TODO in sub-class
`uvm_do_on_with(apb_rd_seq,p_sequencer.apb_mst_sqr,{addr=='hFE0;})
rd_val = apb_rd_seq.data;
void'(this.diff_value( rd_val , 'h24));
`uvm_do_on_with(apb_rd_seq,p_sequencer.apb_mst_sqr,{addr=='hFE4;})
rd_val = apb_rd_seq.data;
void'(this.diff_value( rd_val , 'hb8));
`uvm_do_on_with(apb_rd_seq,p_sequencer.apb_mst_sqr,{addr=='hFE8;})
rd_val = apb_rd_seq.data;
void'(this.diff_value( rd_val , 'h1b));
`uvm_do_on_with(apb_rd_seq,p_sequencer.apb_mst_sqr,{addr=='hFEc;})
rd_val = apb_rd_seq.data;
void'(this.diff_value( rd_val , 'hb0));
`uvm_do_on_with(apb_rd_seq,p_sequencer.apb_mst_sqr,{addr=='hFE0;})
`uvm_info("body", "Exiting...", UVM_LOW)
endtask
endclass
`endif
apb_rd_seq是访问之后返回的读取数据,其具体代码在vip中:
apb_read_sequence:
`uvm_info(get_type_name(),"Starting sequence", UVM_HIGH)
`uvm_do_with(req, {trans_kind == READ; addr == local::addr;})
get_response(rsp);
data = rsp.data;
`uvm_info(get_type_name(),$psprintf("Done sequence: %s",req.convert2string()), UVM_HIGH)
driver中:
```cpp
task apb_master_driver::do_read(apb_transfer t);
`uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
@(vif.cb_mst);
vif.cb_mst.paddr <= t.addr;
vif.cb_mst.pwrite <= 0;
vif.cb_mst.psel <= 1;
vif.cb_mst.penable <= 0;
@(vif.cb_mst);
vif.cb_mst.penable <= 1;
#100ps;
t.data = vif.prdata; //get data
repeat(t.idle_cycles) this.do_idle();
endtask: do_read
对于function diff_value,其定义于base sequence中,这样后续的sequence都可以调用。此时base sequence中声明了该function与一些apb master sequnce
base sequnce 中的function与sequence声明
apb_master_single_write_sequence apb_wr_seq;
apb_master_single_read_sequence apb_rd_seq;
apb_master_write_read_sequence apb_wr_rd_seq;
virtual function bit diff_value(int val1, int val2, string id = "value_compare");
cfg.seq_check_count++;
if(val1 != val2) begin
cfg.seq_error_count++;
`uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2))
return 0;
end
else begin
`uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
return 1;
end
endfunction
2.test侧代码
创建新的test——apbacc_test
apbacc_seq代码:
`ifndef RKV_WATCHDOG_apbacc_test_SV
`define RKV_WATCHDOG_apbacc_test_SV
class rkv_watchdog_apbacc_test extends rkv_watchdog_base_test;
`uvm_component_utils(rkv_watchdog_apbacc_test)
function new (string name = "rkv_watchdog_apbacc_test", 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);
rkv_watchdog_apbacc_virt_seq seq;
seq = rkv_watchdog_apbacc_virt_seq::type_id::create("seq");
super.run_phase(phase);
phase.raise_objection(this);
seq.start(env.virt_sqr);
phase.drop_objection(this);
endtask
virtual task do_init_clks();
// wait reset posedge
#1ns;
@(negedge env.cfg.vif.apb_rstn);
@(posedge env.cfg.vif.apb_rstn);
// wait(env.apb_mst.vif.rstn == 1 );
// wait(env.apb_mst.vif.rstn == 0 );
// wait(env.apb_mst.vif.rstn == 1 );
endtask
virtual task do_init_regs();
// TODO in sub-class
endtask
endclass
`endif
在此test中只需要创建对应的seq,并且将seq挂载到对应的sequencer上即可。同时完成了clk的初始化:即仿真应该在reset信号完成之后再开始,也可以将此task直接放置于父类base test层次中。在此task中,需要拿到rstn信号,一种方式是注释部分的方法:直接从apb_mst层次访问。
但是考虑到日后可能访问watchdog一侧的信号要求,也可以将watchdog的interface传递于env中,这里将interface,registermodel,还有一些仿真记录信号一并传递于cfg中,将它们打包,便于创建与configdb配置。
cfg代码
`ifndef RKV_WATCHDOG_CONFIG_SVH
`define RKV_WATCHDOG_CONFIG_SVH
`include "rkv_watchdog_config.sv"
class rkv_watchdog_config extends uvm_object;
int seq_check_count;
int seq_error_count;
int scb_check_count;
int scb_error_count;
`uvm_object_utils(rkv_watchdog_config)
apb_config apb_cfg;
// USER to specify the config items
virtual rkv_watchdog_if vif;
rkv_watchdog_rgm rgm;
function new (string name = "rkv_watchdog_config");
super.new(name);
apb_cfg = apb_config::type_id::create("apb_cfg");
endfunction : new
endclass
`endif
信号为仿真记录信号,其中包括了需要传递的interface,cfg在base test层次创建,配置到env中。创建代码如下:
base test 层次
cfg = rkv_watchdog_config::type_id::create("cfg");
uvm_config_db#(rkv_watchdog_config)::set(this,"env","cfg",cfg);
env中get cfg(build phase)
if(!uvm_config_db#(rkv_watchdog_config)::get(this, "", "cfg",cfg)) begin
`uvm_fatal("GTCFG","cannot get cfg from configdb")
end
if(!uvm_config_db#(virtual rkv_watchdog_if)::get(this, "", "vif",cfg.vif)) begin
`uvm_fatal("GTCFG","cannot get vif from confgdb")
end
将cfg传递至env中并且对其中的interface进行传递。这样在env中就可以完成watchdog interface的访问,同时加入仿真check的记录功能。打印代码如下:
function void report_phase(uvm_phase phase);
string reports;
super.report_phase(phase);
reports = {"n"};
reports = {reports, $sformatf("====================================== n")};
reports = {reports, $sformatf("compare test summary n")};
reports = {reports, $sformatf("seq check counts:%0d.n", cfg.seq_check_count)};
reports = {reports, $sformatf("seq check error counts:%0d. n", cfg.seq_error_count)};
reports = {reports, $sformatf("scoreboard check counts:%0d n", cfg.scb_check_count)};
reports = {reports, $sformatf("scoreboard error check counts:%0d. n", cfg.scb_error_count)};
reports = {reports, $sformatf("====================================== n")};
`uvm_info("test summary", reports, UVM_LOW)
endfunction
3.仿真运行结果
在vcs中进行make elab 与make run 并传递TESTNAME = rkv_watchdog_apbacc_test就完成apb直接访问的任务了,结果如下:
打印的结果反映了比较情况,波形图反映直接访问是成功的。
二、寄存器模型访问
寄存器模型的生成来源于python脚本与csv文件。本文中不介绍其生成具体过程,直接介绍模型的引入和在验证环境中对寄存器模型进行调用。自动生成的寄存器模型只提供了前门访问方案,本文的访问也采用前门访问。
1.引入registermodel
register model引入环境与cfg类似,也是由test传递至env层次中。需要考虑的是如何在对应的seq中调用register model(rgm),这里采用的方法是将rgm传递至对应的sequencer中,在sequence挂载到sequencer之后,seq可以在body中拿到对应sequencer下的rgm,从而在sequence中实现寄存器模型的访问。
因此第一步是将test层面的rgm传递至env中
base test 层次代码:
function new (string name = "rkv_watchdog_base_test", uvm_component parent);
super.new(name, parent);
rgm = rkv_watchdog_rgm::type_id::create("rgm", this);
rgm.build();
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = rkv_watchdog_config::type_id::create("cfg");
uvm_config_db#(rkv_watchdog_config)::set(this,"env","cfg",cfg);
uvm_config_db#(rkv_watchdog_rgm)::set(this,"env","rgm",rgm);
env = rkv_watchdog_env::type_id::create("env", this);
endfunction
env层次代码:
function void build_phase(uvm_phase phase);
super.build_phase(phase);
apb_mst = apb_master_agent::type_id::create("apb_mst", this);
if(!uvm_config_db#(rkv_watchdog_config)::get(this, "", "cfg",cfg)) begin
`uvm_fatal("GTCFG","cannot get cfg from configdb")
end
if(!uvm_config_db#(virtual rkv_watchdog_if)::get(this, "", "vif",cfg.vif)) begin
`uvm_fatal("GTCFG","cannot get vif from confgdb")
end
virt_sqr = rkv_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
uvm_config_db#(rkv_watchdog_config)::set(this,"virt_sqr","cfg",cfg);
uvm_config_db#(apb_config)::set(this,"apb_mst","cfg",cfg.apb_cfg);
if(!uvm_config_db#(rkv_watchdog_rgm)::get(this, "", "rgm",rgm)) begin
rgm = rkv_watchdog_rgm::type_id::create("rgm", this);
rgm.build();
end
uvm_config_db#(rkv_watchdog_rgm)::set(this,"*","rgm", rgm);
adp = rkv_watchdog_adapter::type_id::create("adp", this);
predictor = uvm_reg_predictor#(apb_transfer)::type_id::create("predictor", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
virt_sqr.apb_mst_sqr = apb_mst.sequencer;
rgm.map.set_sequencer(apb_mst.sequencer, adp);
apb_mst.monitor.item_collected_port.connect(predictor.bus_in);
predictor.map = rgm.map;
predictor.adapter = adp;
endfunction
此中不但实现了rgm的配置并传递于下级,也实现了predictor与adapter的声明,predictor为uvm提供的explicit predictor,只需声明例化即可使用。adapter的具体代码会在后文提供。同时对predictor与adapter进行了连接,使其能够在rgm中发挥作用。
sequencer层次代码:
if(!uvm_config_db#(rkv_watchdog_config)::get(this, "", "cfg",cfg)) begin
`uvm_fatal("GTCFG","cannot get cfg from confgdb")
end
if(!uvm_config_db#(rkv_watchdog_rgm)::get(this, "", "rgm",rgm)) begin
`uvm_fatal("GTCFG","cannot get rgm from confgdb")
end
这样,rgm就从test一路传递至sequencer中,在base sequence中也能够拿的对应的rgm:
base sequence
apb_master_single_write_sequence apb_wr_seq;
apb_master_single_read_sequence apb_rd_seq;
apb_master_write_read_sequence apb_wr_rd_seq;
rkv_watchdog_config cfg;
rkv_watchdog_rgm rgm;
int rd_val,wt_val;
uvm_status_e status = UVM_IS_OK;
virtual rkv_watchdog_if vif;
function new (string name = "rkv_watchdog_base_virtual_sequence");
super.new(name);
//cfg = rkv_watchdog_config::type_id::create("cfg");
endfunction
virtual task body();
// get cfg from p_sequencer
cfg = p_sequencer.cfg;
// get rgm from p_sequencer
rgm = cfg.rgm;
vif = cfg.vif;
`uvm_info("body", "Entered...", UVM_LOW)
// TODO in sub-class
`uvm_info("body", "Exiting...", UVM_LOW)
endtask
经过如上的操作,寄存器模型就在环境中扎根了,但是想要真正通过寄存器模型完成与总线,硬件寄存器之间的交流,predictor和adapter是不可或缺的。前者在env环境中自动生成(代码在env中),后者则需要手动添加至环境中。
2.adapter
adapter是总线transaction与uvm_reg_bus_op之间的桥接工具。通过将二者之间进行相互转换来实现总线与寄存器模型之间的连接,其中的重点就是实现reg2bus,bus2reg两个function,其实就是两个trans类型的转化,具体代码实现如下:
adapter代码如下:
`ifndef RKV_WATCHDOG_ADAPTER_SV
`define RKV_WATCHDOG_ADAPTER_SV
class rkv_watchdog_adapter extends uvm_reg_adapter;
`uvm_object_utils(rkv_watchdog_adapter)
function new(string name = "rkv_watchdog_adapter");
super.new(name);
provides_responses = 1;
endfunction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
apb_transfer t = apb_transfer::type_id::create("t");
t.trans_kind = (rw.kind == UVM_WRITE) ? WRITE : READ;
t.addr = rw.addr;
t.data = rw.data;
t.idle_cycles = 1;
return t;
endfunction
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
apb_transfer t;
if (!$cast(t, bus_item)) begin
`uvm_fatal("CASTFAIL","Provided bus_item is not of the correct type")
return;
end
rw.kind = (t.trans_kind == WRITE) ? UVM_WRITE : UVM_READ;
rw.addr = t.addr;
rw.data = t.data;
rw.status = UVM_IS_OK;
endfunction
endclass
`endif
随着adapter与predicter的集成,rgm在环境中就可以被调用了。
3.regacc sequence
最后,完成通过寄存器模型访问的sequence就可以通过验证环境中的寄存器模型完成watchdog中硬件寄存器的配置了。
regacc_seq代码如下:
`ifndef RKV_WATCHDOG_REGACC_VIRT_SEQ_SV
`define RKV_WATCHDOG_REGACC_VIRT_SEQ_SV
class rkv_watchdog_regacc_virt_seq extends rkv_watchdog_base_virtual_sequence;
`uvm_object_utils(rkv_watchdog_regacc_virt_seq)
function new (string name = "rkv_watchdog_regacc_virt_seq");
super.new(name);
endfunction
virtual task body();
int rd_val;
uvm_status_e status = UVM_IS_OK;
super.body();
`uvm_info("body", "Entered...", UVM_LOW)
// TODO in sub-class
rgm.WDOGPERIPHID0.read(status, rd_val);
void'(this.diff_value( rd_val , 'h24));
rgm.WDOGPERIPHID1.read(status, rd_val);
void'(this.diff_value( rd_val , 'hb8));
rgm.WDOGPERIPHID2.read(status, rd_val);
void'(this.diff_value( rd_val , 'h1b));
rgm.WDOGPERIPHID3.read(status, rd_val);
void'(this.diff_value( rd_val , 'hb0));
`uvm_info("body", "Exiting...", UVM_LOW)
endtask
endclass
`endif
与直接访问相比,寄存器模型访问的方式将硬件寄存器通过软件的方式进行了封装,不再需要查询每个寄存器的地址来进行配置。同时,也可以针对某个寄存器的域来进行配置,增加了配置的灵活程度。下面给出了寄存器配置的运行结果。
4.运行结果
这里给出仿真结果与vcs中rgm的信息。波形结果与之前的apbacc完全相同,就不再放出。
三、集成模式测式
集成测试模式在打开之后WDOGINT与WDOGRES信号直接由WDOGITOP寄存器的低两位值来控制,集成测试的开关在寄存器WDOGITCR中,其最低位为高则集成测试模式打开。
由于之前已经实现apb寄存器访问,可通过此来直接实现集成测试。集成测试的sequence代码如下。
integration test code
`ifndef RKV_WATCHDOG_INTEGRATION_VIRT_SEQ_SV
`define RKV_WATCHDOG_INTEGRATION_VIRT_SEQ_SV
class rkv_watchdog_integration_virt_seq extends rkv_watchdog_base_virtual_sequence;
`uvm_object_utils(rkv_watchdog_integration_virt_seq)
function new (string name = "rkv_watchdog_integration_virt_seq");
super.new(name);
endfunction
task body();
super.body();
`uvm_info("body", "Entered...", UVM_LOW)
`uvm_info("SEQSTART", "virtual sequence body started!!", UVM_LOW)
void'(this.diff_value( vif.wdogint, 'b0));
void'(this.diff_value( vif.wdogres, 'b0));
wt_val = 'h1;
rgm.WDOGITCR.write(status, wt_val);
rgm.WDOGITCR.read(status, rd_val);
void'(this.diff_value( rd_val, wt_val));
wt_val = 'b11;
rgm.WDOGITOP.write(status, wt_val);
void'(this.diff_value( vif.wdogint, 'b1));
void'(this.diff_value( vif.wdogres, 'b1));
wt_val = 'b01;
rgm.WDOGITOP.WDOGINT.set('b1);
rgm.WDOGITOP.WDOGRES.set('b0);
rgm.WDOGITOP.update(status);
void'(this.diff_value( vif.wdogint, 'b1));
void'(this.diff_value( vif.wdogres, 'b0));
wt_val = 'b10;
rgm.WDOGITOP.WDOGINT.set('b0);
rgm.WDOGITOP.WDOGRES.set('b1);
rgm.WDOGITOP.update(status);
void'(this.diff_value( vif.wdogint, 'b0));
void'(this.diff_value( vif.wdogres, 'b1));
wt_val = 'h0;
rgm.WDOGITCR.write(status, wt_val);
rgm.WDOGITCR.read(status, rd_val);
void'(this.diff_value( rd_val, wt_val));
wt_val = 'b11;
rgm.WDOGITOP.WDOGINT.set('b1);
rgm.WDOGITOP.WDOGRES.set('b1);
rgm.WDOGITOP.update(status);
void'(this.diff_value( vif.wdogint, 'b0));
void'(this.diff_value( vif.wdogres, 'b0));
`uvm_info("body", "Exiting...", UVM_LOW)
endtask
endclass
`endif
该测试先配置WDOGITCR寄存器,使watchdog加入集成测试状态,随后对WDOGITOP中的WDOGINT与WDOGRES两个域进行0和1的赋值,检验输出的值是否与赋值相同并且随之变化。
这里WDOGITCR是读写寄存器,故可以对其进行读写操作,而WDOGITOP为只读寄存器,只能对其进行写操作。而功能验证则通过检查返回的信号是否与赋值相同实现即可。
最终输出的波形图如下:
波形上看集成测试没有出现问题,在配置进入集成测试后,输出的int与res信号与配置给寄存器的值是对应变化的。值得注意的是打印结果中出现了一个uvm_error,其来源是断言错误‘PRDATA not available once PENABLE rose’,在apb总线vip中可找到这一断言:
property p_prdata_available_once_penable_rose;
@(posedge clk) $rose(penable) && !pwrite |-> !$stable(prdata);
endproperty: p_prdata_available_once_penable_rose
assert property(p_prdata_available_once_penable_rose) else `uvm_error("ASSERT", "PRDATA not available once PENABLE rose")
其意思是说:在读取信号时被读信号的值没有按要求跳变。观察波形可知,由于读会的信号是32’b0(即配置关闭集成测试),其信号确实不会跳变。因此虽然这一时序断言未被满足,但是实际的硬件功能测试并没有出现问题。这一error是可忽略的。集成测试仍然是成功的。
总结——当前验证结构框图
本文继续watchdog验证之路,加入rgm之后验证环境日渐完备,接下来就可以进行一些功能测试了。
加入寄存器模型之后,验证环境可表示如下:
寄存器访问的优势不但是在访问时通过封装不再需要查找每次访问的地址和对应值。更重要的是其提供了更多的寄存器访问方法,即使在没有加入后门访问的前提下,也可以通过get的方式不耗时地获得寄存器的值。还有其中对寄存器不同域的访问,以及多样的访问方法,为验证带来了许多便利,这一点还会在接下来的验证中体会。
最后
以上就是调皮帽子为你收集整理的(数字ic验证)从零开始的apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)文章目录前言一、apb直接访问二、寄存器模型访问三、集成模式测式总结——当前验证结构框图的全部内容,希望文章能够帮你解决(数字ic验证)从零开始的apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)apb_watchdog验证模块搭建(二、apb直接发送激励与寄存器模型加入)文章目录前言一、apb直接访问二、寄存器模型访问三、集成模式测式总结——当前验证结构框图所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复