接口(Interface)是SystemVerilog新增的重要部分,专门用于封装模块间的交互。接口同样有助于design复用。接口是层次化结构,可以包含其他接口。
下面是使用接口的一些优势:
- 接口封装连接性,接口可以像单个部件一样使用,这样可以用一个替换传统的一组型号名。这简化的端口连接的代码,有利于维护,增加可读性。
- 接口封装功能性,通过接口接连的模块,可以更容易划分。这样,抽象等级和交互协议的颗粒度可以独立于模块之外进行优化。
- 接口可以包含参数,常数,变量,函数和任务,以及进程和连续赋值。这不仅有助于系统级的建模,也利于TB的应用。
- 接口有助于建立功能覆盖率的记录和报告,协议检查和断言等应用。
- 接口可用于Port-less的访问,一个接口在模块内可以直接例化成静态数据对象。这样,用于访问接口相关内部状态信息的方法就可以在不同时间点被调用,用来共享信息。
- 灵活性,接口可以像模块一样参数化。同样,一个模块头可以建立的时候带着不指定的接口例化,称为原接口。这种接口可以在后面模块例化的时指定。
简单来说,接口就是一捆线网的命名,类似结构体,除了接口可以作为模块的端口而结构体不行。
下面例子展示了接口的定义和使用:
// Interface definition
interface Bus;
logic [7:0] Addr, Data;
logic RWn;
endinterface
// Using the interface
module TestRAM;
Bus TheBus(); // Instance the interface
logic [7:0] mem [0:7];
RAM TheRAM (.MemBus(TheBus)); // Connect it
initial
begin
TheBus.RWn = 0;
TheBus.Addr = 0;
for (int I=0; I<7; I++)
TheBus.Addr = TheBus.Addr + 1;
TheBus.RWn = 1;
TheBus.Data = mem[0];
end
endmodule
module RAM(Bus MemBus);
logic [7:0] mem[0:255];
always @*
if (MemBus.RWn)
MemBus.Data = mem[MemBus.Addr];
else
mem[MemBus.Addr] = MemBus.Data;
endmodule
接口端口(Interface Ports)
接口同样可以有输入口,输出口和双向口。只有声明在端口列表的变量和线网,可以当端口例化时,根据名字或者位置于外部连接。接口可以与接口共享。端口使用ANSI的风格声明。
下面这个例子展示了带有时钟端口的接口:
interface ClockedBus (input Clk);
logic [7:0] Addr, Data;
logic RWn;
endinterface
Module RAM (ClockedBus Bus);
always @(posedge Bus.Clk)
if (Bus.RWn)
Bus.Data = mem[Bus.Addr];
else
mem[Bus.Addr] = Bus.Data;
endmodule
// Using the interface
module Top;
reg Clock;
// Instance the interface with an input, using named connection
ClockedBus TheBus (.Clk(Clock));
RAM TheRAM (.Bus(TheBus));
...
endmodule
参数化接口(Parameterised Interface)
下面是一个展示参数化接口的简单例子:
interface Channel #(parameter N=0)
(input bit Clock, bit ACK, bit Sig);
bit Buff[N-1:0];
initial
for (int i=0; i<N; i++)
Buff[i] = 0;
always @ (posedge Clock)
if (Ack == 1)
Sig = Buff[N-1];
else
Sig = 0;
endmodule
// Using the interface
module Top;
bit Clock, Ack, Sig;
// Instance the interface. The parameter N is set to 7 using named
// connection while the ports are connected using implict connection
Channel #(.N(7)) TheCh (.*);
TX TheTx (.Ch(TheCh));
...
endmodule
调整接口端口(Modports in Interface)
一个与接口相关的部件也加进来:调整端口。这给模块的接口提供了方向的信息并且在控制了任务和函数在特定模块的使用。
下面这个例子包含了调整端口,用来确定接口中信号的方向。方向是从模块的角度来看,在我们的例子中就是TheRAM
interface MSBus (input Clk);
logic [7:0] Addr, Data;
logic RWn;
modport Slave (input Addr, inout Data);
endinterface
module TestRAM;
logic Clk;
MSBus TheBus(.Clk(Clk));
RAM TheRAM (.MemBus(TheBus.Slave));
...
endmodule
module RAM (MSBus.Slave MemBus);
// MemBus.Addr is an input of RAM
endmodule
接口中的任务(Tasks in Interfaces)
任务和函数可以在接口中定义,实现更抽象的建模。下面这个例子展示了两个用来建模总线功能的任务。任务在testRAM模块中调用。
interface MSBus (input Clk);
logic [7:0] Addr, Data;
logic RWn;
task MasterWrite (input logic [7:0] waddr,
input logic [7:0] wdata);
Addr = waddr;
Data = wdata;
RWn = 0;
#10ns RWn = 1;
Data = 'z;
endtask
task MasterRead (input logic [7:0] raddr,
output logic [7:0] rdata);
Addr = raddr;
RWn = 1;
#10ns rdata = Data;
endtask
endinterface
module TestRAM;
logic Clk;
logic [7:0] data;
MSBus TheBus(.Clk(Clk));
RAM TheRAM (.MemBus(TheBus));
initial
begin
// Write to the RAM
for (int i = 0; i<256; i++)
TheBus.MasterWrite(i[7:0],i[7:0]);
// Read from the RAM
for (int i = 0; i<256; i++)
begin
TheBus.MasterRead(i[7:0],data);
ReadCheck : assert (data === i[7:0])
else $error("memory read error");
end
end
endmodule