systemverilog interface

普通的模塊使用法:注意咱們這裏只實現了部分功能。。。。不是徹底的讀寫模塊。。。。html

 
 
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的方法
 
一、 首先定義一個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
 
 
 
二、定義一個基於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
 
 
          三、定義一個基於 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
 
 
四、‘最後使用一個頂層模塊將它們組合起來
 
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
相關文章
相關標籤/搜索