rgm的常规方法
- 关于reg的三个值
- mirrored value镜像值:由模型预测给出,即在前门访问时通过观察总线、在后门访问时通过自动预测等方式给出
- desired value期望值:先利用rgm修改软件对象值,然后利用该值更新硬件值,mv是表示当前硬件的已知状态值
- actual value硬件的真实值:
- uvm两种跟踪寄存器值的方式
- 自动预测auto prediction:调用uvm_reg_map::set_auto_predict()
- 具体地,user没有在env中集成独立的predictor,而是利用reg的操作来自动记录每一次reg的读写数值,并在后台自动调用predict()方法,此为ap
- 缺点:若seq直接在总线层面对reg进行操作,跳过了reg级别的读写,或者通过其他总线来访问reg,ap就无法获得mv和dv
- 显式预测explicit:预测的更准确,具体地,这是在物理总线上通过monitor来捕捉总线事务,然后传递给外部例化的predictor(由uvm参数化类uvm_reg_predictor例化并集成在顶层环境)
- 集成时需要将adapter与map的句柄一起传递给predictor,同时将monitor采集到的事务通过analysis port接入到predictor。
- monitor拿到trans→adapter转换→更新到map
- 默认情况采用显式预测
- 自动预测auto prediction:调用uvm_reg_map::set_auto_predict()
class mcdf_bus_env extends uvm_env;
mcdf_bus_agent agent;
mcdf_rgm rgm;
reg2mcdf_adapter reg2mcdf;
uvm_reg_predictor #(mcdf_bus_trans) mcdf2reg_predictor;
...//注册+例化
function void build_phase(u p);
agent=mcdf_bus_agent::type_id::create("agent", this);
if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
`uvm_info()
rgm=mcdf_rgm::type_id::create("", this);
`uvm_info() end
rgm.build();
reg2mcdf=reg2mcdf_adapter::type_id::create("");
mcdf2reg_predictor=uvm_reg_predictor#(mcdf_bus_trans)::type_id::create("", this);
mcdf2reg_predictor.map=rgm.map;
mcdf2reg_predictor.adapter=reg2mcdf;
endfunction
function void connect_phase(u p);
rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
agent.monitor.ap.conncet(mcdf2reg_predictor.bus_in);
endfunction
endclass
uvm_reg的访问方法
uvm_reg_sequence提供的方法
针对reg对象,而不是reg block或者field
- 前门访问的read()和write,在总线事务完成时,mv和dv才会更新为和总线上相同的值→显式预测
- peek()、poke()、后门访问模式下的read()write(),在零时刻方法调用返回后mv和dv也相应修改,不通过总线
- reset()和get_reset():硬件在复位触发后,会将内部reg复位;而rgm在捕捉到复位事件后也会复位,复位的对象时rgm,不是硬件,保持和硬件行为一致
@(negedge p_sequencer.vif.rstn);
rgm.reset();//reg块的mv和dv复位
rgm.chnl-_ctrl_reg.reset();//reg级别复位
rgm.chnl0_ctrl_reg.pkt_len.reset();//reg域的复位
- 复位后user可以通过读取rgm的复位值,与前门访问获取的reg复位值比较,以此判断硬件各个reg的复位值是否按照reg描述去实现
rstval=rgm.chnl0_ctrl_reg.get_reset();//rgm的复位值,不是硬件的
rgm.chnl0_ctrl_reg.read(status, data, UVM_BACKDOOR, .parent(this));
if(rstval !=data) `uvm_error()
- mirror()不会返回读回的数值,会修改对应的mv;在修改前user可以选择是否将读回的值与模型中的原mv进行比较,如下
rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_FRONTDOOR, .parent(this));
- 用set()和updata()对reg做批量修改
- set()的对象时rgm本身,修改dv
- 当reg的dv与mv不相同时,可以通过updata()来将不同reg通过前后门访问做全部修改
- 主要与write()和poke()相比
void'(rgm.chnl0_ctrl_reg.randomize());
rgm.chnl0_ctrl_reg.pkt_len.set('h3);
rgm.chnl0_ctrl_reg.updata(statusm, UVM_FRONTDOOR, .parent(this));
void'(rgm.chnl1_ctrl_reg.randomize());
rgm.chnl0_ctrl_reg.set('h22);//1 or 0 ?
rgm.updata(statusm, UVM_FRONTDOOR, .parent(this));
mem与reg的比较
- uvm的rgm也可以用来对存储建模,uvm_mem类可以模拟RW、RO、WO类型的存储且可以配置存储模型的数据宽度和地址范围
- 考虑到物理存储映射到uvm_mem会带来更大的资源消耗,uvm_mem不支持预测和影子存储shadow storage,没有镜像值和期望值
- uvm_mem可以利用自带的方法去访问硬件存储,利用存储模型访问硬件存储便于维护和复用;访问过程中可以利用模型的资质范围来测试硬件的地址范围是否全覆盖;
- uvm_mem提供前后门访问,存储测试可以考虑先后门访问预先加在存储内容,然后前门访问读取存储内容,进而做数据对比(传统的测试方式是利用系统函数或者仿真器实现存储加载)
- uvm_mem也有uvm_reg常规的访问方法,还有burst_read()和burst_write(),目的是更高速通过总线burst方式连续存储,贴合实际访问存储中的场景,对burst访问需要考虑:
- 目前挂载的总线UVC是否支持burst形式访问
- 这两个方法的参数列表中uvm_reg_data_t value[]采用的是数组形式,可以传递多个数据;在后台,这些数据首先需要装载到uvm_reg_item对象中,value数组可以直接写入,另外两个成员需要分别制定为element_kind=UVM_MEM,kind=UVM_BURST_READ
寄存器内建序列built_in sequence
uvm针对寄存器模型内建的seq,建议在验证项目一开始先做检查
class mcdf_example_seq extends uvm_reg_sequence;
mcdf_rgm rgm;
`uvm_object_utils()
`uvm_declare_p_sequencer()
...
task body();
uvm_status_e status;
uvm_reg_data_t data;
uvm_reg_hw_reset_seq reg_rst_seq=new();
uvm_reg_bit_bash_seq reg_bit_bas_seq=new();
uvm_reg_access_seq reg_acc_seq=new();
if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
`uvm_error() end
@(negedge p_sequencer.vif.rstn);
@(posedge p_sequencer.vif.rstn);
`uvm_info()
reg_rst_seq.model=rgm;
reg_rst_seq.start(m_sequencer);
`uvm_info()
`uvm_info()
reg_bit_bash_seq.model=rgm;
reg_bit_bash_seq.start(m_sequencer);
`uvm_info()
`uvm_info()
reg_acc_seq.model=rgm;
reg_acc_seq.start(m_sequencer);
`uvm_info()
endtask
endclass
应用场景
- 检查寄存器的方式
- 前门写,前门读。无法检查地址是否正确映射
- 前门写,后门读。前门write(),后门read()或peek()
- 后门写,前门读。后门write()或poke(),前门read()或peek()
- 对状态寄存器,peek()获取,再用mirror()方法从前门访问且与之前更新的镜像值比较
- 以上都已经在rgm的内建序列中实现
本文含有隐藏内容,请 开通VIP 后查看