[UVM]UVM Register Test Sequence

本文解析了UVM中的uvm_reg_hw_reset_seq、uvm_reg_access_seq和uvm_reg_single_bit_bash_seq,展示了如何使用这些序列进行寄存器默认值检查、单一访问及位级操作的测试,包括自定义序列和模型管理技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                    UVM Register Test Sequence

一、源码分析

 1.1 uvm_reg_hw_reset_seq

       利用uvm自带的uvm_reg_hw_reset_seq,可以完成对寄存器default value的check。此seq通过frontdoor读取寄存器的值与RAL模型中的mirror值做比较,不一样则报错。

       从此seq的介绍中可以看到,在body函数中会检查modle是否为null,因为原始的seq中没有对model赋值,所以实例化sequence之后,首先需要对model赋值。为了以后测试的方便,例如有些寄存器是只写的,那么在检查复位值时,就需要exclude,因此,可以通过继承该sequence构建自定义的default value test sequence。

class uvm_reg_hw_reset_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   `uvm_object_utils(uvm_reg_hw_reset_seq)

   function new(string name="uvm_reg_hw_reset_seq");
     super.new(name);
   endfunction


   // Variable: model
   //
   // The block to be tested. Declared in the base class.
   //
   //| uvm_reg_block model; 


   // Variable: body
   //
   // Executes the Hardware Reset sequence.
   // Do not call directly. Use seq.start() instead.

   virtual task body();

      if (model == null) begin
         `uvm_error("uvm_reg_hw_reset_seq", "Not block or system specified to run sequence on");
         return;
      end
      uvm_report_info("STARTING_SEQ",{"\n\nStarting ",get_name()," sequence...\n"},UVM_LOW);
      
      this.reset_blk(model);
      model.reset();

      do_block(model);
   endtask: body

// Task: do_block
   //
   // Test all of the registers in a given ~block~
   //
   protected virtual task do_block(uvm_reg_block blk);
      uvm_reg_map maps[$];
      uvm_reg_map sub_maps[$];

      if (uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_TESTS", 0) != null ||
          uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_HW_RESET_TEST", 0) != null ) begin
            return;

      end

      
      blk.get_maps(maps);
      // Iterate over all maps defined for the RegModel block

      foreach (maps[d]) begin
        uvm_reg regs[$];
        maps[d].get_submaps(sub_maps);
        if(sub_maps.size() !=0) begin 
          continue;
        end

        // Iterate over all registers in the map, checking accesses
        // Note: if map were in inner loop, could test simulataneous
        // access to same reg via different bus interfaces 

        regs.delete();
        maps[d].get_registers(regs);

        foreach (regs[i]) begin

          uvm_status_e status;

          // Registers with certain attributes are not to be tested
          if (uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                 "NO_REG_TESTS", 0) != null ||
              uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                 "NO_REG_HW_RESET_TEST", 0) != null )
              continue;

          `uvm_info(get_type_name(),
                    $sformatf("Verifying reset value of register %s in map \"%s\"...",
                    regs[i].get_full_name(), maps[d].get_full_name()), UVM_LOW);
            
          regs[i].mirror(status, UVM_CHECK, UVM_FRONTDOOR, maps[d], this);

          if (status != UVM_IS_OK) begin
             `uvm_error(get_type_name(),
                    $sformatf("Status was %s when reading reset value of register \"%s\" through map \"%s\".",
                    status.name(), regs[i].get_full_name(), maps[d].get_full_name()));
          end
        end
      end

      begin
         uvm_reg_block blks[$];
         
         blk.get_blocks(blks);
         foreach (blks[i]) begin
            do_block(blks[i]);
         end
      end

   endtask:do_block


   //
   // task: reset_blk
   // Reset the DUT that corresponds to the specified block abstraction class.
   //
   // Currently empty.
   // Will rollback the environment's phase to the ~reset~
   // phase once the new phasing is available.
   //
   // In the meantime, the DUT should be reset before executing this
   // test sequence or this method should be implemented
   // in an extension to reset the DUT.
   //
   virtual task reset_blk(uvm_reg_block blk);
   endtask

endclass: uvm_reg_hw_reset_seq

 1.2 uvm_reg_access_seq

