数字硬件建模SystemVerilog之Interface方法概述

描述

概述

SystemVerilog Interface是modport的一种,但比简单的输入、输出或输入输出端口的功能更多。在其最简单的形式中,Interface端口将相关的信号捆绑在一起作为一个单一的复合端口。例如,构成AMBA AXI总线的所有单个信号都可以被归纳为一个Interface端口。一个Interface可以做的不仅仅是封装总线信号。SystemVerilog Interface为设计者提供了一种集中总线功能的方法,而不是将功能分散在设计中的几个模块中。这就模拟了设计工程师在RTL层面的工作,并让综合工作在整个设计中适当地分配门级总线硬件。

当遵循特定的建模准则和限制时,Interface是可以综合的。Interface也可以用在不可综合级别的建模,并作为验证测试平台的一部分。先进的验证方法,如UVM 、OVM和VMM,都使用Interface。

Interface方法(任务和函数)

SystemVerilog Interface的作用不仅仅是将相关的信号组合在一起。Interface还可以封装模块间的通信功能。通过将通信功能添加到Interface中,每个使用该Interface的模块可以简单地引用该功能,而不必在每个模块中重复该功能。Interface中的封装功能也可以独立于使用该Interface的模块进行验证。

Interface中封装的功能可以通过使用任务和函数来定义。Interface中的任务和函数被称为Interface方法(interface methods)。Interface方法(任务和函数)可以通过在模块的modport定义中使用导入语句被导入到需要它们的模块中。在modport中导入函数类似于从包中导入函数。

下面的例子给简单的AHB Interface增加了两个函数--一个是生成奇偶校验位值(使用奇数校验),另一个函数是检查数据是否与计算出的奇偶校验相符。hwdata和hrdata向量被声明为比以前的例子宽1位,额外的位被用作奇偶校验位。

例10-6:带有奇偶性逻辑Interface方法(函数)的Interface

 

///////////////////////////////////////////////////////////
// Simple AHB Interface with parity methods
///////////////////////////////////////////////////////////
//`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 [32:0] hwdata;  // data to slave, with parity bit
  logic [32:0] hrdata;  // data from slave, with parity bit
  logic [ 2:0] hsize;   // transfer size
  logic        hwrite;  // 1 for write, 0 for read
  logic        hready;  // 1 for transfer finished

  function automatic logic parity_gen(logic [31:0] data);
    return(^data); // calculate parity of data (odd parity)
  endfunction

  function automatic logic parity_chk(logic [31:0] data,
                                      logic        parity);
    return (parity === ^data); // 1=OK, 0=parity error
  endfunction

  // 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
    import parity_gen, parity_check      // function import
  );

  // 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
    import parity_check                  // function import
  );

  // slave module port directions
  modport slave_ports_alt (
    output hrdata, hready,               // to AHB master
    input  haddr, hwdata, hsize, hwrite, // from AHB master
    input  hclk, hresetN,                // from chip level
    import function logic parity_chk(logic [31:0] data,
                                     logic        parity)
  );
endinterface: simple_ahb
//`end_keywords 

 

在这个例子中,master_ports 的modport定义同时导入了parity_gen 和 parity_chk。modport从主模块的角度定义了端口方向和导入。因此,一个使用master_portsmodport的模块正在导入这些函数,类似于一个模块从包中导入函数的方式。

slave_portsmodport只导入parity_chk。一个使用slave_ports modport的模块不能访问parity_gen。因为这个方法没有被包含在modport 的导入中,所以从 slave_ports 提供的Interface来看,这个方法就好像不存在一样。

使用方法原型导入方法。作为选择,modport导入声明可以指定任务或函数参数的完整原型。而不是仅仅导入方法名,导入关键字后面是实际方法定义的声明行。这种风格的导入声明的基本语法是:

 

modport(import task))。
modport (import function   () ) 。

 

比如说。

时钟发生器

函数原型不包括automatic关键字,即使实际的函数被声明为automatic(这是综合所需要的)。

使用完整的原型来导入一个方法并没有什么好处。一些工程师认为,完整的原型可以直接记录任务或函数的参数,作为模口声明的一部分。当实际的任务或函数被定义在一个包中,并被导入到Interface中时,这种额外的代码记录会很方便。原型使方法类型和参数在Interface定义中可见,这样工程师就不需要到包含包的文件中去看方法类型和参数。

调用Interface中定义的方法

导入的方法是Interface的一部分,通过使用Interface端口名称来调用,与引用Interface中的信号的方式相同。语法是

 

.

 

前面展示的主模块有一个名为ahb       的         Interface端口        。因此,主模块可以通过引用ahb.parity_gen来调用Interface parity_gen方法。例如:

 

always_ff @(posedge ahb.hclk)
    ahb.hwdata[32] <= ahb.parity_gen(ahb.hwdata[31:0])。

 

