1.1 randomize的影响
(1) randomize操作会改变register field的期望值(uvm_reg_field的post_randomize函数内,会将期望值设置为随机的结果),镜像值不会改变;
1 function void uvm_reg_field::post_randomize();
2 m_desired = value;
3 endfunction: post_randomize
(2) 如果在randomize后,跟着调用update,会将期望值写入DUT;
1.2 randomize的使用
(1) 在向uvm_reg中加入uvm_reg_field时,将加入的uvm_reg_field定义为rand类型;
(2) 在将uvm_reg加入到uvm_reg_block中时,将uvm_reg定义为rand类型;
(3) 如果要使某个uvm_reg_field能够随机化,只是将其定义为rand类型并不够; 在将uvm_reg_field加入uvm_reg时,要调用其configure函数并设置合适的参数;此外,uvm_reg_field还需是合适的类型,如RW, WRC, WRS等;
(4) 可以在uvm_reg_field, uvm_reg等中为randomize定义约束;
2. update
2.1 update操作的影响
(1) update操作会检查寄存器的期望值和镜像值是否一致(调用needs_update进行检查);
(2) 如果期望值(可以调用set()或者randomize()函数进行改变)和镜像值不同,调用update函数,期望值会通过write函数被配置到DUT中(可以指定使用FRONTDOOR还是BACKDOOR),同时镜像值也会更新;
1 my_reg1.myfield2.set(0x11);
2 my_reg1.myfield3.set(1);
3 my_reg1.update();
3. get & set
(1) 获取(get)或修改(set)期望值(不是硬件实际值);set操作会更新期望值,但是镜像值不会改变;
(2) 如果在set后,跟着调用update,会将期望值写入DUT;
4. mirror
4.1 mirror操作的影响
(1) mirror方法会调用read()函数,读取DUT(读取的方式可以选择BACKDOOR或者FRONTDOOR),然后根据read的返回值,更新镜像值和期望值;
(2) mirror方法的两种应用场景:
一是在仿真中不断的调用它,使得到整个寄存器模型的镜像值与DUT中寄存器的值保持一致,此时check选项是关闭的(即如果mirror操作发现DUT中寄存器的值和寄存器模型中的镜像值不一致,那么在更新寄存器模型之前不给出错误提示);
二是在仿真即将结束时,检查DUT中寄存器的值与寄存器模型中DUT的镜像值是否一致,这种情况下,check选项是打开的;
4.2 mirror源码
(1) mirror操作中会根据check参数的情况,来决定是否把mirror前的寄存器值保存下来; 如果设置了UVM_CHECK,后面会对mirror前后的寄存器值进行比较;
5. predict
5.1 predict操作的影响
(1) 可以实现人为地更新镜像值和期望值为要设置的值,但是同时又不对DUT进行任何操作;这种情况下,需要采用kind的默认值,即UVM_PREDICT_DIRECT;
5.2 predict操作的使用
(1) read/peek和write/poke操作对DUT完成读写后,也会调用此函数,只是它们给出的参数是UVM_PREDICT_READ和UVM_PREDICT_WRITE(使用这两个参数的区别? 详见uvm_reg_field的do_predict函数);
(2) 在显式预测中,predictor内会调用uvm_predict函数进行镜像值的更新;
6. reset
(1) reset方法将寄存器的期望值和镜像值设置为预定义的register reset值;
(2) 当硬件发生reset时,会将内部寄存器值复位,寄存器模型在捕捉到复位事件时,需要调用register model.reset保证硬件和register model的一致性;
(3) 硬件复位后,用户可以通过读取寄存器模型的复位值(与寄存器描述文件一致),与前门访问获取的寄存器复位值进行比较,以此判断硬件各个寄存器的复位值是否按照寄存器描述去实现。
7. get_reset
(1) get_reset方法返回register或者register field预定义的reset值;
(2) get_reset通常和read/mirror共同使用,用于检查register是否被正确的reset;
8.get_parent
9.get_mirrored_value
10. peek/poke (path=BACKDOOR)
(1) peek/poke同属于后门访问register/register field方式, 与backdoor read/write类似,但是peek/poke不会模拟寄存器的行为;
(2)如果对一个只读寄存器进行write操作,无论是BACKDOOR还是FRONTDOOR,都不能写进去,而对其进行poke操作(即写操作),能写进去;
(3) 如果对一个读清零的寄存器来说,进行read操作,无论是BACKDOOR还是FRONTDOOR, DUT中此寄存器的值在read操作之后都会变为0,而peek则会得到寄存器的值,但是DUT中寄存器的值依然保持不变;
1 regmodel.register.poke(status,value,.parent(this));
2 regmodel.register.peek(status,value,.parent(this));
(4) peek/poke操作完成后,寄存器模型会根据操作的结果更新期望值和镜像值(二者相等);
2. peek源码
(1) peek task内部会判断寄存器是否能进行BACKDOOR操作,比如是否设置该寄存器的hdl path;
(2) 主要是调用backdoor_rd task与do_predict(UVM_PREDICT_READ); do_predict用于更新register model中寄存器的相关值;
task uvm_reg::peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
$sformatf("No backdoor access available to peek register \"%s\"",
get_full_name()));
status = UVM_NOT_OK;
return;
end
if(!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("mem_peek_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.bd_kind = kind;
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
status = rw.status;
value = rw.value[0];
`uvm_info("RegModel", $sformatf("Peeked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH);
do_predict(rw, UVM_PREDICT_READ);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: peek
3. poke源码
(1) 主要是调用backdoor_wr task与do_predict(UVM_PREDICT_WRITE);
(2) 本质上采用的是deposit函数;
(3) poke与backdoor write的区别在于后者在调用backdoor_write操作前,先使用backdoor的read读出原来寄存器的值,把读出来的值以及要写入的数值通过调用uvm_reg_field的XpredictX,得到一个新的值,这个值其实就完全模拟了FRONTDOOR行为的一个值,之后再把这个值写入,而前者则是直接写入;
task uvm_reg::poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind = "",
input uvm_sequence_base parent = null,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
uvm_reg_backdoor bkdr = get_backdoor();
uvm_reg_item rw;
m_fname = fname;
m_lineno = lineno;
if (bkdr == null && !has_hdl_path(kind)) begin
`uvm_error("RegModel",
{"No backdoor access available to poke register '",get_full_name(),"'"})
status = UVM_NOT_OK;
return;
end
if (!m_is_locked_by_field)
XatomicX(1);
// create an abstract transaction for this operation
rw = uvm_reg_item::type_id::create("reg_poke_item",,get_full_name());
rw.element = this;
rw.path = UVM_BACKDOOR;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.bd_kind = kind;
rw.value[0] = value & ((1 << m_n_bits)-1);
rw.parent = parent;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
if (bkdr != null)
bkdr.write(rw);
else
backdoor_write(rw);
status = rw.status;
`uvm_info("RegModel", $sformatf("Poked register \"%s\": 'h%h",
get_full_name(), value),UVM_HIGH);
do_predict(rw, UVM_PREDICT_WRITE);
if (!m_is_locked_by_field)
XatomicX(0);
endtask: poke
11. read/write (frontdoor)
1 regmodel.register.read(status,value,UVM_FRONTDOOR,.parent(this)); 2 regmodel.register.write(status,value,UVM_FRONTDOOR,.parent(this));
11.1 read/write (frontdoor)操作的影响
(1) 当使用front-door(path=BFM), 一个或多个实际的transaction会发往DUT进行register的读写;
(2) 无论通过前门访问还是后门访问的方式从DUT中读取或写入寄存器的值,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(其实是有前提条件的,对于后门访问,会自动更新镜像值和期望值; 对于前门访问,更新镜像值和期望值有两种方法: (a) auto_predict功能需要打开,即在env中调用uvm_reg_map.set_auto_predict(1),然后调用do-predict函数更新寄存器模型的mirror值; (b)关闭auto_predict功能,使用uvm_reg_predictor进行更新);
(3) 如果寄存器map到多个uvm_address_map,在调用uvm_reg的write/read task进行前门访问时,需要指定uvm_reg_map参数;
(1) uvm_reg::write
(1.1) FRONTDOOR write操作最终会转换为uvm_reg_map的do_write任务;
(1.2) uvm_reg_map的do_write任务会查看系统是否设置了adapter,如果没有设置,就直接启动sequence, 让sequencer发送uvm_reg_item类型的transaction;如果设置了,那就调用do_bus_write任务.
(1.3) uvm_reg_map的do_write完成后,如果auto predict功能打开了, uvm_reg的do_write会根据写入的值更新register model中寄存器的值;
(2) uvm_reg::do_write
(3) uvm_reg_map.do_write
(3.1) 24到26行把要写的item通过sequencer发送出去,27行调用rw.end_event的wait_on;
(3.2) sequence.finish_item task中会调用sequencer.end_tr,而sequencer.end_tr会在driver调用item_done后结束;
(3.3) sequencer.end_tr内会触发end_event事件;
12. read/write (backdoor)
(1) 当使用back-door(path=BACKDOOR)时, 会通过uvm_hdl_read/uvm_hdl_deposit函数获取或修改DUT register值,而不会通过物理上的interface;
(2) 但是back-door的访问方式会尽量模拟前门访问的行为,比如如果对一个只读寄存器进行后门写操作,由于要模拟DUT的只读行为,所以是写不进去的;
(3) 无论通过前门访问还是后门访问的方式,从DUT中读取或写入寄存器的值,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(对于前门访问而言,前提条件是system_map.get_auto_predict为1);
寄存器模型有期望值和镜像值,镜像值是用于和DUT保持同步;镜像值是改变DUT中寄存器值的一个变量。可以直接调用write()写入dut;也可以通过set()更新期望值,然后通过update()将期望值写到DUT,最后更新镜像值。
update是将寄存器的期望值与镜像值作对比,不同就将期望值写进dut并更新镜像值;
mirror是将dut的值读出与寄存器模型的值比较并更新寄存器模型的mirror值;
set()会更新期望值,不会更新镜像值;
get()会返回寄存器模型中当前寄存器的期望值;