• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SystemVerilog interface使用说明

武飞扬头像
一只迷茫的小狗
帮助1

1. Interface概念

        System Verilog中引入了接口定义,接口与module 等价的定义,是要在其他的接口、module中直接定义,不能写在块语句中,跟class是不同的。接口是将一组线捆绑起来,可以将接口传递给module

2. 接口的优点

        一)通过接口在module之间或内部进行信号,模块的输入列表就是一个接口,这样简单,避免手动连线的错误。

        二)如果需要增加模块的IO,只需要在接口中增加,不需要改变模块的输入列表,防止输入错误、少改了哪个模块的列表。

        三)在UVM 中需要在不同的class之间传递信号,用接口的话,传递一组信号只需要uvm_config_db一个接口就可以了,如果不用接口,那么就需要好多条uvm_config_db语句。
        四)接口中可以定义一些initial(生成时钟),always块,任务,函数,类的句柄。

3. 定义接口

        可以在接口中定义一些信号、函数、任务、class对象,也可以有always,initial语句块。比如可以在initial块中生成时钟clk。

3.1 定义

  1.  
    interface if(input bit clk);
  2.  
    logic data;
  3.  
    logic valid;
  4.  
    logic addr;
  5.  
    endinterface

3.2 modport

        可以用modport将接口中的信号分组。比如总线接口中,master、slave、arbiter需要的信号是不同的,输入输出也不同。
 

  1.  
    interface if(input bit clk);
  2.  
    logic [7:0] data;
  3.  
    logic valid;
  4.  
    logic [7:0] addr;
  5.  
    logic request;
  6.  
    logic grant;
  7.  
    logic command;
  8.  
    logic ready;
  9.  
    modport MASTER(output request,addr,command);
  10.  
    modport SLAVE(input request,addr,command,output ready);
  11.  
    modport ARBITER(input request,output grant);
  12.  
    endinterface
  13.  
    module Master (if.MASTER if_u);
  14.  
    ...
  15.  
    endmodule
  16.  
    module test;
  17.  
    if if_u;
  18.  
    Master m_u(if_u.MASTER);
  19.  
    endmodule
学新通

4. 激励时序

        测试平台需要和设计之间的时序密切配合。比如在同一个时间片内,一个信号需要被同时写入和读取,那么采样到新值还是旧值?非阻塞赋值可以解决这个问题,值的计算在active区域,值的更新在NBA区域——采样到的是旧值

4.1 时钟块控制同步信号的时序

        在接口中定义时钟块,时钟块中的任何信号都相对于时钟同步驱动和采样。时钟块大都在测试平台中使用。

  1.  
    interface if(input bit clk);
  2.  
    logic [7:0] data;
  3.  
    logic valid;
  4.  
    logic [7:0] addr;
  5.  
    clocking cb@(posedge clk);
  6.  
    input valid;
  7.  
    input data;
  8.  
    input addr;
  9.  
    endclocking
  10.  
    modport TEST(clocking cb);
  11.  
    modport DUT(input valid ,input data);
  12.  
    endinterface

        一个接口中可以有多个时钟块,但每个时钟块只有一个时钟表达式。如@(posedge clk)定义了单时钟;@(clk)定义了DDR时钟(双数据率,两个沿)

4.2 logic还是wire

        在测试平台中,如果用过程赋值语句驱动接口中的信号,那么信号要在接口中定义为logic,如果是连续赋值驱动,定义成wire。

        定义成logic的一个好处是,如果多个信号驱动logic,那么编译器会报错,这样你就知道写错了,如果是wire,这个错误就隐藏了。

4.3 对测试平台和DUT中事件的调度

        如果没有用时钟块,测试平台对DUT的驱动和采样存在竞争,这是因为测试平台的事件和DUT的事件混合在同一个时间片中。

        SV中将测试平台中的事件和DUT中的事件分离。时间片划分:
学新通

SV的主要调度区域:
学新通

4.4 设计和测试平台之间的时序

        时钟块(测试平台)在#1step延时之后采样DUT,也就是采样上一个时间片postponed区域的数据。也就是前面讲的采样旧值。

        时钟块(测试平台)在#0延时之后驱动DUT信号。0延迟说明还在同一个time slot,DUT能够捕捉到变化。

更细致的时间片划分:

time slot  
active design
inactive 显示0延迟阻塞赋值;
observed SVA
reactive SV
postponed SV 采样