class uvm_reg_single_access_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   // Variable: rg
   // The register to be tested
   uvm_reg rg;

   `uvm_object_utils(uvm_reg_single_access_seq)

   function new(string name="uvm_reg_single_access_seq");
     super.new(name);
   endfunction

   virtual task body();
      uvm_reg_map maps[$];

      if (rg == null) begin
         `uvm_error("uvm_reg_access_seq", "No register specified to run sequence on")
         return;
      end

      // Registers with some attributes are not to be tested
      if (uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
                                             "NO_REG_TESTS", 0) != null || 
          uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
                                             "NO_REG_ACCESS_TEST", 0) != null )
            return;

      // Can only deal with registers with backdoor access
      if (rg.get_backdoor() == null && !rg.has_hdl_path()) begin
         `uvm_error("uvm_reg_access_seq", {"Register '",rg.get_full_name(),
         "' does not have a backdoor mechanism available"})
         return;
      end

      // Registers may be accessible from multiple physical interfaces (maps)
      rg.get_maps(maps);

      // Cannot test access if register contains RO or OTHER fields
      begin
         uvm_reg_field fields[$];

         rg.get_fields(fields);
         foreach (maps[k]) begin
	        int ro;
	       	ro=0;
	     	foreach (fields[j]) begin    
               if (fields[j].get_access(maps[k]) == "RO") begin
                  ro++;
               end
               if (!fields[j].is_known_access(maps[k])) begin
                  `uvm_warning("uvm_reg_access_seq", {"Register '",rg.get_full_name(),
                    "' has field with unknown access type '",
                    fields[j].get_access(maps[k]),"', skipping"})
                  return;
               end
	     	end
	     	if(ro==fields.size()) begin
	     		`uvm_warning("uvm_reg_access_seq", {"Register '",
                rg.get_full_name(),"' has only RO fields in map ",maps[k].get_full_name(),", skipping"})
                return;
	     	end	
         end
      end
      
      // Access each register:
      // - Write complement of reset value via front door
      // - Read value via backdoor and compare against mirror
      // - Write reset value via backdoor
      // - Read via front door and compare against mirror
      foreach (maps[j]) begin
         uvm_status_e status;
         uvm_reg_data_t  v, exp;
         
         `uvm_info("uvm_reg_access_seq", {"Verifying access of register '",
             rg.get_full_name(),"' in map '", maps[j].get_full_name(),
             "' ..."}, UVM_LOW)
         
         v = rg.get();
         
         rg.write(status, ~v, UVM_FRONTDOOR, maps[j], this);

         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_access_seq", {"Status was '",status.name(),
                                 "' when writing '",rg.get_full_name(),
                                 "' through map '",maps[j].get_full_name(),"'"})
         end
         #1;
         
         rg.mirror(status, UVM_CHECK, UVM_BACKDOOR, uvm_reg_map::backdoor(), this);
         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_access_seq", {"Status was '",status.name(),
                                 "' when reading reset value of register '",
                                 rg.get_full_name(), "' through backdoor"})
         end
         
         rg.write(status, v, UVM_BACKDOOR, maps[j], this);
         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_access_seq", {"Status was '",status.name(),
                                 "' when writing '",rg.get_full_name(),
                                 "' through backdoor"})
         end
         
         rg.mirror(status, UVM_CHECK, UVM_FRONTDOOR, maps[j], this);
         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_access_seq", {"Status was '",status.name(),
                                 "' when reading reset value of register '",
                                 rg.get_full_name(), "' through map '",
                                 maps[j].get_full_name(),"'"})
         end
      end
   endtask: body
endclass: uvm_reg_single_access_seq

