【IC每日一题:IC验证面试_UVM-1】
IC验证面试_UVM-1
- 2 UVM相关
- 2.1 什么是UVM,以及简述UVM的工厂机制
- 2.2 UVM从哪里启动,接口怎么传递到环境中?
- 2.3 UVM的优势,为什么要用UVM?对UVM验证方法学的理解,一个具体的uvm示例?
- 2.4 UVM的树形结构,及UVM验证组件组成?
- 2.5 virtual sequencer 和sequencer的区别? sequence和sequencer的关系? virtual sequence和sequence的区别?
- 2.6 平台往里面输入数据的话怎么输入,sequence,sequencer和driver之间的通信;
- 2.7 启动sequence的方法
- 2.8 UVM中哪些继承于component,哪些继承于object?
【博客首发与微信公众号《漫谈芯片与编程》,欢迎专注一下】
本篇博客开始介绍IC验证基本功–UVM的相关问题;
2 UVM相关
2.1 什么是UVM,以及简述UVM的工厂机制
UVM(Universal Verification Methodology)是一种用于验证硬件设计的方法学,它提供了一套标准化的框架和库,以帮助验证工程师更高效地开发和管理验证环境。
UVM的工厂机制是其核心特性之一,它允许用户在运行时动态地创建和配置对象,从而提高了验证环境的灵活性和可重用性。
工厂机制的特点:
- 注册:在UVM中,所有的组件和对象都可以通过工厂进行注册。注册后的类可以在运行时通过工厂创建实例。注册的方式是在类定义中使用uvm_component_utils或uvm_object_utils宏,会在构造函数中自动调用 uvm_factory::register() 方法。
- 动态创建:对象的创建通常通过工厂来完成。工厂提供了一个全局的接口,用于创建和配置对象。用户可以通过工厂的create方法来创建对象,并指定对象的类型和参数。
- 替换(类型覆盖和重载):UVM的工厂机制允许在运行时替换已创建的对象。这可以通过工厂的override_by_type或override_by_name方法来实现。允许用户在不修改原始代码的情况下,用一个新的类替换现有的类。这对于在不改变原有设计的情况下引入新的功能或进行调试非常有用。
- 查找:工厂还提供了查找对象的方法,用户可以通过对象的类型或名称来查找已创建的对象;查找机制uvm_factory::get();
- 参数化对象创建:UVM 工厂支持创建参数化的对象实例,可以在创建对象时传递参数,从而影响对象的行为或属性。
// driver.sv
class driver extends uvm_driver#(transaction);
`uvm_component_utils(driver)
//...
endclass
// monitor.sv
class monitor extends uvm_monitor;
`uvm_component_utils(monitor)
//...
endclass
//-----使用工厂机制
// test.sv
class test extends uvm_test;
driver drv;
monitor mon;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = driver::type_id::create("drv", this);
mon = monitor::type_id::create("mon", this);
endfunction
task run_phase(uvm_phase phase);
//...
// 在运行时替换driver组件
uvm_factory factory = uvm_factory::get();
factory.override_by_type(driver::get_type(), new_driver::get_type());
//...
endtask
endclass
通过这种方式,UVM的工厂机制允许我们在不修改现有代码的情况下,动态地改变验证环境的行为,从而大大提高了验证环境的灵活性和可拓展性。
2.2 UVM从哪里启动,接口怎么传递到环境中?
uvm的启动通常是从一个顶层测试用例test开始的。在uvm中,测试用例testcase是一个继承uvm_test的类,定义了验证环境的结构和行为;
uvm的启动流程:
依次执行uvm_test容器中的各个component组件中的phase机制,按照顺序:
build-phase(自顶向下构建UVM 树)
connet_phase(自低向上连接各个组件)
end_of_elaboration_phase
start_of_simulation_phase
run_phase() objection机制仿真挂起,通过start启动sequence(每个sequence都有一个body任务。当一个sequence启动后,会自动执行sequence的body任务),等到sequence发送完毕则关闭objection,结束run_phase()(UVM_objection提供component和sequence共享的计数器,当所有参与到objection机制中的组件都落下objection时,计数器counter才会清零,才满足run_phase()退出的条件)
执行后面的phase;
接口用于连接验证环境与DUT,接口传到验证环境中的方式:
1.通过配置数据库(uvm_config_db)传递–最常用;在测试类或更高层级的组件中,使用 uvm_config_db 将接口传递给需要它的组件。
class my_test extends uvm_test;
`uvm_component_utils(my_test)
virtual my_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 获取接口
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});
end
// 将接口传递给其他组件
uvm_config_db#(virtual my_if)::set(this, "env*", "vif", vif);
endfunction
endclass
//======使用
module top;
my_if vif();
initial begin
// 创建UVM环境
run_test("my_test");
// 通过配置数据库将接口传递给UVM环境
uvm_config_db#(virtual my_if)::set(null, "*", "vif", vif);
end
endmodule
传递配置对象(uvm_object):如果整合各个组件中的变量,将其放置在一个uvm_object中,再对中心化的配置对象进行传递,将有利于整体环境的修改维护,提升代码的复用性。
2.3 UVM的优势,为什么要用UVM?对UVM验证方法学的理解,一个具体的uvm示例?
UVM验证方法学是一种基于SystemVerilog的验证方法学,它提供了一套完整的验证框架和库,用于构建和管理验证环境。UVM的核心思想是将验证环境划分为不同的组件和对象,通过工厂机制和配置管理来实现组件的动态创建和配置。UVM还提供了丰富的验证技术,如随机化、约束求解、覆盖率收集等,以帮助验证工程师更高效地完成验证任务。
uvm的优势:
1.标准化:
2.可重用性:
3.层次化:
4.丰富强大的类库;
uvm验证步骤:
1.定义验证计划:确定验证目标、验证策略和验证资源。
2.构建验证环境:使用UVM组件和对象构建验证环境,包括测试用例、测试平台、接口和DUT模型等。
3.编写测试用例:使用UVM提供的测试用例模板和随机化技术编写测试用例,以覆盖不同的验证场景。
4.运行仿真:使用仿真工具运行验证环境,收集覆盖率数据和调试信息。
5.分析结果:分析仿真结果,确保设计符合预期的功能和性能要求。
6.优化验证环境:根据分析结果优化验证环境,提高验证效率和覆盖率。
uvm主要组成部分:
组件component:构成验证环境的基本单元,如代理(Agent)、监视器(Monitor)、驱动器(Driver)、记分板(Scoreboard)等。
序列sequence:定义测试刺激的逻辑,可以是预定义的序列,也可以是随机生成的。
事务transaction:代表验证过程中传递的数据包或命令。
配置数据库config_db:用于在整个验证环境中传递配置信息。
工厂机制factory:用于动态创建和管理对象实例。
相位机制phase,:定义了验证环境的生命周期,包括构建、配置、运行等阶段
uvm验证示例:
//1.接口 interface
interface my_if (input bit clk, input bit rst_n);
logic [7:0] data_in;
logic [7:0] data_out;
logic valid_in;
logic ready_out;
clocking cb @(posedge clk);
output data_in;
output valid_in;
input data_out;
input ready_out;
endclocking : cb
endinterface
//2.事务transa
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)
rand bit [7:0] data_in;
bit [7:0] data_out;
bit valid_in;
bit ready_out;
constraint c_valid_in { valid_in dist {0 := 10, 1 := 90}; }
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
//3.驱动driver
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual my_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Could not get virtual interface")
endfunction
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req);
seq_item_port.item_done();
end
endtask
task drive(my_transaction tr);
@(vif.cb);
vif.cb.data_in <= tr.data_in;
vif.cb.valid_in <= tr.valid_in;
endtask
endclass
//4.监视monitor
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Could not get virtual interface")
endfunction
task run_phase(uvm_phase phase);
forever begin
@(vif.cb);
if (vif.cb.ready_out) begin
my_transaction tr = my_transaction::type_id::create("tr");
tr.data_in = vif.cb.data_in;
tr.valid_in = vif.cb.valid_in;
tr.data_out = vif.cb.data_out;
tr.ready_out = vif.cb.ready_out;
ap.write(tr);
end
end
endtask
endclass
//5.代理agent
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver driver;
my_monitor monitor;
uvm_sequencer #(my_transaction) sequencer;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = my_driver::type_id::create("driver", this);
monitor = my_monitor::type_id::create("monitor", this);
sequencer = uvm_sequencer#(my_transaction)::type_id::create("sequencer", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
endclass
//6.环境env
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction
endclass
//7.序列sequence
class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence");
super.new(name);
endfunction
task body();
repeat (10) begin
`uvm_do(req)
end
endtask
endclass
//8.用例testcase
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
my_sequence seq;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
seq = my_sequence::type_type::create("seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(env.agent.sequencer);
#100us; // 等待一段时间
phase.drop_objection(this);
endtask
endclass
//9.顶层模块
module top;
// 时钟和复位信号
bit clk;
bit rst_n;
// DUT实例
my_dut dut(clk, rst_n);
// 接口实例
my_if vif(clk, rst_n);
// 时钟生成
always #5 clk = ~clk;
// 初始化
initial begin
clk = 0;
rst_n = 0;
#10 rst_n = 1;
// 启动UVM测试
run_test("my_test");
// 通过配置数据库将接口传递给UVM环境
uvm_config_db#(virtual my_if)::set(null, "*", "vif", vif);
end
endmodule
2.4 UVM的树形结构,及UVM验证组件组成?
uvm_component:所有UVM组件的基础类。
uvm_env:代表整个验证环境,通常包含多个子组件,如代理(agents)、记分板(scoreboards)等。
uvm_agent:代表一个特定的验证代理,通常包含驱动器(driver)、监视器(monitor)和序列器(sequencer)。
uvm_driver:负责将事务(transaction)转换为信号,驱动到DUT(Design Under Test)。
uvm_monitor:负责监控DUT的输入和输出,并将这些信号转换为事务。
uvm_sequencer:负责从序列(sequence)中获取事务,并将其发送给驱动器。
uvm_scoreboard:负责检查DUT的输出是否符合预期,通常与监视器配合使用。
uvm_checker:类似于记分板,但更专注于特定的检查逻辑。
uvm_reg_block:代表一组寄存器及其层次结构,用于寄存器模型
2.5 virtual sequencer 和sequencer的区别? sequence和sequencer的关系? virtual sequence和sequence的区别?
1.组件component:
uvm_component:这是所有UVM组件的基础类。每个UVM组件都必须继承自uvm_component。
uvm_env:代表整个验证环境,是顶层组件,通常包含多个子组件,如代理(agents)、记分板(scoreboards)等。
uvm_agent:代表一个特定的验证代理,通常包含驱动器(driver)、监视器(monitor)和序列器(sequencer)。
uvm_driver:负责将事务(transaction)转换为信号,驱动到DUT(Design Under Test)。
uvm_monitor:负责监控DUT的输入和输出,并将这些信号转换为事务。
uvm_sequencer:负责从序列(sequence)中获取事务,并将其发送给驱动器。
uvm_scoreboard:计分板,将DUT的输出与checker的比较得分。
uvm_checker:参考模型。
2.事务transaction
uvm_object:这是所有UVM对象的基础类,事务类通常继承自uvm_object。
uvm_sequence_item:事务类通常继承自uvm_sequence_item,它包含了事务的数据和操作方法;
3.序列sequence
uvm_sequence:定义了事务的生成逻辑,可以生成随机或固定的事务序列。
uvm_sequence_base:是所有序列的基础类。
4.工厂(Factory)
uvm_factory:提供了创建和管理对象实例的机制,支持类型覆盖和重载。
5.配置数据库(Configuration Database)
uvm_config_db:用于在组件之间传递配置信息,如接口实例、参数等;
6.相位(Phases)
uvm_phases:定义了验证环境的生命周期,包括构建(build)、配置(configure)、运行(run)等多个阶段。
7.报告(Reporting)
uvm_report_object:提供了丰富的报告机制,支持多种级别的消息(如UVM_INFO、UVM_WARNING、UVM_ERROR、UVM_FATAL)。
8.TLM(Transaction-Level Modeling)
uvm_tlm_*:提供了一系列的TLM接口和方法,用于在组件之间传递事务。
9.寄存器模型(Register Model)
uvm_reg 和 uvm_reg_block:用于描述和操作DUT中的寄存器和寄存器块。
// 事务类
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)
rand bit [7:0] data;
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
// 驱动器
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual my_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Could not get virtual interface")
endfunction
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req);
seq_item_port.item_done();
end
endtask
task drive(my_transaction tr);
vif.data <= tr.data;
@(posedge vif.clk);
endtask
endclass
// 监视器
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual my_if vif;
uvm_analysis_port #(my_transaction) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Could not get virtual interface")
endfunction
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
if (vif.valid) begin
my_transaction tr = my_transaction::type_id::create("tr");
tr.data = vif.data;
ap.write(tr);
end
end
endtask
endclass
// 代理
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_driver driver;
my_monitor monitor;
uvm_sequencer #(my_transaction) sequencer;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = my_driver::type_id::create("driver", this);
monitor = my_monitor::type_id::create("monitor", this);
sequencer = uvm_sequencer#(my_transaction)::type_id::create("sequencer", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
endclass
// 环境
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agent;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction
endclass
// 序列
class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence");
super.new(name);
endfunction
task body();
repeat (10) begin
`uvm_do(req)
end
endtask
endclass
// 测试类
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
my_sequence seq;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
seq = my_sequence::type_id::create("seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(env.agent.sequencer);
#100us; // 等待一段时间
phase.drop_objection(this);
endtask
endclass
// 顶层模块
module top;
bit clk;
bit rst_n;
my_if vif(clk, rst_n);
initial begin
clk = ½;
rst_n = 0;
#10 rst_n = 1;
run_test("my_test");
uvm_config_db#(virtual my_if)::set(null, "*", "vif", vif);
end
always #5 clk = ~clk;
endmodule
2.6 平台往里面输入数据的话怎么输入,sequence,sequencer和driver之间的通信;
平台向DUT里输入数据通常有以下步骤:
1.创建Sequence:首先,需要创建一个或多个uvm_sequence或其子类的实例。这些序列定义了要发送到DUT的事务(Transactions)的类型和顺序。
2.启动Sequence:在测试用例的run_phase中,通过调用start方法启动序列。这通常发生在环境(Environment)或测试(Test)组件中。
3.序列生成事务transaction(Sequence Item)传递:启动的序列会生成事务(Sequence Items),并将它们发送到sequencer。
4.Sequencer传递给Driver:sequencer接收到事务后,将其传递给连接的driver。
5.Driver处理事务:driver接收到事务后,将其转换为DUT可以理解的信号电平,并通过接口(Interface)发送到DUT。
// transaction.sv
class transaction extends uvm_sequence_item;
rand logic [7:0] data;
`uvm_object_utils_begin(transaction)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "transaction");
super.new(name);
endfunction
endclass
// my_sequence.sv
class my_sequence extends uvm_sequence#(transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence");
super.new(name);
endfunction
task body();
transaction tr;
// 随机化事务
assert(tr.randomize());
// 发送事务到sequencer
`uvm_info("MY_SEQUENCE", "Sending transaction", UVM_LOW)
seq_item_port.put(tr);
endtask
endclass
// driver.sv
class driver extends uvm_driver#(transaction);
virtual my_interface vif;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_interface)::get(this, "", "vif", vif))
`uvm_fatal("DRV", "Failed to get vif from config DB")
endfunction
task run_phase(uvm_phase phase);
transaction tr;
forever begin
seq_item_port.get_next_item(tr);
`uvm_info("DRV", "Received transaction", UVM_LOW)
// 处理事务,将数据发送到DUT
drive(req);
seq_item_port.item_done();
end
endtask
task drive(my_transaction tr);
vif.data <= tr.data;
vif.valid <= 1;
@(posedge vif.clk);
vif.valid <= 0; endtask
endclass
- 序列(Sequence)
序列负责生成事务(transaction),并将这些事务发送给序列器。序列可以生成随机或预定义的事务。 - 序列器(Sequencer)
序列器充当序列和驱动器之间的中介。它负责从序列中获取事务,并将这些事务发送给驱动器。 - 驱动器(Driver)
驱动器从序列器中获取事务,并将这些事务转换为DUT(Design Under Test)可以理解的信号。
2.7 启动sequence的方法
1.在testcase中sequence.start(sqr);
// test.sv
class my_test extends uvm_test;
my_sequence seq;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
seq = my_sequence::type_id::create("seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(sequencer); // 启动序列
phase.drop_objection(this);
endtask
endclass
2.序列在其他序列启动
class main_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(main_sequence)
function new(string name = "main_sequence");
super.new(name);
endfunction
task body();
my_sequence sub_seq;
// 创建子序列实例
sub_seq = my_sequence::type_id::create("sub_seq");
// 启动子序列
sub_seq.start(m_sequencer);
endtask
endclass
3.uvm_do_on(with)
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_env env;
my_sequence seq;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
seq = my_sequence::type_id::create("seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
// 在指定的序列器上启动序列
`uvm_do_on(seq, env.agent.sequencer)
// 等待序列完成
#100us; // 可以根据需要调整等待时间
phase.drop_objection(this);
endtask
endclass
4.uvm_do(with)
允许你在当前序列器上启动序列,并且可以对序列进行约束;
class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
function new(string name = "my_sequence");
super.new(name);
endfunction
task body();
repeat (10) begin
`uvm_do_with(req, {data == ˜hFF;}) // 施加约束
end
endtask
endclass
【总结】
start 方法:直接调用序列的 start 方法来启动序列。
uvm_do 宏:简化序列的启动过程。
uvm_do_on 宏:在指定的序列器上启动序列。
uvm_do_on_with 宏:在指定的序列器上启动序列,并施加约束。
uvm_do_with 宏:在当前序列器上启动序列,并施加约束。
2.8 UVM中哪些继承于component,哪些继承于object?
uvm_component 和 uvm_object 是两个核心基类。
uvm_component:是所有UVM组件的基础类。组件是有层次结构的,可以包含其他组件,并且它们在仿真过程中会经历一系列的相位(phases)。
uvm_object 是所有UVM对象的基础类。对象没有层次结构,也不参与仿真相位。它们主要用于表示数据和事务的基本单元。以下是常见的继承自 uvm_object 的类:
uvm_sequence_item:代表事务(transaction),用于在组件之间传递数据。
uvm_sequence:定义了事务的生成逻辑,可以生成随机或固定的事务序列。
uvm_reg:代表单个寄存器,用于寄存器模型。
uvm_reg_field:代表寄存器中的字段,用于寄存器模型。
uvm_object_wrapper:用于工厂机制,创建对象实例。
uvm_transaction:旧版本UVM中的事务类,现在通常使用 uvm_sequence_item。
uvm_reg_map:代表寄存器映射,用于寄存器模型。
uvm_reg_adapter**:用于寄存器模型,将寄存器事务转换为总线事务。
uvm_reg_predictor**:用于寄存器模型,预测寄存器的值。
uvm_reg_predictor**:用于寄存器模型,预测寄存器的值。
代码示例:
//====uvm_component
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
virtual my_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Could not get virtual interface")
endfunction
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req);
seq_item_port.item_done();
end
endtask
task drive(my_transaction tr);
vif.data <= tr.data;
@(posedge vif.clk);
endtask
endclass
//======uvm_object
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)
rand bit [7:0] data;
function new(string name = "my_transaction");
super.new(name);
endfunction
// 可以在这里添加约束
constraint c_data {
data inside {[8'h00 : 8'hFF]};
}
endclass
以上是IC验证基本功–UVM相关知识点;