综合Interface方法

从概念上讲,综合编译器通过在模块中创建该方法的本地副本来替换导入的方法,然后对该本地副本进行综合。综合后的模块版本将包含导入方法的逻辑,而不再从Interface中获取该功能。

最佳实践指南10-4
对于可综合的RTL Interface,只在Interface中使用函数和无效函数。不要使用任务或always程序。

综合编译器对放置在模块中的Interface内容施加了同样的RTL编码限制。这些限制之一是,任务必须在零时间内执行。使用void函数而不是任务来执行这个综合限制。

注意事项
导入的函数或任务必须被声明为automatic,并且不能包含静态声明,以便被综合。这与模块从包中导入函数或任务时的同义词规则相同。

一个automatic函数或任务在每次被调用时都会分配新的存储空间。当一个模块调用一个导入的方法时,所有内部存储的新副本被分配。这允许综合处理该方法,就好像它是模块内的一个本地副本。

抽象的、不可综合的Interface

SystemVerilog Interface能够以比RTL综合编译器所支持的更高的抽象水平来表示总线协议。例如,一个Interface任务,可能需要多个时钟周期来执行,可以代表一个完整的主从握手协议。该协议可以从主机发出传输请求开始,仲裁哪个从机收到请求,等待从机的反馈,传输数据,并收到数据被接收的确认信息。

这些Interface功能对抽象的事务级建模很有用,但目前的RTL综合编译器不支持。目前的SystemVerilog综合工具要求将Interface中编码的功能限制为零延迟和零时钟周期模型。这些综合限制可以通过将Interface中定义的功能代码限制为函数来满足。SystemVerilog的语法规则要求函数必须在零模拟时间内执行,这就要求综合器对零延迟Interface功能的要求。

一个Interface也可以包含验证程序和断言。这些验证代码可以通过将其包含在pragma中而隐藏起来。

 

//synthesis translate off and / /synthesis translate on.

 

Interface程序代码

除了任务和函数方法,Interface还可以包含初始和always程序块和assign。程序代码可以用来在一个Interface内建立功能模型,这些功能会影响到在Interface所代表的总线上交流的信息。

例10-7为简单AHB总线增加了一个时钟发生器hclk,为总线增加了一个复位同步器hresetN。在这个Interface的前几个例子中,这些信号是在Interface的外部产生的,并作为简单AHB Interface的输入端口传入。这个例子用芯片(或系统)级的时钟和复位取代了这些输入,并使用这些芯片级的信号来产生本地总线时钟和总线复位。这个本地功能然后成为主模块和从模块之间封装的总线通信的一部分。

例10-7:与内部程序代码的Interface,生成总线功能

 

///////////////////////////////////////////////////////////
// Simple AHB Interface with clock generator and reset
// synchronizer procedural code
///////////////////////////////////////////////////////////
//`begin_keywords "1800-2012"
interface simple_ahb (
  input logic  chip_clk,  // external clock from the chip
  input logic  chip_rstN  // bus reset, active low
);
  logic        hclk;    // local bus transfer clk
  logic        hresetN; // local 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

  // generate AHB clock (divide-by-two of chip_clk)
  always_ff @(posedge chip_clk or negedge chip_rstN)
    if (!chip_rstN) hclk <= '0;
    else            hclk <= ~hclk;

  // sync trailing edge of hresetN to hclk
  logic rstN_tmp; // temp variable used inside the interface
  always_ff @(posedge hclk or negedge chip_rstN)
    if (!chip_rstN) begin   // asynchronous active-low reset
      rstN_tmp <= '0;
      hresetN  <= '0;
    end 
    else begin 
      rstN_tmp <= '1;       // begin end of reset
      hresetN  <= rstN_tmp; // stabilize reset
    end 

  // 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中的程序代码并没有很好的定义,这与Interface方法(任务和函数)的情况不同。方法的综合是通过在概念上将方法代码复制到具有Interface端口的模块中,并对本地副本进行综合。这可以做到,因为方法是从模块内调用的,执行起来就像方法是该模块的一部分。然而,程序代码是从Interface内执行的,并影响到所有使用该Interface的模块。Interface中的程序代码类似于全局功能,综合编译器并不支持这种功能,如果有的话。

最佳实践指南10-5
使用函数来模拟Interface内的功能。不要在可综合的RTL Interface中使用初始程序、always程序或assign赋值。

综合编译器对Interface内的程序代码的支持并不充分。如果支持的话,不同的综合编译器对程序性代码的处理方式也可能大相径庭。

参数化的Interface

Interface可以用与模块相同的方式使用参数重定义。这使得Interface模型可以被配置,因此Interface的每个实例可以有不同的配置。通过使用SystemVerilog的参数重定义结构,参数可以在Interface中使用,使向量大小和Interface中的其他声明可以重新配置。当Interface被实例化时,Interface的参数值可以被重新细化,这与模块重新定义的方式相同。

