Title

SystemVerilog -- 6.1 Interface ~ Introduction

SystemVerilog Interface Intro

SystemVerilog 允许我们将多个信号组合在一起,并将它们表示为单个端口。所有这些信号都可以在一个地方声明和维护,并且易于维护。Interface 中的信号由 Interface 实例句柄访问。

Syntax

Interface blockinterfaceendinterface 关键字中间定义和描述。它可以像带或不带 portmodule 一样实例化。

interface [name] ([port_list]);
    [list_of_signals]
endinterface

interface 还可以具有 functionstasksvariablesparameters,使其更像 class template。它还能够通过该结构定义不同的模块端口的方向信息策略,以及带有 clocking blocks 的 tb 同步功能。它还可以具有 assertionscoverage recording 记录和其他协议检查元素。最后但并非最不重要的一点是,它还可以包含 initialalways 过程语句和 assign 连续赋值语句。

模块不能在接口中实例化!但是接口可以在模块中实例化。

SystemVerilog 现在作为 HDL 很流行,让我们看看两种情况,其中接口在 Verilog 和 SystemVerilog 中都使用相同的设计。为了在这个介绍性实例中保持简单,我们将创建一个简单的界面。
让我们看看如何在tb中使用接口,并通过端口列表连接到标准Verilog设计。

Interface with a Verilog Design

  1. 功能和指标定义:up-down counter
  • load_en有效,load加载到count
  • load_en无效,down有效,count倒计时
  • load_en无效,down无效,count正常计时
  • rollover输出指示counter何时从max_value转换到0或0转换到max_value。
  1. RTL 编写
module counter_ud #(parameter WIDTH = 4) (
    input                  clk,
    input                  rstn,
    input                  load_en,
    input                  down,
    input [WIDTH-1:0]      load,

    output                 rollover,
    output reg [WIDTH-1:0] count
);

    always @(posedge clk or negedge rstn) begin
      if (!rstn) begin
          count <= 0;
      end else begin
          if (load_en) begin
            count <= load;
          end else begin
            if (down) begin
               count <= count - 1;
            end else begin
               count <= count + 1;
            end
          end
      end

    assign rollover = &count;
endmodule

下面声明了一个名为cnt_if的接口,其中包含一个可参数化的值作为counter信号的宽度。此任务还有一个任务init ()来分配值。

interface cnt_if #(parameter WIDTH = 4) (input bit clk);
    logic             rstn;
    logic             load_en;
    logic             down;
    logic [WIDTH-1:0] load;
    logic [WIDTH-1:0] count;
    logic             rollover;
endinterface

module tb;
    // Design with a clock -> here half_period = 10ns => 50 MHZ
    always #10 clk = ~clk;

    cnt_if cnt_if0(clk);
    
    counter_ud c0 (
      .clk (cnt_if0.clk),
      .rstn(cnt_if0.rstn),
      .load(cnt_if0.load),
      .load_en(cnt_if0.load_en),
      .down(cnt_if0.down),
      .count(cnt_if0.count),
      .rollover(cnt_if0.rollover)
    );

    initial begin
        bit load_en, down;
        bit [3:0] load;

        $monitor("[%0t] down=%0b load_en=%0b load=0x%0h count=0x%0h rollover=%0b", $time, cnt_if0.down, cnt_if0.load_en, cnt_if0.load, cnt_if0.rollover);

        // Initialize testbench varibles
        clk             <= 0;
        cnt_if0.rstn    <= 0;
        cnt_if0.load_en <= 0;
        cnt_if0.load    <= 0;
        cnt_if0.down    <= 0;

        // Drive design out of reset after 5 clocks
        repeat (5) @(posedge clk);
        cnt_if0.rstn <= 1;

        // Drive inputs after some random delay
        for (int i = 0; i < 5; i++) begin
          // Drive inputs after some random delay
          int delay = $urandom_range (1,30);
          #(delay);

          // Randomize input values to be driven
          std::randomize(load, load_en, down);

          // Assign tb values to interface signals
          cnt_if0.load    <= load;
          cnt_if0.load_en <= load_en;
          cnt_if0.down    <= down;
        end

        // Wait for 5 clocks and finish simulation
        repeat(5) @ (posedge clk);
        $finish;
    end
endmodule
模拟日志
ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=0 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=1 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0

Interface with a SystemVerilog Design

现在让我们看看如何在tb中使用接口并连接到SystemVerilog设计模块。SystemVerilog允许接受接口作为端口列表,而不是单个信号。在下面显示的设计示例中,我们用用于定义设计功能的接口句柄替换了counter_ud的端口列表。

