UVM tips and tricks

本文围绕UVM展开,介绍了使用宏克服包中错误、fork - join_none中使用loop、random机制、uvm_config_db使用注意事项等内容。还提及了刺激对象的factory override、避免使用uvm_printer、循环性能优化等实用技巧,对UVM使用者有较大帮助。

目录

1.Use of Macros to Overcome Errors Faced in Package

2.fork-join_none 中使用loop

3.random 机制

3.1 如果名字重复,使用local

3.2 动态数组需要定义size

3.3 检查randomize 结果

4.uvm_config_db的使用要注意

5. 最小的factory override for stimulus objects

6.避免使用uvm_printer,使用conver2string()

8. Loop performance optimization

9.文件目录

10.sequence 

11.getenv

12.match_string

13.uvm_printer 打印array all element

14.uvm_compare mismatch的时候不打印信息


1.Use of Macros to Overcome Errors Faced in Package

package pkg1;
  `include "adder_design.sv"
  `include "tb.sv"
endpackage

package pkg2;
  `include "adder_design.sv"
  `include "adder_tb.sv"
endpackage

module top();
  `ifndef ADDER_DESIGN
  `define ADDER_DESIGN
  import pkg2::*;
  `endif
  ...
endmodule

2.fork-join_none 中使用loop

使用automatic

module top;
  initial begin
    for (int i = 0; i < 4; i++) begin
	  fork
	    automatic int l = i;
	     display(l);
	  join_none
    end
  end
  task display(int i);
	$display("i = %d", i);
  endtask
endmodule

3.random 机制

3.1 如果名字重复,使用local

rand_success = trans.randomize() with {trans.addr == local::addr; };

3.2 动态数组需要定义size

class Ethernet;
      rand bit [3:0]payload[];
      constraint c { payload.size() == 4;}
endclass

3.3 检查randomize 结果

4.uvm_config_db的使用要注意

a.尽量避免传递数值,有需要的话可以封装成class

b. wildcard谨慎使用

c. it is recommended to minimize the number of uvm_config_db entries.

...

5. 最小的factory override for stimulus objects

Low performance, 因为多次调用了factory中的create

//Low performance code
task body;
  seq_item item;
  repeat (200) begin
    item = seq_item::type_id::crease("item"0;
    start_item(item);
    if (item.randomize()) begin ... end
    finish_item(item);
  endtask

High performane, 只第一次create,然后clone 它

//High performance
task body;
  seq_item orig_item = seq_item::type_id::create("item");
  seq_item item;
  repeat(200) begin
	$cast(item, orig_item.clone());
	start_item(item);
	assert(item.randomize());
	finish_item(item);
  end 
endtask

6.避免使用uvm_printer,使用conver2string()

在class 自定义conver2string

virtual function string convert2string();
      string s = super.convert2string();
      s = { s, $psprintf( "\nname      : %s", get_name()    ) };
      s = { s, $psprintf( "\nflavor    : %s", flavor.name() ) };
      s = { s, $psprintf( "\ncolor     : %s", color.name()  ) };
      return s;
endfunction: convert2string

7.UVM objections 使用

Objections should only be used by the controlling threads, and it is also very necessary to place the objections in the run-time method of the top level test class, or in the body method of a virtual sequence. Using them in any other place is likely to be unnecessary and also cause a degradation in performance.

在virtual sequence中的body;或者在top level的testcase的run-time method

//High Performance code
class sequence extends uvm_sequence#(seq_item);
 
task body;
seq_item item = seq_item::type_id::create("item");
repeat(5) begin
     start_item(item);
     assert(item.randomize());
     finish_item(item);
 end
 
  sequencer seqr;
 
task body;
   `uvm_objection objection = new("objection");
    sequence seq=    sequence::type_id::create("seq");
    objection.raise_objection(seqr);
    seq.start(seqr);
    objection.drop_objection(seqr);
endtask

8. Loop performance optimization

1.loop的性能取决于loop中的工作

下面高效代码,减少了每次循环化size的计算

//Less efficient code
int arr[];
int total = 0;
for(int i = 0;i< arr.size();i++) begin
  total += arr[i];
end

//High Performance Code
int arr[];
int arr_size;
int tot = 0;
arr_size = arr.size();
for(int i = 0; i < arr_size; i++) begin
  tot += arr[i];
end

9.文件目录

10.sequence 

1.避免使用pre_body() and post_body, 另一篇文章有解释

2.启动一个sequence, 推荐使用sequence.start(sequencer);

3.启动一个sequence_item 推荐使用, start_item(), randomize(), finish_item()

11.getenv

import "DPI-C" function string getenv(input string env_name);

 function string get_env(string name);
    return getenv(name); 
 endfunction

12.match_string

   function bit match_string(string s1, string s2);
        int l1,l2;
        bit vld;
        vld = 0; 
        l1 = s1.len();
        l2 = s2.len();
        if (l2 > l1) return 0;

        for(int i = 0;i < l1 - l2 + 1; i ++) 
            if( s1.substr(i,i+l2 -1) == s2) vld = 1; 
        return vld;
    endfunction

