相关文章推荐
System Verilog 学习笔记3:interface

System Verilog 学习笔记3:interface

不定期更新在我的github和知乎,转载请先联系我~

在我们设计数字系统或验证平台时,使用信号线将各个模块连接起来是一个非常重要的步骤。当我们使用verilog做设计时,每当我们想要对某一模块的接口进行修改的时候,我们常常会感到非常苦恼,因为我们可能因为一个模块接口的修改,而连带的修改好几个相关的模块接口,尤其是当系统比较复杂时,这样的修修补补非常容易留下错误。System Verilog引入了interface这一新结构类型来解决这一问题。

如果还是无法理解interface的作用,我们可以做一个形象的比喻:

比如我们常用的usb接口,其中一共有四根数据线,VCC,VDD,D+,D-。如果这个世界没有usb接口,我们每次将计算机与usb设备链接时,就需要一根一根的将四根线分别连接才能通过计算机使用usb设备;而usb接口将四根线与插接方式打包固定了下来,当我们有了usb接口后,使用usb设备时直接插接接口即可,简单方便。

对于sv中的interface来说,它与举例中的usb接口作用差不多,是将模块接口的连线封装起来,便于使用。

1 interface的声明与使用

interface通过关键词 interface 来声明,声明方式与module非常类似,示例如下:

interface module_if(input clk);
    logic port_a_0 ;
    logic port_a_1 ;
    logic port_b_0 ;
    logic port_b_1 ;
endinterface

下面我们来体会一下使用interface与不使用interface的区别。

假设我们有两个模块module_a与module_b,且在顶层分别例化与连接,示例如下:

module module_a(
    input clk,
    input rst_n,
    input port_a_0 ,
    input port_a_1 ,
    output port_b_0 ,
    output port_b_1
    ......
endmodule
module module_b(
    input clk,
    input rst_n,
    input port_b_0 ,
    input port_b_1 ,
    output port_a_0 ,
    output port_a_1
    ......
endmodule
module top();
    logic clk ;
    logic rst_n ;
    logic port_a_0 ;
    logic port_a_1 ;
    logic port_b_0 ;
    logic port_b_1 ;
    always #10 clk = ~clk ;
    initial begin
        rst_n = 0 ;
        #50;
        rst_n = 1 ;
    module_a U_A(
        .clk(clk),
        .rst_n(rst_n),
        .port_a_0(port_a_0),
        .port_a_1(port_a_1),
        .port_b_0(port_b_0),
        .port_b_1(port_b_1)
    module_b U_B(
        .clk(clk),
        .rst_n(rst_n),
        .port_b_0(port_b_0),
        .port_b_1(port_b_1),
        .port_a_0(port_a_0),
        .port_a_1(port_a_1)
endmodule

显然,虽然代码非常简单,但是各种net还是非常凌乱,而且如果我们需要根据设计需要增加模块接口信号时,我们会增加超级多的工作,比如模块U_A和U_B均增加了一组交互信号port_c_0和port_c_1时,我们需要修改module_a的声明位置,module_b的声明位置, 以及例化U_A和U_B等4个位置的代码。

以上仅仅是简单的增加了两个信号的交互,如果增加更多的信号修改,或者某些信号修改位宽......简直不敢想象会有多少工时的加班。但是当有了interface这种结构,一切都变得简单了很多:

interface module_if(input clk);
    logic rst_n,
    logic port_a_0 ;
    logic port_a_1 ;
    logic port_b_0 ;
    logic port_b_1 ;
endinterface
module module_a( module_if U_IF);
    ......
endmodule
module module_b( module_if U_IF);
    ......
endmodule
module top();
    logic clk ;
    always #10 clk = ~clk ;
    module_if U_IF(clk);
    initial begin
        U_IF.rst_n = 0 ;
        #50;
        U_IF.rst_n = 1 ;
    module_a U_A(U_IF);
    module_b U_B(U_IF);
endmodule

这样一来,不但代码少了很多,而且当我们需要更改模块的接口设计时,仅需要在interface内部一处修改。

2 使用modport分类接口

但是如果向上面一样简单的使用interface可能还是会有点不太方便:

(1)interface中的信号可能有很多,并不是所有的模块都会用得到interface中声明的全部信号。

(2)不使用interface是,模块接口的input/output属性可以帮助我们检查连线方向的正确性,所以最好在interface中也可以规定好每一个信号在某个模块的方向。

其实在sv中,这两个问题已经得到了解决,那就是使用modport将interface中的信号进行分组打包。比如针对上面的例子,可以做一个修改:

interface module_if(input clk);
    logic rst_n,
    logic port_a_0 ;
    logic port_a_1 ;
    logic port_b_0 ;
    logic port_b_1 ;
    modport A(
        input rst_n ,
        input port_a_0 ,
        input port_a_1 ,
        output port_b_0 ,
        output port_b_1
    modport B(
        input rst_n ,
        output port_a_0 ,
        output port_a_1 ,
        input port_b_0 ,
        input port_b_1 
endinterface
module module_a( module_if.A U_IF);
    ......
endmodule
module module_b( module_if.B U_IF);
    ......
endmodule
module top();
    logic clk ;
    always #10 clk = ~clk ;
    module_if U_IF(clk);
    initial begin
        U_IF.rst_n = 0 ;
        #50;
        U_IF.rst_n = 1 ;
 
推荐文章