`timescale 1ns/1ns

// This module accepts an interface object as the port list
module counter_ud #(parameter WIDTH = 4) (cnt_if _if);
    always @(posedge _if.clk or negedge _if.rstn) begom
       if (!_if.rstn) begin
          _if.count <= 0;
        end else begin
          if (_if.load_en) begin
            _if.count <= _if.load;
          end else begin
            if (_if.down) begin
               _if.count <= _if.count - 1;
            end else begin
               _if.count <= _if.count + 1;
            end
          end
      end

    assign _if.rollover = &_if.count;

endmodule

设计实例传递一个名为cnt_if的接口句柄,用于驱动来自tb的设计输入。如果需要,可以使用相同的接口句柄来监控设计的输出。

// Interface definition is the same as before

module tb
    reg clk;

    always #10 clk= ~clk;

    cnt_if cnt_if0 (clk);

    counter_ud c0 (cnt_if0);
endmodule
模拟日志
ncsim> run
[0] down=0 load_en=0 load=0x0 count=0x0 rollover=0
[96] down=0 load_en=1 load=0x1 count=0x0 rollover=0
[102] down=1 load_en=0 load=0x9 count=0x0 rollover=0
[108] down=1 load_en=1 load=0x1 count=0x0 rollover=0
[110] down=1 load_en=1 load=0x1 count=0x1 rollover=0
[114] down=1 load_en=0 load=0xc count=0x1 rollover=0
[120] down=1 load_en=0 load=0x7 count=0x1 rollover=0
[130] down=1 load_en=0 load=0x7 count=0x0 rollover=0
[150] down=1 load_en=0 load=0x7 count=0xf rollover=1
[170] down=1 load_en=0 load=0x7 count=0xe rollover=0
[190] down=1 load_en=0 load=0x7 count=0xd rollover=0
Simulation complete via $finish(1) at time 210 NS + 0

What makes it different from Verilog ?

Verilog 通过其模块端口在不同module之间连接。对于大型设计,这种连接方法可能会变得更加耗时和重复。其中一些端口可能包括与总线协议(如 AXI/AHB)、时钟和复位引脚、进出RAM/memory和其它外围设备的信号相关的信号。

Using Verilog Ports

这是Verilog中传统的端口连接方式。

moduoe d_slave (
                input clk,
                      reset,
                      enable,
                      // Many more input signals
                output gnt,
                       irq,
                       // Many more output signals
              );
                        
              // Some design functionality
endmodule

module d_top ( [top_level_ports] );
    reg [`NUM_SLAVES-1:0] clk;
    reg [`NUM_SLAVES-1:0] tb_reset;
    // Other declarations

    d_slave slave_0 (
                    .clk(d_clk[0]),
                    .rest(d_reset[0]),
                        ...
                    .gnt(d_gnt[0]),
                    ...
                    );
    d_slave slave_1 ( ... );
    d_slave slave_2 ( ... );
endmodule

让我们考虑一个场景,在上面显示的设计中有12个slaves。如果在d_slave模块端口上进行了更改,则该更改也必须反映在d_top中的所有12个slaves实例连接中。

Disadvantages

使用Verilog端口方法进行连接的一些缺点是:

  • 跟踪、调试和维护繁琐
  • 设计功能太容易成败
  • 设计要求的变化可能需要对多个模块进行修改
  • 需要在多个模块、通信协议和其他地方进行复制

Using SystemVerilog Interface

请注意,模块d_top只是使用接口与slave实例连接,而不是像从前所示重复声明与slave模块的每个信号的连接。

interface slave_if (input logic clk, reset);
    reg clk;
    reg reset;
    reg enable;

    reg gnt;
    // Declarations for other signals follow
endinterface

module d_slave (slave_if s_if);
    always (s_if.enable & s_if.gnt) begin
      // Some behavior
    end
endmodule


module d_top (input clk, reset);
    slave_if slave_if_inst (.clk(clk), .reset(reset));

    d_slave slave_0 (.s_if (slave_if_inst));
    d_slave slave_1 (.s_if (slave_if_inst));
    d_slave slave_2 (.s_if (slave_if_inst));
endmodule

现在,如果接口中的某个信号发生更改,则会自动应用于所有实例。在SystemVerilog中,模块端口列表还可以具有inout接口类型的端口,而不是通常的inputoutput

Interface Array

在下面的示例中,在 top_tb模块中创建并实例化了一个名为myInterface的接口,该接口的端口列表为空。省略空端口列表的括号,用分号阶截断语句也是可以的。

// interface myInterface

interface myInterface ();
    reg       gnt;
    reg       ack;
    reg [7:0] irq;
endinterface

module tb
  // Single interface handle
  myInterface if0 ();
  
  // An array of interfaces
  myInterface wb_if [3:0] ();
endmodule

可以实例化一个名为if0的接口,并且应通过引用此句柄来访问此接口中的信号。然后,这可用于驱动和采样进入DUT的信号。

我们还可以有一个Interface Array。这里这个数组用名称wb_if引用,它有4个接口实例。

module myDesign (myInterface dut_if,
                 input logic clk);
    always @(posedge clk) begin
        if (dut_if.ack)
          dut_if.gnt <= 1;
    end
endmodule

module tb;
    reg clk;

    myInterface if0;

    myDesign top (if0, clk);

    
    myDesign md0 (wb_if[0], clk);
    myDesign md0 (wb_if[1], clk);
    myDesign md0 (wb_if[2], clk);
    myDesign md0 (wb_if[3], clk);
endmodule

当接口被引用为端口时,假定其中的变量和网络分别具有和访问。如果在设计中使用相同的标识符作为接口实例名称和端口名称,则也可以使用隐式端口连接。

module tb;
    reg clk;
    myInterface dut_if();
    // Can use implicit port connection when all port signals have same name
    myDesign top (.*);
endmodule

posted on 2024-04-18 21:22  松—松  阅读(103)  评论(0编辑  收藏  举报

导航