概述
文章目录
- 前言
- 一、验证结构
- 二、各组件功能
- 1.lvc_apb_master_agent
- 1.1 driver功能
- 1.2 monitor功能
- 1.3 sequencer功能
- 2.lvc_i2c_slave_agent
- 2.1 driver功能
- 2.2 monitor功能
- 2.3 sequencer功能
- 3. Scoreboard功能
- 4. Predictor功能
- 5. Virtual sequencer功能
- 总结
前言
I2C项目验证结构以及各验证组件的功能描述,仅为学习目的。
一、验证结构
由lvc_apb_master_agent(配置为master模式),lvc_i2c_slave_agent(配置为slave模式),scoreboard和virtual sequencer,以及predictor等组件构成,如下图
二、各组件功能
1.lvc_apb_master_agent
包括driver, monitor, sequencer,在agent中完成了
- 在build_phase中对各组件包括driver, monitor, sequencer的例化和配置(由于配置为master模式,所以含有driver和sequencer,monitor无论master/slave模式均有)
function void lvc_apb_master_agent::build();
super.build();
// get top config(agent work mode/apb bus signal(pready,pslverr))
if( !uvm_config_db#(lvc_apb_config)::get(this,"","cfg", cfg)) begin
`uvm_warning("GETCFG","cannot get config object from config DB")
cfg = lvc_apb_config::type_id::create("cfg");
end
// get virtual interface(drive and monitor the signal of apb bus)
if( !uvm_config_db#(virtual lvc_apb_if)::get(this,"","vif", vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
monitor = lvc_apb_master_monitor::type_id::create("monitor",this);
monitor.cfg = cfg;
//judge the cfg mode
if(cfg.is_active == UVM_ACTIVE) begin
sequencer = lvc_apb_master_sequencer::type_id::create("sequencer",this);
sequencer.cfg = cfg;
driver = lvc_apb_master_driver::type_id::create("driver",this);
driver.cfg = cfg;
end
endfunction : build
- 在connect_phase完成了虚接口的传递,以及driver的seq_item_port与sequencer的seq_item_export的端口连接(这两个端口是uvm专门定义的端口,无需例化)
function void lvc_apb_master_agent::connect();
assign_vi(vif);
if(is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect
1.1 driver功能
- get_and_drive(): 主体任务为drive_transfer(), 根据从sequencer侧获得的seq的trans_kind属性对apb总线执行响应的读写任务(do_write/do_read_do_idle);代码以do_write为例,do_read与之相似,只不过在setup周期pwrite信号拉低,从端需要在enable周期内返回prdata(传输无误)。
task lvc_apb_master_driver::do_write(lvc_apb_transfer t);
`uvm_info(get_type_name(), "do_write ...", UVM_HIGH)
@(vif.cb_mst);
//apb总线setup状态,psel=1, penable=0,表示有传输要进行,只在此状态停留一个周期,写入addr和data, pwrite拉高
vif.cb_mst.paddr <= t.addr;
vif.cb_mst.pwrite <= 1;
vif.cb_mst.psel <= 1;
vif.cb_mst.penable <= 0;
vif.cb_mst.pwdata <= t.data;
@(vif.cb_mst);
//apb总线enable状态,penable被置为1,pwrite/paddr/pdata/psel均保持有效,完成一次传输后地址信号和写信号捕获改变,直到下一次传输开始
vif.cb_mst.penable <= 1;
#10ps;
//pready置为1结束此次transfer,此信号由slave产生,如果想延长响应时间可拉低此信号
wait(vif.pready === 1);
#1ps;
//监测pslverr信号判断传输是否发送错误,当psel,penable,pready同时有效时可采集到此信号,为高表示传输错误,在apb transfer的最后一个周期有效
if(vif.pslverr === 1) begin
t.trans_status = ERROR;
if(cfg.master_pslverr_status_severity == UVM_ERROR)
`uvm_error(get_type_name(), "PSLVERR asserted!")
else
`uvm_warning(get_type_name(), "PSLVERR asserted!")
end
//未监测到pslverr表明传输正常
else begin
t.trans_status = OK;
end
repeat(t.idle_cycles) this.do_idle();
endtask: do_write
- reset_listener(): 在复位信号有效时对apb总线执行复位操作。
1.2 monitor功能
1.collect_transfer(): 通过lvc_apb_if监测apb接口的地址线、数据线、控制线以及传输情况;
task lvc_apb_master_monitor::collect_transfer();
// Advance clock
//psel为高,penable为低,apb总线处于setup周期
@(vif.cb_mon iff (vif.cb_mon.psel === 1'b1 && vif.cb_mon.penable === 1'b0));
trans_collected = lvc_apb_transfer::type_id::create("trans_collected");
case(vif.cb_mon.pwrite)
1'b1 : begin//监测到写信号
//监测到pready为高即写传输结束的前一周期,把地址、数据、指令和传输状态传入事务包中
@(vif.cb_mon iff vif.cb_mon.pready === 1'b1);
trans_collected.addr = vif.cb_mon.paddr;
trans_collected.data = vif.cb_mon.pwdata;
trans_collected.trans_kind = WRITE;
trans_collected.trans_status = vif.cb_mon.pslverr === 1'b0 ? OK : ERROR;
end
1'b0 : begin//监测到读信号
@(vif.cb_mon iff vif.cb_mon.pready === 1'b1);
trans_collected.addr = vif.cb_mon.paddr;
trans_collected.data = vif.cb_mon.prdata;
trans_collected.trans_kind = READ;
trans_collected.trans_status = vif.cb_mon.pslverr === 1'b0 ? OK : ERROR;
end
default : `uvm_error(get_type_name(), "ERROR pwrite signal value")
endcase
endtask: collect_transfer
- 根据check_enable/coverage_enable两个开关,通过analysis_port传递监测到的信号执行相应的功能。
// This field controls if this monitor has its checkers enabled
// (by default checkers are on)
bit checks_enable = 1;
// This field controls if this monitor has its coverage enabled
// (by default coverage is on)
bit coverage_enable = 1;
// perform_transfer_checks
function void lvc_apb_master_monitor::perform_transfer_checks();
// USER: do some checks on the transfer here
endfunction : perform_transfer_checks
// perform_transfer_coverage
function void lvc_apb_master_monitor::perform_transfer_coverage();
// USER: coverage implementation
-> lvc_apb_master_cov_transaction;
endfunction : perform_transfer_coverage
1.3 sequencer功能
无特别任务。
2.lvc_i2c_slave_agent
- 在build_phase中将agent配置为passive模式(slave),并例化driver, monitor, sequencer组件;(由于对I2C配置是可以更改的(可以控制slave和master的数量),为了便于后续配置更改,i2c_slave_agent在build_phase中具有层次化的配置,代码看起来比apb复杂,此处省略不贴代码)
- 在connect_phase完成driver和sequencer的端口连接(代码与apb_master_agent相似,省略)
- run_phase中根据agent配置模式通过reconfigure_via_task完成对driver, sequencer的重新配置(monitor在slave/master模式下均有)
2.1 driver功能
- wait_for_reset():等待复位信号到来(该任务位于lvc_i2c_bfm_common中);
- consum_from_seq_item_port():在通过seq_item_port收到有效事务后,将事务按照协议驱动给i2c总线,并为此事务做标记后复制一份resp发回给sequencer(主体任务send_xact位于lvc_i2c_driver_common中)
注:涉及代码太多,后续分析
2.2 monitor功能
包含两个analysis_port,xact_observed_port(当观测到完整事务,monitor通过write方法将信号放进port); data_observed_port(monitor一旦监测到数据就会通过port传递信息,而不会等整个事务都出现在总线上)
- wait_for_reset(): 等待复位信号到来(该任务位于lvc_i2c_bfm_common中);
- received_and_sent():按照i2c协议监测事务,并在数据不为空或监测未结束时,两个analysis_port持续执行write方法将信号写入端口
2.3 sequencer功能
无特别功能,需要检查特定设置下是否获得相应配置。
3. Scoreboard功能
包含两个analysis port的接收端口,分别来自apb_master和i2c_slave中的monitor,负责接收来自两边的观测数据,并通过访问寄存器模型获得读写指令,再依据apb写入寄存器模型中的数据模拟出refmod的期望数据,最后将其与i2c侧观测到的数据进行对比。
- i2c_refmod(): 模拟DUV工作时使用APB接口配置控制寄存器,并模拟前门访问寄存器模型行为产生读/写命令下的数据期望值(模拟apb总线产生的数据对寄存器配置后模块输出的数据);
task i2c_refmod();
lvc_apb_transfer tr;
ral_reg_rkv_i2c_IC_DATA_CMD data_cmd_r;
bit[7:0] data;
data_cmd_r = new("data_cmd_r");
data_cmd_r.build();//创建一个控制寄存器
forever begin
wait(apb_trans_observed.size() > 0) tr = apb_trans_observed.pop_front();//一旦通过analysis port观测到apb总线事务,就从队列前面取出从apb总线观测到的事务元素存入tr中
data_cmd_r.set(tr.data);//通过前门访问,将控制寄存器的期望值修改为tr.data;
if(tr.trans_kind == lvc_apb_pkg::WRITE && cfg.rgm.IC_DATA_CMD_CMD.get() == RGM_WRITE) begin
//如果观测到的事务和通过前门访问获得的寄存器模型期望值都是写命令,把寄存器中的数据以push_back的方式放入期望值写数据的队列中,并计数加一
write_data_expected.push_back(data_cmd_r.DAT.get());
write_count_expected++;
end
//如果观测到的事务和通过前门访问获得的寄存器模型期望值都是读命令,把寄存器中的数据以push_back的方式放入期望值读数据的队列中,并计数加一
else if(tr.trans_kind == lvc_apb_pkg::READ && cfg.rgm.IC_DATA_CMD_CMD.get() == RGM_READ) begin
read_data_expected.push_back(data_cmd_r.DAT.get());
read_count_expected++;
end
end
endtask
- i2c_write_compare():写数据的对比,把实际观测到的值和refmod产生的期望值进行对比,统计错误次数;
task i2c_write_comparer();
bit[7:0] exp, obs;//两个数组分别存放写命令下的期望值和实际观测值
forever begin
fork
wait(write_data_expected.size() > 0) exp = write_data_expected.pop_front();
wait(write_data_observed.size() > 0) obs = write_data_observed.pop_front();
join
compare_transaction(exp, obs);//对比期望值和观测值
end
endtask
function void compare_transaction(bit[7:0] exp, bit[7:0] obs);
bit mismatch_detected = 0 ;//默认检测结果匹配
if(exp != obs) begin//如果期望值和观测值不同,把错误标记置1
`uvm_error(get_type_name(), $sformatf("Byte transferred different in expected value is %h and slave observed value is %h", exp, obs))
mismatch_detected = 1;
end
// check for no mismatch
if(!mismatch_detected)//错误标记为低,打印正确信息,否则错误计数加1
`uvm_info(get_type_name(), $sformatf("Trans match between expected %h and observed %h", exp, obs), UVM_LOW)
else
mismatch_count++;
endfunction: compare_transaction
- i2c_read_compare():读数据的对比,把在i2c侧实际观测到的值和refmod产生的期望值进行对比,统计错误次数;
task i2c_read_comparer();
bit[7:0] exp, obs;//两个数组分别存放读命令下的期望值和实际观测值
forever begin
fork
wait(read_data_expected.size() > 0) exp = read_data_expected.pop_front();
wait(read_data_observed.size() > 0) obs = read_data_observed.pop_front();
join
compare_transaction(exp, obs);//此任务代码在上面已贴
end
endtask
- i2c_mon_interrupt():监测中断,监测到RX FIOFO溢出中断、TXFIFO为空中断以及TX发送数据异常中断,就插入中断并关闭scoreboard数据比对功能。
task i2c_mon_interrupt();
forever begin
// wait any interrupt asserted
cfg.vif.wait_intr();
if( cfg.vif.get_intr(IC_RX_OVER_INTR_ID)
|| cfg.vif.get_intr(IC_TX_ABRT_INTR_ID)
|| cfg.vif.get_intr(IC_TX_EMPTY_INTR_ID)
) begin
interrupt_abort = 1;
enable = 0;
`uvm_info(get_type_name(), "monitored aborted/exceptional interrupt asserted, and disable scoreboard.", UVM_LOW)
end
end
endtask
//读写观测值来源于analysis port的write任务,对应于i2c agent中的monitor,期望值则来源于analysis port的write任务产生的apb_trans_observed(此队列用于配置refmod中的控制寄存器,进而产生期望读写值),对应于apb agent中monitor。
4. Predictor功能
更新寄存器模型有两种途径,一种是自动预测途径,当driver将读取值返回后,寄存器模型会更新寄存器的镜像值和期望值;
另一种是经由predictor的途径,monitor从apb总线上收集到的transaction交给寄存器模型,再由寄存器模型更新相应寄存器的值,这种方式则需要实例化一个reg_predictor并为这个reg_predictor例化一个adapter。
a. 使用predictor时,需要为其设置adapter及map变量,只有设置了map后,才能将predictor和寄存器模型关联在一起; uvm_predictor使用adapter将monitor传递来的bus transaction转换成uvm_reg_item, 然后使用map根据uvm_reg_item中的address信息找到对应的register,并更新其镜像值和期望值。
build_phase中:
adapter = lvc_apb_reg_adapter::type_id::create("adapter", this);
predictor = uvm_reg_predictor#(lvc_apb_transfer)::type_id::create("predictor", this);
connect_phase中:
apb_mst.monitor.item_collected_port.connect(predictor.bus_in);
predictor.map = rgm.default_map;
predictor.adapter = adapter;
5. Virtual sequencer功能
实现对apb_mst_sqr, i2c_slave_sqr的挂载,最终实现各类seq在test中的启动。
run_phase中
seq.start(env.sqr);
总结
本文主要结合代码介绍了i2c验证环境中的各类组件功能,有不同见解的地方可以交流讨论喔。
最后
以上就是单身金鱼为你收集整理的I2C虚拟项目验证结构前言一、验证结构二、各组件功能总结的全部内容,希望文章能够帮你解决I2C虚拟项目验证结构前言一、验证结构二、各组件功能总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复