在使用ral model时,会用到write 和read 操作,今天就讲一下~~
首先讲一下task write & read:
virtual task 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
write task 会调用do_write,其中path default value是UVM_FRONTDOOR,如果是UVM_BACKDOOR,在找不到对应的reg时,会使用FRONTDOOR访问该register。
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
uvm_reg_data_t msk;
int lsb;
foreach (m_fields[i]) begin
uvm_reg_field_cb_iter cbs = new(m_fields[i]);
uvm_reg_field f = m_fields[i];
lsb = f.get_lsb_pos();
msk = ((1<> lsb;
f.pre_write(rw);
for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next()) begin
rw.element = f;
rw.element_kind = UVM_FIELD;
cb.pre_write(rw);
end
value = (value & ~msk) | (rw.value[0] << lsb);
end
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
uvm_reg_data_t final_val;
uvm_reg_backdoor bkdr = get_backdoor();
value = rw.value[0];
// Mimick the final value after a physical read
rw.kind = UVM_READ;
if (bkdr != null)
bkdr.read(rw);
else
backdoor_read(rw);
if (rw.status == UVM_NOT_OK) begin
m_write_in_progress = 1'b0;
return;
end
begin
foreach (m_fields[i]) begin
uvm_reg_data_t field_val;
int lsb = m_fields[i].get_lsb_pos();
int sz = m_fields[i].get_n_bits();
field_val = m_fields[i].XpredictX((rw.value[0] >> lsb) & ((1<> lsb) & ((1<> f.get_lsb_pos()) & ((1<
uvm_reg中的do_write会调用uvm_reg_map中的do_write
task uvm_reg_map::do_write(uvm_reg_item rw);
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();//如果bus driver需要特定的item type,可在adapter中設定parent_sequence;否則是uvm_sequence_base 類型
assert($cast(seq,o));
seq.set_parent_sequence(rw.parent);
rw.parent = seq;
end
if (rw.parent == null)
rw.parent = new("default_parent_seq");
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
endtask
do_wtite 会调用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;
Xget_bus_infoX(rw, map_info, n_bits, lsb, skip);
`UVM_DA_TO_QUEUE(addrs,map_info.addr)
// if a memory, adjust addresses based on offset
if (rw.element_kind == UVM_MEM)
foreach (addrs[i])
addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset;
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
int temp_be;
int idx;
n_access_extra = lsb%(bus_width*8);
n_access = n_access_extra + n_bits;
temp_be = n_access_extra;
value = value << n_access_extra;
while(temp_be >= 8) begin
byte_en[idx++] = 0;
temp_be -= 8;
end
temp_be += n_bits;
while(temp_be > 0) begin
byte_en[idx++] = 1;
temp_be -= 8;
end
byte_en &= (1< (n_bits/(bus_width*8) + 1))
void'(addrs.pop_back());
end
foreach(addrs[i]) begin: foreach_addr
uvm_sequence_item bus_req;
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*8) ? bus_width*8 : n_bits;
rw_access.byte_en = byte_en;
adapter.m_set_item(rw);
bus_req = adapter.reg2bus(rw_access);//通过调用reg2bus,把reg item转化为要发送的bus item
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); //設定item的sequencer,這樣start_item才知道do給哪個sequencer
rw.parent.start_item(bus_req,rw.prior);//把bus item do 给sequencer
if (rw.parent != null && i == 0)
rw.parent.mid_do(rw);
rw.parent.finish_item(bus_req);//把bus item do 给sequencer
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...",
data, addrs[i], rw.map.get_full_name(), rw.status.name()), UVM_FULL)
if (rw.status == UVM_NOT_OK)
break;
curr_byte += bus_width;
n_bits -= bus_width * 8;
end: foreach_addr
foreach (addrs[i])
addrs[i] = addrs[i] + map_info.mem_range.stride;
end: foreach_value
endtask: do_bus_write
其中,provides_responses的作用介绍一下:
对于支持outstanding的bus agent,需要将provides_responses置为1,对于读操作ral model需要获得data,所以需要等response回过来才能采到数据。
task uvm_reg_map::do_bus_read (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 size, n_bits;
int skip;
int lsb;
int unsigned curr_byte;
int n_access_extra, n_access;
Xget_bus_infoX(rw, map_info, n_bits, lsb, skip);
`UVM_DA_TO_QUEUE(addrs,map_info.addr)
size = n_bits;
// if a memory, adjust addresses based on offset
if (rw.element_kind == UVM_MEM)
foreach (addrs[i])
addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset;
foreach (rw.value[val_idx]) begin: foreach_value
/* calculate byte_enables */
if (rw.element_kind == UVM_FIELD) begin
int temp_be;
int idx;
n_access_extra = lsb%(bus_width*8);
n_access = n_access_extra + n_bits;
temp_be = n_access_extra;
while(temp_be >= 8) begin
byte_en[idx++] = 0;
temp_be -= 8;
end
temp_be += n_bits;
while(temp_be > 0) begin
byte_en[idx++] = 1;
temp_be -= 8;
end
byte_en &= (1< (n_bits/(bus_width*8) + 1))
void'(addrs.pop_back());
end
rw.value[val_idx] = 0;
foreach (addrs[i]) begin
uvm_sequence_item bus_req;
uvm_reg_bus_op rw_access;
uvm_reg_data_logic_t data;
`uvm_info(get_type_name(),
$sformatf("Reading address 'h%0h via map \"%s\"...",
addrs[i], get_full_name()), UVM_FULL);
if (rw.element_kind == UVM_FIELD)
for (int z=0;z> (n_access_extra)) & ((1<
summary:
write --> do_write --> do_bus_write --> reg2bus --> do item --> wait response -->bus2reg
加入hdl path的方法
法1:
class reg_model extends uvm_reg_block;
rand reg_invert invert;
rand reg_counter_high counter_high;
rand reg_counter_low counter_low;
virtual function void build();
invert.configure(this,null,"invert");
counter_high.configure(this,null,"counter[31:16]");
counter_low.configure(this,null,"counter[15:0]");
endfunction
endclass
function void base_test::build_phase(uvm_phase phase);
rm = reg_model::type_id::create("rm",this);
rm.configure(null,"");
rm.build();
rm.lock_model();
rm.reset();
rm.set_hdl_path_root("top.my_dut");
endfunction
法2:
class reg_model extends uvm_reg_block;
rand reg_invert invert;
rand reg_counter_high counter_high;
rand reg_counter_low counter_low;
virtual function void build();
invert.configure(this,null,"");//第三个参数为空
counter_high.configure(this,null,"");
counter_low.configure(this,null,"");
add_hdl_path("top.my_dut");
invert.add_hdl_path_slice("invert",0,16);
//invert.add_hdl_path('{ '{"invert", -1, -1} });
counter_high.add_hdl_path_slice("count[31:16]",16,16);
counter_low.add_hdl_path_slice("count[15:0]",0,16);
endfunction
endclass
关于add_hdl_path
function void add_hdl_path ( uvm_hdl_path_slice slices[],
string kind = "RTL" )
Add an HDL path
Add the specified HDL path to the register instance for the specified design abstraction. This method may be called more than once for the same design abstraction if the register is physically duplicated in the design abstractild be specified using the following literal value
1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
Bits: 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+---+-------------+---+-------+
|A|xxx| B |xxx| C |
+-+---+-------------+---+-------+
add_hdl_path('{ '{"A_reg", 15, 1},
'{"B_reg", 6, 7},
'{'C_reg", 0, 4}
}
);
If the register is implementd using a single HDL variable, The array should specify a single slice with its offset and size specified as -1. For example:
r1.add_hdl_path('{ '{"r1", -1, -1} });
uvm_reg::peek()/poke()
这两个方法只针对后门访问。其不管DUT的行为,比如一个只读的寄存器进行写操作,其可以写进去。
class reg_seq extends uvm_sequence;
task body();
uvm_status_e status;
uvm_reg_data_t data;
p_sequencer.p_rm.counter_low.poke(status,16'hFFFD);
p_sequencer.p_rm.counter_low.peek(status,data);
endtask
endclass