虚接口的两种传递方式

虚接口的使用

绿皮书中虚接口传递interface的方式:

module dut(
    input clk,
    input rst_n,

    input [3 : 0] load_value,
    input load_vld,

    output reg [3 : 0] q
);
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) q <= 'd0;
        else if(load_vld) q <= load_value;
    end
endmodule

interface dut_if(input logic clk);
    logic [3 : 0] load_value;
    logic load_vld;
endinterface 

class transaction;
    randc logic [3 : 0] load_value;
    randc logic load_vld;
endclass

class driver;
    virtual dut_if vif;
    function new (input virtual dut_if vif);
        this.vif = vif;
    endfunction

    transaction tr;
    task run(input int n);
        for (int i = 0; i < n ; i++)begin
            tr = new();
            tr.randomize();
            $display("tr.load_value=%0d, tr.load_vld=%0d", tr.load_value, tr.load_vld);
            @(posedge vif.clk)begin
                vif.load_value <= tr.load_value;
                vif.load_vld <= tr.load_vld;
            end
        end
    endtask
endclass

module tb_dut();
    logic clk, rst_n;
    logic [3 : 0] q;

    //==================parameter
    parameter PERIOD = 10;

    //==================definaiton
    dut_if dutif(clk);
    driver my_driver;

    //==================initial
    initial begin
        clk = 0;
        forever #(PERIOD / 2) clk = ~clk;
    end
    initial begin
        rst_n = 0;repeat(5) @(posedge clk);
        rst_n = 1;repeat(50) @(posedge clk);
        $finish;
    end
    initial begin
        my_driver = new(dutif);
        @(rst_n);
        repeat(5) @(posedge clk);
        my_driver.run(20);
    end

    //===================dut
    dut u_dut(
        .clk(clk),
        .rst_n(rst_n),

        .load_value(dutif.load_value),
        .load_vld(dutif.load_vld),

        .q(q)
    );


`ifdef FSDB
initial begin
    $fsdbDumpfile("dut.fsdb");
    $fsdbDumpvars;
        $fsdbDumpMDA();
end
`endif
endmodule

这里的虚接口,在给类的句柄创建对象时将接口传递给类中的虚接口,从而完成连线,这样在类中可以调用接口内的变量。

在测试平台中使用virtual interface时,需要满足以下3个要求

1、 实例化的接口必须正确连接到DUT。

2、 必须在类中声明virtual interface句柄

3、必须将指定模块的interface赋值给virtual interface

在UVM中虚接口传递interface的方式

这里时顶层

`timescale 1ns/1ps
`include "uvm_macros.svh"

import uvm_pkg::*;
`include "my_if.sv"
`include "my_driver.sv"

module top_tb;

reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;

my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);

dut my_dut(.clk(clk),
           .rst_n(rst_n),
           .rxd(input_if.data),
           .rx_dv(input_if.valid),
           .txd(output_if.data),
           .tx_en(output_if.valid));

initial begin
   clk = 0;
   forever begin
      #100 clk = ~clk;
   end
end

initial begin
   rst_n = 1'b0;
   #1000;
   rst_n = 1'b1;
end

initial begin
   run_test("my_driver");
end

initial begin
   uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end

endmodule


/这里时接口

interface my_if(input clk, input rst_n);

   logic [7:0] data;
   logic valid;
endinterface

/这里时my_driver类

class my_driver extends uvm_driver;

   virtual my_if vif;

   `uvm_component_utils(my_driver)
   function new(string name = "my_driver", uvm_component parent = null);
      super.new(name, parent);
      `uvm_info("my_driver", "new is called", UVM_LOW);
   endfunction

   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      `uvm_info("my_driver", "build_phase is called", UVM_LOW);
      if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
         `uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
   endfunction

   extern virtual task main_phase(uvm_phase phase);
endclass

task my_driver::main_phase(uvm_phase phase);
   phase.raise_objection(this);
   `uvm_info("my_driver", "main_phase is called", UVM_LOW);
   vif.data <= 8'b0; 
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   for(int i = 0; i < 256; i++)begin
      @(posedge vif.clk);
      vif.data <= $urandom_range(0, 255);
      vif.valid <= 1'b1;
      `uvm_info("my_driver", "data is drived", UVM_LOW);
   end
   @(posedge vif.clk);
   vif.valid <= 1'b0;
   phase.drop_objection(this);
endtask

在UVM中,由于使用了factory机制,使用内置函数run_test自动创建my_driver实例,从而就不能在创建对象时传递interface(绿皮书中就是在创建对象时传递的interface)。这时就使用config_db来传递interface。

config_db使用举例

set函数和get函数的原型是:

set函数和get函数的使用方法是:

下面再展开介绍下这些参数怎么配:

uvm_config_db是一个参数化类,拥有一个类型参数type T。在调用set和get函数的时候,需要首先将要存放的数据对象的类型传递给该类型参数,即my_type。举个栗子,如果要存放一个整型,那么my_type就是int;如果要存放一个接口句柄,那么my_type就是该接口类名。这就是前一小节提到的类型(type)属性标签。

cntxt是context的意思,表示当前的上下文环境。在config_db的应用中,该参数的类型是uvm_component,其具体指的是某一个UVM的组件层次。后面的参数inst_name是字符串类型,支持glob通配符,即* + 和 ?这三种符号。cntxt参数和inst_name参数来共同组成前面小节说的可见范围(scope)属性标签。举个栗子,如果要存放的数据对象的可见范围是m_agent以下,那么cntxt参数可以设为uvm_root::get()来获得全局的顶层实例,inst_name可以设为“m_env.m_agent.*”;当然cntxt也可以直接设为null,然后inst_name设为“uvm_test_top.m_env.m_agent.*”,效果是一样的。

field_name的类型同样是string,它指的就是上一小节提到的资源名称(name)这个属性标签。不需要过多考虑,它就只是一个名字而已。但建议在选名字的时候要慎重,避免在不同组件中都set了范围有重叠且名字又一样的数据对象,除非你已经清楚哪一次set的数据对象优先级更高,可以看看下一小节的例程和扩展阅读部分。

最后一个参数value,表示的就是要存放的数据本身了。它可以用某一个变量或者句柄来表示具体的数据对象,也可以是直接给一个常数。这里需要注意,value的数据类型就是前面已经配置的my_type类型,如果不一致会引入错误。

另外,从函数原型应该可以看到,get函数是返回值的,该返回值可以用来判断是否成功获取到想要的资源对象,这一点将非常有用。

部分内容参考:system verilog:虚接口virtual interface_acmgotoac的博客-CSDN博客_systemverilog虚接口

SystemVerilog | UVM | Config_db机制基础 - 知乎 (zhihu.com)

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值