UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤


前言

本文结合具体的例子和代码,介绍在UVM中加入寄存器模型的方法步骤。


0. uvm环境中加入寄存器模型的步骤

  1. 保证前门访问正常。
  2. 编写ralf文件。
  3. 生成UVM格式的寄存器模型。
  4. 编写UVM寄存器模型的适配器。
  5. 将寄存器模型加入到验证环境中。
  6. 编写并执行能够访问寄存器模型的sequence。
  7. 加入镜像和预测功能(可选)。
  8. 运行内建的自检测试(可选)。
  9. 寄存器模型覆盖率查看。

在这里插入图片描述

1. 保证前门访问正常

在没有寄存器模型的情况下,先保证通过寄存器总线,能够正常访问寄存器。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

编写host_sequence_base

class host_sequence_base extends uvm_sequence #(host_data);
  `uvm_object_utils(host_sequence_base)

  virtual host_io    vif;
  uvm_sequencer_base p_sqr;

  function new(string name = "host_sequence_base");
    super.new(name);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    `ifdef UVM_POST_VERSION_1_1
     set_automatic_phase_objection(1);
    `endif
  endfunction: new

  virtual task pre_start();
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    p_sqr = get_sequencer();

    `ifdef UVM_VERSION_1_1
    if ((get_parent_sequence() == null) && (starting_phase != null)) begin
      starting_phase.raise_objection(this);
    end
    `endif

    if (uvm_config_db#(virtual host_io)::get(p_sqr.get_parent(), "", "vif", vif)) begin
      `uvm_info("HOST_SEQ_CFG", "Has access to host interface", UVM_HIGH);
    end
  endtask: pre_start

  `ifdef UVM_VERSION_1_1
  virtual task post_start();
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if ((get_parent_sequence() == null) && (starting_phase != null)) begin
      starting_phase.drop_objection(this);
    end
  endtask: post_start
  `endif

endclass: host_sequence_base

编写host_bfm_sequence

