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

文章目录

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


前言

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


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

UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤_第1张图片

  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如下图描述所示。
UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤_第2张图片
UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤_第3张图片

编写的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.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。
UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤_第4张图片
不同内建sequence,通过test_base中的clp,获取命令行中+seq=后边的参数传递进去。
UVM寄存器模型(三) —— uvm环境中加入寄存器模型的步骤_第5张图片

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,uvm,寄存器模型,uvm加入寄存器模型,方法,步骤)