zoukankan      html  css  js  c++  java
  • SV学习之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

     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    概率论与统计学---笔记
    实用概率论与数理统计学--笔记
    并发编程总结5-JUC-REENTRANTLOCK-3(非公平锁)
    并发编程总结4-JUC-REENTRANTLOCK-2(公平锁)
    并发编程总结3——JUC-LOCK-1
    DOCKER & SWARM1.2
    Docker
    hdfs命令
    并发编程总结2——java线程基础2
    并发编程总结1——java线程基础1
  • 原文地址:https://www.cnblogs.com/chip/p/4302563.html
Copyright © 2011-2022 走看看