UVM_RAL 理解

1. 寄存器信息描述

 2. module 定义

module traffic (  input          pclk,
                  input          presetn,
                  input [31:0]   paddr,
                  input [31:0]   pwdata,
                  input          psel,
                  input          pwrite,
                  input          penable,
 
                  // Outputs
                  output [31:0]  prdata);
 
   reg [3:0]      ctl_reg;    // profile, blink_red, blink_yellow, mod_en RW
   reg [1:0]      stat_reg;   // state[1:0] 
   reg [31:0]     timer_0;    // timer_g2y[31:20], timer_r2g[19:8], timer_y2r[7:0] RW
   reg [31:0]     timer_1;    // timer_g2y[31:20], timer_r2g[19:8], timer_y2r[7:0] RW
 
   reg [31:0]     data_in;
   reg [31:0]     rdata_tmp;
 
   // Set all registers to default values
   always @ (posedge pclk) begin
      if (!presetn) begin
         data_in <= 0;
         ctl_reg  <= 0; 
         stat_reg <= 0; 
         timer_0  <= 32'hcafe_1234; 
         timer_1  <= 32'hface_5678;
      end
   end
 
   // Capture write data
   always @ (posedge pclk) begin
      if (presetn & psel & penable) 
         if (pwrite) 
            case (paddr)
               'h0   : ctl_reg <= pwdata;
               'h4   : timer_0 <= pwdata;
               'h8   : timer_1 <= pwdata;
               'hc   : stat_reg <= pwdata;
            endcase
   end
 
   // Provide read data
   always @ (penable) begin
      if (psel & !pwrite) 
         case (paddr)
            'h0 : rdata_tmp <= ctl_reg;
            'h4 : rdata_tmp <= timer_0;
            'h8 : rdata_tmp <= timer_1;
            'hc : rdata_tmp <= stat_reg;
         endcase
   end
 
   assign prdata = (psel & penable & !pwrite) ? rdata_tmp : 'hz;
 
endmodule

3. register model 

