为了限制模块内的接口访问,有一些modport列表,其中包含在接口内声明的方向。关键字modport表示方向是在模块内部声明的。
interface i2;
wire a, b, c, d;
modport master (input a, b, output c, d);
modport slave (output a, b, input c, d);
endinterface
在本例中,可以在模块头部中指定modport列表名称(master or slave),其中接口名称选择接口,modport名称为模块头部中访问的接口信号选择适当的方向信息。
module m (i2.master i);
...
endmodule
module s (i2.slave i);
...
endmodule
module top;
i2 i();
m u1(.i(i));
s u2(.i(i));
endmodule
interface_name.modport_name reference_name的语法为层次引用提供了一个本地名称。通过编写interface.modport_name reference_name,可以将此技术推广到具有给定modport名称的任何接口。modport列表名称(master or slave)也可以在与模块实例的端口连接中指定,其中modport名称是从接口实例分层的。
module m (i2 i);
...
endmodule
module s (i2 i);
...
endmodule
module top;
i2 i();
m u1(.i(i.master));
s u2(.i(i.slave));
endmodule
如果端口连接在模块实例和模块头部声明中都指定了modport列表名称,则这两个modport列表名应相同。modport声明中使用的所有名称应由与modport本身相同的接口声明。特别是,使用的名称不应是由另一个封闭接口声明的名称,modport声明也不应隐式声明新端口。
以下接口声明是非法的:
interface i;
wire x, y;
interface illegal_i;
wire a, b, c, d;
// x, y not declared by this interface
modport master(input a, b, x, output c, d, y);
modport slave(output a, b, x, input c, d, y);
endinterface : illegal_i
endinterface : i
interface illegal_i;
// a, b, c, d not declared by this interface
modport master(input a, b, output c, d);
modport slave(output a, b, input c, d);
endinterface : illegal_i
向接口添加modports不需要在使用接口时使用任何modports。如果在模块头部或端口连接中没有指定modport,那么接口中的所有网络和变量都可以通过方向inout或ref访问,如前面的示例所示。
这个接口示例展示了如何使用modports来控制端口声明中的信号方向。它在模块定义中使用modport名称。
interface simple_bus (input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
modport slave (input req, addr, mode, start, clk,
output gnt, rdy,
ref data);
modport master(input gnt, rdy, clk,
output req, addr, mode, start,
ref data);
endinterface: simple_bus
module memMod (simple_bus.slave a); // interface name and modport name
logic avail;
always @(posedge a.clk) // the clk signal from the interface
a.gnt <= a.req & avail; // the gnt and req signal in the interface
endmodule
module cpuMod (simple_bus.master b);
always @(posedge b.clk)
b.start<=1;
endmodule
module top;
logic clk = 0;
simple_bus sb_intf(clk); // Instantiate the interface
initial repeat(10) #10 clk++;
memMod mem(.a(sb_intf)); // Connect the interface to the module instance
cpuMod cpu(.b(sb_intf));
endmodule
这个接口示例展示了如何使用modports来限制接口信号访问并控制其方向。它在模块实例化中使用modport名称。
interface simple_bus (input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
modport slave (input req, addr, mode, start, clk,
output gnt, rdy,
ref data);
modport master(input gnt, rdy, clk,
output req, addr, mode, start,
ref data);
endinterface: simple_bus
module memMod(simple_bus a); // Uses just the interface name
logic avail;
always @(posedge a.clk) // the clk signal from the interface
a.gnt <= a.req & avail; // the gnt and req signal in the interface
endmodule
module cpuMod(simple_bus b);
...
endmodule
module top;
logic clk = 0;
simple_bus sb_intf(clk); // Instantiate the interface
initial repeat(10) #10 clk++;
memMod mem(sb_intf.slave); // Connect the modport to the module instance
cpuMod cpu(sb_intf.master);
endmodule
这个接口示例展示了如何使用modports来控制信号方向。它显示了interface关键字在模块定义中的使用。实际接口和modport在模块实例化中指定。
interface simple_bus (input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
modport slave (input req, addr, mode, start, clk,
output gnt, rdy,
ref data);
modport master(input gnt, rdy, clk,
output req, addr, mode, start,
ref data);
endinterface: simple_bus
module memMod(interface a); // Uses just the interface
logic avail;
always @(posedge a.clk) // the clk signal from the interface
a.gnt <= a.req & avail; // the gnt and req signal in the interface
endmodule
module cpuMod(interface b);
...
endmodule
module top;
logic clk = 0;
simple_bus sb_intf(clk); // Instantiate the interface
memMod mem(sb_intf.slave); // Connect the modport to the module instance
cpuMod cpu(sb_intf.master);
endmodule
modport表达式允许数组和结构的元素、元素的级联以及接口中声明的元素的赋值形式表达式包含在modport列表中。此modport表达式使用端口标识符显式命名,仅通过modport连接可见。
与模块端口声明中显式命名的端口一样,端口标识符存在于每个modport列表各自的名称空间中。当modport项只是一个简单的端口标识符时,该标识符既用作接口项的引用,也用作端口标识符。一旦定义了一个端口标识符,就不会有其他具有相同名称的端口定义。
例如:
interface I;
logic [7:0] r;
const int x=1;
bit R;
modport A (output .P(r[3:0]), input .Q(x), R);
modport B (output .P(r[7:4]), input .Q(2), R);
endinterface
module M ( interface i);
initial i.P = i.Q;
endmodule
module top;
I i1 ();
M u1 (i1.A);
M u2 (i1.B);
initial #1 $display("%b", i1.r); // displays 00100001
endmodule
端口表达式的自行确定的类型将变为端口的类型。端口表达式不应被视为类似赋值的上下文。端口表达式应解析为模块端口类型的合法表达式(见23.3.3)。在前面的示例中,Q端口不能是output或inout,因为端口表达式是常量。端口表达式是可选的,因为可以定义不连接到端口内部任何内容的端口。
modport构造还可以用于指定接口内声明的clocking块的方向。与其他modport声明一样,clocking块的方向是从接口成为端口的模块中看到的方向。其语法如语法25-2所示。
modport_declaration ::= modport modport_item { , modport_item } ; // from A.2.9
modport_item ::= modport_identifier ( modport_ports_declaration { , modport_ports_declaration } )
modport_ports_declaration ::=
{ attribute_instance } modport_simple_ports_declaration
| { attribute_instance } modport_tf_ports_declaration
| { attribute_instance } modport_clocking_declaration
modport_clocking_declaration ::= clocking clocking_identifier
Syntax 25-2—Modport clocking declaration syntax (excerpt from Annex A)
modport声明中使用的所有clocking块应由与modport本身相同的接口声明。与所有modport声明一样,时钟信号的方向是从接口成为端口的模块中看到的方向。以下示例显示如何使用modports来创建同步端口和异步端口。当与虚拟接口结合使用时(见25.9.2),这些构造有助于创建抽象同步模型。
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
module top;
logic clk;
A_Bus b1( clk );
A_Bus b2( clk );
dev1 d1( b1 );
dev2 d2( b2 );
T tb( b1, b2 );
endmodule
program T (A_Bus.STB b1, A_Bus.STB b2 ); // testbench: 2 synchronous ports
assert property (b1.sb.p1); // assert property from within program
initial begin
b1.sb.req <= 1;
wait( b1.sb.gnt == 1 );
...
b1.sb.req <= 0;
b2.sb.req <= 1;
wait( b2.sb.gnt == 1 );
...
b2.sb.req <= 0;
end
endprogram
该示例显示了使用由接口端口b1和b2的时钟modport指定的同步接口的程序块。除了程序驱动和时钟块信号的采样之外,程序断言其接口b1之一的属性p1。
specify块用于描述模块上的各种路径,并执行时序检查以验证在模块输入处发生的事件满足由模块描述的设备的时序约束。模块路径是从模块输入端口到输出端口,时序检查是相对于模块输入的。 specify块将这些端口称为terminal描述符。模块输入输出端口可以用作输入或输出端口。当其中一个端口实例是接口时,接口中的每个信号都成为可用的终端,默认方向为接口定义或受modport限制。ref端口不能用作指定块中的终端。
以下显示了将接口与指定块一起使用的示例:
interface itf;
logic c,q,d;
modport flop (input c,d, output q);
endinterface
module dtype (itf.flop ch);
always_ff @(posedge ch.c) ch.q <= ch.d;
specify
( posedge ch.c => (ch.q+:ch.d)) = (5,6);
$setup( ch.d, posedge ch.c, 1 );
endspecify
endmodule