本篇是讨论SystemVerilog接口和接口参数化处理策略的三部分系列的第二部分。
在本系列的第一部分中,介绍了SystemVerilog接口的基本概念,并介绍了接口的参数化会给测试平台代码带来的问题。这篇文章将描述这个问题的一个可能的解决方法,但是要付出一定的代价...
特洛伊木马:偷袭方法
虚拟接口不支持多态,因为它们与静态设计元素相关联。但是,SystemVerilog类确实支持多态,这个事实可用于创建接口访问器类。
创建一个虚拟类,声明用于在SystemVerilog接口上执行特定操作的纯虚访问方法。然后,参数化类拓展此类以执行对强类型接口的实际访问。VIP代码仅与非参数化的基本访问器类进行交互,因此不需要对VIP代码进行参数化以使用强类型接口。测试平台必须设置强类型访问器类并将其传递给VIP,但在此之后,可以执行与VIP的所有交互,而无需参数化VIP。以下代码段演示了如何设置它。
首先,我们定义参数化虚拟接口,它与上一篇文章代码段中使用的接口相同:
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
接下来,我们定义非参数化接口访问类和参数化的扩展类,以使用参数化接口:
//=======================================================================
virtual class port_wrapper extends uvm_object;
pure virtual task do_write(logic[31:0] data);
pure virtual task do_read(output logic[31:0] data);
pure virtual task sync_driver_cb();
endclass
//=======================================================================
class port_wrapper_typed#(type vif_t=param_vif) extends port_wrapper;
vif_t vif;
function new(vif_t vif);
this.vif = vif;
endfunction
virtual task do_write(logic[31:0] data);
vif.active_cb.data <= data;
endtask
virtual task do_read(output logic[31:0] data);
data = vif.active_cb.data;
endtask
virtual task sync_driver_cb();
@(vif.active_cb);
endtask
endclass
在此之后,实现VIP代码以使用此访问者类而不是直接访问虚拟接口。下面的代码段展示了驱动程序和代理类,并演示了必须使用访问方法的各个位置。有关此VIP代码的一点需要注意的是,它通过config DB而不是接口接受非参数化的访问者类:
//=======================================================================
class cust_driver extends uvm_driver#(cust_data);
`uvm_component_utils(cust_driver)
port_wrapper port;
task consume_from_seq_item_port();
forever begin
seq_item_port.get_next_item(req);
port.do_write(req.data);
port.sync_driver_cb();
…
task sample_signals();
forever begin
bit[31:0] sampled_data;
port.do_read(sampled_data);
port.sync_driver_cb();
…
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(port_wrapper)::get(this, "", "port", port))
`uvm_fatal("build", "A valid port wrapper was not received.");
…
//=======================================================================
class cust_agent extends uvm_agent;
`uvm_component_utils(cust_agent)
cust_driver driver;
function void build_phase(uvm_phase phase);
port_wrapper port;
if (!uvm_config_db#(port_wrapper)::get(this, "", "port", port))
`uvm_fatal("build", "A valid port wrapper was not received.");
uvm_config_db#(port_wrapper)::set(this, "driver", "port", port);
driver = cust_driver::type_id::create("driver", this);
…
最后,介绍测试平台代码。测试用例可以在不参数化的情况下访问VIP,但是实例化接口的顶层模块必须实现一些额外的步骤。它不仅必须实例化参数化接口,还必须创建参数化接口访问者类并将其传递给VIP实例:
//=======================================================================
class cust_test extends uvm_test;
`uvm_component_utils(cust_test)
cust_agent agent8;
cust_agent agent16;
cust_agent agent32;
virtual function void build_phase(uvm_phase phase);
agent8 = cust_agent::type_id::create("agent8", this)
agent16 = cust_agent::type_id::create("agent16", this);
agent32 = cust_agent::type_id::create("agent32", this);
endfunction
endclass
//=======================================================================
module test_top;
param_if#(8) if8();
param_if#(16) if16();
param_if#(32) if32();
initial begin
port_wrapper_typed#(virtual param_if#(8)) port_wrapper8;
port_wrapper_typed#(virtual param_if#(16)) port_wrapper16;
port_wrapper_typed#(virtual param_if#(32)) port_wrapper32;
port_wrapper8 = new(if8);
port_wrapper16 = new(if16);
port_wrapper32 = new(if32);
uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent8", "port", port_wrapper8);
uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent16", "port", port_wrapper16);
uvm_config_db#(port_wrapper)::set(uvm_root::get(), "uvm_test_top.agent32", "port", port_wrapper32);
run_test("cust_test");
end
endmodule
如您所见,此解决方案确实解决了参数化接口的使用模型问题。创建正确类型的端口包装器需要一些额外的代码,但是测试平台编写者可以访问使用不同专用接口的代理,而无需知道这些接口是如何规范化的。
但是,该解决方案对VIP如何利用虚拟接口施加了严格的限制。驱动程序和监视器类不能直接与接口交互,而必须将这些访问推迟到强类型包装类。通过将驱动程序和监视器类的大部分功能移动到强类型包装类,可以抵消这种生产力损失; 然而,这种更复杂的结构可能会造成混淆并且不够灵活。上面的代码段只显示了驱动程序类可能需要的一些访问方法,但在实际环境中,此列表有可能增长许多倍,监视器类也需要一整套访问者方法。
经过反思,蛮力方法和特洛伊木马处理参数化接口的方法都不是理想的。使用前一种方法,向类添加参数使VIP的使用模型复杂化。使用后一种方法,必须提前规划接口包装器类支持的访问方法,并限制对虚拟接口的VIP访问。如果VIP代码可以启用与参数化接口类似的功能而不实际使用参数化接口,该怎么办?这导致了第三个也是最后一个解决方案,将在最后的帖子中进行描述。
您可以在Synopsys Verification IP 和 VC Verification IP数据表中获得更多信息。