您的位置:首页 > 其它

something about the systemverilog--- interface

2015-09-07 11:00 211 查看
普通的模块使用法:注意我们这里只实现了部分功能。。。。不是完全的读写模块。。。。

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(inputlogic [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引入一个重要的数据类型: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。





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

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

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

1、首先定义一个interface 

interface arb_if(input bit clk); 
  logic [1:0] grant,request; 
  logicreset; 

 clocking cb @(posedge clk);           
 //在其中定义一个时钟块。供下面的测试program使用。测试program中所有使用到的信号都应该定义在其中

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

  modport TEST (clocking cb,                        // 使用modport,将信号分组
              output reset);

  modport DUT (input request, reset,clk,
             outputgrant);

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

endinterface

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

module arb (arb_if.DUT arbif);        //该interface参数要实例化
         reglast_winner;
         reg winner;
        reg [1:0] next_grant;
        reg [1:0] state, nxState;
  
    always @(posedgearbif.clk or posedge arbif.reset) 
    begin
    。。。
      end
endmodule

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

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

taskreset_test();
  
  begin

$display("Task reset_test: asserting and checkingreset");
     
     arbif.reset <= 0;
      #100arbif.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

taskrequest_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、‘最后使用一个顶层模块将它们组合起来

moduletop;
  bit clk;
  always #5clk = !clk; 

  arb_ifarbif(clk);     //实例化一个interface
  arb a1(arbif);        //实例化一个module,参数调用上面实例化的interface
  testt1(arbif);          //实例化一个测试program,参数调用上面实例化的interface

endmodule

当然也可以隐式端口连接,值使用.*即可。
module top;
  bit clk;
  always #5clk = !clk; 

  arb_ifarbif(.*);
  arb a1(.*);       
  testt1(.*);         
endmodule

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

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

但是在class里使用virtualinterface时之前有两部必须提前完成:
l定义是将接口作为一个类进行定义。
l实例化:在RTL级的顶层中对接口进行实例化。

先定义一个接口。

interface Rx_if (input logic clk);
    logic [7:0] data;
    logic soc, en, clav,rclk;

    clocking cb @(posedgeclk);
     output data, soc, clav;
      input en;
    endclocking : cb

    modport DUT (output en,rclk,
              input  data,soc, clav);

    modport TB (clockingcb);
endinterface : Rx_if

例如网络交换机中DUT的每一个通道都有一个接口。,一个Driver类可能会连接到很多接口。
我们可以在Driver类中使用一个虚接口作为参数。 
classDriver;
virtual Rx_if.TB Rx;               
//想一想,如果不是虚接口,而是一个普通接口,就像一个普通模块一样,是一个静态变量。比如我们在顶层模块例化了这个接口 Rx,那么下面所有的 实例化的  drv[i]都是对这同一个接口 Rx进行操作,这显然不是我们想要的。

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

...
...
endclass

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

program automatic test(Rx_if.TBRx[4],
             Tx_if.TB Tx[4],
                    outputlogic 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 logicclk);
    logic [7:0] din,dout;
    logic reset_l,load;
    
    clocking cb @(posedgeclk);
    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,
               outputdout);

    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 negedgexi.reset_l)
    begin
      if(!xi.reset_l)  count = 0;
      elseif (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跨模块连接。这种是推荐做法,就不用带参数了programautomatic 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 interfaceinstances

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, overridingthe parameter with number of instances
  test tb();

endmodule : top
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: