SV_interface

一.端口的缺点

数字系统模块之间的通信是至关重要的领域。在Verilog中,使用模块端口连接模块。对于大型模块效率低下:
【1】手动连接数百个端口可能会导致错误;
【2】需要所有端口的详细信息;
【3】设计更改,难以更改;
【4】更加耗时;
【5】大多数端口声明工作在许多模块中都是重复的。
让我们看一个verilog示例:

module Dut (input clk, read, enable,
Input [7:0] addr,
output [7:0] data);
....
assign data = temp1 ? temp2 : temp3 ;
always @(posedge clk)
....
endmodule

module Testbench(input clk,
Output read, enable,
output [7:0] addr,
input [7:0] data );
endmodule

将以上两个模块集成到顶部模块中。
在这里插入图片描述

1 module top();
2 reg clk;
3 wire read, enable;
4 wire [7:0] addr;
5 wire [7:0] data;
6
7 Dut D (clk,read,enable,Addr,data);
8
9 Testbench TB(clk,read,enable,Addr,data);
10
11 endmodule

所有的连接clk,读取,启用,地址,数据都是手动完成的。 第7行和第9行具有相同的代码结构,因此重复工作。 如果添加了新端口,则需要更改DUT端口,TestBench端口以及顶部模块的7、9行。 这很耗时,并且随着端口列表的增加,维护起来很复杂。

二.接口

为了解决上述问题,SystemVerilog添加了一个新的强大功能,称为接口。接口封装了块之间的互连和通信。
上例的接口声明:

interface intf #(parameter BW = 8)(input clk);如:#(100)代表接口中第一个参数;
logic read, enable;
logic [BW -1 :0] addr,data;
endinterface :intf

此处,read,enable,addr,data的信号被分组为“ intf”。 接口也可以将方向指定为输入,输出和输入。 在上面的示例中,clk信号用作接口的输入。 接口也可以具有模块之类的参数。 接口声明就像模块声明一样。 使用关键字interface,endinterface进行定义。 在模块内部,对接口中的信号使用层次名称。
提示:如果有多个驱动器,请使用wire类型。如果是单个驱动程序,请使用logic类型。
让我们使用上面声明的接口查看DUT和Te​​stbench模块。

module Dut (intf dut_if); // 声明接口

always @(posedge dut_if.clk)
if(dut_if.read) // 采样信号
$display(" Read is asserted");

endmodule

module Testbench(intf tb_if);

initial
begin
tb_if.read = 0;
repeat(3) #20 tb_if.read = ~tb_if.read;// 驱动信号
$finish;
end

endmodule

将以上两个模块集成到顶部模块中。
在这里插入图片描述

module top();
bit clk;

initial
forever #5 clk = ~clk;

intf bus_if(clk); //接口实例化
Dut d(bus_if); //Dut模块实例化d,使用接口连接D和TB
Testbench TB (bus_if);

endmodule

三.使用接口的优点:

【1】接口可以作为单个信号传递;
【2】它允许结构化信息在块之间流动;
【3】它可以包含模块中可能包含的任何内容,但其他模块定义或实例除外;
【4】接口定义独立于模块;
【5】提高可重用性;
【6】它的Interface可以在一个单独的文件中声明,也可以单独编译;
【7】接口可以包含任务(tasks)和功能(functions)。通过这种方法,所有连接到此信息的模块可以共享一个方法;
【8】接口可以包含使用断言(assertions)和功能(functional)覆盖块的协议检查;
【9】减少了在模块连接期间可能引起的错误;
【10】易于添加或删除信号。易于维护。

四.接口端口

在前面的示例中,信号clk被声明为接口的端口。 接口端口的工作方式类似于模块端口。 实例化接口时,可以按名称或位置在外部连接端口列表的成员,如模块顶部代码的第3行所示。

intf bus_if(clk);

五.Modports

在上面的示例中,我们没有提到信号的方向。Dut和Testbench模块clk信号方向为输入。但是对于其余信号,方向并不相同。在接口中使用modport结构,能够将信号分组,并指定方向。让我们在前面的示例中了解modport的用法。需要2个mod端口定义,一个用于DUT,另一个用于TestBench。
上例的接口声明:

interface intf (input clk);
logic read, enable,
logic [7:0] addr,data;

modport dut (input read,enable,addr,output data);
modport tb (output read,enable,addr,input data);
endinterface :intf

Modport选择可以通过两种方式完成。一个在模块声明中,另一个在实例化中。

5.1Modport选择任务模块定义

module Dut (intf.dut dut_if); // 调用接口intf中dut部分,并实例化名为dut_if
....
assign dut_if.data = temp1 ? temp2 : temp3 ; 
always @(posedge intf.clk)
....
endmodule

module Testbench(intf.tb tb_if);
.....
.....
endmodule

 module top();
 logic clk;
 intf bus_if(clk); //接口实例化
 Dut d(bus_if); 
Testbench TB (bus_if); 
endmodule

5.2Modport选择任务模块实例化完成

module Dut (intf dut_if); 
....
assign dut_if.data = temp1 ? temp2 : temp3 ; 
always @(posedge intf.clk)
....
endmodule

module Testbench(intf tb_if);
.....
.....
endmodule

1 module top();
2 logic clk;
3 intf bus_if(clk);
4 Dut d(bus_if.dut); // 将modport传递到模块中
5 Testbench TB (bus_if.tb);
6 endmodule

Mod端口还可以定义表达式。他们还可以定义自己的名称。模块可以使用modport声明的名称。例如:

modport dut (input read,enable,.addr(2),output .d(data[1:5]);

module dut(intf.dut dut_if);
assign dut_if.d = temp; // 使用modport声明的信号名。

六.接口中的方法

接口可以包括任务(tasks)和功能(function)定义。这允许更抽象的建模级别。

interface intf (input clk);
logic read, enable,
logic [7:0] addr,data;

task masterRead(input logic [7:0] raddr); // masterRead
...
endtask: masterRead

task slaveRead; // slaveRead method
...
endtask: slaveRead

endinterface :intf

参考文献:
【1】http://www.testbench.in/IF_03_INTERFACE_METHODS.html

发布了67 篇原创文章 · 获赞 7 · 访问量 4924

猜你喜欢

转载自blog.csdn.net/qq_43042339/article/details/104414149