class uvm_reg_access_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   // Variable: model
   //
   // The block to be tested. Declared in the base class.
   //
   //| uvm_reg_block model; 


   // Variable: reg_seq
   //
   // The sequence used to test one register
   //
   protected uvm_reg_single_access_seq reg_seq;
   
   `uvm_object_utils(uvm_reg_access_seq)

   function new(string name="uvm_reg_access_seq");
     super.new(name);
   endfunction


   // Task: body
   //
   // Executes the Register Access sequence.
   // Do not call directly. Use seq.start() instead.
   //
   virtual task body();

      if (model == null) begin
         `uvm_error("uvm_reg_access_seq", "No register model specified to run sequence on")
         return;
      end

      uvm_report_info("STARTING_SEQ",{"\n\nStarting ",get_name()," sequence...\n"},UVM_LOW);
      
      reg_seq = uvm_reg_single_access_seq::type_id::create("single_reg_access_seq");

      this.reset_blk(model);
      model.reset();

      do_block(model);
   endtask: body


   // Task: do_block
   //
   // Test all of the registers in a block
   //
   protected virtual task do_block(uvm_reg_block blk);
      uvm_reg regs[$];
      
      if (uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_TESTS", 0) != null ||
          uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_ACCESS_TEST", 0) != null )
         return;

      // Iterate over all registers, checking accesses
      blk.get_registers(regs, UVM_NO_HIER);
      foreach (regs[i]) begin
         // Registers with some attributes are not to be tested
         if (uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                "NO_REG_TESTS", 0) != null ||
	     uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                "NO_REG_ACCESS_TEST", 0) != null )
              continue;
         
         // Can only deal with registers with backdoor access
         if (regs[i].get_backdoor() == null && !regs[i].has_hdl_path()) begin
            `uvm_warning("uvm_reg_access_seq", {"Register '",regs[i].get_full_name(),
                   "' does not have a backdoor mechanism available"})
            continue;
         end
         
         reg_seq.rg = regs[i];
         reg_seq.start(null,this);
      end

      begin
         uvm_reg_block blks[$];
         
         blk.get_blocks(blks);
         foreach (blks[i]) begin
            do_block(blks[i]);
         end
      end
   endtask: do_block


   // Task: reset_blk
   //
   // Reset the DUT that corresponds to the specified block abstraction class.
   //
   // Currently empty.
   // Will rollback the environment's phase to the ~reset~
   // phase once the new phasing is available.
   //
   // In the meantime, the DUT should be reset before executing this
   // test sequence or this method should be implemented
   // in an extension to reset the DUT.
   //
   virtual task reset_blk(uvm_reg_block blk);
   endtask

endclass: uvm_reg_access_seq

 1.3 uvm_reg_single_bit_bash_seq

class uvm_reg_single_bit_bash_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   // Variable: rg
   // The register to be tested
   uvm_reg rg;

   `uvm_object_utils(uvm_reg_single_bit_bash_seq)

   function new(string name="uvm_reg_single_bit_bash_seq");
     super.new(name);
   endfunction

   virtual task body();
      uvm_reg_field fields[$];
      string mode[`UVM_REG_DATA_WIDTH];
      uvm_reg_map maps[$];
      uvm_reg_data_t  dc_mask;
      uvm_reg_data_t  reset_val;
      int n_bits;
      string field_access;
         
      if (rg == null) begin
         `uvm_error("uvm_reg_bit_bash_seq", "No register specified to run sequence on");
         return;
      end

      // Registers with some attributes are not to be tested
      if (uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
                                             "NO_REG_TESTS", 0) != null ||
          uvm_resource_db#(bit)::get_by_name({"REG::",rg.get_full_name()},
                                             "NO_REG_BIT_BASH_TEST", 0) != null )
            return;
      
      n_bits = rg.get_n_bytes() * 8;
         
      // Let's see what kind of bits we have...
      rg.get_fields(fields);
         
      // Registers may be accessible from multiple physical interfaces (maps)
      rg.get_maps(maps);
         
      // Bash the bits in the register via each map
      foreach (maps[j]) begin
         uvm_status_e status;
         uvm_reg_data_t  val, exp, v;
         int next_lsb;
         
         next_lsb = 0;
         dc_mask  = 0;
         foreach (fields[k]) begin
            int lsb, w, dc;

            field_access = fields[k].get_access(maps[j]);
            dc = (fields[k].get_compare() == UVM_NO_CHECK);
            lsb = fields[k].get_lsb_pos();
            w   = fields[k].get_n_bits();
            // Ignore Write-only fields because
            // you are not supposed to read them
            case (field_access)
             "WO", "WOC", "WOS", "WO1", "NOACCESS": dc = 1;
            endcase
            // Any unused bits on the right side of the LSB?
            while (next_lsb < lsb) mode[next_lsb++] = "RO";
            
            repeat (w) begin
               mode[next_lsb] = field_access;
               dc_mask[next_lsb] = dc;
               next_lsb++;
            end
         end
         // Any unused bits on the left side of the MSB?
         while (next_lsb < `UVM_REG_DATA_WIDTH)
            mode[next_lsb++] = "RO";
         
         `uvm_info("uvm_reg_bit_bash_seq", $sformatf("Verifying bits in register %s in map \"%s\"...",
                                    rg.get_full_name(), maps[j].get_full_name()),UVM_LOW);
         
         // Bash the kth bit
         for (int k = 0; k < n_bits; k++) begin
            // Cannot test unpredictable bit behavior
            if (dc_mask[k]) continue;

            bash_kth_bit(rg, k, mode[k], maps[j], dc_mask);
         end
            
      end
   endtask: body


   task bash_kth_bit(uvm_reg         rg,
                     int             k,
                     string          mode,
                     uvm_reg_map     map,
                     uvm_reg_data_t  dc_mask);
      uvm_status_e status;
      uvm_reg_data_t  val, exp, v;
      bit bit_val;

      `uvm_info("uvm_reg_bit_bash_seq", $sformatf("...Bashing %s bit #%0d", mode, k),UVM_HIGH);
      
      repeat (2) begin
         val = rg.get();
         v   = val;
         exp = val;
         val[k] = ~val[k];
         bit_val = val[k];
         
         rg.write(status, val, UVM_FRONTDOOR, map, this);
         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when writing to register \"%s\" through map \"%s\".",
                                        status.name(), rg.get_full_name(), map.get_full_name()));
         end
         
         exp = rg.get() & ~dc_mask;
         rg.read(status, val, UVM_FRONTDOOR, map, this);
         if (status != UVM_IS_OK) begin
            `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when reading register \"%s\" through map \"%s\".",
                                        status.name(), rg.get_full_name(), map.get_full_name()));
         end

         val &= ~dc_mask;
         if (val !== exp) begin
            `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Writing a %b in bit #%0d of register \"%s\" with initial value 'h%h yielded 'h%h instead of 'h%h",
                                        bit_val, k, rg.get_full_name(), v, val, exp));
         end
      end
   endtask: bash_kth_bit

endclass: uvm_reg_single_bit_bash_seq

       uvm_reg_bit_bash_seq实现对寄存器中每个bit的遍历操作,通过frontdoor write、frontdoor read确认每个bit读写操作是否ok。此操作对RW寄存器有效,因为本身其就要支持读写操作。如果读写有问题就可以发现错误。

       对于诸如RO、RC这样属性的寄存器,本身就不支持写操作,会导致误报错误,所以后续需要exclude掉.

class uvm_reg_bit_bash_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

   // Variable: model
   //
   // The block to be tested. Declared in the base class.
   //
   //| uvm_reg_block model; 


   // Variable: reg_seq
   //
   // The sequence used to test one register
   //
   protected uvm_reg_single_bit_bash_seq reg_seq;
   
   `uvm_object_utils(uvm_reg_bit_bash_seq)

   function new(string name="uvm_reg_bit_bash_seq");
     super.new(name);
   endfunction


   // Task: body
   //
   // Executes the Register Bit Bash sequence.
   // Do not call directly. Use seq.start() instead.
   //
   virtual task body();
      
      if (model == null) begin
         `uvm_error("uvm_reg_bit_bash_seq", "No register model specified to run sequence on");
         return;
      end

      uvm_report_info("STARTING_SEQ",{"\n\nStarting ",get_name()," sequence...\n"},UVM_LOW);

      reg_seq = uvm_reg_single_bit_bash_seq::type_id::create("reg_single_bit_bash_seq");

      this.reset_blk(model);
      model.reset();

      do_block(model);
   endtask


   // Task: do_block
   //
   // Test all of the registers in a given ~block~
   //
   protected virtual task do_block(uvm_reg_block blk);
      uvm_reg regs[$];

      if (uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_TESTS", 0) != null ||
          uvm_resource_db#(bit)::get_by_name({"REG::",blk.get_full_name()},
                                             "NO_REG_BIT_BASH_TEST", 0) != null )
         return;

      // Iterate over all registers, checking accesses
      blk.get_registers(regs, UVM_NO_HIER);
      foreach (regs[i]) begin
         // Registers with some attributes are not to be tested
         if (uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                "NO_REG_TESTS", 0) != null ||
	     uvm_resource_db#(bit)::get_by_name({"REG::",regs[i].get_full_name()},
                                                "NO_REG_BIT_BASH_TEST", 0) != null )
            continue;
         
         reg_seq.rg = regs[i];
         reg_seq.start(null,this);
      end

      begin
         uvm_reg_block blks[$];
         
         blk.get_blocks(blks);
         foreach (blks[i]) begin
            do_block(blks[i]);
         end
      end
   endtask: do_block


   // Task: reset_blk
   //
   // Reset the DUT that corresponds to the specified block abstraction class.
   //
   // Currently empty.
   // Will rollback the environment's phase to the ~reset~
   // phase once the new phasing is available.
   //
   // In the meantime, the DUT should be reset before executing this
   // test sequence or this method should be implemented
   // in an extension to reset the DUT.
   //
   virtual task reset_blk(uvm_reg_block blk);
   endtask

endclass: uvm_reg_bit_bash_seq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

元直数字电路验证

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值