interface中modport和clocking


前言

interface是UVM验证过程中的一个重要的组件,主要起到连接测试用例与DUT的作用,具有简化代码,易于修改等特点。本文主要介绍interface中的modport和clocking的用法。


1 modport和clocking的介绍

modport和clocking都是interface组件中的块,主要用于对信号进行分组和同步采样。

1.1 modport

modport主要用于将接口中的信号进行分组,同时指定输入输出属性。
modprot的参数列表可以是port,也可以是clocking

interface i2;
    wire a, b, c, d;
    modport master (input a, b, output c, d);
    modport slave (output a, b, input c, d);
endinterface

在上面例子中,modport将信号分为master和slave两组,并分别指定了其输入输出方向。

1.2 clocking

clocking时钟模块主要用于信号的同步和采样。
clocking skew决定了信号从时钟事件到采样或驱动的时间单位是多少。
在这里插入图片描述

clocking bus @(posedge clock1);
    default input #10ns output #2ns;
    input data, ready, enable = top.mem1.enable;
    output negedge ack;
    input #1step addr;
endclocking

在上面的例子中,第一行声明了一个称为总线的时钟块,它将在信号clock1的正边缘上被计时。第二行指定在默认情况下,时钟块中的所有信号都将使用10ns的输入倾斜和2ns的输出倾斜。下一行将三个输入信号添加到时钟块中:data、ready和enable;最后一个信号指的是分层信号top.mem1.enable。第四行将信号ack添加到时钟块中,并覆盖默认输出倾斜,使ack被驱动在时钟的负边缘上。最后一行添加信号addr并覆盖默认的输入倾斜,以便addr在时钟的正边缘前一步被采样。

1.3 modport和clock之间的关系

interface A_Bus( input bit clk );
    wire req, gnt;
    wire [7:0] addr, data;
    clocking sb @(posedge clk);
        input gnt;
        output req, addr;
        inout data;
    endclocking
    modport STB ( clocking sb ); // synchronous testbench modport
endinterface

在上面例子中,主要演示clocking时钟块作为modport参数的用法。

从上面的例子中可以看出,modport介于interface和clocking之间,主要用于对信号进行分组,指定信号的输入输出方向,而clocking则是作用于信号的更底层,主要用于信号时序相关的控制,例如采样、同步等,当然clocking也可以指定信号的输入输出方向。(这么来看,modport好像没啥作用,modport能做的,clocking都能做。)

2 interface实战

下面主要演示一下interface在实际工程中的应用。

2.1 interface的例化

这里主要演示一个interface的具体实现。包含mst和mon两个clocking时钟块,分别用于driver和monitor组件的使用。另外一个dut的modport块,用于连接dut。
host_io.sv

`ifndef HOST_IO__SV
`define HOST_IO__SV

interface host_io(input logic clk);
  logic        wr_n, rd_n;
  wire  [15:0] address;
  wire  [15:0] data;

  clocking mst @(posedge clk);
    inout   data;
    output  address;
    output  wr_n;
    output  rd_n;
  endclocking

  clocking mon @(posedge clk);
    input  data;
    inout  address;
    input  wr_n;
    input  rd_n;
  endclocking

  modport dut(input wr_n, rd_n, inout data, address);
