来到了SV最后一部分,预计三篇文章,两周更完,所有的思维导图如下:
概述
SystemVerilog Interface是modport的一种,但比简单的输入、输出或输入输出端口的功能更多。在其最简单的形式中,Interface端口将相关的信号捆绑在一起作为一个单一的复合端口。例如,构成AMBA AXI总线的所有单个信号都可以被归纳为一个Interface端口。一个Interface可以做的不仅仅是封装总线信号。SystemVerilog Interface为设计者提供了一种集中总线功能的方法,而不是将功能分散在设计中的几个模块中。这就模拟了设计工程师在RTL层面的工作,并让综合工作在整个设计中适当地分配门级总线硬件。
当遵循特定的建模准则和限制时,Interface是可以综合的。Interface也可以用在不可综合级别的建模,并作为验证测试平台的一部分。先进的验证方法,如UVM 、OVM和VMM,都使用Interface。
Interface端口的概念
对于可综合的RTL建模,Interface的主要目的是将多信号总线的声明和一些协议功能信号封装在一个单独的定义中。然后,这个Interface定义可以在任何的模块中使用,而不必重复声明总线信号。
一个Interface是在关键字interface和end interface之间定义的。一个可综合的RTL Interface定义可以包含:
带有数据类型和向量宽度的变量(Variable)和wire声明。
modport定义,给出信号的方向。可以为将使用该Interface的不同模块指定不同的定义。
用于仿真零延迟、零时钟周期总线功能。
Interface也可以包含不可综合的事务级功能和验证代码,包括初始程序、always程序、任务和断言 。本文没有讨论Interface的这些不可综合的方面。
本章的例子使用AMBA AHB总线的简化版本,被称为"simple AHB",在主模块和从模块之间进行通信。这个简化版本只使用了构成完 整AMBA AHB总线的19个信号中的8个。简单的AHB信号是:
表10-1:简化的AMBA AHB信号信号名 | 位宽 | 备注 |
---|---|---|
hclk | 1-bit | 总线传输时钟,外部产生 |
hresetN | 1-bit | 有源低电平总线复位,外部产生 |
haddr | 32-bit | 转移地址 |
hwdata | 32-bit | 从主机发送至从机的数据值(有些例子增加了一个1位奇偶位) |
hrdata | 32-bit | 从机发回给主机的数据值(有些例子增加了一个1位的奇偶位) |
hsize | 3-位 | 指示传输规模的控制信号 |
hwrite | 1-bit | 从主机向从机转移方向控制(1为写,0为读) |
hready | 1-bit | 从机的响应,表明传输已经完成 |
这种简单的AHB总线在单个主机和单个从机模块之间进行通信,因此不需要完整的AMBA AHB总线所需要的总线仲裁器和解码器模块。
传统的Verilog总线连接
如果没有Interface,构成通信总线的信号必须在每个使用总线的模块中作为单独的端口来声明。这些端口的声明必须在每个使用总线的模块中重复进行,并在连接总线和其他模块的网表模块中作为wire再次重复进行。
图10-1显示了将主模块和从模块连接在一起的框图,使用了简化版的AMBA AHB总线的8个信号。图中还显示了四个与简单的AHB总线无关的额外信号。使用某种形式的总线协议进行通信的模块,除了构成总线的那些信号外,通常还有其他的输入和输出。
图10-1:使用独立interface连接主机和从机的方框图例10-1显示了连接图10-1中的主模块和从模块的代码。请注意,构成简单AHB总线的8个信号的声明是重复的。同样的信号必须在主模块、从模块、连接主模块和从模块以及主模块和从模块实例的连接中声明。例10-1是用传统的Verilog-2001风格和数据类型建模的。
例10-1:使用单独的端口连接主模块和从模块///
// Master Module Port List -- Verilog-2001 style
///
//`begin_keywords "1364-2001"
module master (
// simplified AHB bus signals
input wire hclk, // bus transfer clk
input wire hresetN, // bus reset, active low
output reg [31:0] haddr, // transfer start address
output reg [31:0] hwdata, // data sent to slave
output reg hwrite, // return data from slave
output reg [ 2:0] hsize, // transfer size
input wire [31:0] hrdata, // 1 for write, 0 for read
input wire hready, // 1 for transfer finished
// other signals
input wire m_clk, // master clock
input wire rstN, // reset, active low
input wire [7:0] thing1, // misc signal; not part of bus
output reg [7:0] thing2 // misc signal; not part of bus
);
//... // master module functionality not shown
endmodule: master
//`end_keywords
///
// Slave Module Port List -- Verilog-2001 style
///
//`begin_keywords "1364-2001"
module slave (
// simplified AHB bus signals
input wire hclk, // bus transfer clk
input wire hresetN, // bus reset, active low
input wire [31:0] haddr, // transfer start address
input wire [31:0] hwdata, // data sent to slave
input wire hwrite, // return data from slave
input wire [ 2:0] hsize, // transfer size
output reg [31:0] hrdata, // 1 for write, 0 for read
output reg hready, // 1 for transfer finished
// other signals
input wire s_clk, // slave clock
input wire rstN, // reset, active low
output reg [7:0] thing1, // misc. signal; not part of bus
input wire [7:0] thing2 // misc. signal; not part of bus
);
//... // slave module functionality not shown
endmodule: slave
//`end_keywords
///
// Top-level Netlist Module -- Verilog-2001 style
///
//`begin_keywords "1364-2001"
module chip_top;
// Simplified AHB bus signals
wire hclk; // bus transfer clk
wire hresetN; // bus reset, active low
wire [31:0] haddr; // transfer start address
wire [31:0] hwdata; // data sent to slave
wire hwrite; // return data from slave
wire [ 2:0] hsize; // transfer size
wire [31:0] hrdata; // 1 for write, 0 for read
wire hready; // 1 for transfer finished
// Other signals
wire m_clk; // master clock
wire s_clk; // slave clock
wire chip_rstN; // reset, active low
wire [7:0] thing1; // misc signal; not part of bus
wire [7:0] thing2; // misc signal; not part of bus
master m (// simplified AHB bus connections
.hclk(hclk),
.hresetN(hresetN),
.haddr(haddr),
.hwdata(hwdata),
.hsize(hsize),
.hwrite(hwrite),
.hrdata(hrdata),
.hready(hready),
// 0ther connections
.m_clk(m_clk),
.rstN(chip_rstN),
.thing1(thing1),
.thing2(thing2)
);
slave s (// simplified AHB bus connections
.hclk(hclk),
.hresetN(hresetN),
.haddr(haddr),
.hwdata(hwdata),
.hsize(hsize),
.hwrite(hwrite),
.hrdata(hrdata),
.hready(hready),
// 0ther connections
.s_clk(s_clk),
.rstN(chip_rstN),
.thing1(thing1),
.thing2(thing2)
);
//... // remaining chip-level code not shown
endmodule: chip_top
//`end_keywords
与主端口的连接必须再次复制简化的AHB信号(SystemVerilog的点名或如果所有的名字都匹配,点星的快捷方式可以减少这种冗余。 正是如此。
「离散的输入和输出modport的缺点」。为总线信号使用单独的modport,描述设计中各块之间的互连提供了一种简单而直观的方法。独立的端口准确地模拟了构成总线物理实现的信号。然而,在大型复杂的设计中,使用单个modport有几个缺点。其中一些缺点是:
声明必须在多个模块中重复进行。
通信协议,如握手顺序,必须在七个模块中重复使用。
在不同的模块中存在着不匹配声明的风险。
设计规范的改变可能需要在多个模块中同步进行修改。
在前面的例子中,构成简化AHB总线的信号必须在每个使用总线的模块中声明,以及在将主模块和从模块连接在一起的顶层网表中声明。即使是上面列出的简化AHB总线例子--它只使用了19个AHB总线信号中的8个,并且只有一个从属模块--名称的重复也是很明显的。每个AHB信号总共被命名了7次!
这种重复不仅需要输入大量的代码行,而且很有可能出现编码错误。在一个地方输入错误的名称或不正确的矢量大小可能会导致设计中的功能错误,直到设计过程的后期,当模块被连接在一起进行完整的验证时才会被发现。
复制的端口声明也意味着,如果在设计过程中(或在下一代设计中)总线的规格发生变化,每一个共享总线的模块都必须被改变。用于连接使用该总线的模块的网表也必须改变。这种大范围的改变效果是与良好的编码风格相违背的。好的编码的一个目标是将代码结构化,使一个地方的小变化不需要改变代码的其他区域。使用离散的输入和输出端口的一个弱点是,一个模块中的端口的改变通常需要其他文件的改变。
使用离散的输入和输出modport的另一个缺点是,通信协议必须在每个利用模块之间相互连接的信号的模块中重复进行。例如,如果有三个模块从一个共享的存储器设备上进行读写,那么读写控制逻辑必须在每个模块中重复进行。
SystemVerilog Interface定义
SystemVerilog给Verilog增加了一个强大的新端口类型,称为Interface端口。一个Interface允许一些信号被组合在一起,并被表示为一个单一的端口。构成Interface的信号的声明被封装在关键字interface和end interface之间。每个使用这些信号的模块都有一个Interface类型的单一端口,而不是许多不相干的信号的端口。
图10-2显示了一个Interface如何将几个单独的端口组综合成一个单一的端口,连接到一个Interface。
例10-3显示了主模块和从模块的定义。主模块上用于简单AHB总线的8个独立端口已经被单个Interface端口所取代。这个Interface端口没有被声明为输入、输出或inout,而是被声明为simple_ahb,也就是例10-2中定义的Interface的名称。当使用传统的单独的输入和输出端口时,Interface端口消除了主从模块内多余的简单AHB信号声明,如例10-1中的情况。
例10-4显示了连接主模块和从模块的高层网表。不再有24行的代码来声明8个独立的总线信号和然后将这8个信号连接到例10-1中列出的主模块和从模块的端口。相反,simple_ahb Interface的实例化方式与模块相同,实例名称与主模块和从模块实例的Interface端口相连。
图10-2:使用Interface端口连接主机和从机的方框图下面的三个例子显示了使用Interface如何减少为上面所示的简单AHB通信总线建模所需的代码量。
例10-2显示了一个Interface端口的定义,它将构成简单AHB的信号封装为一个Interface。
例10-2:简单AMBA AHB总线的Interface定义///
// Simple AMBA AHB Interface -- SystemVerilog-2012 style
///
//`begin_keywords "1800-2012"
interface simple_ahb (
input logic hclk, // bus transfer clk
input logic hresetN // bus reset, active low
);
logic [31:0] haddr; // transfer start address
logic [31:0] hwdata; // data sent to slave
logic [31:0] hrdata; // return data from slave
logic [ 2:0] hsize; // transfer size
logic hwrite; // 1 for write, 0 for read
logic hready; // 1 for transfer finished
// master module port directions
modport master_ports (
output haddr, hwdata, hsize, hwrite, // to AHB slave
input hrdata, hready, // from AHB slave
input hclk, hresetN // from chip level
);
// slave module port directions
modport slave_ports (
output hrdata, hready, // to AHB master
input haddr, hwdata, hsize, hwrite, // from AHB master
input hclk, hresetN // from chip level
);
endinterface: simple_ahb
//`end_keywords
「Interface上的端口。」一个Interface就像一个模块一样可以有输入、输出和inout端口。例10-2中所示的简单AHB Interface有两个输入端口,hc1k和hresetN,这些信号是在Interface之外产生的,通过两个输入端口传入Interface。Interface上的端口的声明与模块上的端口相同。
一个Interface可以连接到另一个Interface。例如,一个设计的主总线可能有一个或多个子总线。主总线和它的子总线都可以被建模为Interface,而子总线的Interface可以被用作主总线Interface的端口。
上面的Interface定义包括两个mod端口的定义,名字分别是master_ports和slave_ports。关键字modport是 "模块的端口 "的缩写,它定义了一个模块是否将Interface中的信号视为模块的输入或模块的输出。Interface的一个好处是,总线协议中使用的信号的数据类型和矢量大小是一次性定义的。modport的定义只是从模块的角度为Interface中定义的信号增加一个方向。
下面是一个主模块和从模块的例子,说明使用simple_ahb Interface作为每个模块的端口。观察一下这个单一的Interface端口是如何取代主模块中显示的8个离散的输入和输出端口,以及从属模块中的另外8个端口.
例10-3:带有Interface端口的主模块和从模块///
// Master Module Port List -- SystemVerilog-2012 style
///
//`begin_keywords "1800-2012"
module master
(simple_ahb.master_ports ahb, // interface port & modport
// other ports
input logic m_clk, // master clock
input logic rstN, // reset, active low
input logic [7:0] thing1, // misc signal; not part of bus
output logic [7:0] thing2 // misc signal; not part of bus
);
//... // master module functionality not shown
endmodule: master
//`end_keywords
///
// Slave Module Port List -- SystemVerilog-2012 style
///
//`begin_keywords "1800-2012"
module slave
(simple_ahb.slave_ports ahb, // interface port & modport
// other ports
input logic s_clk, // slave clock
input logic rstN, // reset, active low
output logic [7:0] thing1, // misc signal; not part of bus
input logic [7:0] thing2 // misc signal; not part of bus
);
//... // slave module functionality not shown
endmodule: slave
/// version with no modport selection ///
//module slave
//(simple_ahb ahb, // interface port without modport
// // other ports
// input logic s_clk, // slave clock
// input logic rstN, // reset, active low
// output logic [7:0] thing1, // misc signal; not part of bus
// input logic [7:0] thing2 // misc signal; not part of bus
//);
// // //... // slave module functionality not shown
//endmodule: slave
//`end_keywords
对于传统的modport,顶层模块必须为总线信号声明单独的网络,然后为每个单独的信号与每个模块实例的端口做单独的连接。
当一个带有Interface端口的模块被实例化时,一个Interface的实例被连接到Interface端口。
下面的代码实例化了simple_ahb Interface并给它一个实例名ahbl 。然后这个实例名被用于主模块和从模块实例的端口连接。
例10-4:连接主Interface和从Interface的网表///
// Top-level Netlist Module -- SystemVerilog-2012 style
///
//`begin_keywords "1800-2012"
module chip_top;
logic m_clk; // master clock
logic s_clk; // slave clock
logic hclk; // AHB bus clock
logic hresetN; // AHB bus reset, active low
logic chip_rstN; // reset, active low
logic [7:0] thing1; // misc signal; not part of bus
logic [7:0] thing2; // misc signal; not part of bus
//
// instantiate the interface
// (using same syntax as a module instance)
simple_ahb ahb1(.hclk(hclk),
.hresetN(hresetN)
);
// instantiate master and connect the interface instance
// to the interface port
master m (.ahb(ahb1), // connect interface port
.rstN(chip_rstN),
.m_clk, // dot-name connection shortcut
.thing1, // for the other ports
.thing2
);
// instantiate slave and connect the interface instance
// to the interface port
slave s (.ahb(ahb1), // connect interface port
.rstN(chip_rstN),
.* // wildcard connection shortcut
); // for the other ports
// instantiate slave and pick modport in the connection
// slave s (.ahb(ahb1.slave_ports), // select slave modport
// .rstN(chip_rstN),
// .* // wildcard connection shortcut
// );
//... // remaining chip-level code not shown
endmodule: chip_top
//`end_keywords
在上面的例子中,构成简单AHB总线协议的所有信号都被封装在simple_ahbInterface中。主模块、从模块和顶层模块并不重复声明这些总线信号。相反,主模块和从模块只是使用该Interface作为模块之间的连接。该Interface消除了独立modport的冗余声明。
注意事项 |
---|
一个模块的Interface端口不能不连接。 |
一个模块的输入、输出或输入输出端口可以在一个模块实例上不连接。Interface端口则不是这样。一个interface必须连接到一个模块上。如果一个Interface端口没有被连接,将发生一个阐述错误。 |
在一个Interface内引用信号
Interface端口是一个复合端口,端口内部有信号。在一个有Interface端口的模式中,通过使用端口名称来访问Interface内部的信号,使用的语法如下。
<port_name>.<internal interface signal_name>。
上面的simple_ahbInterface包含一个叫做hc1k 的信号,master有一个名为ahb的Interface端口 。主控模块可以通过使用ahb.hclk访问hclk。
always_ff @(posedge ahb.hclk)
最佳实践指南10-1 |
---|
在RTL模型中,使用简短的名称作为Interface端口名称。端口名称在RTL代码中需要经常被引用。 |
由于Interface内的信号是通过在信号名称前加上Interface端口名称来访问的,所以使用短名称作为Interface端口名称是很方便的。
模块和Interface之间的区别
Interface和模块之间有三个基本区别。首先,一个Interface不能包含设计层次。与模块不同,Interface不能包含模块或基元的实例,这将创造一个新的实现层次结构。第二,Interface可以作为modport使用,这就是允许Interface代表模块之间的通信渠道。在端口列表中使用一个模块是非法的。第三,一个Interface可以包含Interface,它允许连接到该Interface的每个模块以不同的方式看到该Interface。
源代码声明顺序
一个Interface的名字在两种情况下被引用:作为一个模块的端口,以及作为一个Interface的实例。Interface可以作为模块的端口被实例化和使用,而不需要考虑文件顺序的依赖性。就像模块一样,Interface的名称可以在软件工具读入包含Interface定义的源代码之前被引用。这意味着任何模块都可以使用一个Interface作为模块的端口,而不必担心源代码的编译顺序。