一、期望值、镜像值、实际值
寄存器模型中有两个值,分别为期望值和镜像值。DUT中的寄存器值称为实际值。
1.利用实际值更新另外两个值:有三种操作
①:使用[write&read任务通过前门访问or后门访问的方式]向DUT中的寄存器实际值写入&读取一个新值,寄存器模型中的期望值与镜像值会自动更新为新值。
②使用[peek&poke操作]向DUT中的寄存器实际值写入&读取一个新值,寄存器模型中的期望值与镜像值会自动更新为新值。
③:使用[mirror操作通过前门或后门方式]读取DUT中的实际值,并将该实际值更新到寄存器模型中的期望值与镜像值中。
此外mirror操作还可以设置第二个参数来开启或关闭check,若开启check,则当检测到实际值与镜像值不一致时,给出错误提示,并更新寄存器模型中的值;若关闭check则不给出错误提示,自动更新寄存器模型中的值。check功能一般在仿真过程中关闭,仿真即将结束时开启。
2.利用期望值更新另外两个值:有两个操作:
①:使用set&get更新或读取寄存器模型中的期望值,镜像值不变,然后调用updata任务,该任务会检查期望值与镜像值是否一致,若不一致,则将期望值更新到DUT中的实际值和寄存器模型中的镜像值。
②:使用randomise随机化寄存器模型中的期望值,镜像值不变,然后调用update任务,检查期望值与镜像值是否一致,若不一致,则将期望值更新到DUT中的实际值和寄存器模型中的镜像值。
注:对期望值的操作函数:set(value)、get()。对镜像值的操作:get_mirrored_value();
二、寄存器模型的预测
1.auto predict:自动预测
对于寄存器模型的读操作,之前采用的方式是通过读取driver返回的预测值(req或者rsp)来更新寄存器中的期望值与镜像值的,如下图的虚线所示。这种预测更新方式被称为寄存器模型的自动预测:auto predict。
使用自动预测的方法:通过在test层集成寄存器模型时打开该功能:rm.default_map.set_auto_predict(1);
可以看出自动预测是基于寄存器模型操作层面来自动记录每一次操作的数值的。假如一些sequence跳过了寄存器模型层面,而通过直接操作总线来修改寄存器的实际值,此时,寄存器模型中的值便不会自动更新,因为没有通过寄存器模型发起读写操作。这就造成了寄存器模型中的值与硬件实际值不一致的情况,所以自动预测不是很准确。
2.explicit predict:显示预测
针对自动预测无法监控寄存器模型操作层面以外的操作的问题,提出了另一种预测方式:显示预测。该种预测方式通过监测总线行为来预测并更新寄存器模型中的值 ,这需要在环境中添加一个preditor与配套的adapter。
使用显示预测的方法:
①:在test层面集成寄存器模型时,先定义一个preditor和一个adapter,这个adapter与之前已经定义好的adapter是一个类型,也就是说在环境中有两个同类型的adapter,一个用于自动预测路径,另一个用于显示预测路径。
my_adapter reg_sqr_adapter;//自动预测路径上的adapter
my_adapter mon_sqr_adapter;//显示预测路径上的adapter
uvm_reg_predictor#(bus_transaction) reg_predictor;//显示预测要用到的predictor
②:在test层的build函数中实例化adapter与predictor,注意predictor属于component,实例化时有两个参数
reg_aqr_adapter=new("reg_sqr_adapter");
mon_reg_adapter=new("mon_reg_adapetr");
reg_predictor=new("reg_predictor",this);
③:在test层的connect函数中将adapter与map的句柄也一并传递给predictor,同时将monitor采集的事务通过analysis port接入到predictor一侧。[注意此处的连接方式与自动预测路径中adapter与sequencer的连接方式不一样。]
这种集成关系可以使得,monitor一旦捕捉到有效事务,会发送给predictor,再由其利用adapter的桥接方法,实现事务信息转换,并将转换后的寄存器模型有关消息更新到map中。
//自动预测连接设置
rm.default_map.set_sequencer(env.bus_agt.aqr,reg_seq_adapter);
rm.default_map.set_auto_predict(1);
//显示预测连接设置
reg_predictor.default_map=rm.default_map;//只有设置了map后才能将寄存器模型与predictor关联到一起
reg_predictor.adapter=mon_reg_adapter;
env.bus_agt.ap.connect(reg_predictor.bus_in);//bus_in是一个默认的analysis import
至此,寄存器模型预测有两条通路。如果想要关闭自动预测,则将set_auto_predict(1)设置成set_auto_predict(0)即可。默认情况下系统采用显示预测方式。
三、predit操作:人为的修改寄存器模型中的值
从目前已掌握的知识来看,寄存器模型中的值要么通过读取DUT获得,要么通过set函数设置,然后更新到DUT寄存器中。
现在考虑这样一个场景,DUT中有一个计数器,在仿真过程中,DUT中的计数器不断累加,如果想在reference model中对该计数器的值进行验证,该怎么办?
因为要验证DUT这个计数器是否正确,所以reference model中的寄存器模型值就不能用DUT的值,也就是不通过读取DUT中的值来更新寄存器模型值,这个问题本质上是想人为的改变寄存器模型的值,且该值不影响DUT中寄存器的值。
uvm的predict操作就提供了人为修改寄存器模型中的值(包括期望值和镜像值),而不更新到DUT的功能。
function bit uvm_reg::predict (
uvm_reg_data_t value, //表示要预测的值
uvm_reg_byte_en_t be = -1, //默认为-1,表示全部有效
uvm_predict_e kind = UVM_PREDICT_DIRECT,//默认为该值,则预测值不影响DUT中的实际值
uvm_path_e path = UVM_FRONTDOOR, //可设置为前门访问或者后门访问
…);
该函数的具体应用:在reference model中人为的修改寄存器模型值,并更新到期望值和镜像值:
task my_model::main_phase(uvm_phase phase);
…
p_rm.invert.read(status, value, UVM_FRONTDOOR);
while(1) begin
port.get(tr);
…
if(value)
invert_tr(new_tr);
counter = p_rm.counter.get();
length = new_tr.pload.size() + 18;
counter = counter + length;
p_rm.counter.predict(counter);//将计数器的值更新到寄存器模型中
ap.write(new_tr);
end
endtask
在my_model中, 每得到一个新的transaction, 就先从寄存器模型中得到counter的期望值( 此时与镜像值一致) , 之后将新的transaction的长度加到counter中, 最后使用predict函数将新的counter值更新到寄存器模型中。 predict操作会更新镜像值和期望值。
在测试用例中, 仿真完成后可以检查DUT中counter的值是否与寄存器模型中的counter值一致:
class case0_vseq extends uvm_sequence;
…
virtual task body();
…
dseq = case0_sequence::type_id::create("dseq");
dseq.start(p_sequencer.p_my_sqr);
#100000;
p_sequencer.p_rm.counter.mirror(status, UVM_CHECK, UVM_FRONTDOOR);
…
endtask
endclass