日常记录(46)寄存器模型、工厂、回调
寄存器模型
前门访问
通过协议进行数据正规访问
定义seq
class reg_access_sequence extends uvm_sequence#(bus_transaction);
string tID = get_type_name();
bit[15:0] addr;
bit[15:0] rdata;
bit[15:0] wdata;
bit is_wr;
`uvm_object_utils(reg_access_sequence)
function new(string name = "reg_access_sequence");
super.new(name);
endfunction
virtual task body();
bus_transaction tr;
tr = new("tr");
tr.addr = this.addr;
tr.wr_data = this.wdata;
tr.bus_op = (is_wr ? BUS_WR : BUS_RD);
`uvm_info(tID, $sformatf("begin to access register: is_wr = %0d, addr = %0h", is_wr, addr), UVM_MEDIUM)
`uvm_send(tr)
`uvm_info(tID, "successfull access register", UVM_MEDIUM)
this.rdata = tr.rd_data;
endtask
endclass
在寄存器模型
main_phase中进行数据获取
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;
bus_sequencer p_sqr;
extern function new(string name, uvm_component parent);
extern function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
extern virtual function void invert_tr(my_transaction tr);
`uvm_component_utils(my_model)
endclass
function my_model::new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void my_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
endfunction
function void my_model::invert_tr(my_transaction tr);
tr.dmac = tr.dmac ^ 48'hFFFF_FFFF_FFFF;
tr.smac = tr.smac ^ 48'hFFFF_FFFF_FFFF;
tr.ether_type = tr.ether_type ^ 16'hFFFF;
tr.crc = tr.crc ^ 32'hFFFF_FFFF;
for(int i = 0; i < tr.pload.size; i++)
tr.pload[i] = tr.pload[i] ^ 8'hFF;
endfunction
task my_model::main_phase(uvm_phase phase);
my_transaction tr;
my_transaction new_tr;
reg_access_sequence reg_seq;
super.main_phase(phase);
reg_seq = new("reg_seq");
reg_seq.addr = 16'h9;
reg_seq.is_wr = 0;
reg_seq.start(p_sqr);
while(1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.copy(tr);
//`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
//new_tr.print();
if(reg_seq.rdata)
invert_tr(new_tr);
ap.write(new_tr);
end
endtask
后门手动数据访问
使用interface
interface backdoor_if(input clk, input rst_n);
function void poke_counter(input bit[31:0] value);
top_tb.my_dut.counter = value;
endfunction
function void peek_counter(output bit[31:0] value);
value = top_tb.my_dut.counter;
endfunction
endinterface
poke预置数据
在my_case0中声明并调用
class my_case0 extends base_test;
virtual backdoor_if vif;
function new(string name = "my_case0", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual task configure_phase(uvm_phase phase);
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.main_phase",
"default_sequence",
case0_vseq::type_id::get());
void'(uvm_config_db#(virtual backdoor_if)::get(this, "", "vif", vif));
endfunction
task my_case0::configure_phase(uvm_phase phase);
phase.raise_objection(this);
@(posedge vif.rst_n);
vif.poke_counter(32'hFFFD);
phase.drop_objection(this);
endtask
后门接口数据访问
定义好reg_block(寄存器模型)
其中包括了三个寄存器模型,然后分别定义了他们的地址和配置等。
class reg_invert extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_invert)
function new(input string name="reg_invert");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_counter_low extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 16, 0, "W1C", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_counter_low)
function new(input string name="reg_counter_low");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_counter_high extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 16, 0, "W1C", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_counter_high)
function new(input string name="reg_counter_high");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
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();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "invert");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
counter_high = reg_counter_high::type_id::create("counter_high", , get_full_name());
counter_high.configure(this, null, "counter[31:16]");
counter_high.build();
default_map.add_reg(counter_high, 'h5, "RW");
counter_low = reg_counter_low::type_id::create("counter_low", , get_full_name());
counter_low.configure(this, null, "counter[15:0]");
counter_low.build();
default_map.add_reg(counter_low, 'h6, "RW");
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
my_vsqr
class my_vsqr extends uvm_sequencer;
my_sequencer p_my_sqr;
bus_sequencer p_bus_sqr;
reg_model p_rm;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
`uvm_component_utils(my_vsqr)
endclass
然后在seq中(my_case0)
使用p_sequencer的方式调用内部uvm的peek和poke
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
bit[31:0] counter;
if(starting_phase != null)
starting_phase.raise_objection(this);
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("invert's initial value is %0h", value), UVM_LOW)
p_sequencer.p_rm.invert.write(status, 1, UVM_FRONTDOOR);
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("after set, invert's value is %0h", value), UVM_LOW)
p_sequencer.p_rm.counter_low.read(status, value, UVM_FRONTDOOR);
counter[15:0] = value[15:0];
p_sequencer.p_rm.counter_high.read(status, value, UVM_FRONTDOOR);
counter[31:16] = value[15:0];
`uvm_info("case0_cfg_vseq", $sformatf("counter's initial value(FRONTDOOR) is %0h", counter), UVM_LOW)
p_sequencer.p_rm.counter_low.poke(status, 16'hFFFD);
p_sequencer.p_rm.counter_low.read(status, value, UVM_FRONTDOOR);
counter[15:0] = value[15:0];
p_sequencer.p_rm.counter_high.read(status, value, UVM_FRONTDOOR);
counter[31:16] = value[15:0];
`uvm_info("case0_cfg_vseq", $sformatf("after poke, counter's value(FRONTDOOR) is %0h", counter), UVM_LOW)
p_sequencer.p_rm.counter_low.peek(status, value);
counter[15:0] = value[15:0];
p_sequencer.p_rm.counter_high.peek(status, value);
counter[31:16] = value[15:0];
`uvm_info("case0_cfg_vseq", $sformatf("after poke, counter's value(BACKDOOR) is %0h", counter), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
层次化的寄存器模型
可能较为通用,首先底层是reg,中层是结合不同reg的blk,上层是综合的blk
class reg_invert extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_invert)
function new(input string name="reg_invert");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_depth extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 16, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_depth)
function new(input string name="reg_depth");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_vlan extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 10, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_vlan)
function new(input string name="reg_vlan");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class global_blk extends uvm_reg_block;
rand reg_invert invert;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
endfunction
`uvm_object_utils(global_blk)
function new(input string name="global_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class buf_blk extends uvm_reg_block;
rand reg_depth depth;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
depth = reg_depth::type_id::create("depth", , get_full_name());
depth.configure(this, null, "");
depth.build();
default_map.add_reg(depth, 'h3, "RW");
endfunction
`uvm_object_utils(buf_blk)
function new(input string name="buf_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class mac_blk extends uvm_reg_block;
rand reg_vlan vlan;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
vlan = reg_vlan::type_id::create("vlan", , get_full_name());
vlan.configure(this, null, "");
vlan.build();
default_map.add_reg(vlan, 'h40, "RW");
endfunction
`uvm_object_utils(mac_blk)
function new(input string name="mac_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class reg_model extends uvm_reg_block;
rand global_blk gb_ins;
rand buf_blk bb_ins;
rand mac_blk mb_ins;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
gb_ins = global_blk::type_id::create("gb_ins");
gb_ins.configure(this, "");
gb_ins.build();
gb_ins.lock_model();
default_map.add_submap(gb_ins.default_map, 16'h0);
bb_ins = buf_blk::type_id::create("bb_ins");
bb_ins.configure(this, "");
bb_ins.build();
bb_ins.lock_model();
default_map.add_submap(bb_ins.default_map, 16'h1000);
mb_ins = mac_blk::type_id::create("mb_ins");
mb_ins.configure(this, "");
mb_ins.build();
mb_ins.lock_model();
default_map.add_submap(mb_ins.default_map, 16'h2000);
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
reg_file
管理blk。
reg_invert、reg_depth、reg_vlan、reg_regA、reg_regB是寄存器,
regfile是寄存器文件,global_blk包括reg_invert,buf_blk包括reg_depth,mac_blk包括两个regfile,
reg_regA,reg_regB,reg_vlan,并将regA和regB的configure过程使用对应的file取代
class reg_invert extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_invert)
function new(input string name="reg_invert");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_depth extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 16, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_depth)
function new(input string name="reg_depth");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_vlan extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 10, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_vlan)
function new(input string name="reg_vlan");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_regA extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 10, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_regA)
function new(input string name="reg_regA");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class reg_regB extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 10, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_regB)
function new(input string name="reg_regB");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
class regfile extends uvm_reg_file;
function new(string name = "regfile");
super.new(name);
endfunction
`uvm_object_utils(regfile)
endclass
class global_blk extends uvm_reg_block;
rand reg_invert invert;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "invert");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
endfunction
`uvm_object_utils(global_blk)
function new(input string name="global_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class buf_blk extends uvm_reg_block;
rand reg_depth depth;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
depth = reg_depth::type_id::create("depth", , get_full_name());
depth.configure(this, null, "depth");
depth.build();
default_map.add_reg(depth, 'h3, "RW");
endfunction
`uvm_object_utils(buf_blk)
function new(input string name="buf_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class mac_blk extends uvm_reg_block;
rand regfile file_a;
rand regfile file_b;
rand reg_regA regA;
rand reg_regB regB;
rand reg_vlan vlan;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
file_a = regfile::type_id::create("file_a", , get_full_name());
file_a.configure(this, null, "fileA");
file_b = regfile::type_id::create("file_b", , get_full_name());
file_b.configure(this, null, "fileB");
regA = reg_regA::type_id::create("regA", , get_full_name());
regA.configure(this, file_a, "regA");
regA.build();
default_map.add_reg(regA, 'h31, "RW");
regB = reg_regB::type_id::create("regB", , get_full_name());
regB.configure(this, file_b, "regB");
regB.build();
default_map.add_reg(regB, 'h32, "RW");
vlan = reg_vlan::type_id::create("vlan", , get_full_name());
vlan.configure(this, null, "vlan");
vlan.build();
default_map.add_reg(vlan, 'h40, "RW");
endfunction
`uvm_object_utils(mac_blk)
function new(input string name="mac_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
class reg_model extends uvm_reg_block;
rand global_blk gb_ins;
rand buf_blk bb_ins;
rand mac_blk mb_ins;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
gb_ins = global_blk::type_id::create("gb_ins");
gb_ins.configure(this, "global_reg");
gb_ins.build();
gb_ins.lock_model();
default_map.add_submap(gb_ins.default_map, 16'h0);
bb_ins = buf_blk::type_id::create("bb_ins");
bb_ins.configure(this, "buf_reg");
bb_ins.build();
bb_ins.lock_model();
default_map.add_submap(bb_ins.default_map, 16'h1000);
mb_ins = mac_blk::type_id::create("mb_ins");
mb_ins.configure(this, "mac_reg");
mb_ins.build();
mb_ins.lock_model();
default_map.add_submap(mb_ins.default_map, 16'h2000);
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
寄存器的不同域
在reg_model中,定义一个reg,其中3个field。
class three_field_reg extends uvm_reg;
rand uvm_reg_field fieldA;
rand uvm_reg_field fieldB;
rand uvm_reg_field fieldC;
virtual function void build();
fieldA = uvm_reg_field::type_id::create("fieldA");
fieldB = uvm_reg_field::type_id::create("fieldB");
fieldC = uvm_reg_field::type_id::create("fieldC");
endfunction
`uvm_object_utils(three_field_reg)
function new(input string name="three_field_reg");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
使用blk括起来,tf_reg,注意修改了基地址位和偏移位。
add_hdl_path_slice设置相对路径
class mac_blk extends uvm_reg_block;
rand reg_vlan vlan;
rand three_field_reg tf_reg;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
vlan = reg_vlan::type_id::create("vlan", , get_full_name());
vlan.configure(this, null, "vlan");
vlan.build();
default_map.add_reg(vlan, 'h40, "RW");
tf_reg = three_field_reg::type_id::create("tf_reg", , get_full_name());
tf_reg.configure(this, null, "");
tf_reg.build();
tf_reg.fieldA.configure(tf_reg, 2, 0, "RW", 1, 0, 1, 1, 1);
tf_reg.add_hdl_path_slice("fieldA", 0, 2);
tf_reg.fieldB.configure(tf_reg, 3, 2, "RW", 1, 0, 1, 1, 1);
tf_reg.add_hdl_path_slice("fieldA", 2, 3);
tf_reg.fieldC.configure(tf_reg, 4, 5, "RW", 1, 0, 1, 1, 1);
tf_reg.add_hdl_path_slice("fieldA", 5, 4);
default_map.add_reg(tf_reg, 'h41, "RW");
endfunction
`uvm_object_utils(mac_blk)
function new(input string name="mac_blk");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
寄存器占据多个地址位(数据宽度大于总线宽度)
这里的数据宽度32
class reg_counter extends uvm_reg;
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
// parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand, individually accessible
reg_data.configure(this, 32, 0, "W1C", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_counter)
function new(input string name="reg_counter");
//parameter: name, size, has_coverage
super.new(name, 32, UVM_NO_COVERAGE);
endfunction
endclass
default_map中定义了2byte,总线宽度16位,
class reg_model extends uvm_reg_block;
rand reg_invert invert;
rand reg_counter counter;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "invert");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
counter= reg_counter::type_id::create("counter", , get_full_name());
counter.configure(this, null, "counter");
counter.build();
default_map.add_reg(counter, 'h5, "RW");
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
在my_case0中,和原来读取方式相同(read、poke等)
存储器
和寄存器(uvm_reg)不同,为uvm_mem。在reg_model中进行了定义。
其中的default添加mem后,可用于前门访问(read、write)。不加入也具有后门访问(peek、poke)。
这里定义的1024个单元,每个单位宽度16。若定义512个单元长度为32,则最大偏移仍然为512,寄存器模型会默认读写两次。
class my_memory extends uvm_mem;
function new(string name="my_memory");
super.new(name, 1024, 16);
endfunction
`uvm_object_utils(my_memory)
endclass
class reg_model extends uvm_reg_block;
rand reg_invert invert;
rand reg_counter counter;
rand my_memory mm;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "invert");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
counter= reg_counter::type_id::create("counter", , get_full_name());
counter.configure(this, null, "counter");
counter.build();
default_map.add_reg(counter, 'h5, "RW");
mm = my_memory::type_id::create("mm", , get_full_name());
mm.configure(this, "stat_blk.ram1024x16_inst.array");
default_map.add_mem(mm, 'h100);
endfunction
`uvm_object_utils(reg_model)
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
期望值与镜像值
最大可能地和DUT保持同步,为DUT的镜像值。(获取性质)
期望值是DUT需要变成的值,使用set对期望值进行设置,使用update将期望值更新到DUT中。(写入性质)
以下是get和set用于期望值,镜像值不变。update将期望值不同于镜像值时,将期望值写入DUT,更新镜像值。
get_mirrored_value用于镜像值。peek从后面获取DUT实际值。read、write、peek、poke会更新期望值和镜像值,使得二者相等。
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
if(starting_phase != null)
starting_phase.raise_objection(this);
p_sequencer.p_rm.invert.read(status, value, UVM_FRONTDOOR);
p_sequencer.p_rm.invert.set(16'h1);
value = p_sequencer.p_rm.invert.get();
`uvm_info("case0_cfg_vseq", $sformatf("invert's desired value is %0h", value), UVM_LOW)
value = p_sequencer.p_rm.invert.get_mirrored_value();
`uvm_info("case0_cfg_vseq", $sformatf("invert's mirrored value is %0h", value), UVM_LOW)
p_sequencer.p_rm.invert.update(status, UVM_FRONTDOOR);
value = p_sequencer.p_rm.invert.get();
`uvm_info("case0_cfg_vseq", $sformatf("invert's desired value is %0h", value), UVM_LOW)
value = p_sequencer.p_rm.invert.get_mirrored_value();
`uvm_info("case0_cfg_vseq", $sformatf("invert's mirrored value is %0h", value), UVM_LOW)
p_sequencer.p_rm.invert.peek(status, value);
`uvm_info("case0_cfg_vseq", $sformatf("invert's actual value is %0h", value), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
检查后门访问路径
定义了uvm_reg_mem_hdl_paths_seq,然后设定了model为被测的寄存器模型。由于不需要sqr,因此start给null。
当无法读取,会给错误提示。
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
uvm_reg_mem_hdl_paths_seq ckseq;
if(starting_phase != null)
starting_phase.raise_objection(this);
ckseq = new("ckseq");
ckseq.model = p_sequencer.p_rm;
ckseq.start(null);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
检查寄存器模型复位值与DUT的默认值是否相同
DUT初始化后为默认值,寄存器初始化后为0,调用reset后为复位值。
DUT的数据是通过寄存器进行前后门访问获取的。
的uvm_reg_hw_reset_seq为检查过程。
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_reg_hw_reset_seq ckseq;
if(starting_phase != null)
starting_phase.raise_objection(this);
ckseq = new("ckseq");
ckseq.model = p_sequencer.p_rm;
ckseq.start(null);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
uvm_config_db从uvm_resource_db中派生,后者可以设置不检查的寄存器值(2选1)
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.configure_phase",
"default_sequence",
case0_cfg_vseq::type_id::get());
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.main_phase",
"default_sequence",
case0_vseq::type_id::get());
uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
"NO_REG_TESTS", 1, this);
uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
"NO_REG_HW_RESET_TEST", 1, this);
endfunction
检查读写功能
使用前门访问写,后门访问读。然后反之,检查读写功能。
uvm_reg_access_seq。等
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_reg_access_seq ckseq;
uvm_mem_access_seq ckseq2;
if(starting_phase != null)
starting_phase.raise_objection(this);
ckseq = new("ckseq");
ckseq.model = p_sequencer.p_rm;
ckseq.start(null);
ckseq2 = new("ckseq2");
ckseq2.model = p_sequencer.p_rm;
ckseq2.start(null);
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
使用uvm_resource_db可以跳过寄存器、存储器的检查(2选1,3选1)
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.configure_phase",
"default_sequence",
case0_cfg_vseq::type_id::get());
uvm_config_db#(uvm_object_wrapper)::set(this,
"v_sqr.main_phase",
"default_sequence",
case0_vseq::type_id::get());
//set for reg access sequence
uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
"NO_REG_TESTS", 1, this);
uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
"NO_REG_ACCESS_TEST", 1, this);
//set for mem access sequence
uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"},
"NO_REG_TESTS", 1, this);
uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"},
"NO_MEM_TESTS", 1, this);
uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
"NO_MEM_ACCESS_TEST", 1, this);
endfunction
预测器与自动预测
自动预测功能使用,
set_auto_predict,1打开,0关闭。用于driver读取数据返回后,更新期望值与镜像值。
function void base_test::connect_phase(uvm_phase phase); super.connect_phase(phase); v_sqr.p_my_sqr = env.i_agt.sqr; v_sqr.p_bus_sqr = env.bus_agt.sqr; v_sqr.p_rm = this.rm; rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter); rm.default_map.set_auto_predict(1); endfunction
也可手动定义预测器
定义预测器的同时也需要一个新的适配器。mon_reg_adapter。
然后连接1.default_map,2.adapter,3.bus_in。数据从agt到predictor到adapter到reg_model.
class base_test extends uvm_test;
my_env env;
my_vsqr v_sqr;
reg_model rm;
my_adapter reg_sqr_adapter;
my_adapter mon_reg_adapter;
uvm_reg_predictor#(bus_transaction) reg_predictor;
function new(string name = "base_test", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
`uvm_component_utils(base_test)
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
v_sqr = my_vsqr::type_id::create("v_sqr", this);
rm = reg_model::type_id::create("rm", this);
rm.configure(null, "");
rm.build();
rm.lock_model();
rm.reset();
reg_sqr_adapter = new("reg_sqr_adapter");
mon_reg_adapter = new("mon_reg_adapter");
reg_predictor = new("reg_predictor", this);
env.p_rm = this.rm;
endfunction
function void base_test::connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.p_my_sqr = env.i_agt.sqr;
v_sqr.p_bus_sqr = env.bus_agt.sqr;
v_sqr.p_rm = this.rm;
rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
rm.default_map.set_auto_predict(1);
reg_predictor.map = rm.default_map;
reg_predictor.adapter = mon_reg_adapter;
env.bus_agt.ap.connect(reg_predictor.bus_in);
endfunction
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
server = get_report_server();
err_num = server.get_severity_count(UVM_ERROR);
if (err_num != 0) begin
$display("TEST CASE FAILED");
end
else begin
$display("TEST CASE PASSED");
end
endfunction
DUT数据更新到寄存器模型
使用mirror函数
class case0_vseq extends uvm_sequence;
`uvm_object_utils(case0_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_vseq");
super.new(name);
endfunction
virtual task body();
case0_sequence dseq;
uvm_status_e status;
uvm_reg_data_t value;
if(starting_phase != null)
starting_phase.raise_objection(this);
#10000;
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);
p_sequencer.p_rm.counter.peek(status, value);
`uvm_info("case0_vseq", $sformatf("counter's value is %0h", value), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
人为更新寄存器模型镜像值,但不改变DUT值
task my_model::main_phase(uvm_phase phase);
my_transaction tr;
my_transaction new_tr;
uvm_status_e status;
uvm_reg_data_t value;
int length;
bit[31:0] counter;
super.main_phase(phase);
p_rm.invert.read(status, value, UVM_FRONTDOOR);
while(1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.copy(tr);
//`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
//new_tr.print();
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
获取顶层blk
uvm_reg_block::get_root_blocks
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
bit[31:0] counter;
uvm_reg_block blks[$];
reg_model p_rm;
if(starting_phase != null)
starting_phase.raise_objection(this);
uvm_reg_block::get_root_blocks(blks);
if(blks.size() == 0)
`uvm_fatal("case0_cfg_vseq", "can't find root blocks")
else begin
if(!$cast(p_rm, blks[0]))
`uvm_fatal("case0_cfg_vseq", "can't cast to reg_model")
end
p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("invert's initial value is %0h", value), UVM_LOW)
p_rm.invert.write(status, 1, UVM_FRONTDOOR);
p_rm.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("after set, invert's value is %0h", value), UVM_LOW)
p_rm.counter.read(status, value, UVM_FRONTDOOR);
counter = value;
`uvm_info("case0_cfg_vseq", $sformatf("counter's initial value(FRONTDOOR) is %0h", counter), UVM_LOW)
p_rm.counter.poke(status, 32'hFFFD);
p_rm.counter.read(status, value, UVM_FRONTDOOR);
counter = value;
`uvm_info("case0_cfg_vseq", $sformatf("after poke, counter's value(FRONTDOOR) is %0h", counter), UVM_LOW)
p_rm.counter.peek(status, value);
counter = value;
`uvm_info("case0_cfg_vseq", $sformatf("after poke, counter's value(BACKDOOR) is %0h", counter), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
通过地址获取指针
p_sequencer.p_rm.default_map.get_reg_by_offset(addr)
class case0_cfg_vseq extends uvm_sequence;
`uvm_object_utils(case0_cfg_vseq)
`uvm_declare_p_sequencer(my_vsqr)
function new(string name= "case0_cfg_vseq");
super.new(name);
endfunction
virtual task read_reg(input bit[15:0] addr, output bit[15:0] value);
uvm_status_e status;
uvm_reg target;
uvm_reg_data_t data;
uvm_reg_addr_t addrs[];
target = p_sequencer.p_rm.default_map.get_reg_by_offset(addr);
if(target == null)
`uvm_error("case0_cfg_vseq", $sformatf("can't find reg in register model with address: 'h%0h", addr))
target.read(status, data, UVM_FRONTDOOR);
void'(target.get_addresses(null,addrs));
if(addrs.size() == 1)
value = data[15:0];
else begin
int index;
for(int i = 0; i < addrs.size(); i++) begin
if(addrs[i] == addr) begin
data = data >> (16*(addrs.size() - i));
value = data[15:0];
break;
end
end
end
endtask
virtual task body();
uvm_status_e status;
uvm_reg_data_t value;
if(starting_phase != null)
starting_phase.raise_objection(this);
p_sequencer.p_rm.gb_ins.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("invert's initial value is %0h", value), UVM_LOW)
p_sequencer.p_rm.gb_ins.invert.write(status, 1, UVM_FRONTDOOR);
p_sequencer.p_rm.gb_ins.invert.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("after set, invert's value is %0h", value), UVM_LOW)
p_sequencer.p_rm.bb_ins.depth.read(status, value, UVM_FRONTDOOR);
`uvm_info("case0_cfg_vseq", $sformatf("not existed reg depth's read value is %0h", value), UVM_LOW)
read_reg('h0005, value);
`uvm_info("case0_cfg_vseq", $sformatf("'h0005's value is %0h", value), UVM_LOW)
read_reg('h0006, value);
`uvm_info("case0_cfg_vseq", $sformatf("'h0006's value is %0h", value), UVM_LOW)
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
endclass
Factory机制
重载
任务、函数、约束支持重载。而重载可通过factory实现。
factory的重载当检测到定义过的重载记录后, 使用新的类型代替旧的类型。
重载的类型
set_type_override_by_type,根据类型对全局类型进行重载。
function void my_case0::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
super.build_phase(phase);
set_type_override_by_type(bird::get_type(), parrot::get_type());
bird_inst = bird::type_id::create("bird_inst");
parrot_inst = parrot::type_id::create("parrot_inst");
print_hungry(bird_inst);
print_hungry(parrot_inst);
endfunction
set_inst_override_by_type限定了重载的范围
set_inst_override则使用字符串即可完成重载,同样set_type_override。
class my_case0 extends base_test;
function new(string name = "my_case0", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
//set_inst_override_by_type("env.o_agt.mon", my_monitor::get_type(), new_monitor::get_type());
//set_inst_override("env.o_agt.mon", "my_monitor", "new_monitor");
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
case0_sequence::type_id::get());
endfunction
以上4个函数用于component中,在top的initial中,使用另外不同的4个函数用于重载(名字部分重复,使用factory.进行调用)
set_type_override_by_type
set_type_override_by_name
set_inst_override_by_type
set_inst_override_by_name
连续重载、替换重载
连续,
需要父子关系
在连续重载的中间过程可以没有父子关系,但是无法实例化这些中间的类型,因为没有完整的父子关系。但是可以实例化开头的类型。头和尾之间必有父子关系。
function void my_case0::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
super.build_phase(phase);
set_type_override_by_type(bird::get_type(), parrot::get_type());
set_type_override_by_type(parrot::get_type(), big_parrot::get_type());
bird_inst = bird::type_id::create("bird_inst");
parrot_inst = parrot::type_id::create("parrot_inst");
print_hungry(bird_inst);
print_hungry(parrot_inst);
endfunction
替换,
后面替换前面的重载定义
function void my_case0::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
super.build_phase(phase);
set_type_override_by_type(bird::get_type(), parrot::get_type());
set_type_override_by_type(bird::get_type(), sparrow::get_type(), 0);
bird_inst = bird::type_id::create("bird_inst");
parrot_inst = parrot::type_id::create("parrot_inst");
print_hungry(bird_inst);
print_hungry(parrot_inst);
endfunctionz
输出重载信息
cmop的print_override_info与factory.debug_create_by_type效果相同。
另外的print为0,打印重载信息,为1打印增加用户定义到factory的类,为2增加了包括定义到factory的类。
print_topology则是打印系统top结构。(build实现的拓扑)
function void my_case0::connect_phase(uvm_phase phase);
super.connect_phase(phase);
//env.o_agt.mon.print_override_info("my_monitor");
//factory.debug_create_by_type(my_monitor::get_type(), "uvm_test_top.env.o_agt.mon");
//factory.print(2);
//uvm_top.print_topology();
uvm_top.print();
endfunction
回调机制
这里的回调往往是指代post,pre之类的自动执行函数。
定义回调类和池子
池子中实例化类的pre_tran会被自动调用(在do_callbacks中声明)
class A extends uvm_callback; virtual task pre_tran(my_driver drv, ref my_transaction tr); endtask endclass typedef uvm_callbacks#(my_driver, A) A_pool;
连接到drv
task my_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
`uvm_do_callbacks(my_driver, A, pre_tran(this, req))
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
在使用过程中,从A派生出callback,然后放入池子中。A需要重载callback
class my_callback extends A;
virtual task pre_tran(my_driver drv, ref my_transaction tr);
`uvm_info("my_callback", "this is pre_tran task", UVM_MEDIUM)
endtask
`uvm_object_utils(my_callback)
endclass
function void my_case0::connect_phase(uvm_phase phase);
my_callback my_cb;
super.connect_phase(phase);
my_cb = my_callback::type_id::create("my_cb");
A_pool::add(env.i_agt.drv, my_cb);
endfunction
回调继承
两处:1.继承后并使用set_super_type,2.do_callback过程使用父类,其它相同。
typedef class A;
class my_driver extends uvm_driver#(my_transaction);
virtual my_if vif;
`uvm_component_utils(my_driver)
`uvm_register_cb(my_driver, A)
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunction
extern task main_phase(uvm_phase phase);
extern task drive_one_pkt(my_transaction tr);
endclass
task my_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
`uvm_do_callbacks(my_driver, A, pre_tran(this, req))
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
task my_driver::drive_one_pkt(my_transaction tr);
byte unsigned data_q[];
int data_size;
data_size = tr.pack_bytes(data_q) / 8;
//`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
repeat(3) @(posedge vif.clk);
for ( int i = 0; i < data_size; i++ ) begin
@(posedge vif.clk);
vif.valid <= 1'b1;
vif.data <= data_q[i];
end
@(posedge vif.clk);
vif.valid <= 1'b0;
//`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask
class new_driver extends my_driver;
`uvm_component_utils(new_driver)
`uvm_set_super_type(new_driver, my_driver)
function new(string name = "new_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
extern task main_phase(uvm_phase phase);
endclass
task new_driver::main_phase(uvm_phase phase);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
while(1) begin
seq_item_port.get_next_item(req);
`uvm_info("new_driver", "this is new driver", UVM_MEDIUM)
`uvm_do_callbacks(my_driver, A, pre_tran(this, req))
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
另外就是关于参数化类,以及代码重用机制。这部分更需要一个框架以后才理解。
Le vent se lève! . . . il faut tenter de vivre!
Le vent se lève! . . . il faut tenter de vivre!

浙公网安备 33010602011771号