13.uvm_printer 打印array all element

在任意地方(tb/test)加入

uvm_default_printer = uvm_default_table_printer;

uvm_default_printer.knobs.begin_elements=-1;

14.uvm_compare mismatch的时候不打印信息


class my_comparer extends uvm_comparer;
  function new ();
    super.new();
    this.show_max = 0;
  endfunction
endclass

my_comparer comparer = new();

left.item.compare(right.item, this.comparer);


<think> 我们正在讨论UVM寄存器模型的读写操作。根据引用内容,我们可以总结如下: 1. 引用[1]提到在env或base_test中通过uvm_config_db设置寄存器模型实例,以便在环境中访问。 2. 引用[2]展示了一个适配器(reg_adapter)的实现,该适配器负责在寄存器操作和总线事务之间进行转换。其中: - reg2bus函数将寄存器操作(uvm_reg_bus_op)转换为总线事务(master_data)。这里需要注意操作类型(读/写)的转换以及数据和地址的传递。 - bus2reg函数将总线事务转换回寄存器操作状态,包括操作类型、数据、地址和状态。 3. 引用[3]概述了使用UVM寄存器模型的步骤,包括创建寄存器字段、寄存器、寄存器块、适配器,以及如何连接和操作。 针对用户的问题:UVM寄存器模型的读写操作(方法、示例、注意事项) ### 读写操作方法 在UVM寄存器模型中,读写操作主要通过以下方法: 1. **前门访问**:通过总线序列产生总线事务来访问硬件寄存器。这需要适配器(adapter)和寄存器模型与总线序列器的连接。 2. **后门访问**:直接通过层次路径访问硬件寄存器,不经过总线。通常用于初始化和快速检查。 ### 读写操作示例 以下是一些关键代码示例: #### 1. 寄存器模型配置 在顶层测试环境中设置寄存器模型实例: ```systemverilog class base_test extends uvm_test; reg_top_block reg_rm; // 顶层寄存器块 virtual function void build_phase(uvm_phase phase); super.build_phase(phase); reg_rm = reg_top_block::type_id::create("reg_rm", this); reg_rm.build(); // 构建寄存器模型 reg_rm.lock_model(); // 锁定模型 // 将寄存器模型实例设置到config_db uvm_config_db #(reg_top_block)::set(this, "*", "reg_top_block", reg_rm); endfunction endclass ``` #### 2. 适配器实现 适配器继承自`uvm_reg_adapter`,需要实现`reg2bus`和`bus2reg`方法: ```systemverilog class reg_adapter extends uvm_reg_adapter; `uvm_object_utils(reg_adapter) function new(string name = "reg_adapter"); super.new(name); endfunction // 将寄存器操作转换为总线事务 virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); bus_transaction tr = bus_transaction::type_id::create("tr"); tr.addr = rw.addr; tr.data = rw.data; tr.kind = (rw.kind == UVM_READ) ? READ : WRITE; // 假设总线定义了READ和WRITE return tr; endfunction // 将总线事务转换回寄存器操作 virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw); bus_transaction tr; if (!$cast(tr, bus_item)) begin `uvm_fatal("CAST_FAIL", "Bus item cast failed") return; end rw.kind = (tr.kind == READ) ? UVM_READ : UVM_WRITE; rw.addr = tr.addr; rw.data = tr.data; rw.status = (tr.status == OK) ? UVM_IS_OK : UVM_NOT_OK; // 简化状态转换 endfunction endclass ``` #### 3. 寄存器模型连接 在环境中连接寄存器模型和适配器: ```systemverilog class my_env extends uvm_env; reg_top_block reg_rm; reg_adapter adapter; uvm_reg_predictor #(bus_transaction) predictor; virtual function void build_phase(uvm_phase phase); // 从config_db获取寄存器模型 if (!uvm_config_db #(reg_top_block)::get(this, "", "reg_top_block", reg_rm)) `uvm_fatal("NO_REG_MODEL", "Register model not found") adapter = reg_adapter::type_id::create("adapter"); predictor = uvm_reg_predictor#(bus_transaction)::type_id::create("predictor", this); endfunction virtual function void connect_phase(uvm_phase phase); // 设置寄存器的映射(map)的适配器 reg_rm.default_map.set_sequencer(bus_sequencer, adapter); // 设置预测器 predictor.map = reg_rm.default_map; predictor.adapter = adapter; // 连接监视器到预测器 bus_monitor.item_collected_port.connect(predictor.bus_in); endfunction endclass ``` #### 4. 读写操作示例 在测试序列中进行读写操作: ```systemverilog class reg_test_seq extends uvm_sequence; `uvm_object_utils(reg_test_seq) reg_top_block reg_rm; uvm_status_e status; uvm_reg_data_t data; virtual task body(); // 通过后门写入寄存器 reg_rm.my_reg.write(status, 'h1234, .path(UVM_BACKDOOR)); // 通过前门读取寄存器 reg_rm.my_reg.read(status, data, .path(UVM_FRONTDOOR)); endtask endclass ``` ### 注意事项 1. **路径选择**:读写操作可以选择前门(UVM_FRONTDOOR)或后门(UVM_BACKDOOR)。后门操作绕过总线,直接访问硬件,但需要正确设置寄存器的后门路径。 2. **状态检查**:每次读写操作后应检查状态(status),确保操作成功。 3. **适配器实现**:确保适配器的`reg2bus`和`bus2reg`方法正确转换事务,特别是操作类型(读/写)和状态。 4. **预测器**:如果使用自动预测,需要连接监视器到预测器,以便在总线事务发生时更新寄存器模型。 5. **锁定模型**:在配置完成后调用`lock_model()`,防止后续修改寄存器模型结构。 6. **同步问题**:前门操作可能需要多个时钟周期,因此需要等待操作完成。而后门操作是零时间的。 §§相关问题§§ 1. 如何设置寄存器的后门路径? 2. 如何处理寄存器读写中的错误状态? 3. 如何实现寄存器模型的覆盖率收集? 4. 在UVM中使用寄存器模型进行复位验证的方法是什么? </think> ### UVM寄存器模型读写操作详解 #### 一、核心方法 1. **前门访问(Frontdoor)**: - 通过总线协议物理访问寄存器 - 使用`read()`/`write()`方法: ```systemverilog uvm_status_e status; uvm_reg_data_t value; my_reg_block.my_reg.read(status, value, .path(UVM_FRONTDOOR)); my_reg_block.my_reg.write(status, 'hFF, .path(UVM_FRONTDOOR)); ``` 2. **后门访问(Backdoor)**: - 直接通过HDL路径访问(不经过总线) - 零时间仿真: ```systemverilog my_reg_block.my_reg.peek(status, value); // 后门读 my_reg_block.my_reg.poke(status, 'hAA); // 后门写 ``` 3. **预定义操作**: ```systemverilog my_reg_block.my_reg.set('h55); // 设置期望值 my_reg_block.my_reg.update(status); // 同步硬件值 my_reg_block.my_reg.mirror(status); // 检查硬件值 ``` #### 二、适配器实现(关键组件) 寄存器操作需通过适配器转换为总线事务[^2]: ```systemverilog class reg_adapter extends uvm_reg_adapter; virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); bus_transaction tr = new(); tr.addr = rw.addr; tr.data = rw.data; tr.kind = (rw.kind == UVM_READ) ? READ : WRITE; // 转换操作类型 return tr; endfunction virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw); bus_transaction tr; if(!$cast(tr, bus_item)) `uvm_fatal(...) rw.data = tr.data; rw.status = (tr.status == OK) ? UVM_IS_OK : UVM_NOT_OK; // 状态映射 endfunction endclass ``` #### 三、完整操作流程 1. **模型配置**: ```systemverilog // 在test中设置全局访问接口 uvm_config_db #(reg_top_block)::set(null, "*", "reg_top_block", reg_rm); ``` 2. **连接适配器**: ```systemverilog // 在环境中连接 reg_rm.default_map.set_sequencer(bus_sequencer, adapter); ``` 3. **执行序列**: ```systemverilog class reg_access_seq extends uvm_sequence; task body(); my_reg.read(status, value); // 自动使用默认访问路径 my_reg.write(status, 'hDEAD); endtask endclass ``` #### 四、关键注意事项 1. **状态检查**: - 必须检查`status != UVM_NOT_OK`,避免静默失败 - 错误状态包括:`UVM_NOT_OK`(操作失败)、`UVM_HAS_X`(存在X态) 2. **路径选择**: ```mermaid graph LR A[读写操作] --> B{路径选择} B -->|物理访问| C[Frontdoor] B -->|直接访问| D[Backdoor] C --> E[通过总线协议] D --> F[零时间访问] ``` 3. **同步问题**: - 前门操作需等待总线响应(异步) - 使用`mirror()`时需考虑硬件更新延迟 - 预测器(predictor)需连接监视器实现自动更新 4. **自定义字段**: ```systemverilog class my_reg extends uvm_reg; uvm_reg_field data_field; virtual function void build(); data_field = uvm_reg_field::type_id::create("data"); data_field.configure(this, 8, 0, "RW", 0, 'h0); // 配置字段属性 endfunction endclass ``` 5. **调试建议**: - 使用`uvm_reg::set_coverage(UVM_CVR_ALL)`启用覆盖率 - 调用`reg_block.print()`检查寄存器映射 - 通过`+UVM_REG_SEQ=path/to/reg`指定后门路径 #### 五、典型错误示例 ```systemverilog // 错误:未检查操作状态 my_reg.write(status, value); // 未验证status // 错误:适配器未正确处理状态 function void bus2reg(...); rw.status = UVM_IS_OK; // 应映射实际总线状态 endfunction ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值