endinterface: host_io
`endif

2.2 interface在验证组件中的应用

这里主要演示interface在driver、monitor以及agent中的应用。

2.2.1 driver

利用uvm_config_db的get函数获取interface的句柄。
host_driver.sv

class host_driver extends uvm_driver #(host_data);
  `uvm_component_utils(host_driver)

  virtual host_io vif;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    uvm_config_db#(virtual host_io)::get(this, "", "vif", vif);
  endfunction: build_phase

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if (vif == null) begin
      `uvm_fatal("CFGERR", "Interface for host driver not set");
    end
  endfunction: end_of_elaboration_phase

  virtual task run_phase(uvm_phase phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    forever begin
      seq_item_port.get_next_item(req);
      `uvm_info("RUN", { "Before process\n", req.sprint() }, UVM_FULL);
      data_rw(req);
      rsp = host_data::type_id::create("rsp", this);
      rsp.set_id_info(req);
      `uvm_info("RUN", { "After process\n", req.sprint() }, UVM_FULL);
      seq_item_port.item_done();
    end
  endtask: run_phase

  virtual task data_rw(host_data req);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if (req.addr inside {['h4000:'h4fff]}) begin // emulating RAM access
      case(req.kind)
        UVM_READ, UVM_BURST_READ: begin
            vif.rd_n    <= '0;
            vif.address <= req.addr;
            @(vif.mst);
            req.data = vif.mst.data;
            vif.rd_n    <= '1;
            vif.address <= 'z;
          end
        UVM_WRITE, UVM_BURST_WRITE: begin
            vif.wr_n    <= '0;
            vif.data    <= req.data;
            vif.address <= req.addr;
            @(vif.mst);
            vif.wr_n    <= '1;
            vif.data    <= 'z;
            vif.address <= 'z;
          end
        default: begin `uvm_fatal("REGERR", $sformatf("%p is not a valid Register Command", req.kind)); end
      endcase
    end else begin // emulating register access
      case(req.kind)
        UVM_READ, UVM_BURST_READ: begin
            vif.rd_n    <= '0;
            vif.address <= req.addr;
            @(vif.mst);
            req.data = vif.mst.data;
            vif.rd_n    <= '1;
            vif.address <= 'z;
          end
        UVM_WRITE, UVM_BURST_WRITE: begin
            vif.wr_n    <= '0;
            vif.data    <= req.data;
            vif.address <= req.addr;
            @(vif.mst);
            vif.wr_n    <= '1;
            vif.data    <= 'z;
            vif.address <= 'z;
          end
        default: begin `uvm_fatal("REGERR", $sformatf("%p is not a valid Register Command", req.kind)); end
      endcase
    end
  endtask: data_rw
endclass: host_driver

2.2.2 monitor

利用uvm_config_db的get函数直接获取interface。

host_monitor.sv

class host_monitor extends uvm_monitor;
  uvm_analysis_port #(host_data) analysis_port;
  virtual host_io vif;
  host_data tr;

  `uvm_component_utils(host_monitor)

  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    uvm_config_db#(virtual host_io)::get(this, "", "vif", vif);
    analysis_port = new("analysis_port", this);
  endfunction: build_phase

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if (vif == null) begin
      `uvm_fatal("CFGERR", "Interface for host monitor not set");
    end
  endfunction: end_of_elaboration_phase

  virtual task run_phase(uvm_phase phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    forever begin
      tr = host_data::type_id::create("tr", this);
      data_detect(tr);
      `uvm_info("HOST_MON", {"\n", tr.sprint()}, UVM_HIGH);
      analysis_port.write(tr);
    end
  endtask: run_phase

  virtual task data_detect(host_data tr);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    fork
      begin
        fork
          wr_detect();
          rd_detect();
        join_any
        disable fork;
      end
    join
  endtask: data_detect

  virtual task wr_detect();
    @(vif.mon);
    wait(vif.mon.wr_n == 0);
    tr.addr = vif.mon.address;
    tr.data = vif.mon.data;
    tr.kind = UVM_WRITE;
    `uvm_info("GOT_WRITE", {"\n", tr.sprint()}, UVM_FULL);
  endtask: wr_detect

  virtual task rd_detect();
    @(vif.mon);
    wait(vif.mon.rd_n == 0);
    tr.addr = vif.mon.address;
    tr.data = vif.mon.data;
    tr.kind = UVM_READ;
    `uvm_info("GOT_READ", {"\n", tr.sprint()}, UVM_FULL);
  endtask: rd_detect
endclass: host_monitor

2.2.3 agent

在host_agent中,利用uvm_config_db的get函数获取interface,再利用uvm_config_db的set函数,将interface传递给底层的driver和monitor组件。

host_agent.sv

class host_agent extends uvm_agent;
  typedef uvm_sequencer #(host_data) host_sequencer;

  uvm_analysis_port #(host_data) analysis_port;
  virtual host_io vif;
  host_sequencer  sqr;
  host_driver     drv;
  host_monitor    mon;

  `uvm_component_utils(host_agent)

  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    uvm_config_db#(virtual host_io)::get(this, "", "vif", vif);
    uvm_config_db#(virtual host_io)::set(this, "*", "vif", vif);

    if (is_active == UVM_ACTIVE) begin
      sqr = host_sequencer::type_id::create("sqr", this);
      drv  = host_driver::type_id::create("drv", this);
    end
    mon = host_monitor::type_id::create("mon", this);
    analysis_port = new("analysis_port", this);
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if (is_active == UVM_ACTIVE) begin
      drv.seq_item_port.connect(sqr.seq_item_export);
    end
    mon.analysis_port.connect(this.analysis_port);
  endfunction: connect_phase

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    if (vif == null) begin
      `uvm_fatal("CFGERR", "Interface for host agent not set");
    end
  endfunction: end_of_elaboration_phase