// Register definition for the register called "ctl"
class ral_cfg_ctl extends uvm_reg;
  rand uvm_reg_field mod_en;      // Enables the module
  rand uvm_reg_field bl_yellow;   // Blinks yellow
  rand uvm_reg_field bl_red;      // Blinks red
  rand uvm_reg_field profile;     // 1 : Peak, 0 : Off-Peak
 
  `uvm_object_utils(ral_cfg_ctl)
 
  function new(string name = "traffic_cfg_ctrl");
    super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
  endfunction: new
 
  // Build all register field objects
  virtual function void build();
    this.mod_en     = uvm_reg_field::type_id::create("mod_en",,   get_full_name());
    this.bl_yellow  = uvm_reg_field::type_id::create("bl_yellow",,get_full_name());
    this.bl_red     = uvm_reg_field::type_id::create("bl_red",,   get_full_name());
    this.profile    = uvm_reg_field::type_id::create("profile",,  get_full_name());
 
    // configure(parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible); 
    this.mod_en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 0, 0);
    this.bl_yellow.configure(this, 1, 1, "RW", 0, 1'h0, 1, 0, 0);
    this.bl_red.configure(this, 1, 2, "RW", 0, 1'h0, 1, 0, 0);
    this.profile.configure(this, 1, 3, "RW", 0, 1'h0, 1, 0, 0);
  endfunction
endclass 
 
// Register definition for the register called "stat"
class ral_cfg_stat extends uvm_reg;
  uvm_reg_field state;    // Current state of the design
 
  `uvm_object_utils(ral_cfg_stat)
  function new(string name = "ral_cfg_stat");
    super.new(name, 32, build_coverage(UVM_NO_COVERAGE));
  endfunction
 
  virtual function void build();
    this.state = uvm_reg_field::type_id::create("state",, get_full_name());
 
    // configure(parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible); 
    this.state.configure(this, 2, 0, "RO", 0, 1'h0, 0, 0, 0);
  endfunction
endclass
 
// Register definition for the register called "timer"
class ral_cfg_timer extends uvm_reg;
  uvm_reg_field timer;     // Time for which it blinks
 
  `uvm_object_utils(ral_cfg_timer)
  function new(string name = "traffic_cfg_timer");
    super.new(name, 32,build_coverage(UVM_NO_COVERAGE));
  endfunction
 
  virtual function void build();
     this.timer = uvm_reg_field::type_id::create("timer",,get_full_name());
 
    // configure(parent, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible); 
     this.timer.configure(this, 32, 0, "RW", 0, 32'hCAFE1234, 1, 0, 1);
     this.timer.set_reset('h0, "SOFT");
  endfunction
endclass
 
// These registers are grouped together to form a register block called "cfg"
class ral_block_traffic_cfg extends uvm_reg_block;
  rand ral_cfg_ctl    ctrl;       // RW
  rand ral_cfg_timer  timer[2];   // RW
         ral_cfg_stat   stat;       // RO
 
  `uvm_object_utils(ral_block_traffic_cfg)
 
  function new(string name = "traffic_cfg");
    super.new(name, build_coverage(UVM_NO_COVERAGE));
  endfunction
 
  virtual function void build();
    this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
    this.ctrl = ral_cfg_ctl::type_id::create("ctrl",,get_full_name());
    this.ctrl.configure(this, null, "");
    this.ctrl.build();
    this.default_map.add_reg(this.ctrl, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);
 
 
    this.timer[0] = ral_cfg_timer::type_id::create("timer[0]",,get_full_name());
    this.timer[0].configure(this, null, "");
    this.timer[0].build();
    this.default_map.add_reg(this.timer[0], `UVM_REG_ADDR_WIDTH'h4, "RW", 0);
 
    this.timer[1] = ral_cfg_timer::type_id::create("timer[1]",,get_full_name());
    this.timer[1].configure(this, null, "");
    this.timer[1].build();
    this.default_map.add_reg(this.timer[1], `UVM_REG_ADDR_WIDTH'h8, "RW", 0);
 
    this.stat = ral_cfg_stat::type_id::create("stat",,get_full_name());
    this.stat.configure(this, null, "");
    this.stat.build();
    this.default_map.add_reg(this.stat, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
  endfunction 
endclass 
 
// The register block is placed in the top level model class definition
class ral_sys_traffic extends uvm_reg_block;
  rand ral_block_traffic_cfg cfg;
 
  `uvm_object_utils(ral_sys_traffic)
  function new(string name = "traffic");
    super.new(name);
  endfunction
 
  function void build();
      this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
      this.cfg = ral_block_traffic_cfg::type_id::create("cfg",,get_full_name());
      this.cfg.configure(this, "tb_top.pB0");
      this.cfg.build();
      this.default_map.add_submap(this.cfg.default_map, `UVM_REG_ADDR_WIDTH'h0);
  endfunction
endclass
 

4. register env

class reg2apb_adapter extends uvm_reg_adapter;
   `uvm_object_utils (reg2apb_adapter)
 
   function new (string name = "reg2apb_adapter");
      super.new (name);
   endfunction
 
   virtual function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
      bus_pkt pkt = bus_pkt::type_id::create ("pkt");
      pkt.write = (rw.kind == UVM_WRITE) ? 1: 0;
      pkt.addr  = rw.addr;
      pkt.data  = rw.data;
      `uvm_info ("adapter", $sformatf ("reg2bus addr=0x%0h data=0x%0h kind=%s", pkt.addr, pkt.data, rw.kind.name), UVM_DEBUG) 
      return pkt; 
   endfunction
 
   virtual function void bus2reg (uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
      bus_pkt pkt;
      if (! $cast (pkt, bus_item)) begin
         `uvm_fatal ("reg2apb_adapter", "Failed to cast bus_item to pkt")
      end
 
      rw.kind = pkt.write ? UVM_WRITE : UVM_READ;
      rw.addr = pkt.addr;
      rw.data = pkt.data;
      `uvm_info ("adapter", $sformatf("bus2reg : addr=0x%0h data=0x%0h kind=%s status=%s", rw.addr, rw.data, rw.kind.name(), rw.status.name()), UVM_DEBUG)
   endfunction
endclass
 
// Register environment class puts together the model, adapter and the predictor
class reg_env extends uvm_env;
   `uvm_component_utils (reg_env)
   function new (string name="reg_env", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   ral_sys_traffic                m_ral_model;         // Register Model
   reg2apb_adapter                m_reg2apb;           // Convert Reg Tx <-> Bus-type packets
   uvm_reg_predictor #(bus_pkt)   m_apb2reg_predictor; // Map APB tx to register in model
   my_agent                       m_agent;             // Agent to drive/monitor transactions
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_ral_model          = ral_sys_traffic::type_id::create ("m_ral_model", this);
      m_reg2apb            = reg2apb_adapter :: type_id :: create ("m_reg2apb");
      m_apb2reg_predictor  = uvm_reg_predictor #(bus_pkt) :: type_id :: create ("m_apb2reg_predictor", this);
 
      m_ral_model.build ();
      m_ral_model.lock_model ();
      uvm_config_db #(ral_sys_traffic)::set (null, "uvm_test_top", "m_ral_model", m_ral_model);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_apb2reg_predictor.map       = m_ral_model.default_map;
      m_apb2reg_predictor.adapter   = m_reg2apb;
   endfunction   
endclass
// Declare a sequence_item for the APB transaction 
class bus_pkt extends uvm_sequence_item;
   rand bit [31:0]  addr;
   rand bit [31:0]  data;
   rand bit         write;
 
   `uvm_object_utils_begin (bus_pkt)
      `uvm_field_int (addr, UVM_ALL_ON)
      `uvm_field_int (data, UVM_ALL_ON)
      `uvm_field_int (write, UVM_ALL_ON)
   `uvm_object_utils_end
 
   function new (string name = "bus_pkt");
      super.new (name);
   endfunction
 
   constraint c_addr { addr inside {0, 4, 8};}
endclass
 
// Drives a given apb transaction packet to the APB interface
class my_driver extends uvm_driver #(bus_pkt);
   `uvm_component_utils (my_driver)
 
   bus_pkt  pkt;
 
   virtual bus_if    vif;
 
   function new (string name = "my_driver", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      if (! uvm_config_db#(virtual bus_if)::get (this, "*", "bus_if", vif))
         `uvm_error ("DRVR", "Did not get bus if handle")
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      bit [31:0] data;
 
      vif.psel <= 0;
      vif.penable <= 0;
      vif.pwrite <= 0;
      vif.paddr <= 0;
      vif.pwdata <= 0;
      forever begin
         seq_item_port.get_next_item (pkt);
         if (pkt.write)
            write (pkt.addr, pkt.data);
         else begin
            read (pkt.addr, data);
            pkt.data = data;
         end
         seq_item_port.item_done ();
      end
   endtask
 
   virtual task read (  input bit    [31:0] addr, 
                        output logic [31:0] data);
      vif.paddr <= addr;
      vif.pwrite <= 0;
      vif.psel <= 1;
      @(posedge vif.pclk);
      vif.penable <= 1;
      @(posedge vif.pclk);
      data = vif.prdata;
      vif.psel <= 0;
      vif.penable <= 0;
   endtask
 
   virtual task write ( input bit [31:0] addr,
                        input bit [31:0] data);
      vif.paddr <= addr;
      vif.pwdata <= data;
      vif.pwrite <= 1;
      vif.psel <= 1;
      @(posedge vif.pclk);
      vif.penable <= 1;
      @(posedge vif.pclk);
      vif.psel <= 0;
      vif.penable <= 0;
   endtask
endclass
 
// Monitors the APB interface for any activity and reports out
// through an analysis port
class my_monitor extends uvm_monitor;
   `uvm_component_utils (my_monitor)
   function new (string name="my_monitor", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   uvm_analysis_port #(bus_pkt)  mon_ap;
   virtual bus_if                vif;
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      mon_ap = new ("mon_ap", this);
      uvm_config_db #(virtual bus_if)::get (null, "uvm_test_top.*", "bus_if", vif);
   endfunction
 
   virtual task run_phase (uvm_phase phase);
      fork
         forever begin
            @(posedge vif.pclk);
            if (vif.psel & vif.penable & vif.presetn) begin
               bus_pkt pkt = bus_pkt::type_id::create ("pkt");
               pkt.addr = vif.paddr;
               if (vif.pwrite)
                  pkt.data = vif.pwdata;
               else
                  pkt.data = vif.prdata;
               pkt.write = vif.pwrite;
               mon_ap.write (pkt);
            end 
         end
      join_none
   endtask
endclass
 
// The agent puts together the driver, sequencer and monitor
class my_agent extends uvm_agent;
   `uvm_component_utils (my_agent)
   function new (string name="my_agent", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   my_driver                  m_drvr;
   my_monitor                 m_mon;
   uvm_sequencer #(bus_pkt)   m_seqr; 
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_drvr = my_driver::type_id::create ("m_drvr", this);
      m_seqr = uvm_sequencer#(bus_pkt)::type_id::create ("m_seqr", this);
      m_mon = my_monitor::type_id::create ("m_mon", this);
   endfunction
 
   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_drvr.seq_item_port.connect (m_seqr.seq_item_export);
   endfunction
endclass
 

5. testbench env

class my_env extends uvm_env;
   `uvm_component_utils (my_env)
 
   my_agent       m_agent;   
   reg_env        m_reg_env;
 
   function new (string name = "my_env", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_agent = my_agent::type_id::create ("m_agent", this);
      m_reg_env = reg_env::type_id::create ("m_reg_env", this);
      uvm_reg::include_coverage ("*", UVM_CVR_ALL);
   endfunction
 
   // Connect analysis port of monitor with predictor, assign agent to register env
   // and set default map of the register env
   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_reg_env.m_agent = m_agent;
      m_agent.m_mon.mon_ap.connect (m_reg_env.m_apb2reg_predictor.bus_in);
      m_reg_env.m_ral_model.default_map.set_sequencer(m_agent.m_seqr, m_reg_env.m_reg2apb);
   endfunction
 
endclass

6. sequence

class reset_seq extends uvm_sequence;
   `uvm_object_utils (reset_seq)
   function new (string name = "reset_seq");
      super.new (name);
   endfunction
 
   virtual bus_if    vif; 
 
   task body ();
      if (!uvm_config_db #(virtual bus_if) :: get (null, "uvm_test_top.*", "bus_if", vif)) 
         `uvm_fatal ("VIF", "No vif")
 
      `uvm_info ("RESET", "Running reset ...", UVM_MEDIUM);
      vif.presetn <= 0;
      @(posedge vif.pclk) vif.presetn <= 1;
      @ (posedge vif.pclk);
   endtask
endclass

7.test

class base_test extends uvm_test;
   `uvm_component_utils (base_test)
 
   my_env         m_env;
   reset_seq      m_reset_seq;
   uvm_status_e   status;
 
   function new (string name = "base_test", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   // Build the testbench environment, and reset sequence
   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_env = my_env::type_id::create ("m_env", this);
      m_reset_seq = reset_seq::type_id::create ("m_reset_seq", this);
   endfunction
 
   // In the reset phase, apply reset
   virtual task reset_phase (uvm_phase phase);
      super.reset_phase (phase);
      phase.raise_objection (this);
      m_reset_seq.start (m_env.m_agent.m_seqr);
      phase.drop_objection (this);
   endtask
endclass
 
class reg_rw_test extends base_test;
   `uvm_component_utils (reg_rw_test)
   function new (string name="reg_rw_test", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   // Note that main_phase comes after reset_phase, and is performed when
   // DUT is out of reset. "reset_phase" is already defined in base_test
   // and is always called when this test is started
   virtual task main_phase(uvm_phase phase);
      ral_sys_traffic   m_ral_model;
      uvm_status_e      status;
      int               rdata;
 
      phase.raise_objection(this);
 
      m_env.m_reg_env.set_report_verbosity_level (UVM_HIGH);
 
      // Get register model from config_db
      uvm_config_db#(ral_sys_traffic)::get(null, "uvm_test_top", "m_ral_model", m_ral_model);
 
      // Write 0xcafe_feed to the timer[1] register, and read it back
      m_ral_model.cfg.timer[1].write (status, 32'hcafe_feed);
      m_ral_model.cfg.timer[1].read (status, rdata);
 
      // Set 0xface as the desired value for timer[1] register
      m_ral_model.cfg.timer[1].set(32'hface);
      `uvm_info(get_type_name(), $sformatf("desired=0x%0h mirrored=0x%0h", m_ral_model.cfg.timer[1].get(), m_ral_model.cfg.timer[1].get_mirrored_value()), UVM_MEDIUM)
 
      // Predict that current value of timer[1] is 0xcafe_feed and check it is true
      m_ral_model.cfg.timer[1].predict(32'hcafe_feed);
      m_ral_model.cfg.timer[1].mirror(status, UVM_CHECK);
 
      // Set desired value of the field "bl_yellow" in register ctrl to 1
      // Then start bus transactions by calling "update" to update DUT with 
      // desired value
      m_ral_model.cfg.ctrl.bl_yellow.set(1);
      m_ral_model.cfg.update(status);
 
    // Attempt to write into a RO register "stat" with some value
      m_ral_model.cfg.stat.write(status, 32'h12345678);
      phase.drop_objection(this);
   endtask
 
  // Before end of simulation, allow some time for unfinished transactions to
  // be over
  virtual task shutdown_phase(uvm_phase phase);
    super.shutdown_phase(phase);
    phase.raise_objection(this);
    #100ns;
    phase.drop_objection(this);
  endtask
endclass

 8. define backdoor hdl path

// Assume that ral_cfg_ctl and other "uvm_reg" classes are already
// defined as in the frontdoor example
 
class ral_block_traffic_cfg extends uvm_reg_block;
  rand ral_cfg_ctl    ctrl;       // RW
  rand ral_cfg_timer  timer[2];   // RW
       ral_cfg_stat   stat;       // RO
 
  `uvm_object_utils(ral_block_traffic_cfg)
 
  function new(string name = "traffic_cfg");
    super.new(name, build_coverage(UVM_NO_COVERAGE));
  endfunction
 
  virtual function void build();
    default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
    ctrl = ral_cfg_ctl::type_id::create("ctrl",,get_full_name());
    ctrl.configure(this, null, "");
    ctrl.build();
 
    // HDL path to this instance is "tb.DUT.ctl_reg"
    ctrl.add_hdl_path_slice("ctl_reg", 0, ctrl.get_n_bits());
    default_map.add_reg(this.ctrl, `UVM_REG_ADDR_WIDTH'h0, "RW", 0);
 
    timer[0] = ral_cfg_timer::type_id::create("timer[0]",,get_full_name());
    timer[0].configure(this, null, "");
    timer[0].build();
 
    // HDL path to this instance is "tb.DUT.timer_0"
    timer[0].add_hdl_path_slice("timer_0", 0, timer[0].get_n_bits());
    default_map.add_reg(this.timer[0], `UVM_REG_ADDR_WIDTH'h4, "RW", 0);
 
    timer[1] = ral_cfg_timer::type_id::create("timer[1]",,get_full_name());
    timer[1].configure(this, null, "");
    timer[1].build();
 
    // HDL path to this instance is "tb.DUT.timer_1"
    timer[1].add_hdl_path_slice("timer_1", 0, timer[1].get_n_bits());
    default_map.add_reg(this.timer[1], `UVM_REG_ADDR_WIDTH'h8, "RW", 0);
 
    stat = ral_cfg_stat::type_id::create("stat",,get_full_name());
    stat.configure(this, null, "");
    stat.build();
 
    // HDL path from DUT to the status register will now be 
    // "tb.DUT.stat_reg" after the previous hierarchies are used 
    // for path concatenation
    stat.add_hdl_path_slice("stat_reg", 0, stat.get_n_bits());
    default_map.add_reg(this.stat, `UVM_REG_ADDR_WIDTH'hc, "RO", 0);
    add_hdl_path("DUT");
  endfunction 
endclass 
 
class ral_sys_traffic extends uvm_reg_block;
  rand ral_block_traffic_cfg cfg;
 
  `uvm_object_utils(ral_sys_traffic)
  function new(string name = "traffic");
    super.new(name);
  endfunction
 
  function void build();
    default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
    cfg = ral_block_traffic_cfg::type_id::create("cfg",,get_full_name());
 
    // Since registers exist at the DUT level in our design, configure
    // "cfg" class to have an HDL path called "DUT". So complete path to 
    // DUT is now "tb.DUT"
    cfg.configure(this, "DUT");
    cfg.build();
 
    // Path to this top level regblock in our testbench environment is "tb"
    add_hdl_path("tb");
    default_map.add_submap(this.cfg.default_map, `UVM_REG_ADDR_WIDTH'h0);
  endfunction
endclass

9. backdoor access

class reg_backdoor_test extends base_test;
   `uvm_component_utils (reg_backdoor_test)
   function new (string name="reg_backdoor_test", uvm_component parent);
      super.new (name, parent);
   endfunction
 
   virtual task main_phase(uvm_phase phase);
      ral_sys_traffic   m_ral_model;
      uvm_status_e      status;
      int               rdata;
 
      phase.raise_objection(this);
 
      m_env.m_reg_env.set_report_verbosity_level (UVM_HIGH);
      uvm_config_db#(ral_sys_traffic)::get(null, "uvm_test_top", "m_ral_model", m_ral_model);
 
      // Perform a normal frontdoor access -> write some data first and then read it back
      m_ral_model.cfg.timer[1].write(status, 32'h1234_5678);
      m_ral_model.cfg.timer[1].read(status, rdata);
      `uvm_info(get_type_name(), $sformatf("desired=0x%0h mirrored=0x%0h", m_ral_model.cfg.timer[1].get(), m_ral_model.cfg.timer[1].get_mirrored_value()), UVM_MEDIUM)
 
      // Perform a backdoor access for write and then do a frontdoor read 
      m_ral_model.cfg.timer[1].write(status, 32'ha5a5_a5a5, UVM_BACKDOOR);
      m_ral_model.cfg.timer[1].read(status, rdata);
      `uvm_info(get_type_name(), $sformatf("desired=0x%0h mirrored=0x%0h", m_ral_model.cfg.timer[1].get(), m_ral_model.cfg.timer[1].get_mirrored_value()), UVM_MEDIUM)
 
      // Perform a frontdoor write and then do a backdoor read
      m_ral_model.cfg.timer[1].write(status, 32'hface_face);
     // Wait for a time unit so that backdoor access reads update value
     #1;  
      m_ral_model.cfg.timer[1].read(status, rdata, UVM_BACKDOOR);
      `uvm_info(get_type_name(), $sformatf("desired=0x%0h mirrored=0x%0h", m_ral_model.cfg.timer[1].get(), m_ral_model.cfg.timer[1].get_mirrored_value()), UVM_MEDIUM)
 
      phase.drop_objection(this);
   endtask
 
  virtual task shutdown_phase(uvm_phase phase);
    super.shutdown_phase(phase);
    phase.raise_objection(this);
    #100ns;
    phase.drop_objection(this);
  endtask
endclass

 10. reg_model 寄存器过域问题处理

 

posted on 2019-05-21 23:39  hematologist  阅读(1204)  评论(0编辑  收藏  举报

导航