Chpater 22 UVM Agent
- 将测试平台不断模块化是一件美妙的事情,无论我们使用它来封装硬件中的块,固件中的子程序还是软件中的对象,模块化都会隐藏复杂性并改善生活。 模块化还允许我们将经过测试的功能重用为硬件中的IP,固件中的库和软件中的类库。
- 成功模块化的关键是支持严格且不变的接口。定义接口允许用户(以及未来的自我)自信地使用模块化组件,而不必担心其内部工作。
- UVM 提供了Agent, configuration objects,configuration database实现UVM的模块化。Agent允许我们封装相似的对象,cfg object允许我们存储agent需要的信息,cfg databas允许我们传递配置信息到同类型的不同Agent。
22.1 模块化前后的测试平台
- 模块化前的测试平台如下所示:

测试品台需要产生激励,检查结果,监测覆盖率。但是这些功能都是分开的一个个碎片,如果我们的设计中使用大量的TinyALU,我们需要拷贝所有的对象,port来实现重用。
- 模块化后的测试平台如下所示:

我们使用和此前一致的objects,并使用uvm_component类型的组件uvm_agent来封装这些组件。各组件间的连接方式不变。
此外,我们在uvm_agent顶层声明了两个analysis port,这样我们可以在更高的层次中看到uvm_agent中传输的数据,但是scoreboard和coverage不做任何改变。
虚线中的模块包括tester, command_f, driver对象,只有在初始化时希望产生激励是才**,否则uvm_agent中不包括这三个模块,但我们仍可以使用agent的其他组件监控TinyALU。
22.2 创建TinyALU Agent
- TinyALU看来和env十分类似,他们之间的区别是uvm agent引入configuration机制来传输BFM,并且可以决定agent中是否包含stimulus。
- TynyALU Agent中包含了此前测试平台中所有的组件和FIFO,并且定义了agent定测接口的两个analysis port。
class tinyalu_agent extends uvm_agent;
`uvm_component_utils(tinyalu_agent)
tinyalu_agent_config tinyalu_agent_config_h;
tester tester_h;
driver driver_h;
scoreboard scoreboard_h;
coverage coverage_h;
command_monitor command_monitor_h;
result_monitor result_monitor_h;
uvm_tlm_fifo #(command_transaction) command_f;
uvm_analysis_port #(command_transaction) cmd_mon_ap;
uvm_analysis_port #(result_transaction) result_ap;
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
if(!uvm_config_db #(tinyalu_agent_config)::get(this, "","config",tinyalu_agent_config_h))
`uvm_fatal("AGENT", "Failed to get config object");
is_active = tinyalu_agent_config_h.get_is_active();
if (get_is_active() == UVM_ACTIVE) begin : make_stimulus
command_f = new("command_f", this);
tester_h = tester::type_id::create( "tester_h",this);
driver_h = driver::type_id::create("driver_h",this);
end
command_monitor_h = command_monitor::type_id::create("command_monitor_h",this);
result_monitor_h = result_monitor::type_id::create("result_monitor_h",this);
coverage_h = coverage::type_id::create("coverage_h",this);
scoreboard_h = scoreboard::type_id::create("scoreboard_h",this);
cmd_mon_ap = new("cmd_mon_ap",this);
result_ap = new("result_ap", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
if (get_is_active() == UVM_ACTIVE) begin : make_stimulus
driver_h.command_port.connect(command_f.get_export);
tester_h.command_port.connect(command_f.put_export);
end
command_monitor_h.ap.connect(cmd_mon_ap);
result_monitor_h.ap.connect(result_ap);
command_monitor_h.ap.connect(scoreboard_h.cmd_f.analysis_export);
command_monitor_h.ap.connect(coverage_h.analysis_export);
result_monitor_h.ap.connect(scoreboard_h.analysis_export);
endfunction : connect_phase
endclass : tinyalu_agent
class tinyalu_agent_config;
virtual tinyalu_bfm bfm;
protected uvm_active_passive_enum is_active;
function new (virtual tinyalu_bfm bfm, uvm_active_passive_enum
is_active);
this.bfm = bfm;
this.is_active = is_active;
endfunction : new
function uvm_active_passive_enum get_is_active();
return is_active;
endfunction : get_is_active
endclass : tinyalu_agent_config
22.3 使用TinyALU Agent
- 我们可以在测试平台中例化两个TinyALU(class_tinyalu & module_tinyalu),并且使用UVM agent完成测试平台连接,但是分别使用agent中的stimulus和tester+bfm的方式给ALU施加激励。
22.3.1 顶层例化两个tynialu,并声明两个bfm
module top;
import uvm_pkg::*;
import tinyalu_pkg::*;
`include "tinyalu_macros.svh"
`include "uvm_macros.svh"
tinyalu_bfm class_bfm();
tinyalu class_dut (.A(class_bfm.A), .B(class_bfm.B), .op(class_bfm.op),
.clk(class_bfm.clk), .reset_n(class_bfm.reset_n),
.start(class_bfm.start), .done(class_bfm.done),
.result(class_bfm.result));
tinyalu_bfm module_bfm();
tinyalu module_dut (.A(module_bfm.A), .B(module_bfm.B), .op(module_bfm.op),
.clk(module_bfm.clk), .reset_n(module_bfm.reset_n),
.start(module_bfm.start), .done(module_bfm.done),
.result(module_bfm.result));
tinyalu_tester_module stim_module(module_bfm);
initial begin
uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "class_bfm", class_bfm);
uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "module_bfm", module_bfm);
run_test("dual_test");
end
endmodule : top
22.3.2 声明dual_test
- 在测试用例中声明同样名称的两个bfm,通过config_db取得top中的BFM。
- 通过env_cfg将两个BFM传入给测试环境env。
class dual_test extends uvm_test;
`uvm_component_utils(dual_test);
env env_h;
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
virtual tinyalu_bfm class_bfm, module_bfm;
env_config env_config_h;
if(!uvm_config_db #(virtual tinyalu_bfm)::get(this, "","class_bfm", class_bfm))
`uvm_fatal("DUAL TEST", "Failed to get CLASS BFM");
if(!uvm_config_db #(virtual tinyalu_bfm)::get(this, "","module_bfm", module_bfm))
`uvm_fatal("DUAL TEST", "Failed to get MODULE BFM");
env_config_h = new(.class_bfm(class_bfm), .module_bfm(module_bfm));
uvm_config_db #(env_config)::set(this, "env_h*", "config", env_config_h);
env_h = env::type_id::create("env_h",this);
endfunction : build_phase
endclass
- config_db的模糊匹配机制:
- 第一个,我们传递this变量来设置存储信息的上下文。
- 第二个,我们传递一个字符串,告诉uvm_config_db谁可以看到这些数据。 在这种情况下,我们将读者限制为在此测试中实例化的env_h类。 这在这里是多余的,因为我们只实例化一个对象。 我们将使用此过滤功能来更好地影响env。
22.3.3 声明env_config
class env_config;
virtual tinyalu_bfm class_bfm;
virtual tinyalu_bfm module_bfm;
function new(virtual tinyalu_bfm class_bfm, virtual tinyalu_bfm module_bfm);
this.class_bfm = class_bfm;
this.module_bfm = module_bfm;
endfunction : new
endclass : env_config
22.3.4 声明TinyALU Enviroment
- 在此之前,env用来逐个声明连接环境组建;
- 模块化之后,只需要在env中为两个TinyALU声明不同的angent即可。
class env extends uvm_env;
`uvm_component_utils(env);
tinyalu_agent class_tinyalu_agent_h, module_tinyalu_agent_h;
tinyalu_agent_config class_config_h, module_config_h;
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction : new
function void build_phase(uvm_phase phase);
virtual tinyalu_bfm class_bfm;
virtual tinyalu_bfm module_bfm;
env_config env_config_h;
if(!uvm_config_db #(env_config)::get(this, "","config", env_config_h))
`uvm_fatal("RANDOM TEST", "Failed to get CLASS BFM");
class_config_h = new(.bfm(env_config_h.class_bfm), .is_active(UVM_ACTIVE));
module_config_h = new(.bfm(env_config_h.module_bfm), .is_active(UVM_PASSIVE));
uvm_config_db #(tinyalu_agent_config)::set(this, "class_tinyalu_agent_h*","config", class_config_h);
uvm_config_db #(tinyalu_agent_config)::set(this, "module_tinyalu_agent_h*","config", module_config_h);
class_tinyalu_agent_h = new("class_tinyalu_agent_h",this);
module_tinyalu_agent_h = new("module_tinyalu_agent_h",this);
endfunction : build_phase
endclass
- 当同类型的agnent的多个实例想要使用相同的字符串访问数据库时,我们该怎么办?如上例中两个agent都希望得到各自的agent config,那么uvm config_db机制如何实现?
- 答案是uvm_config_db允许我们将不同的配置对象应用于UVM层次结构的不同部分。 env从uvm_config_db获取配置数据,为每个tinyalu_agent实例创建配置对象并存储配置对象,以便每个agent实例看到正确的config对象.(第三个参数指定了那些对象可以看到传输的config对象)
- 在声明Agent时,name需要和uvm_config_db中的第三个string类型的参数匹配。这样两个Agent就可以得到属于各自的agent config了。
22.4 Summary
- 在本章中,我们学习了如何将组件封装到UVM Agent类中。 这种封装使我们可以轻松地重用我们的测试平台组件,或创建相同组件的多个实例。 我们还学习了如何创建生成激励的Active Agent,以及仅监视接口的Passsive Agent,模块化使我们的工作变得轻松。
- 如果每个层次结构都具有明确定义的配置对象,则对象中的这种封装行为模式是递归的。 我们可以实例化两个env类来模拟四个TinyALU,只需要很小的修改。
- 接下来只需要做一件事:创建真正可重用和模块化的测试平台。 您可能已经注意到我们的Agent类很奇怪。 虽然我们希望通过我们的Agent运行许多不同类型的激励,但内置tester仅提供一组默认的激励。 当然,我们可以通过覆盖override command_transaction在某种程度上控制它,但这只是一种解决问题的方法.
- 我们真正想做的是:使用一种方法给Agent提供不同的命令,以便产生不同类型的激励。 我们希望将激励产生与测试平台结构分开。 我们将在下一章关于UVM Sequence的内容中做到这一点。