endclass: host_agent

2.3 interface在dut顶层的连接

在dut的顶层,将interface的接口与dut连接。

module router_test_top;
  parameter simulation_cycle = 100 ;
  bit  SystemClock;

  host_io   host_if(SystemClock);
  reset_io  reset_if(SystemClock);
  
  router    dut(.clk(SystemClock), 
                .reset_n(reset_if.dut.reset_n),
                .address(host_if.dut.address),
                .wr_n(host_if.dut.wr_n),
                .rd_n(host_if.dut.rd_n),
                .data(host_if.dut.data));

  initial begin
    $fsdbDumpvars;
    forever #(simulation_cycle/2) SystemClock = ~SystemClock ;
  end
endmodule  

2.4 interface在testbench顶层的连接

在testbench的顶层。利用uvm_resource_db将interface传递下去。

test.sv

program automatic test;
import uvm_pkg::*;
import router_test_pkg::*;

initial begin

  uvm_resource_db#(virtual reset_io)::set("reset_vif", "", router_test_top.reset_if);

  //
  // Store the host interface in the resource database.
  //
  uvm_resource_db#(virtual host_io)::set("host_vif", "", router_test_top.host_if);

  run_test();
end

endprogram

在test_base的build_phase中,利用uvm_resource_db的read_by_type得到interface,再利用uvm_config_db的set函数,将interface传递给底层的组件。

test_base.sv

class test_base extends uvm_test;
  `uvm_component_utils(test_base)

  router_env env;

  virtual reset_io     reset_vif;
  virtual host_io      host_vif
  
  function new(string name, uvm_component parent);
    super.new(name, parent);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
  endfunction: new

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
    env = router_env::type_id::create("env", this);
    
    uvm_resource_db#(virtual reset_io)::read_by_type("reset_vif", reset_vif, this);
    uvm_config_db#(virtual reset_io)::set(this, "env.r_agt", "vif", reset_vif);

    // The test is responsible for picking it up and configure the agent with the interface.
    uvm_resource_db#(virtual host_io)::read_by_type("host_vif", host_vif, this);
    uvm_config_db#(virtual host_io)::set(this, "env.h_agt", "vif", host_vif);
  endfunction: build_phase

  virtual function void final_phase(uvm_phase phase);
    super.final_phase(phase);
    `uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);

    if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "TOPOLOGY")) begin
      uvm_root::get().print_topology();
    end

    if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "FACTORY")) begin
      uvm_factory::get().print();
    end
  endfunction: final_phase
endclass: test_base

3 interconnect

reg_define.svh

`define DMA_REG_MAX_ADDR_WIDTH 32
`define DMA_REG_MAX_DATA_WIDTH 32

reg_if.sv

`include "reg_define.svh"
interface reg_if (
  input clk,
  input rst_n
	);

  // interface pins
  logic [`DMA_REG_MAX_ADDR_WIDTH-1:0] address; // 地址信号
  logic req;
  logic write_enable;  // 写使能信号
  logic [`DMA_REG_MAX_DATA_WIDTH-1:0] write_data; // 写数据
  logic [`DMA_REG_MAX_DATA_WIDTH-1:0] read_data; // 读数据	
    
    clocking drv_ck @(posedge clk);
	    default input #1step output #1ps;
	    input read_data;
	    output address, req, write_enable, write_data;
    endclocking : drv_ck
    
    clocking mon_ck @(posedge clk);
	    default input #1step;
	    input address, req, write_enable, write_data, read_data;
    endclocking : mon_ck
    
    //modport dut_mp(input read_data, output address, req, write_enable, write_data);
  // debug signals

endinterface

tb.sv

    // interfaces
  clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
	
  reg_if dma_reg_if(.clk(clk), .rst_n(rst_n));

  // dut
  dma dut (
    .clk_i                (clk      ),
    .reset_n              (rst_n    ),
    
    .address              (dma_reg_if.address),
    .req                  (dma_reg_if.req),
    .write_enable         (dma_reg_if.write_enable),
    .write_data           (dma_reg_if.write_data),
    .read_data            (dma_reg_if.read_data)
  );

总结

本主要总结了modport和clocking的基本用法,同时实战演示了interface的用法,以及在uvm环境中的传递方式。

  • 11
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值