寄存器模型
前门访问
通过协议进行数据正规访问
定义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
另外就是关于参数化类,以及代码重用机制。这部分更需要一个框架以后才理解。