5. 接口采样和驱动信号的时序

        为了同步接口中的信号,可以在时钟沿采样或者驱动接口信号。可以在接口中定义时钟块来同步接口信号:

  1.  
    interface if(input bit clk);
  2.  
    logic data;
  3.  
    logic valid;
  4.  
    logic addr;
  5.  
    clocking cb@(posedge clk);
  6.  
    input valid;
  7.  
    input data;
  8.  
    input addr;
  9.  
    endclocking
  10.  
    modport TEST(clocking cb);
  11.  
    modport DUT(input valid ,input data);
  12.  
    endinterface

在测试平台中的信号才需要同步。

5.1 接口信号采样时序

        如果时钟块中的信号采样DUT中的信号,采样的是上一个时间片(time slot)postponed区域的数据。即如果DUT信号在时钟沿发生0-1跳变,那么采样到0。DUT接口想要驱动TEST接口中时钟块里的信号,需要先给DUT接口信号赋值:

  1.  
    module dut(if.DUT if0);
  2.  
    ....
  3.  
    #10 if0.valid = 1;
  4.  
    #10 if0.valid = 2;
  5.  
    ....
  6.  
    endmodule

学新通

5.2 接口信号驱动时序

        如果时钟块驱动DUT信号,值会立即传入到设计中。即如果时钟块中的信号在时钟沿发生0-1跳变,则时钟沿之后DUT中为1。时钟块想要驱动DUT,需要在testbench给时钟块中的信号赋值,在tb中驱动时钟块中的信号需要同步驱动,用“<=”符号。时钟块中的信号驱动采样

  1.  
    program tb(if.TEST if1);
  2.  
    ...
  3.  
    #10 if1.cb.valid <= 1;
  4.  
    #10 if1.cb.valid <= 0;
  5.  
    ...
  6.  
    endprogram

学新通

6. 使用虚接口

        之前介绍的接口都是跟module一样来描述硬件的;在SV中有面向对象的概念,在class里面使用虚接口——virtual interface。

        虚接口是一个物理接口的句柄(handler),同这个句柄来访问硬件接口。虚接口是唯一链接动态对象和静态模块、接口的一种机制

6.1 在测试平台中使用接口

  1.  
    interface inf; //定义接口
  2.  
    ...
  3.  
    endinterface
  4.  
    program test(inf if0); // 接口传入测试平台
  5.  
    driver drv;
  6.  
    initial begin
  7.  
    drv = new(if0); // 接口传给driver对象
  8.  
    end
  9.  
    endprogram
  10.  
    class driver;
  11.  
    virtual vif; //class中为虚接口
  12.  
    function new(inf i);
  13.  
    vif=i;
  14.  
    endfunction
  15.  
    endclass
  16.  
    module top;
  17.  
    inf inf0(); // 例化接口
  18.  
    test t1(inf0);
  19.  
    dut d1(inf0);
  20.  
    endmodule
学新通

也可以在tb中跨模块引用XMR(cross module reference)接口

  1.  
    program test(); //没有接口参数
  2.  
    virtual inf if0=top.inf0;//top是顶层模块
  3.  
    ...
  4.  
    endprogram
  5.  
    module top;
  6.  
    inf inf0(); // 例化接口
  7.  
    test t1(); // tb无接口列表
  8.  
    dut d1(inf0);
  9.  
    endmodule

 6.2 使用端口传递接口数组

  1.  
    interface inf(input clk);
  2.  
    ...
  3.  
    endinterface
  4.  
    parameter NUM=10;
  5.  
    module top;
  6.  
    inf xi[NUM](clk); // 顶层例化多个接口,接口名后跟个数
  7.  
    test t1(xi);// 接口作为参数
  8.  
    dut...
  9.  
    endmodule
  10.  
    program test(inf xi[NUM]); // 接口参数列表
  11.  
    virtual inf vxi[NUM];
  12.  
    initial begin
  13.  
    vxi=xi;
  14.  
    end
  15.  
    endprogram
学新通

也可以用跨模块引用。

7. 接口中的代码

接口中可以定义信号、函数、任务、class对象,也可以有always,initial语句块。

