reg model使用篇-uvm_reg常用操作part3(XatomicX/read/write(frontdoor/backdoor))
1. XatomicX
(1) 当要访问互斥的资源时,即只允许一个进程访问,其他进程必须在此进行访问完成之后才能访问,可以使用XatomicX(1) + access the resource +XatomicX(0)实现;
(2) read, write, poke, peek, mirror等操作中,都用类似的方法保证同一时间只有一个进程对互斥资源进行访问;
注1: register model可以由多个线程并行访问,但是register model内部会串行处理这些进程,这样做是为了确保当同一个register field被访问时,其隐式更新的镜像值可预测;
注2: 每一个register内都有一个旗语m_atomic来确保它同时只能被一个进程访问; 如果一个线程在执行过程中,被显式杀掉, 有必要调用uvm_reg::reset()释放掉它所占用的key;
注3:在实际仿真过程中,发现采用寄存器后门读一个uvm_reg_field时,没有立即返回读取值,而是隔了很久,就是因为在XatomicX中获取旗语时hang住;hang住的原因是在另一处调用了前门操作对该uvm_reg_field所在uvm_reg的另一个uvm_reg_field进行写操作;
local semaphore m_atomic;
local process m_process;
function void uvm_reg::reset(string kind = "HARD");
foreach (m_fields[i])
m_fields[i].reset(kind);
// Put back a key in the semaphore if it is checked out
// in case a thread was killed during an operation
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
Xset_busyX(0);
endfunction: reset
task uvm_reg::XatomicX(bit on);
process m_reg_process;
m_reg_process=process::self();
if (on) begin
if (m_reg_process == m_process)
return;
m_atomic.get(1);
m_process = m_reg_process;
end
else begin
// Maybe a key was put back in by a spurious call to reset()
void'(m_atomic.try_get(1));
m_atomic.put(1);
m_process = null;
end
endtask: XatomicX
2. read/write (frontdoor)

regmodel.register.read(status,value,UVM_FRONTDOOR,.parent(this));
regmodel.register.write(status,value,UVM_FRONTDOOR,.parent(this));
2.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参数;
2.2 write (frontdoor)的源码

(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中寄存器的值;
task uvm_reg::write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
XatomicX(1);
set(value);
rw = uvm_reg_item::type_id::create("write_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_WRITE;
rw.value[0] = value;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_write(rw);
status = rw.status;
XatomicX(0);
endtask
(2) uvm_reg::do_write
task uvm_reg::do_write (uvm_reg_item rw);
uvm_reg_cb_iter cbs = new(this);
uvm_reg_map_info map_info;
uvm_reg_data_t value;
m_fname = rw.fname;
m_lineno = rw.lineno;
if (!Xcheck_accessX(rw,map_info,"write()"))
return;
XatomicX(1);
m_write_in_progress = 1'b1;
rw.value[0] &= ((1 << m_n_bits)-1);
value = rw.value[0];
rw.status = UVM_IS_OK;
// PRE-WRITE CBS - FIELDS
begin : pre_write_callbacks
...
end
rw.element = this;
rw.element_kind = UVM_REG;
rw.value[0] = value;
// PRE-WRITE CBS - REG
pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next())
cb.pre_write(rw);
if (rw.status != UVM_IS_OK) begin
m_write_in_progress = 1'b0;
XatomicX(0);
return;
end
// EXECUTE WRITE...
case (rw.path)
// ...VIA USER BACKDOOR
UVM_BACKDOOR: begin
...
end
UVM_FRONTDOOR: begin
uvm_reg_map system_map = rw.local_map.get_root_map();
m_is_busy = 1;
// ...VIA USER FRONTDOOR
if (map_info.frontdoor != null) begin
uvm_reg_frontdoor fd = map_info.frontdoor;
fd.rw_info = rw;
if (fd.sequencer == null)
fd.sequencer = system_map.get_sequencer();
fd.start(fd.sequencer, rw.parent);
end
// ...VIA BUILT-IN FRONTDOOR
else begin : built_in_frontdoor
rw.local_map.do_write(rw);
end
m_is_busy = 0;
if (system_map.get_auto_predict()) begin
uvm_status_e status;
if (rw.status != UVM_NOT_OK) begin
sample(value, -1, 0, rw.map);
m_parent.XsampleX(map_info.offset, 0, rw.map);
end
status = rw.status; // do_predict will override rw.status, so we save it here
do_predict(rw, UVM_PREDICT_WRITE);
rw.status = status;
end
end
endcase
...
XatomicX(0);
endtask: 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事件;
task uvm_reg_map::do_write(uvm_reg_item rw);
uvm_sequence_base tmp_parent_seq;
uvm_reg_map system_map = get_root_map();
uvm_reg_adapter adapter = system_map.get_adapter();
uvm_sequencer_base sequencer = system_map.get_sequencer();
if (adapter != null && adapter.parent_sequence != null) begin
uvm_object o;
uvm_sequence_base seq;
o = adapter.parent_sequence.clone();
assert($cast(seq,o));
seq.set_parent_sequence(rw.parent);
rw.parent = seq;
tmp_parent_seq = seq;
end
if (rw.parent == null) begin
rw.parent = new("default_parent_seq");
tmp_parent_seq = rw.parent;
end
if (adapter == null) begin
rw.set_sequencer(sequencer);
rw.parent.start_item(rw,rw.prior);
rw.parent.finish_item(rw);
rw.end_event.wait_on();
end
else begin
do_bus_write(rw, sequencer, adapter);
end
if (tmp_parent_seq != null)
sequencer.m_sequence_exiting(tmp_parent_seq);
endtask
2.3 read (frontdoor)源码

task uvm_reg::read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
XatomicX(1);
XreadX(status, value, path, map, parent, prior, extension, fname, lineno);
XatomicX(0);
endtask: read
task uvm_reg::XreadX(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path,
input uvm_reg_map map,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
// create an abstract transaction for this operation
uvm_reg_item rw;
rw = uvm_reg_item::type_id::create("read_item",,get_full_name());
rw.element = this;
rw.element_kind = UVM_REG;
rw.kind = UVM_READ;
rw.value[0] = 0;
rw.path = path;
rw.map = map;
rw.parent = parent;
rw.prior = prior;
rw.extension = extension;
rw.fname = fname;
rw.lineno = lineno;
do_read(rw);
status = rw.status;
value = rw.value[0];
endtask: XreadX
3. 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);

浙公网安备 33010602011771号