下面是简单AHB例子的变化,增加了参数,使数据矢量宽度在Interface实例化时可以配置。任何与Interface实例相连的模块间面端口将使用该Interface实例的向量大小。

例10-8:具有可配置总线数据字大小的参数化Interface

 

///////////////////////////////////////////////////////////
// Simple AHB Interface with pareterized bus widths
///////////////////////////////////////////////////////////
//`begin_keywords "1800-2012"
interface simple_ahb 
#(parameter DWIDTH=32)        // Data bus width
(
  input logic  hclk,          // bus transfer clk
  input logic  hresetN        // bus reset, active low
);
  logic [31:0]       haddr;   // transfer start address
  logic [DWIDTH-1:0] hwdata;  // data sent to slave
  logic [DWIDTH-1: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 

 

下面的代码片断将例10-8中Interface的数据字大小重新定义为64位字大小。

 

simple_ahb #(.DWIDTH(64)) 

ahbl(.hclk,
.hresetN
) ;

 

综合Interface

Interface是SystemVerilog在原有的Verilog HDL中加入的一个强大的建模结构。Interface端口是从传统的Verilog模型中抽象出来的,在传统的Verilog模型中,一组相关的信号必须每次都要声明一个信号。这些单独的声明必须在每个使用相关信号的模块以及连接模块的块级中重复进行。

在其最基本的形式中,SystemVerilog Interface将相关信号封装在一起,作为一个可重复使用的建模组件。然后,该Interface可以作为模块上的一个端口,取代一组相关信号的多个单独端口。Interface所提供的建模抽象可以成为RTL设计工程师的强大工具。设计师可以一次性定义一组相关的信号,作为一个Interface,然后可以多次使用这些信号,而不必重复定义。

综合编译器能很好地处理使用Interface来封装相关信号的问题。设计工程师可以在更高的抽象水平上工作--具有抽象的所有优点--综合编译器将信号的抽象封装转化为各个模块的端口,工程师不需要为各个端口的声明而烦恼,并确保多个模块中冗余的声明完全匹配。

综合编译器支持两种方式,即在端口声明中指定与模块一起使用的modport,或者在模块例化时指定modport。然而,如果一个模块是独立于其他模块综合的,则必须在端口声明中指定modport。

当一个模块独立于其他模块进行综合时,或者在综合编译器配置为保留RTL模块层次的情况下综合多个模块时,综合编译器将把一个模块的Interface端口扩展为modport定义中代表的各个端口。大多数综合编译器将使用Verilog-1995的端口声明风格,其中端口列表包含端口名称和顺序,而端口大小和数据类型在模块内部声明,而不是在端口列表中。一个模块可以有任意数量的Interface端口,Interface端口可以与其他端口以任意顺序指定。本文中的例子将Interface端口列在前面,只是为了强调Interface端口的作用。

下面的代码片断显示了一个主模块可能的综合前和综合后的模块定义,该模块使用简单ahb Interface。

预综合modport列表,有一个Interface端口。

时钟发生器

综合后模型,使用Verilog-1995编码风格。下面的综合后例子说明了一个Interface端口如何综合的典型结果。

时钟发生器

如果指定了一个modport的定义,综合将使用modport中指定的方向。如果在模型综合时没有指定modport,那么在综合的模块中,Interface内的所有信号都会成为双向的inout端口。

可配置的Interface与可配置的模块一样,都是可综合的。Interface可以使用参数来配置总线宽度和数据类型。Interface也可以用同样的方式进行配置。

Interface也可以通过使用方法(任务和函数)和程序代码来封装与这些信号有关的功能。Interface中的函数是可以综合的。这可能很有用,RTL设计工程师应该利用这种综合能力。将函数与它们所操作的信号封装在一起,是编写稳健、可重复使用的代码的最佳实践编码方式。

最佳实践指南10-6
将Interface的功能限制在可以用函数建模的范围内。

在编写本文时,现有的RTL综合编译器在支持使用Interface来封装使用任务和程序代码的功能方面有些局限。

例如,有可能在一个Interface内封装FIFO的全部功能,这将允许使用封装信号的模块在不同的时钟速度下运行而不损失任何数据。完整的纠错功能以及与一组信号相关的其他复杂操作也可以与这些信号捆绑在一起。这种更高级的封装水平不被大多数综合编译器所支持,或只有有限的支持。这些限制限制了程序性代码在Interface中的实用性。

Interface也可以捆绑验证代码,例如为封装的信号和功能提供断言和自我检查程序。Interface中的验证相关代码可以被综合编译器使用综合translate_off和trans1ate_on pragmas或'if def条件编译忽略。






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分