task uvm_reg::write(output uvm_status_e status....)
写操作通过set函数将期望值设置为value
随后将write函数传入的参数抽象成uvm_reg_item类
调用do_write函数
调用Xcheck_accessX,如果存在后门路径,则为rw.map设置为uvm_reg_map::backdoor();,如果找不到后门路径则用前门替代,get_backdoor返回m_backdoor(应该是用来存放后门路径的变量),m_backdoor为null则逐层向上找blk的后门路径
m_regfile_parent在configure来配置,我们环境里配置为null
has_hdl_path还没仔细看,下次补充
if (rw.path == UVM_BACKDOOR) begin
if (get_backdoor() == null && !has_hdl_path()) begin
`uvm_warning("RegModel",
{"No backdoor access available for register '",get_full_name(),
"' . Using frontdoor instead."})
rw.path = UVM_FRONTDOOR;
end
else
rw.map = uvm_reg_map::backdoor();
end
如果不是后门访问,uvm_reg的m_maps代表该reg在哪些map中add了,在map的add_reg时同时也调用了reg的add_map,填充了m_maps数组。
该函数会返回get_default_map,获得m_maps中索引
rw.local_map = get_local_map(rw.map,caller);
function uvm_reg_map uvm_reg::get_local_map(uvm_reg_map map, string caller="");
if (map == null)
return get_default_map();
if (m_maps.exists(map))
return map;
foreach (m_maps[l]) begin
uvm_reg_map local_map=l;
uvm_reg_map parent_map = local_map.get_parent_map();
while (parent_map != null) begin
if (parent_map == map)
return local_map;
parent_map = parent_map.get_parent_map();
end
end
`uvm_warning("RegModel",
{"Register '",get_full_name(),"' is not contained within map '",map.get_full_name(),"'",
(caller == "" ? "": {" (called from ",caller,")"}) })
return null;
endfunction
获取map_info,在map中add_reg的同时会将该reg的信息譬如offset,access等记录在m_regs_info关联数组中,返回该result,
map_info = rw.local_map.get_reg_map_info(this);
function uvm_reg_map_info uvm_reg_map::get_reg_map_info(uvm_reg rg, bit error=1);
uvm_reg_map_info result;
if (!m_regs_info.exists(rg)) begin
if (error)
`uvm_error("REG_NO_MAP",{"Register '",rg.get_name(),"' not in map '",get_name(),"'"})
return null;
end
result = m_regs_info[rg];
if(!result.is_initialized)
`uvm_warning("RegModel",{"map '",get_name(),"' does not seem to be initialized correctly, check that the top register model is locked()"})
return result;
endfunction
在添加reg info时 frontdoor并没有声明但是unmapped声明为0,为什么呢?unmapped含义?
// If ~unmapped~ is TRUE, the register does not occupy any
// physical addresses and the base address is ignored.
// Unmapped registers require a user-defined ~frontdoor~ to be specified.
if (map_info.frontdoor == null && map_info.unmapped) begin
`uvm_error("RegModel", {"Register '",get_full_name(),
"' unmapped in map '",
(rw.map==null)? rw.local_map.get_full_name():rw.map.get_full_name(),
"' and does not have a user-defined frontdoor"})
rw.status = UVM_NOT_OK;
return 0;
end
前门写
map_info的frontdoor环境中没有设置,所以不会使用USER FRONTDOOR
调用local_map的do_write方法
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
adapter映射到最顶层的map
adapter.parent_sequence没看懂,以后补充
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
最后do_bus_write
task uvm_reg_map::do_bus_write (uvm_reg_item rw,
uvm_sequencer_base sequencer,
uvm_reg_adapter adapter);
uvm_reg_addr_t addrs[$];
uvm_reg_map system_map = get_root_map();
int unsigned bus_width = get_n_bytes();
uvm_reg_byte_en_t byte_en = -1;
uvm_reg_map_info map_info;
int n_bits;
int lsb;
int skip;
int unsigned curr_byte;
int n_access_extra, n_access;
int n_bits_init;
uvm_reg_bus_op accesses[$];
Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip);
addrs=map_info.addr;
// if a memory, adjust addresses based on offset
if (rw.element_kind == UVM_MEM)
foreach (rw.value[val_idx]) begin: foreach_value
uvm_reg_data_t value = rw.value[val_idx];
/* calculate byte_enables */
if (rw.element_kind == UVM_FIELD) begin
。。。。。。。。。。。
end
curr_byte=0;
n_bits= n_bits_init;
accesses.delete();
foreach(addrs[i]) begin: foreach_addr
uvm_reg_bus_op rw_access;
uvm_reg_data_t data;
data = (value >> (curr_byte*8)) & ((1'b1 << (bus_width * 8))-1);
`uvm_info(get_type_name(),
$sformatf("Writing 'h%0h at 'h%0h via map \"%s\"...",
data, addrs[i], rw.map.get_full_name()), UVM_FULL);
if (rw.element_kind == UVM_FIELD) begin
for (int z=0;z<bus_width;z++)
rw_access.byte_en[z] = byte_en[curr_byte+z];
end
rw_access.kind = rw.kind;
rw_access.addr = addrs[i];
rw_access.data = data;
rw_access.n_bits = (n_bits > bus_width*8) ? bus_width*8 : n_bits;
rw_access.byte_en = byte_en;
accesses.push_back(rw_access);
curr_byte += bus_width;
n_bits -= bus_width * 8;
end: foreach_addr
// if set utilizy the order policy
if(policy!=null)
policy.order(accesses);
// perform accesses
foreach(accesses[i]) begin
uvm_reg_bus_op rw_access=accesses[i];
uvm_sequence_item bus_req;
adapter.m_set_item(rw);
bus_req = adapter.reg2bus(rw_access);
adapter.m_set_item(null);
if (bus_req == null)
`uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"});
bus_req.set_sequencer(sequencer);
rw.parent.start_item(bus_req,rw.prior);
if (rw.parent != null && i == 0)
rw.parent.mid_do(rw);
rw.parent.finish_item(bus_req);
bus_req.end_event.wait_on();
if (adapter.provides_responses) begin
uvm_sequence_item bus_rsp;
uvm_access_e op;
// TODO: need to test for right trans type, if not put back in q
rw.parent.get_base_response(bus_rsp);
adapter.bus2reg(bus_rsp,rw_access);
end
else begin
adapter.bus2reg(bus_req,rw_access);
end
if (rw.parent != null && i == addrs.size()-1)
rw.parent.post_do(rw);
rw.status = rw_access.status;
`uvm_info(get_type_name(),
$sformatf("Wrote 'h%0h at 'h%0h via map \"%s\": %s...",
rw_access.data, addrs[i], rw.map.get_full_name(), rw.status.name()), UVM_FULL)
if (rw.status == UVM_NOT_OK)
break;
end
foreach (addrs[i])
addrs[i] = addrs[i] + map_info.mem_range.stride;
end: foreach_value
endtask: do_bus_write
Xget_bus_infoX输出reg的bits,map_info
function void uvm_reg_map::Xget_bus_infoX(uvm_reg_item rw,
output uvm_reg_map_info map_info,
output int size,
output int lsb,
output int addr_skip);
if (rw.element_kind == UVM_MEM) begin
uvm_mem mem;
if(rw.element == null || !$cast(mem,rw.element))
`uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_MEM, ",
"but 'element' does not point to a memory: ",rw.get_name()})
map_info = get_mem_map_info(mem);
size = mem.get_n_bits();
end
else if (rw.element_kind == UVM_REG) begin
uvm_reg rg;
if(rw.element == null || !$cast(rg,rw.element))
`uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_REG, ",
"but 'element' does not point to a register: ",rw.get_name()})
map_info = get_reg_map_info(rg);
size = rg.get_n_bits();
end
else if (rw.element_kind == UVM_FIELD) begin
uvm_reg_field field;
if(rw.element == null || !$cast(field,rw.element))
`uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_FIELD, ",
"but 'element' does not point to a field: ",rw.get_name()})
map_info = get_reg_map_info(field.get_parent());
size = field.get_n_bits();
lsb = field.get_lsb_pos();
addr_skip = lsb/(get_n_bytes()*8);
end
endfunction