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

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

 
 
 
 
 
 
 
 
 
 
 
 

SV学习之interface的更多相关文章

  1. Avalon总线学习 ---Avalon Interface Specifications

    Avalon总线学习 ---Avalon Interface Specifications 1.Avalon Interfaces in a System and Nios II Processor ...

  2. SV中的Interface和Program

    Interface:SV中新定义的接口方式,用来简化接口连接,使用时注意在module或program之外定义interface,然后通过'include来添加进工程. interface  arb_ ...

  3. 被迫开始学习Typescript —— interface

    一开始以为,需要使用 class 来定义呢,学习之后才发现,一般都是使用 interface 来定义的. 这个嘛,倒是挺适合 js 环境的. 参考:https://typescript.bootcss ...

  4. Java学习——接口Interface

    接口: 初期理解可以认为是一个特殊的抽象类 当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示.class用于定义类interface 用于定义接口 接口定义时,格式特点:1,接口中常量见定 ...

  5. golang学习之interface与其它类型转换

    如下函数,将interface变量in转换为int: func formatTimeStamp(in interface{}, layout string) (out string) { timeSt ...

  6. golang学习笔记 ---interface

    1. 什么是interface接口 interface 是GO语言的基础特性之一.可以理解为一种类型的规范或者约定.它跟java,C# 不太一样,不需要显示说明实现了某个接口,它没有继承或子类或“im ...

  7. C# interface

    我们学习了interface,即接口,其与抽象类有点像,但是他们也有一些区别,比如类不能多重继承但是接口却可以多重继承. 接口只包含方法.委托或事件和属性的签名(接口包含的成员).不能包含字段(因为字 ...

  8. Cadence UVM基础视频介绍(UVM SV Basics)

    Cadence关于UVM的简单介绍,包括UVM的各个方面.有中文和英文两种版本. UVM SV Basics 1 – Introduction UVM SV Basics 2 – DUT Exampl ...

  9. java反射以及动态代理的学习

    java反射学习 1)字节码文件的三种获取方式 ①:Object类的getClass()方法:对象.getClass() ②:数据类型的静态的class属性:类名.class ③:通过Class类的静 ...

随机推荐

  1. 洛谷P3209 [HNOI2010]平面图判定(2-SAT)

    传送门 看到哈密顿回路就被吓傻了……结果没有好好考虑性质…… 首先,平面图有个性质:边数小于等于$3n-6$(我也不知道为啥),边数大于这个的直接pass 然后考虑原图,先把哈密顿回路单独摘出来,就是 ...

  2. IDEA设置取消自动显示参数提示

    IDEA设置取消自动显示参数提示 最近在使用IDEA的过程中,发现方法中一直显示形参名的提示,无法选中,也无法删除,基于不同人的使用习惯不同,有的人不喜欢这种提示,我也在网上寻找各种解决方案,由于搜索 ...

  3. C# BitmapData和Marshal.Copy()用法

    C# BitmapData和Marshal.Copy()用法 //此函数用法例子如下: public static byte[] GetGrayArray(Bitmap srcBmp, Rectang ...

  4. bzoj 4860 [BeiJing2017]树的难题

    题面 https://www.lydsy.com/JudgeOnline/problem.php?id=4860 题解 点分治 设当前重心为v 假设已经把所有边按照出发点第一关键字, 颜色第二关键字排 ...

  5. Random Query CodeForces - 846F

    题目 翻译: 给出一个n个数字的数列a[1],...,a[n],f(l,r)表示使a[l],a[l+1],...,a[r]组成的新序列中的重复元素只保留一个后,剩下元素的数量(如果l>r,则在计 ...

  6. Oozie的架构

    Oozie的架构图,如下: 从oozie的架构图中,可以看到所有的任务都是通过oozie生成相应的任务客户端,并通过任务客户端来提交相应的任务. 继续...

  7. linux给文件或目录添加apache权限

    系统环境:ubuntu11.10/apache2/php5.3.6 在LAMP环境中,测试一个简单的php文件上传功能时,发现/var/log/apache2/error.log中出现如下php警告: ...

  8. 动手实现 Redux(二):抽离 store 和监控数据变化

    上一节 的我们有了 appState 和 dispatch: let appState = { title: { text: 'React.js 小书', color: 'red', }, conte ...

  9. MySQL 当记录不存在时insert,当记录存在时更新

    网上基本有三种解决方法. 第一种: 示例一:insert多条记录 假设有一个主键为 client_id 的 clients 表,可以使用下面的语句: INSERT INTO clients (clie ...

  10. html制作简单框架网页二 实现自己的影音驿站 操作步骤及源文件下载 (可播放mp4、avi、mpg、asx、swf各种文件的视频播放代码)

    新增视频播放功能如下图: 左侧网页left.html代码如下: <meta charset="utf-8"> <body style="backgrou ...