下面给一个在《UVMPrimer》中的例子:

  1.  
    interface tinyalu_bfm;
  2.  
    import tinyalu_pkg::*;
  3.  
  4.  
    byte unsigned A;
  5.  
    byte unsigned B;
  6.  
    bit clk;
  7.  
    bit reset_n;
  8.  
    wire [2:0] op;
  9.  
    bit start;
  10.  
    wire done;
  11.  
    wire [15:0] result;
  12.  
    operation_t op_set;
  13.  
  14.  
    assign op = op_set;
  15.  
  16.  
    task reset_alu();
  17.  
    reset_n = 1'b0;
  18.  
    @(negedge clk);
  19.  
    @(negedge clk);
  20.  
    reset_n = 1'b1;
  21.  
    start = 1'b0;
  22.  
    endtask : reset_alu
  23.  
     
  24.  
    task send_op(input byte iA, input byte iB, input operation_t iop, shortint result);
  25.  
    if (iop == rst_op) begin
  26.  
    @(posedge clk);
  27.  
    reset_n = 1'b0;
  28.  
    start = 1'b0;
  29.  
    @(posedge clk);
  30.  
    #1;
  31.  
    reset_n = 1'b1;
  32.  
    end else begin
  33.  
    @(negedge clk);
  34.  
    op_set = iop;
  35.  
    A = iA;
  36.  
    B = iB;
  37.  
    start = 1'b1;
  38.  
    if (iop == no_op) begin
  39.  
    @(posedge clk);
  40.  
    #1;
  41.  
    start = 1'b0;
  42.  
    end else begin
  43.  
    do
  44.  
    @(negedge clk);
  45.  
    while (done == 0);
  46.  
    start = 1'b0;
  47.  
    end
  48.  
    end // else: !if(iop == rst_op)
  49.  
     
  50.  
    endtask : send_op
  51.  
     
  52.  
    command_monitor command_monitor_h;
  53.  
  54.  
    function operation_t op2enum();
  55.  
    case(op)
  56.  
    3'b000 : return no_op;
  57.  
    3'b001 : return add_op;
  58.  
    3'b010 : return and_op;
  59.  
    3'b011 : return xor_op;
  60.  
    3'b100 : return mul_op;
  61.  
    default : $fatal("Illegal operation on op bus");
  62.  
    endcase // case (op)
  63.  
    endfunction : op2enum
  64.  
  65.  
  66.  
    always @(posedge clk) begin : op_monitor
  67.  
    static bit in_command = 0;
  68.  
    command_s command;
  69.  
    if (start) begin : start_high
  70.  
    if (!in_command) begin : new_command
  71.  
    command.A = A;
  72.  
    command.B = B;
  73.  
    command.op = op2enum();
  74.  
    command_monitor_h.write_to_monitor(command);
  75.  
    in_command = (command.op != no_op);
  76.  
    end : new_command
  77.  
    end : start_high
  78.  
    else // start low
  79.  
    in_command = 0;
  80.  
    end : op_monitor
  81.  
  82.  
    always @(negedge reset_n) begin : rst_monitor
  83.  
    command_s command;
  84.  
    command.op = rst_op;
  85.  
    command_monitor_h.write_to_monitor(command);
  86.  
    end : rst_monitor
  87.  
     
  88.  
    result_monitor result_monitor_h;
  89.  
  90.  
    initial begin : result_monitor_thread
  91.  
    forever begin
  92.  
    @(posedge clk) ;
  93.  
    if (done)
  94.  
    result_monitor_h.write_to_monitor(result);
  95.  
    end
  96.  
    end : result_monitor_thread
  97.  
     
  98.  
    initial begin
  99.  
    clk = 0;
  100.  
    forever begin
  101.  
    #10;
  102.  
    clk = ~clk;
  103.  
    end
  104.  
    end
  105.  
    endinterface : tinyalu_bfm
学新通

函数使用的时候通过接口对象调用就行了

  1.  
    virtual tinyalu_bfm inf;
  2.  
    initial begin
  3.  
    inf.send_op(..);
  4.  
    end

8. 接口使用注意事项

  • 接口不能在package中被`include 。

    下面这种写法是会报错的。
     

    1.  
      package pkg;
    2.  
      `include "apb_if.sv"
    3.  
      ……
    4.  
      endpackage

    而要放在package外面

    1.  
      `include "apb_if.sv"
    2.  
      package pkg;
    3.  
      ……
    4.  
      endpackage
    如果要在UVM中要通过hierarchy访问DUT中的信号,最好将这些信号放在interface中,然后将virtual interface传给UVM
    
    
    1.  
      // 在接口中定义信号
    2.  
      interface bfm;
    3.  
      bit[7:0 addr;
    4.  
      endinterface
    5.  
       
    6.  
      // 实例化接口
    7.  
      bfm u_bfm();
    8.  
       
    9.  
      // 将虚接口传给UVM
    10.  
      initial begin
    11.  
      uvm_config_db#(vitual bfm)::set("", uvm_test_top, "bfm", bfm);
    12.  
      end
    13.  
       
    14.  
      // 在UVM可直接操作虚接口
    如果不这样的话,当uvm component(driver, monitor, agent等)文件是通过package来管理的话,就不能在UVM中hierarchy引用DUT中的信号。
    
    
    
    
    
    
    
    

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhihbekg
系列文章
更多 icon
同类精品
更多 icon
继续加载