日常记录(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

  

另外就是关于参数化类,以及代码重用机制。这部分更需要一个框架以后才理解。

 

上一篇:46.第十章 网络协议和管理配置 -- TCP/IP 协议栈和网络配置(七)


下一篇:【题目记录】——第 46 届 ICPC 国际大学生程序设计竞赛亚洲区域赛(上海)