虚拟接口提供了一种将抽象模型和测试程序(programs)与构成设计的实际信号分离的机制。虚拟接口允许同一子程序对设计的不同部分进行操作,并动态控制与子程序相关的信号集。
用户能够操纵一组虚拟信号,而不是直接引用实际的信号集。对底层设计的更改不需要重写使用虚拟接口的代码。通过抽象一组块的连接性和功能,虚拟接口促进了代码重用。
虚拟接口是一个表示接口实例的变量。语法25-3中给出了声明虚拟接口变量的语法。
data_declaration ::= // from A.2.1.3
[ const ] [ var ] [ lifetime ] data_type_or_implicit list_of_variable_decl_assignments ;10
| ...
data_type ::= // from A.2.2.1
...
| virtual [ interface ] interface_identifier [ parameter_value_assignment ] [ . modport_identifier ]
Syntax 25-3—Virtual interface declaration syntax (excerpt from Annex A)
不在过程上下文中的数据声明中,使用automatic关键字是非法的。在data_declaration中,除非使用var关键字,否则在list_of_variable_decl_assignments之前省略显式data_type是非法的。
虚拟接口变量可以作为参数传递给任务、函数或方法。因此,单个虚拟接口变量可以在整个模拟的不同时间表示不同的接口实例。虚拟接口应在引用虚拟接口的组件之前进行初始化;在初始化之前,它的值为null。尝试使用null虚拟接口将导致致命的运行时错误。
接口的类型应包括在接口实例化或虚拟接口变量声明中使用的实际参数,默认参数或重写参数。这些参数的实际值和类型应匹配,以使接口和虚拟接口具有相同类型并具有赋值兼容性(见6.22.3)。
虚拟接口声明可以选择接口的modport,在这种情况下,modport也是其类型的一部分。没有选择modport的接口实例或虚拟接口可以被赋值给选择了modport的虚拟接口。如果接口实例或接口层次结构的参数有defparam,并且该defparam语句是在接口外部声明的,则将接口实例赋值给虚拟接口是非法的。
尽管接口可能包含对其主体或引用其他接口的端口之外的对象的分层引用,但在虚拟接口的声明中使用包含这些引用的接口是非法的。
仅直接允许对虚拟接口变量执行以下操作:
--将(=)赋值给以下项:
•另一个相同类型的虚拟接口
•同一类型的接口实例
•特殊常量null
--等式(==)和不等式(!=)如下:
•另一个相同类型的虚拟接口
•同一类型的接口实例
•特殊常量null
虚拟接口不得用作端口、interface项或 unions成员。一旦虚拟接口被初始化,底层接口实例的所有组件都可以通过点符号直接用于虚拟接口。这些组成部分只能在程序性语句中使用;它们不能用于连续赋值或敏感度列表。为了通过虚拟接口驱动网络,接口本身必须提供这样做的过程性手段。这可以通过时钟块来实现,也可以通过在接口中包含通过变量的连续赋值来更新的驱动程序来实现。
虚拟接口可以声明为类属性,可以按过程初始化,也可以通过new()的参数初始化。这允许在不同的类中使用相同的虚拟接口。以下示例显示了如何使用相同的transactor类与各种不同的设备进行交互:
interface SBus; // A Simple bus interface
logic req, grant;
logic [7:0] addr, data;
endinterface
class SBusTransactor; // SBus transactor class
virtual SBus bus; // virtual interface of type SBus
function new( virtual SBus s );
bus = s; // initialize the virtual interface
endfunction
task request(); // request the bus
bus.req <= 1'b1;
endtask
task wait_for_bus(); // wait for the bus to be granted
@(posedge bus.grant);
endtask
endclass
module devA( SBus s ) ... endmodule // devices that use SBus
module devB( SBus s ) ... endmodule
module top;
SBus s[1:4] (); // instantiate 4 interfaces
devA a1( s[1] ); // instantiate 4 devices
devB b1( s[2] );
devA a2( s[3] );
devB b2( s[4] );
initial begin
SBusTransactor t[1:4]; // create 4 bus-transactors and bind
t[1] = new( s[1] );
t[2] = new( s[2] );
t[3] = new( s[3] );
t[4] = new( s[4] );
// test t[1:4]
end
endmodule
在前面的示例中,事务类SbusTransctor是一个简单的可重用组件。它是在没有任何全局或层次引用的情况下编写的,并且不知道它将与之交互的特定设备。尽管如此,该类可以与任何数量的遵守接口协议的设备(本例中为四个)进行交互。没有选择modport的接口实例或虚拟接口可以分配给选择了modport的虚拟接口。
interface PBus #(parameter WIDTH=8); // A parameterized bus interface
logic req, grant;
logic [WIDTH-1:0] addr, data;
modport phy(input addr, ref data);
endinterface
module top;
PBus #(16) p16();
PBus #(32) p32();
virtual PBus v8; // legal declaration, but no legal assignments
virtual PBus #(35) v35; // legal declaration, but no legal assignments
virtual PBus #(16) v16;
virtual PBus #(16).phy v16_phy;
virtual PBus #(32) v32;
virtual PBus #(32).phy v32_phy;
initial begin
v16 = p16; // legal – parameter values match
v32 = p32; // legal – parameter values match
v16 = p32; // illegal – parameter values don't match
v16 = v32; // illegal – parameter values don't match
v16_phy = v16; // legal assignment from no selected modport to
// selected modport
v16 = v16_phy; // illegal assignment from selected modport to
// no selected modport
v32_phy = p32; // legal assignment from no selected modport to
// selected modport
v32 = p32.phy; // illegal assignment from selected modport to
// no selected modport
end
endmodule
25.9.1 Virtual interfaces and clocking blocks
接口和clocking 块可以被组合以表示同步块之间的互连。而且由于clocking 块提供了将值赋值给网络和变量的过程机制,它们非常适合由虚拟接口使用。
例如:
interface SyncBus( input logic clk );
wire a, b, c;
clocking sb @(posedge clk);
input a;
output b;
inout c;
endclocking
endinterface
typedef virtual SyncBus VI; // A virtual interface type
task do_it( VI v ); // handles any SyncBus via clocking sb
if( v.sb.a == 1 )
v.sb.b <= 0;
else
v.sb.c <= ##1 1;
endtask
在前面的例子中,接口SyncBus包括一个clocking块,任务do_it使用它来提供对接口信号a、b和c的同步访问。更改接口信号的存储类型(从net类型到变量,反之亦然)不需要更改任务。
接口可以实例化如下:
module top;
logic clk;
SyncBus b1( clk );
SyncBus b2( clk );
initial begin
VI v[2] = '{ b1, b2 };
repeat( 20 )
do_it( v[ $urandom_range( 0, 1 ) ] );
end
endmodule
这个top模块展示了如何使用虚拟接口在要操作的一组接口中随机选择,在本例中是由do_it任务操作的。
25.9.2 Virtual interface modports and clocking blocks
如前一个示例所示,一旦声明了虚拟接口,就可以使用点表示法引用其时钟块。然而,这只适用于没有modports的接口。通常,DUT及其测试台显示modport方向。这种常见情况可以通过在25.5.5中描述的相应modport中包括时钟来处理。以下示例显示了与虚拟接口结合使用的modports如何促进抽象同步模型的创建。
interface A_Bus( input logic clk );
wire req, gnt;
wire [7:0] addr, data;
clocking sb @(posedge clk);
input gnt;
output req, addr;
inout data;
property p1; req ##[1:3] gnt; endproperty
endclocking
modport DUT ( input clk, req, addr, // Device under test modport
output gnt,
inout data );
modport STB ( clocking sb ); // synchronous testbench modport
modport TB ( input gnt, // asynchronous testbench modport
output req, addr,
inout data );
endinterface
然后可以如下实例化前面的接口A_Bus:
module dev1(A_Bus.DUT b); // Some device: Part of the design
...
endmodule
module dev2(A_Bus.DUT b); // Some device: Part of the design
...
endmodule
program T (A_Bus.STB b1, A_Bus.STB b2 ); // Testbench: 2 synchronous ports
...
endprogram
module top;
logic clk;
A_Bus b1( clk );
A_Bus b2( clk );
dev1 d1( b1 );
dev2 d2( b2 );
T tb( b1, b2 );
endmodule
而且,在 testbench 程序中,虚拟接口可以直接引用clocking 块。
program T (A_Bus.STB b1, A_Bus.STB b2 ); // Testbench: 2 synchronous ports
typedef virtual A_Bus.STB SYNCTB;
task request( SYNCTB s );
s.sb.req <= 1;
endtask
task wait_grant( SYNCTB s );
wait( s.sb.gnt == 1 );
endtask
task drive(SYNCTB s, logic [7:0] adr, data );
if( s.sb.gnt == 0 ) begin
request(s); // acquire bus if needed
wait_grant(s);
end
s.sb.addr = adr;
s.sb.data = data;
repeat(2) @s.sb;
s.sb.req = 0; //release bus
endtask
assert property (b1.sb.p1); // assert property from within program
initial begin
drive( b1, $random, $random );
drive( b2, $random, $random );
end
endprogram
此示例显示了program块内的任务如何通过虚拟接口引用时钟块。
25.10 Access to interface objects
无论接口是通过端口连接还是通过虚拟接口访问,也无论该接口中是否存在任何声明的modport,都应通过分层名称引用访问接口中声明的对象。modport可用于通过在modport中显式列出可访问对象来限制对通过端口连接或虚拟接口引用的接口中声明的对象的访问。但是,不允许在modport中列出的对象应保持可访问性。例如:
interface ebus_i;
integer I; // reference to I not allowed through modport mp
typedef enum {Y,N} choice;
choice Q;
localparam True = 1;
modport mp(input Q);
endinterface
module Top;
ebus_i ebus ();
sub s1 (ebus.mp);
endmodule
module sub(interface.mp i);
typedef i.choice yes_no; // import type from interface
yes_no P;
assign P = i.Q; // refer to Q with a port reference
initial
Top.ebus.Q = i.True; // refer to Q with a hierarchical reference
initial
Top.ebus.I = 0; // referring to i.I would not be legal because
// is not in modport mp
endmodule