//
// The host_bfm_sequence class is designed to test the DUT registers and memory
// using the host_driver without using RAL.
//
class host_bfm_sequence extends host_sequence_base;
  `uvm_object_utils(host_bfm_sequence)

  function new(string name = "host_bfm_sequence");
    super.new(name);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

    virtual task body();
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    `uvm_do_with(req, {addr == 'h0; kind == UVM_READ;});
    if (req.data != 'h5a03) begin
      `uvm_fatal("BFM_ERR", $sformatf("HOST_ID is %4h instead of 'h5a03", req.data));
    end else begin
      `uvm_info("BFM_TEST", $sformatf("HOST_ID is %4h the expected value is 'h5a03", req.data), UVM_MEDIUM);
    end
  
    `uvm_do_with(req, {addr == 'h100; kind == UVM_READ;});
    if (req.data != '1) begin
      `uvm_fatal("BFM_ERR", $sformatf("LOCK is %4h instead of 'hffff", req.data));
    end
    `uvm_do_with(req, {addr == 'h100; data == '1; kind == UVM_WRITE;});
    `uvm_do_with(req, {addr == 'h100; kind == UVM_READ;});
    if (req.data != '0) begin
      `uvm_fatal("BFM_ERR", $sformatf("LOCK is %4h instead of 'h0000", req.data));
    end else begin
      `uvm_info("BFM_TEST", $sformatf("LOCK is %4h the expected value is 'h0000", req.data), UVM_MEDIUM);
    end
  
    for (int i=0; i<256; i++) begin
      `uvm_do_with(req, {addr == 'h1000+i; data == (i ^ (i >> 1)); kind == UVM_WRITE;});
    end
    for (int i=0; i<256; i++) begin
      `uvm_do_with(req, {addr == 'h1000+i; kind == UVM_READ;});
      if (req.data != (i ^ (i >> 1))) begin
        `uvm_fatal("BFM_ERR", $sformatf("R_ARRAY is %4h instead of %4h", req.data, i ^ (i >> 1)));
      end
    end
    `uvm_info("BFM_TEST", "R_ARRAY contains the expected values", UVM_MEDIUM);
  
    for (int i=0; i<4096; i++) begin
      `uvm_do_with(req, {addr == 'h4000+i; data == 16'b1 << i%16; kind == UVM_WRITE;});
    end
    for (int i=0; i<4096; i++) begin
      `uvm_do_with(req, {addr == 'h4000+i; kind == UVM_READ;});
      if (req.data != 16'b1 << i%16) begin
        `uvm_fatal("BFM_ERR", $sformatf("RAM[%4h] is %4h instead of %4h", i, req.data, 16'b1 << i%16));
      end
    end
    `uvm_info("BFM_TEST", "RAM contains the expected values", UVM_MEDIUM);
  endtask: body

endclass: host_bfm_sequence

编写test_base 测试用例

class test_base extends uvm_test;
  `uvm_component_utils(test_base)

  // For convenience, access to the command line processor is done for you
  uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();

  router_env env;
  virtual router_io    router_vif;
  virtual reset_io     reset_vif;

  // Lab 6 - The declaration of the host virtual interface is done for you.
  virtual host_io      host_vif;

  top_reset_sequencer  top_reset_sqr;


  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    env = router_env::type_id::create("env", this);

    uvm_resource_db#(virtual router_io)::read_by_type("router_vif", router_vif, this);
    uvm_resource_db#(virtual reset_io)::read_by_type("reset_vif", reset_vif, this);

    uvm_config_db#(virtual router_io)::set(this, "env.i_agt[*]", "vif", router_vif);
    uvm_config_db#(virtual router_io)::set(this, "env.o_agt[*]", "vif", router_vif);
    uvm_config_db#(virtual reset_io)::set(this, "env.r_agt", "vif", reset_vif);

    top_reset_sqr = top_reset_sequencer::type_id::create("top_reset_sqr", this);

    // The test is responsible for picking it up and configure the agent with the interface.
    uvm_resource_db#(virtual host_io)::read_by_type("host_vif", host_vif, this);
    uvm_config_db#(virtual host_io)::set(this, "env.h_agt", "vif", host_vif);

    uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.sqr.reset_phase", "default_sequence", null);

    uvm_config_db #(uvm_object_wrapper)::set(this, "top_reset_sqr.reset_phase", "default_sequence", top_reset_sequence::get_type());

    uvm_config_db #(string)::set(this, "env", "hdl_path", "router_test_top.dut");

    set_type_override_by_type(scoreboard::get_type(),ms_scoreboard::get_type());
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    foreach (env.i_agt[i]) begin
      top_reset_sqr.pkt_sqr.push_back(env.i_agt[i].sqr);
    end

    top_reset_sqr.r_sqr = env.r_agt.sqr;

    top_reset_sqr.h_sqr = env.h_agt.sqr;

  endfunction: connect_phase

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    env.regmodel.set_coverage(UVM_CVR_ALL);

  endfunction: end_of_elaboration_phase

  virtual task shutdown_phase(uvm_phase phase);
    super.shutdown_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    phase.raise_objection(this);
    env.sb.wait_for_done();
    phase.drop_objection(this);
  endtask: shutdown_phase

  virtual function void report_phase(uvm_phase phase);
    super.report_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    `uvm_info("SB_REPORT", {"\n", env.sb.convert2string()}, UVM_MEDIUM);
  endfunction: report_phase

  virtual function void final_phase(uvm_phase phase);
    super.final_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "TOPOLOGY")) begin
      uvm_root::get().print_topology();
    end

    if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "FACTORY")) begin
      uvm_factory::get().print();
    end
  endfunction: final_phase
endclass: test_base

编写test_host_bfm测试用例

class test_host_bfm extends test_base;
  `uvm_component_utils(test_host_bfm)
  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.configure_phase", "default_sequence", null);
    uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.main_phase", "default_sequence", null);
    uvm_config_db #(uvm_object_wrapper)::set(this, "env.h_agt.sqr.main_phase", "default_sequence", host_bfm_sequence::get_type());

  endfunction: build_phase
endclass: test_host_bfm

2. 编写ralf文件

按照一定的格式,在ralf这个文件中输入寄存器的基本信息,后面会利用ralgen脚本加载这个文件,生成对应的UVM格式的寄存器模型。

DUT中的寄存器和RAM如下图描述所示。
在这里插入图片描述

编写的host.ralf文件如下所示:

# This file contains the DUT register and memory definitions

register HOST_ID {
  field REV_ID {
    bits 8;
    access ro;
    reset 'h03;
  }
  field CHIP_ID {
    bits 8;
    access ro;
    reset 'h5A;
  }
}

register LOCK {
  field LOCK {
    bits 16;
    access w1c;
    reset 'hffff;
  }
}

register R_ARRAY {
  field H_REG {
    bits 16;
    access rw;
    reset 'h0000;
  }
}

memory RAM {
  size 4k;
  bits 16;
  access rw;
}

#
# The block level declaration is done for you.  This include the hdl_path name for
# the signals in the DUT.  Later on, the hdl_path will be in RAL backdoor access.
#
block host_regmodel {
  bytes 2;
  register HOST_ID        (host_id)       @'h0000;
  register LOCK           (lock)          @'h0100;
  register R_ARRAY[256]   (host_reg[%d])  @'h1000; # array must specify HDL index
  memory   RAM            (ram)           @'h4000;
}

3. 生成UVM格式的寄存器模型

利用synopsys自带的ralgen脚本,加载第二步中写好的ralf文件,生成寄存器模型。
在这里插入图片描述

编写的makefile文件如下所示。

ral:
	ralgen -full64 -uvm -t host_regmodel host.ralf
	
ral_cov:
	ralgen -full64 -uvm -c b -t host_regmodel host.ralf

通过执行make ral或者make ral_cov,可生成寄存器仿真模型。其中make ral生成的寄存器模型没有添加覆盖率,make ral_cov生成的寄存器模型添加了覆盖率。

hefei@ubuntu:~/Desktop/ces_uvm-1.2_2016.06/labs/lab6$ make ral
ralgen -full64 -uvm -t host_regmodel host.ralf

Synopsys UVM Register Abstraction Layer Code Generator
Copyright (c) 1991-2018 by Synopsys Inc.
All Rights Reserved.

生成的寄存器模型文件ral_host_regmodel.sv如下所示:

`ifndef RAL_HOST_REGMODEL
`define RAL_HOST_REGMODEL

import uvm_pkg::*;

class ral_reg_HOST_ID extends uvm_reg;
	uvm_reg_field REV_ID;
	uvm_reg_field CHIP_ID;

	function new(string name = "HOST_ID");
		super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
	endfunction: new
   virtual function void build();
      this.REV_ID = uvm_reg_field::type_id::create("REV_ID",,get_full_name());
      this.REV_ID.configure(this, 8, 0, "RO", 0, 8'h03, 1, 0, 1);
      this.CHIP_ID = uvm_reg_field::type_id::create("CHIP_ID",,get_full_name());
      this.CHIP_ID.configure(this, 8, 8, "RO", 0, 8'h5A, 1, 0, 1);
   endfunction: build

	`uvm_object_utils(ral_reg_HOST_ID)

endclass : ral_reg_HOST_ID


class ral_reg_LOCK extends uvm_reg;
	rand uvm_reg_field LOCK;

	function new(string name = "LOCK");
		super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
	endfunction: new
   virtual function void build();
      this.LOCK = uvm_reg_field::type_id::create("LOCK",,get_full_name());
      this.LOCK.configure(this, 16, 0, "W1C", 0, 16'hffff, 1, 0, 1);
   endfunction: build

	`uvm_object_utils(ral_reg_LOCK)

endclass : ral_reg_LOCK


class ral_reg_R_ARRAY extends uvm_reg;
	rand uvm_reg_field H_REG;

	function new(string name = "R_ARRAY");
		super.new(name, 16,build_coverage(UVM_NO_COVERAGE));
	endfunction: new
   virtual function void build();
      this.H_REG = uvm_reg_field::type_id::create("H_REG",,get_full_name());
      this.H_REG.configure(this, 16, 0, "RW", 0, 16'h0000, 1, 0, 1);
   endfunction: build

	`uvm_object_utils(ral_reg_R_ARRAY)

endclass : ral_reg_R_ARRAY


class ral_mem_RAM extends uvm_mem;
   function new(string name = "RAM");
      super.new(name, `UVM_REG_ADDR_WIDTH'h1000, 16, "RW", build_coverage(UVM_NO_COVERAGE));
   endfunction
   virtual function void build();
   endfunction: build

   `uvm_object_utils(ral_mem_RAM)

endclass : ral_mem_RAM


class ral_block_host_regmodel extends uvm_reg_block;
	rand ral_reg_HOST_ID HOST_ID;
	rand ral_reg_LOCK LOCK;
	rand ral_reg_R_ARRAY R_ARRAY[256];
	rand ral_mem_RAM RAM;
	uvm_reg_field HOST_ID_REV_ID;
	uvm_reg_field REV_ID;
	uvm_reg_field HOST_ID_CHIP_ID;
	uvm_reg_field CHIP_ID;
	rand uvm_reg_field LOCK_LOCK;
	rand uvm_reg_field R_ARRAY_H_REG[256];
	rand uvm_reg_field H_REG[256];

	function new(string name = "host_regmodel");
		super.new(name, build_coverage(UVM_NO_COVERAGE));
	endfunction: new

   virtual function void build();
      this.default_map = create_map("", 0, 2, UVM_LITTLE_ENDIAN, 0);
      this.HOST_ID = ral_reg_HOST_ID::type_id::create("HOST_ID",,get_full_name());
      this.HOST_ID.configure(this, null, "");
      this.HOST_ID.build();
         this.HOST_ID.add_hdl_path('{

            '{"host_id", -1, -1}
         });
      this.default_map.add_reg(this.HOST_ID, `UVM_REG_ADDR_WIDTH'h0, "RO", 0);
		this.HOST_ID_REV_ID = this.HOST_ID.REV_ID;
		this.REV_ID = this.HOST_ID.REV_ID;
		this.HOST_ID_CHIP_ID = this.HOST_ID.CHIP_ID;
		this.CHIP_ID = this.HOST_ID.CHIP_ID;
      this.LOCK = ral_reg_LOCK::type_id::create("LOCK",,get_full_name());
      this.LOCK.configure(this, null, "");
      this.LOCK.build();
         this.LOCK.add_hdl_path('{

            '{"lock", -1, -1}
         });
      this.default_map.add_reg(this.LOCK, `UVM_REG_ADDR_WIDTH'h100, "RW", 0);
		this.LOCK_LOCK = this.LOCK.LOCK;
      foreach (this.R_ARRAY[i]) begin
         int J = i;
         this.R_ARRAY[J] = ral_reg_R_ARRAY::type_id::create($psprintf("R_ARRAY[%0d]",J),,get_full_name());
         this.R_ARRAY[J].configure(this, null, "");
         this.R_ARRAY[J].build();
         this.R_ARRAY[J].add_hdl_path('{

            '{$psprintf("host_reg[%0d]", J), -1, -1}
         });
         this.default_map.add_reg(this.R_ARRAY[J], `UVM_REG_ADDR_WIDTH'h1000+J*`UVM_REG_ADDR_WIDTH'h1, "RW", 0);
			this.R_ARRAY_H_REG[J] = this.R_ARRAY[J].H_REG;
			this.H_REG[J] = this.R_ARRAY[J].H_REG;
      end
      this.RAM = ral_mem_RAM::type_id::create("RAM",,get_full_name());
      this.RAM.configure(this, "ram");
      this.RAM.build();
      this.default_map.add_mem(this.RAM, `UVM_REG_ADDR_WIDTH'h4000, "RW", 0);
   endfunction : build

	`uvm_object_utils(ral_block_host_regmodel)

endclass : ral_block_host_regmodel

`endif

4. 编写UVM寄存器模型的适配器

UVM寄存器模型有一套自己的总线,这里需要根据自己的寄存器访问总线,编写一个转换器,实现这两套总线的适配。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

生成的适配器如下所示:

//
// For the sake of minimizing the number of files in each lab directory, you will be
// creating the translator class in the same file as the host_data class.
//

class reg_adapter extends uvm_reg_adapter;
  `uvm_object_utils(reg_adapter)

  function new(string name="reg_adapter");
    super.new(name);
    `uvm_info("Trace", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    host_data tr;
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    tr = host_data::type_id::create("tr");
    tr.addr = rw.addr;
    tr.data = rw.data;
    tr.kind = rw.kind;
    return tr;
  endfunction: reg2bus

  virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    host_data tr;
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    if (!$cast(tr, bus_item)) begin
      `uvm_fatal("NOT_HOST_REG_TYPE", "bus_item is not correct type");
    end
    rw.addr = tr.addr;
    rw.data = tr.data;
    rw.kind   = tr.kind;
    rw.status = tr.status;
  endfunction: bus2reg
endclass: reg_adapter

5. 将寄存器模型加入到验证环境中

适配器和寄存器模型都要加入到验证环境中,再在connect_phase中,将对应的接口连接上。
在这里插入图片描述
在这里插入图片描述

具体的代码如下所示

class router_env extends uvm_env;
  `uvm_component_utils(router_env)

  reset_agent   r_agt;
  input_agent   i_agt[16];
  output_agent  o_agt[16];
  scoreboard    sb;
  
  host_agent  h_agt;

  ral_block_host_regmodel regmodel;
  reg_adapter adapter;

  typedef uvm_reg_predictor #(host_data) hreg_predictor;
  hreg_predictor hreg_predict;


  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    r_agt = reset_agent::type_id::create("r_agt", this);
    uvm_config_db #(uvm_object_wrapper)::set(this, "r_agt.sqr.reset_phase", "default_sequence", reset_sequence::get_type());

    foreach (i_agt[i]) begin
      i_agt[i] = input_agent::type_id::create($sformatf("i_agt[%0d]", i), this);
      uvm_config_db #(int)::set(this, i_agt[i].get_name(), "port_id", i);
      uvm_config_db #(uvm_object_wrapper)::set(this, {i_agt[i].get_name(), ".", "sqr.reset_phase"}, "default_sequence", router_input_port_reset_sequence::get_type());
      uvm_config_db #(uvm_object_wrapper)::set(this, {i_agt[i].get_name(), ".", "sqr.main_phase"}, "default_sequence", packet_sequence::get_type());
    end

    sb = scoreboard::type_id::create("sb", this);

    foreach (o_agt[i]) begin
      o_agt[i] = output_agent::type_id::create($sformatf("o_agt[%0d]", i), this);
      uvm_config_db #(int)::set(this, o_agt[i].get_name(), "port_id", i);
    end

    h_agt = host_agent::type_id::create("h_agt", this);

    adapter = reg_adapter::type_id::create("adapter", this);

    uvm_config_db #(ral_block_host_regmodel)::get(this, "", "regmodel", regmodel);

    if (regmodel == null) begin
      string hdl_path;
      `uvm_info("HOST_CFG", "Self constructing regmodel", UVM_MEDIUM);
      if (!uvm_config_db #(string)::get(this, "", "hdl_path", hdl_path)) begin
        `uvm_warning("HOST_CFG", "HDL path for DPI backdoor not set!");
      end
      regmodel = ral_block_host_regmodel::type_id::create("regmodel", this);
      regmodel.build();
      regmodel.lock_model();
      regmodel.reset();
      regmodel.set_hdl_path_root(hdl_path);
    end

    uvm_config_db #(ral_block_host_regmodel)::set(this, h_agt.get_name(), "regmodel", regmodel);

    uvm_config_db #(uvm_object_wrapper)::set(this, {h_agt.get_name(), ".", "sqr.configure_phase"}, "default_sequence", ral_port_unlock_sequence::get_type());

    hreg_predict = hreg_predictor::type_id::create("h_reg_predict", this);


  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    foreach (i_agt[i]) begin
      i_agt[i].analysis_port.connect(sb.before_export);
    end

    foreach (o_agt[i]) begin
      o_agt[i].analysis_port.connect(sb.after_export);
    end

    regmodel.default_map.set_sequencer(h_agt.sqr, adapter);

    regmodel.default_map.set_auto_predict(0);
    hreg_predict.map = regmodel.get_default_map();
    hreg_predict.adapter = adapter;
    h_agt.analysis_port.connect(hreg_predict.bus_in);

  endfunction: connect_phase

endclass: router_env

6. 编写并执行能够访问寄存器模型的sequence

编写host_ral_sequence_base:
在这里插入图片描述

//
// The following is the RAL configuration sequence base.  It contains the
// regmodel that the RAL sequences will need.
//
class host_ral_sequence_base extends uvm_reg_sequence #(host_sequence_base);
  `uvm_object_utils(host_ral_sequence_base)

  ral_block_host_regmodel regmodel;

  function new(string name = "host_ral_sequence_base");
    super.new(name);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual task pre_start();
    super.pre_start();
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    if (!uvm_config_db#(ral_block_host_regmodel)::get(p_sqr.get_parent(), "", "regmodel", regmodel)) begin
      `uvm_info("RAL_CFG", "regmodel not set through configuration.  Make sure it is set by other mechanisms", UVM_MEDIUM);
    end
    if (regmodel == null) begin
      `uvm_fatal("RAL_CFG", "regmodel not set");
    end

  endtask: pre_start
endclass: host_ral_sequence_base

编写host_ral_test_sequence:

//
// This is the RAL test sequence.
//
class host_ral_test_sequence extends host_ral_sequence_base;
  `uvm_object_utils(host_ral_test_sequence)

  function new(string name = "host_ral_test_sequence");
    super.new(name);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual task body();
    uvm_status_e status;
    uvm_reg_data_t data;

    regmodel.HOST_ID.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));

    if (data != 'h5a03) begin
      `uvm_fatal("RAL_ERR", $sformatf("HOST_ID is %4h instead of 'h5a03", data));
    end else begin
      `uvm_info("RAL_TEST", $sformatf("HOST_ID is %4h the expected value is 'h5a03", data), UVM_MEDIUM);
    end

    regmodel.LOCK.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));

    if (data != 'hffff) begin
      `uvm_fatal("RAL_ERR", $sformatf("LOCK is %4h instead of 'hffff", data));
    end

    regmodel.LOCK.write(.status(status), .value('1), .path(UVM_FRONTDOOR), .parent(this));
    regmodel.LOCK.read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));

    if (data != '0) begin
      `uvm_fatal("RAL_ERR", $sformatf("LOCK is %4h instead of 'h0000", data));
    end else begin
      `uvm_info("RAL_TEST", $sformatf("LOCK is %4h the expected value is 'h0000", data), UVM_MEDIUM);
    end

    for (int i=0; i<256; i++) begin
      regmodel.R_ARRAY[i].write(.status(status), .value(i ^ (i >> 1)), .path(UVM_FRONTDOOR), .parent(this));
    end

    for (int i=0; i<256; i++) begin
      regmodel.R_ARRAY[i].read(.status(status), .value(data), .path(UVM_BACKDOOR), .parent(this));
      if (data != (i ^ (i >> 1))) begin
        `uvm_fatal("RAL_ERR", $sformatf("R_ARRAY is %4h instead of %4h", data, i ^ (i >> 1)));
      end
    end
    `uvm_info("RAL_TEST", "R_ARRAY contains the expected values", UVM_MEDIUM);

    for (int i=0; i<4096; i++) begin
      regmodel.RAM.write(.status(status), .offset(i), .value(16'b1 << i%16), .path(UVM_FRONTDOOR), .parent(this));
    end

    for (int i=0; i<4096; i++) begin
      regmodel.RAM.read(.status(status), .offset(i), .value(data), .path(UVM_BACKDOOR), .parent(this));
      if (data != 16'b1 << i%16) begin
        `uvm_fatal("RAL_ERR", $sformatf("RAM[%4h] is %4h instead of %4h", i, data, 16'b1 << i%16));
      end
    end
    `uvm_info("RAL_TEST", "RAM contains the expected values", UVM_MEDIUM);
  endtask: body

endclass: host_ral_test_sequence

编写test_host_ral测试用例:

class test_host_ral extends test_base;
  `uvm_component_utils(test_host_ral)
  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

     uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.configure_phase", "default_sequence", null);
    uvm_config_db #(uvm_object_wrapper)::set(this, "env.*.main_phase", "default_sequence", null);
    uvm_config_db #(uvm_object_wrapper)::set(this, "env.h_agt.sqr.main_phase", "default_sequence", host_ral_test_sequence::get_type());

  endfunction: build_phase
endclass: test_host_ral

7. 加入镜像和预测功能(可选)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

镜像和预测功能在上述第5步的代码中已经有体现。

8. 运行内建的自检测试(可选)

测试用例的代码如下所示:

// For the ral self test:
// turn off all sequencer execution.
// Explicitely execute the virtual reset sequence.
// Then explicitely execute the uvm_reg_bit_bash_seq.

class test_ral_selftest extends test_base;
  `uvm_component_utils(test_ral_selftest)
  string                 seq_name="uvm_reg_bit_bash_seq";
  uvm_reg_sequence       selftest_seq;
  top_reset_sequence     top_reset_seq;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    uvm_config_db #(uvm_object_wrapper)::set(this,"*","default_sequence",null);
  endfunction: build_phase

  virtual task run_phase(uvm_phase phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    phase.raise_objection(this, "Starting reset tests");
    top_reset_seq = top_reset_sequence::type_id::create("top_reset_seq", this);
    top_reset_seq.start(top_reset_sqr);
    clp.get_arg_value("+seq=", seq_name);
    $cast(selftest_seq, uvm_factory::get().create_object_by_name(seq_name));
    selftest_seq.model = env.regmodel;
    selftest_seq.start(env.h_agt.sqr);
    phase.drop_objection(this, "Done with register tests");
  endtask: run_phase
endclass: test_ral_selftest

其他内建sequence。
在这里插入图片描述
不同内建sequence,通过test_base中的clp,获取命令行中+seq=后边的参数传递进去。
在这里插入图片描述

9. 查看寄存器模型的功能覆盖率

查看寄存器的功能覆盖率需要满足以下条件:

  1. 保证寄存器模型中带有功能覆盖率统计代码。这个比较简单,在利用ralgen生成寄存器模型的时候,把覆盖率选项的开关打开。
ralgen -full64 -uvm -c b -c a -c f -t host_regmodel host.ralf
#-c表示覆盖率coverage,a、b、f依次表示“Address Map” “Register Bits” “Field Values”功能覆盖率模型。
  1. 打开regmodel的功能覆盖率。在test_base的end_of_elaboration_phase方法中,打开regmodel的覆盖率开关。
env.regmodel.set_coverage(UVM_CVR_ALL);
  1. 打开RAL覆盖率。在testbench的initial模块中,打开RAL的覆盖率开关。
initial
    uvm_reg::include_coverage("*", UVM_CVR_ALL);
  1. 执行仿真,查看覆盖率。

Makefile

cover:
	urg -dir ./output/*/simv.vdb; firefox urgReport/groups.html &

总结

本文结合具体的测试用例以及代码,具体介绍了UVM中加入寄存器模型的方法步骤,以及如何查看寄存器的功能覆盖率。

参考文献

具体的实现可以参考synopsys的讲义,链接如下:
Synopsys_uvm1.2_user_guide1
Synopsys_uvm1.2_user_guide2
Synopsys_uvm1.2_lab
Synopsys_uvm1.2_lab_guide

UVM(Universal Verification Methodology)寄存器模型是一用于验证芯片寄存器功能的标准方法。它提供了一个统一的、可重用的框架,用于建立和管理寄存器模型,以及执行寄存器访问和验证。 UVM寄存器模型的主要组成部分包括寄存器模型寄存器层次结构、寄存器操作和寄存器验证环境。 1. 寄存器模型UVM寄存器模型是一个抽象的表示,用于描述芯片内部的寄存器寄存器字段。它提供了一种结构化的方式来定义寄存器的属性、寄存器字段的位宽和访问权限等。 2. 寄存器层次结构:UVM寄存器模型支持多层级的寄存器结构,可以通过层级关系来描述芯片内部的寄存器模块和子模块。这样可以更好地组织和管理寄存器模型,并提供寄存器之间的相互作用和访问。 3. 寄存器操作:UVM提供了一系列的API,用于执行寄存器读写操作。通过这些API,可以向寄存器模型发送读写请求,并获取响应。同时,还可以对寄存器的访问进行配置和控制,如重置、写入默认值等。 4. 寄存器验证环境UVM寄存器模型可以与其他验证环境进行集成,以验证寄存器功能的正确性。通过使用事务级建模(TLM)接口,可以将寄存器操作与其他验证组件进行交互,并进行功能验证、覆盖率分析和错误注入等。 总之,UVM寄存器模型提供了一种规范化的方法来描述和验证芯片寄存器功能。它具有可重用性、灵活性和扩展性,并能与其他验证组件进行集成,从而提高验证效率和可靠性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值