systemverilog interface 接口

普通的模块使用法:注意我们这里只实现了部分功能。。。。不是完全的读写模块。。。。

 

 

module mem_core( 

 input logic wen,

 input logic ren,

 output logic mrdy=1,

 input logic [7:0] addr,

 input logic [7:0] mem_din,  //写进mem

 output logic [7:0] mem_dout,   //从mem读出

 output logic status,

 input logic clk);

 

 logic[7:0] mem [7:0];   //初始化一个mem

  

  initial $readmemh("d:/init.txt",mem);    //d:/init.txt 文件中是 @01  10  。

  

  //或者   assign mem [2'h01]=8'b00000111;     注意这里一定要用 initial 或者assign等语句,不能直接=

  

  task reply_read(input logic [7:0] data, integer delay);

   #delay;

   @(negedge clk)

   mrdy=1'b0;

   mem_dout=data;   //从图中可看出这两句话几乎同时执行。

   @(negedge clk)

   mrdy=1'b1;

endtask

  

  always@(negedge ren) reply_read(mem[addr],10);

endmodule

 

module cpu_core(

 output logic wen=1,

 output logic ren=1,

 input logic mrdy,

 output logic [7:0] addr=0,

 input logic [7:0] cpu_din,

 output logic [7:0] cpu_dout,

 output logic status=0,

 input logic clk);

 

   task read_memory(input logic [7:0] raddr, output logic [7:0] data);

   @(posedge clk);

   ren=1'b0;

   addr=raddr;

   @(negedge mrdy);

   @(posedge clk);

   data=cpu_din;

   ren=1'b1;

 endtask

 

  initial begin

    logic[7:0] read_data;

    read_memory(2'h01, read_data);

    $display("Read Result", $time,read_data);

  end

endmodule

 

module top;

  logic mrdy,wen,ren;

  logic[7:0] addr,d1,d2;

  wor status;

  logic clk=0;

 

  mem_core mem(.*, .mem_din(d1), .mem_dout(d2));  //采用*对同名的信号做默认连接

  cpu_core cpu(.*, .cpu_din(d2), .cpu_dout(d1));

 

initial for(int i=0;i<=255;i++) #1 clk=!clk;

 

endmodule

 

Systemverilog <wbr>interface

 

另外,SystemVerilog引入一个重要的数据类型:interface。其主要作用有两个:一是简化模块之间的连接;二是实现类和模块之间的通信;

  • 随着复杂度的提高,模块间互联变得复杂,SV引入接口,代表一捆连线的结构,具有智能同步和连接功能的代码;

接口(interface)为硬件模块的端口提供了一个标准化的封装方式。

用interface来封装接口的信号和功能。interface的定

义是独立于模块的,通过关键字interface和endinterface包起来。此外,interface里面还可以

带时钟、断言、方法等定义。

       一个interface 也可以有input,output或是inout端口。当interface例化时,只有当变量或是线网声明在一个interface的端口列表中才能通过名字或是位置来互连.

一种新加的和interface有关系的构造体是Modport 。它提供了module的interface端口和在特定的module中控制task和function使用的方向性信息。这些端口的方向可以在module中可以看到。接口使用无信号的连接方式。Modport将接口中信号分组并指定方向。就像下图中的黑色矩形块里面一样,黑盒,我们从外面看并不关心Modport的定义,只需要考虑clk。

Systemverilog <wbr>interface
 

 

interface membus(input logic clk, output wor status);

 logic mrdy;

 logic wen;

 logic ren;

 logic [7:0] addr;

 logic [7:0] c2m_data;

 logic [7:0] m2c_data;

 

 task reply_read(input logic [7:0] data, integer delay);

   #delay;

   @(negedge clk)

   mrdy=1'b0;

   m2c_data=data;

   @(negedge clk)

   mrdy=1'b1;

endtask

 

 //Task和function可以定义在interface中,从而允许构造更抽象级的模型

 

 task read_memory(input logic [7:0] raddr, output logic [7:0] data);

   @(posedge clk);

   ren=1'b0;

   addr=raddr;

   @(negedge mrdy);

   @(posedge clk);

   data=m2c_data;

   ren=1'b1;

 endtask

 

modport master(output wen, ren, addr, c2m_data, input mrdy, m2c_data, status, read_memory);

modport slave(input wen, ren, addr, c2m_data, output mrdy, m2c_data, status, reply_read);

//控制task和function使用的方向性信息,以便在下面的module中使用

 

endinterface

 

module mem_core(membus.slave mb);    

//modport只需在模块首部指明(或者在()中),在模块例化时不需要指明使用接口时在模块和程序块之外声明接口变量;

//接口信号必须采用非阻塞值赋值来驱动。      

  logic[7:0] mem [7:0];

  assign mem [2'h01]=8'b00000111;

  assign mb.status=0;

  always@(negedge mb.ren) mb.reply_read(mem[mb.addr],100);    //module可使用interface端口

endmodule

 

module cpu_core(membus.master mb);

  assign mb.status=0;

  initial begin

    logic[7:0] read_data;

    mb.read_memory(2'h01, read_data);

    $display("Read Result", $time,read_data);

  end

endmodule

 

module top;

  wor status;

  logic clk=0;

  membus mb(clk,status);

  mem_core mem(.mb(mb.slave));

  cpu_core cpu(.mb(mb.master));

 

initial for(int i=0;i<=255;i++) #1 clk=!clk;

 

endmodule

 

 

 

   System verilog把测试平台的代码放在一个程序块中,包含代码和变量,

 

 

我总结了几步使用interface的方法

 

1、 首先定义一个interface 

 

interface arb_if(input bit clk); 

  logic [1:0] grant, request; 

  logic reset; 

 

 clocking cb @(posedge clk);              

 //在其中定义一个时钟块。供下面的测试program使用。测试program中所有使用到的信号都应该定义在其中

      

    output request;          //注意这里的方向是测试program中所需要的方向,一般跟DUT 中的相反

    input grant; 

  endclocking

 

  modport TEST (clocking cb,                             //  使用modport,将信号分组

                output reset);

 

  modport DUT (input request, reset, clk,

               output grant);

 

  modport MONITOR (input request, grant, reset, clk);

 

endinterface

 

 

 

2、定义一个基于interface参数的设计模块module

 

module arb (arb_if.DUT arbif);          //该interface参数要实例化

          reg last_winner;

           reg winner;

          reg [1:0] next_grant;

          reg [1:0] state, nxState;

  

      always @(posedge arbif.clk or posedge arbif.reset) 

    begin

     。。。

       end

endmodule

 

 

          3、定义一个基于interface参数的测试程序program

 

program automatic test (arb_if.TEST arbif);      //该interface参数也要实例化

   

task reset_test();

  

  begin

$display("Task reset_test: asserting and checking reset");

      

      arbif.reset <= 0;

      #100 arbif.reset <= 1;   //测试program中所有使用到的信号都应该调用在interface中的时钟块里定义的信号

      

      arbif.cb.request <= 0;

      repeat (2) @arbif.cb;

      arbif.reset <= 0;

      @arbif.cb;                   //测试program中是这样等待时钟边沿的。

      a0: assert (arbif.cb.grant == 2'b00);

     。。。

      end

 

 

    endtask

 

task request_grant_test();

    begin

     。。。

      end

    endtask

                                             //注意program中不允许使用always块。

 

    initial begin

      repeat (10) @arbif.cb;

 

      reset_test();

 

      request_grant_test();

 

      repeat (10) @arbif.cb;

      $finish;

 

    end

endprogram

 

 

4、‘最后使用一个顶层模块将它们组合起来

 

module top;

  bit  clk;

  always #5 clk = !clk; 

 

  arb_if arbif(clk);      //实例化一个interface

  arb a1 (arbif);         //实例化一个module,参数调用上面实例化的interface

  test t1(arbif);           //实例化一个测试program,参数调用上面实例化的interface

 

endmodule

 

 

 

 

当然也可以隐式端口连接,值使用.*即可。

module top;

  bit  clk;

  always #5 clk = !clk; 

 

  arb_if arbif(.*);

  arb a1 (.*);         

  test t1(.*);           

endmodule

 

 

 

虚接口:虚接口是物理接口的句柄

 

interface 和 module是一样的, 都是静态的变量, 也就是在程序开始时, 内存中就有了其实例.

 

但是在class里使用virtual interface时之前有两部必须提前完成:

l 定义是将接口作为一个类进行定义。

l 实例化:在RTL级的顶层中对接口进行实例化。

 

 

先定义一个接口。

 

interface Rx_if (input logic clk);

    logic [7:0] data;

    logic soc, en, clav, rclk;

 

    clocking cb @(posedge clk);

      output data, soc, clav;

      input  en;

    endclocking : cb

 

    modport DUT (output en, rclk,

                input  data, soc, clav);

 

    modport TB (clocking cb);

endinterface : Rx_if

 

 

 

例如网络交换机中DUT 的每一个通道都有一个接口。,一个Driver类可能会连接到很多接口。

我们可以在Driver类中使用一个虚接口作为参数。 

class Driver;

virtual Rx_if.TB Rx;                  

//想一想,如果不是虚接口,而是一个普通接口,就像一个普通模块一样,是一个静态变量。比如我们在顶层模块例化了这个接口 Rx, 那么下面所有的 实例化的  drv[i]都是对这同一个接口 Rx进行操作,这显然不是我们想要的。

如果定义了virtual,则每个实例独立。

 

...

...

endclass

 

 

 

 

 

 然后在测试program中 创建一组虚接口

 

 

program automatic test(Rx_if.TB Rx[4],

               Tx_if.TB Tx[4],

                       output logic rst);

     ........

   Driver drv[4];        //实例化了4个   Driver 对象,每个 Driver对象带有1个实例化的虚接口 

.........

 

   initial begin

 

    virtual Rx_if.TB vRx_t=Rx;   

//创建一组虚接口,由于这里定义了virtual,所以实例化的时候可以有Rx[].

 

      for (int i=0; i<4; i++) begin

       

         drv[i] = new(...., vRx[i]);

 

      end

 

      rst <= 1;

      repeat (10) @Rx[0].cb;

      rst <= 0;

      for (int i=0; i<4; i++) begin

        drv[i].run(5, driver_done);          //发送

  .......

      end

..........

endprogram : test

 

最后在顶层:

module top;

  logic clk, rst;

 

   Rx_if Rx[4] (clk);

   ,,,,

  atm_router a1 (Rx[0], Rx[1], Rx[2], Rx[3], Tx[0], Tx[1], Tx[2], Tx[3], clk, rst);

 

  test       t1 (Rx, Tx, rst);

 

  initial begin

    clk = 0;

    forever #20 clk = !clk;

    end

 

endmodule : top

 

定义一个interface,且实例化多个后,如果没有定义virtual,则在任何一个实例中修改了某个信号值,在其他实例中都会受到影响。如果定义了virtual,则每个实例独立。如果该interface只有一个实例,可用可不用virtual,有多个实例,需要virtual。

 

 

再举个例子:8位计数器

 

`timescale 1ns/1ns

 

interface X_if (input logic clk);

    logic [7:0] din, dout;

    logic reset_l, load;

    

    clocking cb @(posedge clk);

    output din, load;

    input dout;

    endclocking

 

    always @cb             //接口里面也可以带子程序,断言,initial,always块等代码。

      $strobe("@ : %m: dout= , din= , load= , reset= ", 

              $time, dout, din, load, reset_l);

    

    modport DUT (input clk, din, reset_l, load,

                 output dout);

 

    modport TB (clocking cb, output reset_l);

endinterface

 

 

 

// Simple 8-bit counter with load and active-low reset

`timescale 1ns/1ns

 

module DUT(X_if.DUT xi);

  logic [7:0] count;

  assign xi.dout = count;   //们想要输出的结果就是计数器

  

  always @(posedge xi.clk or negedge xi.reset_l)

    begin

      if (!xi.reset_l)  count = 0;

      else if (xi.load) count = xi.din;

      else              count++;

    end

 

endmodule

 

`timescale 1ns/1ns

 

program automatic test();

  

  parameter NUM_XI = 2;  // Number of interface instances

    typedef virtual X_if.TB vXi_t;

    vXi_t vxi[NUM_XI];          //虚接口数组

 

 

    class Driver;        //在测试程序中定义类

    vXi_t xi;

    int id;

 

    function new(vXi_t xi, int id);

    this.xi = xi;

    this.id = id;

    endfunction

 

    task reset;

    fork

      begin

        $display("@ : %m: Start reset [ ]", $time, id);

        // Reset the device

        xi.reset_l <= 1;

        xi.cb.load <= 0;

        xi.cb.din <= 0;

        @(xi.cb)

          xi.reset_l <= 0;

        @(xi.cb)

          xi.reset_l <= 1;

        $display("@ : %m: End reset [ ]", $time, id);

      end

    join_none

    endtask

 

    task load;

    fork

      begin

        $display("@ : %m: Start load [ ]", $time, id);

        xi.cb.load <= 1;

        xi.cb.din <= id + 10;

 

        xi.cb.load <= 0;

        repeat (5) @(xi.cb);

        $display("@ : %m: End load [ ]", $time, id);

      end

    join_none

    endtask

 

    endclass

 

 

    Driver driver[];

 

    initial begin

      // Connect the local virtual interfaces to the top

      $display("Test.v: There are NUM_XI = interfaces", NUM_XI);

      if (NUM_XI <= 0) $finish;

 

      driver = new[NUM_XI];            //创建driver,   每个DUT 要对应一个driver

 

      vxi = top.xi;                

//XMR跨模块连接。这种是推荐做法,就不用带参数了program automatic test(X_if xi[NUM_XI]); 了。

//注意这里其实是把top模块中生成的xi[]数组的句柄传过来的

 

 

for (int i=0; i《NUM_XI; i++)

        begin

          driver[i] = new(vxi[i], i);

          driver[i].reset;

        end

 

      foreach (driver[i])

        driver[i].load;

 

      repeat (10) @(vxi[0].cb);

 

      $display("@ : Test completed", $time);

      $finish;

    end

 

endprogram

 

`timescale 1ns/1ns

parameter NUM_XI = 2;  // Number of interface instances

 

module top;

  // Clock generator

  bit clk;

  initial forever #20 clk = !clk;

 

 

  X_if xi [NUM_XI] (clk);  // Instantiate N Xi interfaces

 

  // Generate N DUT instances

  generate

  for (genvar i=0; i《NUM_XI; i++)

    begin : dut

      DUT d (xi[i]);

    end

  endgenerate

 

  // Instantiate the testbench, overriding the parameter with number of instances

  test tb();

 

endmodule : top

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Systemverilog <wbr>interface

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SystemVerilog interface 是 SystemVerilog 语言中的一种特殊类型。它提供了一种在不同模块之间进行接口定义和继承的方式。它可以在不同层次和不同范围内定义接口,并可以在不同的模块之间复用。通过使用 interface,可以将复杂模块的接口和实现分离,提高代码可重用性和可读性。 ### 回答2: SystemVerilog接口interface)是一种用户自定义数据类型,可以看作是由多个时序和结构性信号组成的单个实体。它是SystemVerilog用于描述复杂通信协议的一种方式,通过将协议的各个部分封装到接口中实现。接口在实现电路系统级别设计中的复杂底层通信协议时,是非常有用的。它可以简化系统的设计,并提高代码的可读性和可重用性。接口可以用来描述各种类型的连接,包括模块之间的连接,子系统和电路板之间的连接,以及芯片级别的接口接口可以包含三种类型的成员:信号、任务和函数。信号是接口的主要成员,用于描述信号的宽度,方向和类型。任务和函数与信号类似,但它们是用于描述属于接口的操作和行为的成员。接口还可以包含其他接口和型号的实例,这使得接口更加灵活。 接口还可以用于建模并描述通信协议的时序特性,包括时序控制、同步和异步数据传输、手握握手协议等等。 在实际应用中,接口常用于组合两个或多个模块,从而实现对应的通信功能,减少代码的重复,并且使代码的结构更加模块化和易于理解。它是一种有效的方式来实现并行设计,将设计的复杂性转化为更小的组件。 ### 回答3: SystemVerilog是一种针对硬件描述语言(HDL)的扩展,它提供了许多更高级别的模拟和综合特性。它还引入了一种称为接口的语言结构来改进模块之间和系统之间的通信。 SystemVerilog中的接口是一种类似于模块的结构,它允许多个模块在接口相互通信。接口可以在其他模块中实例化,从而允许它们在各种系统中共享和再利用。接口可以具有多个信号线或子接口,并且可以在接口内部有层次结构。 接口还可以具有时序关系,称为协议,它定义了信号如何传输,什么时间传输以及如何响应接收方。例如,一个接口可以定义一个传输协议,包括传输类型,数据位宽,同步或异步等,并用于数据传输。这使得接口可以有效地描述高层次协议,这些协议在系统级别上非常重要。 SystemVerilog中的接口通过向导向式声明和实例化在模块中使用。通过这种方式,SystemVerilog接口使设计人员能够更快地构建和调试信号传输和系统级通信,从而帮助提高设计的可重用性和维护性,以及降低总体系统开发成本。 总而言之,SystemVerilog接口是一种强大的语言工具,可用于描述和实现各种互联系统,从芯片级别接口到板级和系统级接口。通过定义协议和规定标准接口,系统设计人员可以更好地管理复杂的通信和控制,从而提高设计的效率和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值