精华内容
下载资源
问答
  • 模块使用python实现excel到寄存器模型文件的转换功能。使用前在Windows平台需要安装python3.x 以及openpyxl模块,使用时将需要转换的模块寄存器按照examp.xlsx格式填好excel,excel文件名设置为寄存器名字,将excel...
  • 寄存器模型生成工具,可将excel表格直接生成uvm验证使用的寄存器模型
  • 文章目录前言UVM中加入寄存器模型的方法总结 前言 这里主要记录一下,在UVM中加入寄存器模型的方法步骤。 UVM中加入寄存器模型的方法 步骤1:保证前门访问正常。在没有寄存器模型的情况下,,先保证通过寄存器...


    前言

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


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

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

    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.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
    

    总结

    本文结合具体的测试用例以及代码,具体介绍了UVM中加入寄存器模型的方法步骤。

    参考文献

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

    展开全文
  • 寄存器模型

    2021-09-25 20:51:57
    一、概述 二寄存器模型集成 三、寄存器模型的常规方法 四、寄存器模型的应用场景

    一、概述

    二寄存器模型集成

    三、寄存器模型的常规方法

    四、寄存器模型的应用场景

    展开全文
  • 寄存器模型概览

    2021-03-05 14:24:57
    一、寄存器模型概览 寄存器是模块之间互相交谈的窗口,一方面可以通过读出寄存器的状态,获取硬件当前的状况,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。在验证的过程中,寄存器的验证也排在...

    一、寄存器模型概览

    • 寄存器是模块之间互相交谈的窗口,一方面可以通过读出寄存器的状态,获取硬件当前的状况,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。在验证的过程中,寄存器的验证也排在了验证清单的前列,只为只有首先保证寄存器的功能正确,才会使得硬件与硬件之间的交谈是“语义一致”的。如果寄存器配置结果与寄存器配置内容不同,那么硬件无法工作在想要的模式下,同时寄存器也可能无法正确反映硬件的状态。
    • 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。
    • 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能描述。一个寄存器一般由32个比特位构成,将单个寄存器拆分之后,又可以分为多个域(field),不同的域往往代表着某一项独立的功能。单个的域可能有多个比特位构成,也可能由单一比特位构成,这取决于该域的功能模式可配置的数量。而不同的域,对于外部的读写而言,又大致可以分为WO(只写),RO(只读)和RW(读写),除了这些常见的操作属性以外,还有一些特殊行为(quirky)的寄存器,例如读后擦除模式(clean-on-read,RC),只写一次模式(write-one-to-set,W1S)。

    对于MCDF的寄存器模块描述,将0x00功能寄存器和0x10状态寄存器位用图来表示。
    在这里插入图片描述
    通常来讲,一个寄存器有32位宽,寄存器按照地址索引的关系是按字对齐的,上图中的寄存器有多个域,每个域的属性也可以不相同,reserved域表示的是该域所包含的比特位暂时保留以作日后功能的扩展使用,而对保留域的读写不起任何作用,即无法写入而且读出值也是它的复位值。上面的这些寄存器按照地址排列,即可构成寄存器列表,称之为寄存器块,实际上,寄存器块除了包含寄存器,也可以包含存储器,因为它们的属性都近乎读写功能,以及表示为同外界通信的接口。

    如果将这些寄存器有机的组合在一起,MCDF的寄存器功能模块即可由这样一个register block来表示:
    在这里插入图片描述
    一个寄存器可以由多个域构成,而单个域可以包含多个比特位,一个功能模块中的多个寄存器可以组团构成一个寄存器模型。上面的图中除了包含了DUT的寄存器模块(由硬件实现),还有属于验证环境的寄存器模型。这两个模块包含的寄存器信息是高度一致的,属于验证环境的寄存器模型也可以抽象出一个层次化的寄存器列表,该列表所包含的地址、域、属性等信息都与硬件一侧的寄存器内容一致。对于功能验证而言,可以将总线访问寄存器的方式抽象为寄存器模型访问的方式,这种方式使得寄存器后期的地址修改(例如基地址更改)或者域的添加都不会对已有的激励构成影响,从而提高已有测试序列的复用性。

    二、中心化管理方式

    在这里插入图片描述

    通过软件建立寄存器模型的方法要保证与硬件寄存器的内容属性保持一致,这离不开一份中心化管理的寄存器描述文件。寄存器描述文档使用了结构化的文档描述方式,这也是为什么可以通过XML或者Excel(CSV)等数据结构化的方式来实现寄存器的功能描述。

    通过数据结构化的存储方式,可以在硬件和软件开发过程中以不同方式来使用寄存器描述文档:

    • 系统工程师会撰写并维护寄存器描述文档,而后归置到中心化存储路径供其他工程师开发使用。
    • 硬件工程师会利用寄存器描述文件生成寄存器硬件模块(包含各个寄存器的硬件实现和总线访问模块)。
    • 验证工程师会利用寄存器描述文件来生成UVM寄存器模型,以供验证过程中的激励使用、寄存器测试和功能覆盖率收集。
    • 软件工程师会利用该文件生成用于软件开发的寄存器配置的头文件,从而提高软件开发的可维护性。

    三、uvm_reg相关概念

    在构建UVM寄存器模型的过程中,需要用到如下与模型构建相关的类和它们的功能:

    在这里插入图片描述

    四、MCDF寄存器模型

    class ctrl_reg extends uvm_reg;
    	`uvm_object_utils(ctrl_reg)
    	uvm_reg_field reserved;
    	rand uvm_reg_field pkt_len;
    	rand uvm_reg_field prio_level;
    	rand uvm_reg_field chnl_en;
    	function new(string name="ctrl_reg");
    		super.new(name, 32, UVM_NO_COVERAGE);
    	endfunction
    	virtual function build();
    		reserved = uvm_reg_field::type_id::create("reserved");
    		pkt_len = uvm_reg_field::type_id::create("pkt_len");
    		prio_level = uvm_reg_field::type_id::create("prio_level");
    		chnl_en = uvm_reg_field::type_id::create("chnl_en");
    		reserved.configure(this, 26, 6, "RO", 0, 26'h0, 1, 0, 0);
    		pkt_len.configure(this, 3, 3, "RW", 0, 3'h0, 1, 1, 0);
    		prio_level.configure(this, 2, 1, "RW", 0, 2'h3, 1, 1, 0);
    		chnl_en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 0);
    	endfunction
    endclass
    
    class stat_reg extends uvm_reg;
    	`uvm_object_utils(stat_reg)
    	uvm_reg_field reserved;
    	rand uvm_reg_field fifo_avail;
    	function new(string name="stat_reg");
    		super.new(name, 32, UVM_NO_COVERAGE);
    	endfunction
    	virtual function build();
    		reserved = uvm_reg_field::type_id::create("reserved");
    		fifo_avail = uvm_reg_field::type_id::create("fifo_avail");
    		reserved.configure(this, 24, 8, "RO", 0, 24'h0, 1, 0, 0);
    		fifo_avail.configure(this, 8, 0, "RO", 0, 8'h0, 1, 1, 0);
    	endfunction
    endclass
    
    class mcdf_rgm extends uvm_reg_block;
    	`uvm_object_utils(mcdf_rgm)
    	rand ctrl_reg chnl0_ctrl_reg;
    	rand ctrl_reg chnl1_ctrl_reg;
    	rand ctrl_reg chnl2_ctrl_reg;
    	rand ctrl_reg chnl0_stat_reg;
    	rand ctrl_reg chnl1_stat_reg;
    	rand ctrl_reg chnl2_stat_reg;
    	uvm_reg_map map;
    	function new(string name="mcdf_rgm");
    		super.new(name, UVM_NO_COVERAGE);
    	endfunction
    	virtual function build();
    		chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg");
    		chnl0_ctrl_reg.configure(this);
    		chnl0_ctrl_reg.build();
    		chnl1_ctrl_reg = ctrl_reg::type_id::create("chnl1_ctrl_reg");
    		chnl1_ctrl_reg.configure(this);
    		chnl1_ctrl_reg.build();
    		chnl2_ctrl_reg = ctrl_reg::type_id::create("chnl2_ctrl_reg");
    		chnl2_ctrl_reg.configure(this);
    		chnl2_ctrl_reg.build();
    		chnl0_stat_reg = ctrl_reg::type_id::create("chnl0_stat_reg");
    		chnl0_stat_reg.configure(this);
    		chnl0_stat_reg.build();
    		chnl1_stat_reg = ctrl_reg::type_id::create("chnl1_stat_reg");
    		chnl1_stat_reg.configure(this);
    		chnl1_stat_reg.build();
    		chnl2_stat_reg = ctrl_reg::type_id::create("chnl2_stat_reg");
    		chnl2_stat_reg.configure(this);
    		chnl2_stat_reg.build();
    		//map name, offset, number of bytes, endianess
    		map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
    		map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");
    		map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");
    		map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
    		map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");
    		map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
    		map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
    		lock_model();
    	endfunction	
    endclass
    

    五、寄存器建模

    关于寄存器建模的基本要点和顺序:

    • 在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_reg_field::configure()函数来进一步配置各自属性。
    • 在定义uvm_reg_block时,需要注意reg_blockuvm_memuvm_reg以及uvm_reg_map的包含关系。首先uvm_reguvm_mem分别对应着硬件中独立的寄存器或者存储,而一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型,其中可以容纳多个uvm_reguvm_mem实例,其次map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_reg之后,需要调用uvm_reg::configure()去配置各个uvm_reg实例的属性。
    • 因为uvm_reg_map也会在uvm_reg_block中例化,在例化之后需要通过uvm_reg_map::add_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor)中给出正确的地址。
    • uvm_reg_block可以对更大的系统做寄存器建模,这意味着uvm_reg_block之间也可以存在层次关系,上层uvm_reg_blockuvm_reg_map可以添加子一级uvm_reg_blockuvm_reg_map,用来构建更全局的“版图”,继而通过uvm_reg_blockuvm_reg_map之间的层次关系来构建更系统的寄存器模型。

    六、模型使用流程

    在这里插入图片描述

    展开全文
  • 寄存器模型 — UVM

    2021-06-19 11:11:08
    文章目录基本概念寄存器模型的集成访问寄存器的方式1. 前门访问2. 后门访问3.前门访问和后门访问的比较寄存器模型的常规方法1. mirror、desired 和 actual value2. prediction的分类3. uvm_reg 的访问方法4. uvm_mem...

    一、基本概念

    寄存器模型的组成:

    • 寄存器块(uvm_reg_block)由包含很多的寄存器(uvm_reg),也可以有存储器(uvm_mem);
    • 每个寄存器(uvm_reg)都由很多的域(uvm_reg_field)组成;
    • 单个域(uvm_reg_filed)包含多个bit位。

    寄存器模型相关类(均为Object类型)

    类名功能
    uvm_reg_field用来针对寄存器功能域来构建对应的比特位
    uvm_reg与寄存器相匹配,其内部可以例化和配置多个uvm_reg_field对象
    uvm_mem匹配硬件存储模型
    uvm_reg_map用来指定寄存器列表中各个寄存器的偏移地址、访问属性以及对应的总线 ,并将其转换成可以访问的物理地址(因为寄存器模型中的地址一般都是偏移地址,而不是绝对地址)
    uvm_reg_block可以容纳多个寄存器(uvm_reg)、存储器(uvm_mem)和寄存器列表(uvm_reg_map)

    寄存器模型配置的代码如下:

    class ctrl_reg extends uvm_reg;//寄存器
        uvm_reg_field reserved;
        rand uvm_reg_field fifo_avail;
        ...
        virtual function build();
            reserved = uvm_reg_field::type_id::create("reserved");  
            reserved.configure(this,24,8,"RO",0,24'h0,1,0,0);
            fifo_avail = uvm_reg_field::type_id::create("fifo_avail");
            fifo_avail.configure(this,8,0,"RW",0,8'h0,1,1,0);
            ...
        endfunction
    endclass
    
    class top_rgm extends uvm_reg_block;
        rand ctrl_reg chnl0_ctrl_reg;
        rand ctrl_reg chnl1_ctrl_reg;
    	...
        uvm_reg_map map; //声明 map
        
        virtual function build();
            chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg"); //实例化 uvm_reg 
            chnl0_ctrl_reg.configure(this);// 配置uvm_reg,将 uvm_reg 关联到 uvm_reg_block 上
            chnl0_ctrl_reg.build(); //手动调用 uvm_reg 的build(),实例化reg中的各个域
            ...
            map = create_map("map",'h0,4,UVM_LITTLE_ENDIAN,0);//实例化map
            map.add_reg(chnl0_ctrl_reg,32'h0000_0000,"RW");//将 uvm_reg 添加到 map 中
            ...
            lock_model();//不允许外部访问
        endfunction
    endclass
    

    解释上面代码:

    • uvm_reg_field(域)要在创建和实例化之后,还要在 uvm_reg 中调用configure( )函数对该域进行参数配置。其中:第一个参数指定关联到的寄存器uvm_reg;第二个参数是位数长,第三个是起点位,第四个为访问属性(RW、RO等),第五个为是否volatile,第六个为上电复位后的默认值,第七个为是否可以复位(一般都可以复位,为1),第八个表示是否可以随机化,第九个表示是否可以单独存取。(详细见UVM实战P222)
    • 一个uvm_reg_block 中一定要对应一个uvm_reg_map,调用uvm_reg_block中的 create_map()实例化 map 。其中:第一个参数时名字,第二个参数是基地址,第三个参数是系统总线的宽度(注意单位是按byte计),第四个是大小端,第五个参数是是否可以按byte寻址。
    • uvm_reg_map 是为了存储所有寄存器(uvm_reg)的地址,因此必须要将实例化的寄存器加入到 map 中,否则将无法实现前门访问。通过map.add_reg( ) 添加寄存器,其中:第一个参数是要加入的 uvm_reg_block ,第二个参数是寄存器地址,第三个参数是此寄存器的存起方式。

    注意:

    • uvm_reg在创建后还要调用 configure( )函数,将 uvm_reg 指定关联到哪个 uvm_reg_block 上;
    • 同一个 uvm_reg_block 可以包含多个 uvm_reg_map ,各个 map 可以分别对应不同总线或不同地址段。
    • 寄存器的地址由基地址和偏移地址组成;
    • lock_model( ),不允许外部对这个寄存器模型做访问;
    • 在uvm_reg、uvm_reg_block 中都要定义 build( )函数,由于它们都是 object 类型,所以需要在上一层自己手动调用 build()函数,实现层层递进的寄存器配置。
    • 寄存器模型(top_rgm)在集成环境中时,也需要调用 configure() 和 build() 来完成配置和实例化。但是参数含义是不同的,具体见后文2.3章节。

    二、寄存器模型的集成

        寄存器模型可以使的硬件级别的抽象级上升到寄存器级别,将过去由具体地址指定寄存器的方式,通过指定寄存器名称来替代,同时寄存器模型封装的一些函数使得可以直接对域进行操作。无论寄存器基地址如何变化,寄存器级别实现的配置序列都要比硬件级别的序列具备更好的维护性。那么寄存器模型是怎么集成到测试平台中的呢?

    1. 寄存器模型与总线的桥接

        首先,需要建立寄存器模型与总线的沟通桥梁,让寄存器模型的操作和总线上的操作可以相互转换。而对寄存器模型的操作,Driver 是不能直接“读懂”的。同样地,Monitor采集的总线事务,寄存器模型也不认识,寄存器就没办法做相应地更新和检查。这时就需要一个 adapter 来完成寄存器事务到总线事务的转换。其中步骤包括:

    • 寄存器序列将带有目标寄存器的相关信息存放到 uvm_reg_item 实例(uvm_reg_bus_op)中,送往 adapter;
    • adapter 在接收到 uvm_reg_item(uvm_reg_bus_op)之后,转换成总线可以“认识”的 bus_seq_item 总线事务类型,再由 adapter 将 bus_seq_item 送给sequencer,继而发给Driver;
    • 总线从bus_seq_item中获取地址、数据、操作模式等信息后,发起总线的读写访问;
    • 如果总线上有反馈信号返回,则该信号会成为一个bus_seq_item 的总线事务,由总线sequencer 按照response item 的路径返回至 adapter,adapter对信号处理后,生成 uvm_reg_bus_op,最终作为返回值交给寄存器操作有关的方法。

    2. adapter的实现

    adapter 承担着寄存器模型与总线之间相互沟通的重要作用,那要怎么实现?如下:

    • uvm_reg_bus_op(寄存器操作的transaction)与总线transaction中各自的数据映射,即完成uvm_reg_bus_op和总线transaction之间的转换;
    • 必须要实现uvm_reg_adapter中预定义的两个函数 reg2bus( ) 和 bus2reg( ) ,通过这两个函数实现两种transaction的数据映射;
    • 如果总线支持byte访问,可以在adapter中使能support_byte_enable = 1;
    • 如果总线要返回response数据,应当在adapter中使能provides_response = 1;如果总线不支持返回RSP(如没有调用put_response(RSP)或item_done(RSP) ),则不能使能,否则adapter会致使验证环境挂起;
    • 寄存器的操作,无论是读操作还是写操作,都要经历调用reg2bus(),继而发起总线事务,在完成事务发回反馈后调用bus2reg(),将总线的数据返回至寄存器操作层面。
    • uvm_reg_bus_op类的成员有6个域,详见参考手册或红宝书P415

    adapter 的主要代码如下:

    class top_adapter extends uvm_reg_adapter;
    	`uvm_object_utils(adapter)
        ...
        //reg2bus()函数,完成uvm_reg_bus_op --> bus_trans的内容映射
        function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
        	...
        endfunction
        //bus2reg()函数,完成bus_trans --> uvm_reg_bus_op的内容映射
        function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
        	...
        endfunction
    endclass
    

    注意:

    • adapter 要继承于 uvm_reg_adapter ;
    • adapter 是 Object 类型;
    • 要实现 reg2bus 和 bus2reg 两个方法,完成寄存器事务和总线事务的转换。

    3. 寄存器模型rgm和adapter的集成

    步骤如下:

    • 对于寄存器模型的集成,一般倾向于从顶层传递的方式,即最终从test层传入寄存器模型句柄;
    • 寄存器模型在创建之后,要显式的调用build函数,因为uvm_reg_block是object类型,build函数并不会自动执行;
    • 顶层环境的connect_phase 阶段,需要通过uvm_reg_map::set_sequencer(“bus sequencer句柄”,“adapter句柄”)的方式,将寄存器模型的map组件与bus sequencer和adapter连接,这会将map(寄存器信息)、sequencer(总线侧激励驱动)和 adapter(寄存器级别和硬件总线级别的桥接)关联在一起。只有通过这一步,才能完成adapter的桥接功能。

    代码如下:如在 top_env 中集成rgm 和 adapter

    class top_env extends uvm_env;
    	top_rgm rgm;
    	top_adapter adapter;
    ...
    endclass
    
    function void top_env::build_phase(uvm_phase phase);
    	super.build_phase(phase);
    
    	rgm = top_rgm::type_id::create("rgm",this);
    	rgm.configure(null,"");
    	rgm.build();
    	
    	adapter = top_adapter::type_id::create("adapter");
    	
    endfunction
    
    function void top_env::connect_phase(uvm_phase phase);
    	rgm.map.set_sequencer(env.agt.sqr,adapter);//将 map 与 sequencer 和 adapter 连接
    endfunction
    

    注意:

    • 寄存器模型在集成时,调用了configure() ,其中:第一个参数是 parent block ,第二个参数是后门访问路径。

    三、访问寄存器的方式

    • 前门访问:是在寄存器模型上做的读写操作,通过总线UVC实现总线上的物理时序访问,是真实的物理操作;
    • 后面访问:是利用UVM DPI( uvm_hdl_read()、uvm_hdl_deposite() ),将寄存器的操作直接作用到DUT内的寄存器变量,而不是通过物理总线访问。这种方式速度非常快。

    1. 前门访问

    在uvm_reg_sequence里对寄存器操作的两种方式:

    • uvm_reg::read()/write() 。参数path需要指定为UVM_FRONTDOOR,其他除了status和value需要传入,其他可采用默认值;
    • uvm_reg_sequence::read_reg()/write_reg() 。参数path需要指定为UVM_FRONTDOOR;

    2. 后门访问

    在进行后门访问时,在寄存器模型建立时将各个寄存器映射到了DUT一侧的HDL路径

    • uvm_reg_block::add_hdl_path( ) 将寄存器模型block关联到DUT一端;
    • uvm_reg::add_hdl_path_slice( ) 将寄存器模型中各个寄存器成员与HDL一侧的地址映射;

    寄存器模型与DUT各个寄存器的路径映射:

    class mcdf_reg extends uvm_reg_block;
        ...
        virtual function build();
            ...
            add_hdl_path("reg_backdoor_access.dut");
            chnl0_ctrl_reg.add_hdl_path_slice($sformatf("reg[%0d]",`SLV0_RW_REG),0,32);
            ...
           
            lock_model();//保证模型不会被其他用户修改
        endfunction    
    endclass
    

    在寄存器模型完成HDL路径映射后,才可以利用uvm_reg或uvm_reg_sequence 自带的方法进行后门访问:

    • uvm_reg::read()/write() 。参数path需要指定为UVM_BACKDOOR,其他除了status和value需要传入,其他可采用默认值;
    • uvm_reg_sequence::read_reg()/write_reg() 。参数path需要指定为UVM_BACKDOOR;
    • uvm_reg::peek()/poke() 。分别对应读取寄存器(peek)和修改寄存器(poke)两种操作。这两种方法只针对后门访问,无需指定UVM_BACKDOOR。

    3.前门访问和后门访问的比较

    前门访问后门访问
    通过总线协议访问,耗时,且在总线访问结束时,才能结束前门访问通过UVM DPI关联寄存器信号路径,直接读取或修改硬件,不耗时, 零时刻响应
    一般读写只能按字(word)读写,无法直接读写寄存器域可以对寄存器或寄存器域直接做读写
    依靠监测总线来对寄存器模型内容做预测依靠auto predication方式自动对寄存器内容做预测
    正确反映了时序关系不受硬件时序控制,对硬件做后门访问可能发生时序冲突
    通过总线协议,可以有效捕捉总线错误,继而验证总线访问路径不受总线时序功能影响

    实际应用场景:(P420)

    • 可以先通过前门访问验证寄存器访问的物理通路工作正常,在前门访问被验证充分的前提下,可以在后续的测试中使用后门访问来节省访问多个寄存器的时间;
    • 先通过后门访问随机化整个寄存器列表(在一定的随机限制),随后再通过前门访问来配置寄存器。好处在于,不再只是通过设置复位之后的寄存器这种更有确定性的场景,而是通过让测试序列一开始的寄存器值都随机化来模拟无法预期的硬件配置场景,可以检查意想不到的边界情况;
    • 当寄存器出现地址不匹配的情况时,即便通过先写再读的方式,也无法有效测试出来。可以先通过前门访问配置寄存器之后,在通过后门访问来判断HDL地址映射的寄存器变量值是否改变,最后再通过前门访问读取寄存器的值,由此可以发现地址映射到错误寄存器的问题;
    • 如果DUT实现了一些特殊的寄存器,例如只能写一次的寄存器,建议使用前门访问确保反映真实的硬件行为。

    四、寄存器模型的常规方法

    1. mirror、desired 和 actual value

    • 寄存器模型中每个寄存器的域中都有两个值:镜像值(mirror value)和期望值(desired value);
    • 镜像值:表示当前硬件的已知状态值。往往由模型预测给出。
    • 期望值:是先利用寄存器模型修改软件对象值,而后利用该值更新硬件值(actual value)。

    注意:

    • 镜像值可以在前门访问时通过观察总线或后门访问时通过自动预测的方式得到;
    • 镜像值可能与真实硬件值不一致,例如,状态寄存器的镜像值就无法与硬件实际值保持同步,;
    • mirror value 和 desired value是寄存器模型的属性, actual value对应着硬件的真实数值;
    • mirror value只能做predictor时可以修改,是predict预测出来的。而randomize( )寄存器模型中每个field的时候,就是在设定desired value;desired value紧接着会去更新硬件的actual value,在硬件更新后,又会更新软件一侧的mirror value。

    2. prediction的分类

    分为两种 predict 方式,自动预测和显示预测。

    2.1 自动预测(auto predication)

        如果在环境中没有继承predicator,而是利用寄存器操作来自动记录每一次寄存器的读写数值,并且在后台调自动用predict( ) 方法的话,称为自动预测。自动预测需要在寄存器模型中打开此功能,如下:在env中集成寄存器模型时打开

    function void top_env::connect_phase(uvm_phase phase);
    	...
    	rgm.map.set_auto_predict(1); //打开自动预测功能
    endfunction
    

    注意:

    • 如果其他sequence直接在总线层面上对寄存器进行操作,没有通过寄存器级别的 write( ) / read( )操作方法,或者是其他总线来访问寄存器等,都无法自动得到寄存器的镜像值和期望值;
    • 后门访问时,只能使用自动预测,因为后门访问没有对总线层面操作;

    2.2 显示预测

        显示预测需要在顶层环境中集成 predictor(由参数化类uvm_reg_predictor例化),在总线上通过 monitor 捕捉总线事务。monitor一旦捕捉到了有效事务,会发送给 predictor,再利用 adapter 的桥接作用,实现事务信息转换,并将转换后的寄存器模型有关信息更新到map中,完成对寄存器模型的更新。显示预测对寄存器数值的预测更加准确。

    predictor的集成

    • 声明uvm_reg_predictor #(“传递事务的类型”) mcdf2reg_predictor ,指明需要传递的参数的类型;
    • 给mcdf2reg_predictor做例化,注意是component类型,需要传递两个参数;
    • 需要将寄存器模型中的adapter和map的句柄与predictor中的adapter和map句柄做连接。这样predicator可以利用adapter里面的bus2reg( )函数,生成一个uvm_reg_bus_op事务,根据reg_map里面各个寄存器的地址,更新对应每一个寄存器的数值;
    • 将采集事务的monitor中的analysis port与predictor做连接;

    代码如下:如在 top_env 中集成

    class top_env extends uvm_env;
    ...
    	top_rgm rgm;
    	top_adapter adapter;
    	uvm_reg_predict#(bus_transaction) predict;
    ...
    endclass
    
    function void top_env::build_phase(uvm_phase phase);
    	super.build_phase(phase);
    
    	rgm = top_rgm::type_id::create("rgm",this);
    	rgm.configure(null,"");
    	rgm.build();
    	
    	adapter = top_adapter::type_id::create("adapter");
    	predict = uvm_reg_predict#(bus_transaction)::type_id::create("predict",this);
    endfunction
    
    function void top_env::connect_phase(uvm_phase phase);
    	rgm.map.set_sequencer(env.agt.sqr,adapter);//将 map 与 sequencer 和 adapter 连接
        agt.monitor.analysis_port.connect(predictor.bus_in);//将monitor 的 TLM 端口与 predict 连接
        predictor.map = rgm.map;//设置predict中的map
        predictor.adapter = adapter;//连接 predict 和 adapter 
    endfunction
    

    注意:

    • predictor是一个component类型
    • 注意map组件与bus sequencer和adapter连接(set_sequencer),这样adapter才能起到桥接的作用;

    3. uvm_reg 的访问方法

    【常用访问方法:】

    • read( )/write( ): 前门(reg)、后门( reg和field )访问都可,读回/修改寄存器的实际值(actual value);
    • peek( )/poke( ): 只能在后门( reg和field )访问中用,读回/修改寄存器的实际值(actual value);
    • mirror( ): 前门(reg)、后门(block和reg)访问,读回硬件的实际值,然后更新或检查寄存器模型的镜像值
    • updata( ): 前门、后门访问(均为block和reg),如果期望值/镜像值不同于实际值,则修改硬件的真实值
    • get( )/set( ): 只能在前门访问中使用,获取reg和field的期望值
    • reset( )/get_reset( ): 只能在前门访问中使用,复位/获取复位后的block、 reg和field的期望值和镜像值,即复位的是寄存器模型;

    uvm_reg_sequence提供类似的方法的方法,均是针对reg对象,不涉及field和block;

    注:在register model中,存放这些mirror 和desired value的地方其实是分别存储在各个uvm_reg_field ,而不是uvm_reg中。


    【mirror、desired 和 actual value数值变化的时序:】

    • 对于前门访问的read( )和write( ) ,在总线事务完成时,镜像值和期望值才会更新为与总线上相同的值,这种预测方式是显示预测。
    • 对于peek( )/poke( ),以及后门访问模式下的read( )和write( ) ,由于不通过总线,默认采取自动预测的方式,因此在方法调用返回后,镜像值和期望值也就相应修改

    【寄存器访问方法的使用:】

    • 复位操作——reset( )复位的是寄存器模型,所以应当在寄存器模型捕捉到复位事件时(如@(negedge p_sequencer.vif.rst_n)),为了保持和硬件行为的同步,应当对寄存器模型也进行复位。复位后,可以用get_reset( ) 获取寄存器模型复位值,同前门访问read( )读回来的寄存器复位值做比较 ,判断硬件各个寄存器是否复位;
    • 检查上一次配置是否生效——mirror()不会返回读回来的硬件实际值,但是会将对应的镜像值修改。在修改镜像值之前,还可以传递UVM_CHECK参数,将读回来的数值与模型的镜像值进行比较。代码如: “block/reg”.mirror(status,UVM_CHECK,UVM_FRONTDOOR,.PARENT(this));对于状态寄存器也可以不做比较,因为状态寄存器随时可能被硬件内部逻辑修改;
    • 对寄存器做批量修改——运用set( ) 和 updata( )。在配置寄存器时先对寄存器模型随机化(随机化就是在设定期望值),然后再用set()方法配置个别寄存器或寄存器域,当寄存器的期望值与镜像值不相同时,可以通过updata( )方法将不相同的寄存器通过前门或后门访问的方式做全部修改。

    4. uvm_mem与uvm_reg的联系与差别

    uvm_mem类可以用来模拟RW(读写)、RO(只读)和WO(只写)类型的存储,并且可以配置存储器的数据宽度和地址范围。


    虽然uvm寄存器模型也可以用来存储建模,但是与uvm_men的异同:

    • uvm_mem不支持预测和影子存储(即没有镜像值和期望值);
    • uvm_mem也支持前门访问和后门访问;
    • uvm_mem不但具有常规的访问方法read( )、write( )、peek( )和poke( ),也提供了burst_read( ) 和 burst_write( ),可以实现BURST访问形式。

    uvm_men具有访问硬件存储的方法,相比于直接利用总线访问的优点在于:

    • 类似于寄存器模型访问寄存器,利用uvm_mem访问硬件存储器便于维护和复用;
    • 在访问过程中,可以利用模型的地址范围来预测硬件的地址范围是否全部覆盖;
    • uvm_mem可以先通过后门访问预先加载存储内容,然后通过前门访问读取存储内容,继而做数据对比,不但可以节省时间,还可以在测试方式上保持前后一致性;

    uvm_mem要想实现BURST形式,需要考虑的因素:

    • 目前挂载的总线UVC是否支持BURST访问,例如APB不支持;
    • burst_read( ) 和 burst_write( )参数列表中的一项**uvm_reg_data_t value[ ]**采用的是数组的形式,不再是单一的变量,即表示用户可以传递多个数据。在后台,value[ ]的数据首先需要装载到uvm_reg_item对象中,但是uvm_reg_item的两个成员变量需要指定:element_kind = UVM_MEM,kind = UVM_BURST_READ ;
    • 其他的见《芯片漫游指南》P426
    展开全文
  • 寄存器模型(1)

    2021-08-07 18:37:39
    寄存器 以下寄存器描述: 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能...
  • 寄存器模型的常规方法

    千次阅读 2021-03-06 19:19:26
    在应用寄存器模型的时候,除了利用它的寄存器信息,也会利用它来跟踪寄存器的值。寄存器模型中的每一个寄存器,都应该有两个值,一个是镜像值(mirrored value),一个时期望值(desired value)。期望值是先利用寄存器...
  • Register Access without RAL Model  In this section will see an example that shows one of the ways to access DUT registers without the UVM RAL Model.Let’s consider a DMA design which consists of ...
  • 通过寄存器模型的常规方法,可以用来检查寄存器,以及协助检查硬件设计逻辑进和比对数据。 在软件实现硬件驱动和固件层时,也会实现类似寄存器模型镜像值的方法,即在寄存器配置的底层函数中,同时也声明一些全局的...
  • UVM寄存器模型

    2021-02-17 16:58:40
    为什么需要UVM寄存器模型 @啥是知乎–总结UVM中的寄存器模型 真实DUT里的任何IP都会有寄存器,这些IP都会有一组配置总线,通过配置总线配置IP里的寄存器来更改IP的行为。 若没有寄存器模型,怎么访问寄存器? 假设...
  • UVM的寄存器模型

    2022-01-05 10:23:48
    概述 寄存器 我们知道寄存器是模块之间交谈的窗口,一方面可以通过...UVM的寄存器模型是一组高级抽象的类,用来对DUT中具有地址映射的寄存器和存储器进行建模。它非常贴切的反映DUT中寄存器的各种特性,可以产生激
  • UVM中的寄存器模型

    千次阅读 2020-10-15 15:45:03
    寄存器模型简介 1. 通常来说,DUT中会有一组控制端口,通过控制端口,可以配置DUT中的寄存器,DUT可以根据寄存器的值来改变其行为。这组控制端口就是寄存器配置总线。 在没有寄存器模型之前,只能启动sequence通过...
  • 文章目录一、对寄存器模型的配置怎么反映到总线上?二、怎么保证寄存器模型与寄存器保持一致 ?三、寄存器模型的优势? ????寄存器模型的基础知识可以看:UVM寄存器模型 寄存器模型是根据寄存器描述文档所建立,是...
  • 文章目录 前言 一、uvm寄存器模型的生成命令 二、ralgen -full64 -h 帮助命令 总结 前言 ralgen是VCS工具自带的脚本,用于uvm中生成寄存器模型,本文主要介绍uvmgen脚本。首先,介绍在uvm中,利用ralgen脚本,生成...
  • 寄存器模型理解

    千次阅读 2019-11-27 17:28:01
    寄存器模型 是一个model, 模拟的是reg的行为,就像reference model 模拟的是design的行为 需求: 包括各个寄存器字段描述、寄存器、寄存器组、寄存器地址映射等信息。 前门和后门访问 前门访问需要...
  • UVM 寄存器模型的应用场景 在了解了寄存器模型的常规方法之后,我们需要考虑如何利用这些方法来检查寄存器、以及协助检查硬件设计逻辑和比对数据。 在软件实现硬件驱动和固件层时,也会实现类似寄存器模型镜像值的...
  • uvm_寄存器模型RAL

    2021-07-08 20:29:00
    寄存器是硬件实际存在的,它包含状态寄存器和配置寄存器,将寄存器抽象建模得到的寄存器模型(register abstraction model)RAL model,可以使得reference model 只与寄存器模型打交道,将读写操作交由寄存器模型来...
  • UVM学习笔记--寄存器模型 Register Model

    万次阅读 多人点赞 2019-06-13 17:43:53
    1.寄存器模型( Register model )简介 UVM的寄存器模型是一组高级抽象的类,用来对DUT中具有地址映射的寄存器和存储器进行建模。它非常贴切的反映DUT中寄存器的各种特性,可以产生激励作用于DUT并进行寄存器功能检查...
  • 寄存器模型集成

    2021-03-05 16:17:10
    当cmd为读指令时,即需要从cmd_addr对应的寄存器中读出数据,在下一个周期,cmd_addr对应的寄存器数据被输送至cmd_data_out接口。 二、总线UVC的示例 class mcdf_bus_trans extends uvm_sequence_item; rand bit[1...
  • 目录寄存器模型对DUT的模拟期望值与镜像值常用操作及其对期望值和镜像值的影响寄存器模型中一些内建的sequence*检查后门访问中hdl路径的sequence*检查默认值的sequence*检查读写功能的sequence 寄存器模型对DUT的...
  • DUT中寄存器的值可能是实时变更的, 寄存器模型并不能实时地知道这种变更, 因此, 寄存器模型中的寄存器的值有时与DUT中相关寄存器的值并不一致。 对于任意一个寄存器, 寄存器模型中都会有一个专门的变量用于最大...
  • 文章目录前言一、寄存器描述表格二、生成 .ralf 寄存器文件2.1 field2.2 register2.3 register block2.4 system三、产生UVM寄存器模型 前言     在验证时,寄存器模型是必不可少的一个环境,且在项目中可能面临...
  • uvm寄存器模型RAL

    2021-01-31 16:32:12
    uvm寄存器模型RAL
  • UVM — 寄存器模型相关的一些函数

    千次阅读 2020-10-10 11:50:54
     寄存器模型中的寄存器值应该与DUT保持同步,但是由于DUT的值是实时更新的,所以寄存器模型并不能实时知道这种更新,在寄存器模型中专门有个值来尽可能与DUT中寄存器的值保持一致,叫镜像值(mirrorred value)。...
  • 目录复杂的寄存器模型*层次化的寄存器模型*reg_file的作用*多个域的寄存器*多个地址的寄存器*加入存储器 复杂的寄存器模型 *层次化的寄存器模型 前面寄存器模型是一个最小、最简单的寄存器模型。在整个实现过程中...
  • 1、预测功能(predict) 的实现有两种方式: 实现方式1,使用平台中的driver实现,打开方式是set_auto_predice(1);... 备注:预测功能主要是为了更新寄存器模型中的镜像值和期望值,详细解释见白皮书P251 ...
  • 一方面可以通过读出寄存器的状态,获取硬件当前的状况,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。而在验证的过程中,寄存器的验证也排在了验证清单的前列,因为只有首先保证寄存器的功能正确...
  • UVM——寄存器模型相关的一些函数

    千次阅读 2019-09-25 21:35:00
     寄存器模型中的寄存器值应该与DUT保持同步,但是由于DUT的值是实时更新的,所以寄存器模型并不能实时知道这种更新,在寄存器模型中专门有个值来尽可能与DUT中寄存器的值保持一致,叫镜像值(mirrorred value)。...
  • 写在前面:梳理张强《UVM实战卷Ⅰ》第七章 UVM中的寄存器模型,感觉自己梳理的思路不是很清晰,因此依据另外一份资料做相对清晰的梳理,一些基本概念可能不会提及,建议可以参考白皮书. 目录 一.UVM寄存器 1.寄存器模型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 79,821
精华内容 31,928
关键字:

寄存器模型