3. 开发可重用的验证组件
3.1 Data Items 建模
Data Items:
- 作为DUT激励的transaction对象
- 验证环境处理的transaction
- 用户定义的类的实例
- 进行transaction级覆盖率收集和检查
注:UVM 类库提供了 uvm_sequence_item 基类,每个用户定义的 data item 都应该直接或间接派生自该基类。
创建一个用户定义 data item:
- 参考 DUT 的 transaction 说明,确定相关特性、约束、任务和函数
- 从 uvm_sequence_item 继承出一个 data item 类
- 定义构造函数
- 添加控制域
- 使用 UVM field 宏使能 print、copy、comparing 等
- 按照需要定义 do_* 函数
UVM 有许多内建的自动化方法:
- print()
- copy()
- compare()
为了帮助 debug 和追踪 transaction,uvm_transaction 基类提供了访问transaction ID的方法 get_transaction_id()。uvm_sequence_item 基类继承自 uvm_transaction,同样有一个 get_transaction_id() 成员函数,可以将 sequence item 与其对应的 sequence 相关联。
下面的例子中,定义了几个随机变量和约束,UVM 宏使能了copy、compare、print等方法。
class simple_item extends uvm_sequence_item;
rand int unsigned addr;
rand int unsigned data;
rand int unsigned delay;
constraint c1 { addr < 16'h2000; }
constraint c2 { data < 16'h1000; }
// UVM automation macros for general objects
`uvm_object_utils_begin(simple_item)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(delay, UVM_ALL_ON)
`uvm_object_utils_end
// Constructor
function new (string name = "simple_item");
super.new(name);
endfunction : new
endclass : simple_item
3.1.1 继承和约束
为了达到验证目标,用户需要在data item的生成中添加更多的约束。在Systemverilog中,通过继承来进一步添加约束。下面的例子为添加了额外约束的继承后的data item。
class word_aligned_item extends simple_item;
constraint word_aligned_addr { addr[1:0] == 2'b00; }
`uvm_object_utils(word_aligned_item)
// Constructor
function new (string name = "word_aligned_item");
super.new(name);
endfunction : new
endclass : word_aligned_item
3.1.2 定义控制域
通常可能不需要生成输入空间内的所有可能数值,在 simple_item 的例子中,delay 属性可能被随机化为0到最大值之间的任意值。覆盖整个范围是没有必要的,但是覆盖小、中、大的delay属性是必要的。要完成这个目的,需要定义控制域,这些控制域同样可以用于覆盖率收集。为了增强可读性,使用枚举类型来代表不同的类别。
typedef enum {ZERO, SHORT, MEDIUM, LARGE, MAX} simple_item_delay_e;
class simple_item extends uvm_sequence_item;
rand int unsigned addr;
rand int unsigned data;
rand int unsigned delay;
rand simple_item_delay_e delay_kind; // Control field
// UVM automation macros for general objects
`uvm_object_utils_begin(simple_item)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_enum(simple_item_delay_e, delay_kind, UVM_ALL_ON)
`uvm_object_utils_end
constraint delay_order_c { solve delay_kind before delay; }
constraint delay_c {
(delay_kind == ZERO) -> delay == 0;
(delay_kind == SHORT) -> delay inside { [1:10] };
(delay_kind == MEDIUM) -> delay inside { [11:99] };
(delay_kind == LARGE) -> delay inside { [100:999] };
(delay_kind == MAX ) -> delay == 1000;
delay >=0; delay <= 1000; }
endclass : simple_item
通过这种方法可以创建更多抽象测试,例如可以为delay_kind 的各个取值分配权重:
constraint delay_kind_d {
delay_kind dist {ZERO:=2, SHORT:=1, MEDIUM:=1, LONG:=1, MAX:=2};
}
当创建data item时,要考虑范围内哪些取值是需要关注的,然后添加控制域来控制这些取值的覆盖率收集以及随机化过程。
3.2 Transaction 级组件
一个简单的 transaction 级验证环境的基本组件包括:
- 一个激励生成器(sequencer)来向DUT产生transaction
- 一个driver将transaction转换为信号级的激励,驱动到DUT接口
- 一个monitor来识别DUT接口上的信号级活动并转换为transaction
- 一个analysis组件,如覆盖率收集或scoreboard,来分析transaction
UVM 中的 TLM 接口的一致性和模块化提高了组件的复用性,每个模块可以通过接口相互连接,而不用考虑各自的内部实现。
上图展示了将各个组件封装到可复用接口级验证组件 agent 的建议结构。
3.3 创建 Driver
所有的driver都应该直接或者间接继承自uvm_driver基类。driver有一个可以和sequencer通信的TLM接口。driver也可以实现一个或者多个 run-time phase来管理操作过程。
创建一个driver:
- 从 uvm_driver 继承一个类
- 如果需要,添加 UVM 域的自动化相关宏来使能内建方法
- 从 sequencer 获取下一个 data item 并驱动
- 声明一个 virtual interface 来连接 DUT
下面的例子中,simple_driver 继承自 uvm_driver,参数为 transaction 类型,使用了 seq_item_port 与 sequencer 进行通信。
class simple_driver extends uvm_driver #(simple_item);
simple_item s_item;
virtual dut_if vif;
// UVM automation macros for general components
`uvm_component_utils(simple_driver)
// Constructor
function new (string name = "simple_driver", uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
string inst_name;
super.build_phase(phase);
if(!uvm_config_db#(virtual dut_if)::get(this, “”,"vif",vif))
`uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(),".vif"});
endfunction : build_phase
task run_phase(uvm_phase phase);
forever begin
// Get the next data item from sequencer (may block).
seq_item_port.get_next_item(s_item);
// Execute the item.
drive_item(s_item);
seq_item_port.item_done(); // Consume the request.
end
endtask:drive_item
endclass:simple_driver
3.4 创建 Sequencer
sequencer 生成激励数据并传递到 driver 进行驱动。UVM 类库提供了 uvm_sequencer 基类,参数为 request 和 response item 类型。uvm_sequencer 基类包含了所有 sequence 与 driver 进行通信所需要的基础功能。sequencer 的声明方式如下,默认的 response 类型和 request 类型相同,如果需要不同的 response 类型,可选的第二个参数必须指定为 uvm_sequencer 基类型。
uvm_sequence #(simple_item, simple_rsp) sequencer;
3.5 连接 Driver 和 Sequencer
driver 和 sequencer 通过 TLM 连接,driver 的 seq_item_port 连接到 sequencer 的 seq_item_export