本篇是讨论SystemVerilog接口和接口参数化处理策略的三部分系列的第一部分。
背景
基于SystemVerilog的验证引入了接口的概念来表示模块之间的通信。在最基本的形式中,SystemVerilog接口只是一个命名的信号包,可以通过模块端口作为单个项目进行通信。接收此接口的模块可以通过此接口访问信号。但是,接口的更高级功能还可以提供更强类型的通信,以更好地表示设计意图。以下是SystemVerilog接口中可用的高阶函数的子集:
- 可以通过使用clocking blocks和modports在信号列表上强制执行访问规则
- 函数和任务可用于封装高阶控制序列或访问控制
- 初始块( initial blocks)和始终块(always blocks)等进程可以添加功能
- 连续赋值语句还可以添加功能
- 断言可以确保正确的集成
SystemVerilog接口的一个非常重要的用途是将静态设计元素连接到动态测试平台。动态测试平台需要访问静态设计元素才能采样和驱动信号,但可重用的测试平台除了通过名为虚拟接口的特殊构造外,无法访问静态设计元素。虚拟接口是测试平台代码中的接口的句柄,可以使用接口实例进行分配。虚拟接口是动态属性,可以分配给不同测试平台中的不同接口实例,从而提高可重用性。
设计可重用模块的常用技术是使用参数来使具有独特特征的模块的不同实例化成为可能。例如,可以对模块进行参数化,以允许在声明模块并提供参数值时定义数据总线宽度。SystemVerilog接口也支持参数化,但参数化接口的使用在测试平台引入了无法预料的复杂性。为了兼容赋值,参数化虚拟接口必须专门使用与接口实例专用的值相同的值。除非采取预防措施,否则这可能会导致一些非常难看的测试平台代码甚至更加丑陋的使用模型。
参数增殖:蛮力方法
参数化的虚拟接口引入的问题是访问它的testbench元素必须知道强类型接口。因此,当尚不知道接口规范时,不能编写泛型类以使用虚拟接口参数化。该问题的一个解决方案是参数化访问参数化虚拟接口的类。例如,可以使用必须使用的虚拟接口类型来参数化UVM驱动程序。这只是将问题向上移动一层,因为现在实例化该参数化驱动程序(driver)的代理(agent)也必须进行参数化,以便它可以创建一个正确专用的驱动程序实例。这会继续向上移动层,直到您到达顶层测试平台组件,该组件“知道”正在测试的特定规范存在。以下代码段演示了此问题。
首先我们定义参数化虚拟接口:
interface param_if#(int width = 8);
logic clk;
logic[width-1:0] data;
clocking active_cb @(posedge clk);
default input #1 output #1;
output data;
endclocking
modport active_mp (clocking active_cb);
endinterface
typedef virtual param_if param_vif
接下来,我们定义可重用的VIP代码。此测试平台代码必须设计为可在任何可以使用参数化接口的环境中重用,因此VIP代码本身也必须参数化,以便可以访问正确的接口。以下代码段显示了如何参数化UVM驱动程序类和包含驱动程序的代理程序(agent):
//=======================================================================
class param_driver#(type vif_t=param_vif) extends uvm_driver#(cust_data);
`uvm_component_param_utils(param_driver#(vif_t))
vif_t vif;
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(vif_t)::get(this, "", "vif", vif))
`uvm_fatal("build", "A valid interface was not received.");
endfunction
endclass
//=======================================================================
class cust_agent#(type vif_t=param_vif) extends uvm_agent;
`uvm_component_param_utils(param_agent#(vif_t))
vif_t vif;
param_driver#(vif_t) param_driver;
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(vif_t)::get(this, "", "vif", vif))
`uvm_fatal("build", "A valid interface was not received.");
uvm_config_db#(vif_t)::set(this, "param_driver", "vif", vif);
param_driver = param_driver#(vif_t)::type_id::create("param_driver", this);
endfunction
endclass
到目前为止,这看起来并不那么糟糕!它为类定义增加了一点复杂性,但不是太多。但是,在检查测试台必须如何访问这些参数化类之前,这些问题并不明显。以下部分显示了测试如何根据接口参数化的方式唯一访问VIP的每个实例:
//=======================================================================
class cust_test extends uvm_test;
`uvm_component_utils(cust_test)
param_agent#(virtual param_if#(8)) param_agent8;
param_agent#(virtual param_if#(16)) param_agent16;
param_agent#(virtual param_if#(32)) param_agent32;
virtual function void build_phase(uvm_phase phase);
param_agent8 = param_agent#(virtual param_if#(8))::type_id::create("param_agent8", this);
param_agent16 = param_agent#(virtual param_if#(16))::type_id::create("param_agent16", this);
param_agent32 = param_agent#(virtual param_if#(32))::type_id::create("param_agent32", this);
endfunction
endclass
//=======================================================================
module test_top;
param_if#(8) if8();
param_if#(16) if16();
param_if#(32) if32();
initial begin
uvm_config_db#(virtual param_if#(8))::set(uvm_root::get(), "uvm_test_top.param_agent8", "vif", if8);
uvm_config_db#(virtual param_if#(16))::set(uvm_root::get(), "uvm_test_top.param_agent16", "vif", if16);
uvm_config_db#(virtual param_if#(32))::set(uvm_root::get(), "uvm_test_top.param_agent32", "vif", if32);
run_test("cust_test");
end
endmodule
正如您所看到的,每个对VIP的引用都必须使用适当的接口类型进行参数化。这不仅会影响VIP构造,还会影响回调注册,工厂重载等。这给测试平台开发人员带来了很大的负担,并限制了这些环境的可重用性。
向验证组件添加参数是可重用VIP的有效技术解决方案,但它使使用模型大大复杂化并限制了测试台的可重用性。在本系列的下一篇文章中,我们将研究这个问题的可能解决方法,但这需要付出代价!
您可以在Synopsys Verification IP 和 VC Verification IP数据表中获得更多信息。