问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

SV中虚拟接口的作用及仿真

创作时间:
作者:
@小白创作中心

SV中虚拟接口的作用及仿真

引用
CSDN
1.
https://blog.csdn.net/weixin_42711149/article/details/138965605

在SystemVerilog的硬件验证中,虚拟接口是一个重要的概念,它解决了软件部分(动态)和硬件部分(静态)之间的连接问题。本文将详细介绍虚拟接口的作用及其在仿真中的具体应用,并通过一个计数器的案例进行演示。

虚接口的必要性

在验证环境中,硬件部分(DUT和Interface)属于静态范畴,意味着在编译的时候就要被分配内存进行初始化;软件部分(class)属于动态范畴,在仿真运行时才会被分配空间初始化。二者之间的关系类似动态变量和静态变量。

由此会产生的问题是:当在class中调用接口与DUT链接时时,如果不加virtual关键字,则意味着在编译的时候就要为接口开辟内存初始化,而编译的时候,作为软件部分的class是不会被分配空间的。因此导致矛盾的出现(通俗说法:妈妈还没出生,孩子先出生了)

因此,为了解决矛盾,需要将class中的接口声明为virtual,将物理的接口替换为句柄,编译时不占用内存空间,在仿真跑起来的时候再传入接口实例,从而解决冲突。

就像绿皮书中说的:“虚接口是唯一链接动态对象和静态模块、接口的一种机制”。

虚接口的作用

为了方便地将软件世界的tb产生的激励输送给硬件module,需要一个媒介,即虚拟接口,本质上是一个指向硬件接口的指针,即是物理接口的句柄

仿真代码

class transaction; // 用rand来生成随机化激励
  rand logic load_valid;
  rand logic [3:0] load_value;
endclass

class driver;
  virtual counter_if vif; // 生成一个virtual interface句柄
  function new(input virtual counter_if vif); // 该new函数用来使虚接口句柄指向interface
    this.vif = vif;
  endfunction

  transaction tr;
  task run(int n); // 产生n次激励
    for (int i = 0; i < n; i++) begin
      tr = new;
      assert(tr.randomize());
      $display("tr.load_valid=%0d,tr.load_value=%0d", tr.load_valid, tr.load_value);
      @(posedge vif.clk) begin // 在clk上升沿,通过虚接口将transaction中的随机激励传送到interface上
        vif.load_valid <= tr.load_valid;
        vif.load_value <= tr.load_value;
      end
    end
  endtask
endclass

module tb_top;
  logic clk, rstn;
  logic [3:0] q;

  counter_if dutif(clk); // 实例化interface
  driver my_driver; // 声明句柄
  counter u_counter(
    .resetn(rstn),
    .clk(clk),
    .load_valid(dutif.load_valid),
    .load_value(dutif.load_value),
    .q(out) // 实例化DUT
  );

  initial begin
    my_driver = new(dutif); // 实例化驱动,使虚接口句柄指向interface
    repeat (2) @(posedge clk);
    @(posedge rstn);
    repeat (5) @(posedge clk);
    my_driver.run(20); // 产生20次随机激励
  end

  initial begin // 生成T=10的时钟
    clk = 1'b0;
    forever #5 clk <= ~clk;
  end

  initial begin // 复位信号的生成
    rstn = 1;
    repeat (2) @(posedge clk);
    rstn = 0;
    repeat (5) @(posedge clk);
    rstn = 1;
    repeat (50) @(posedge clk);
    $finish;
  end
endmodule

interface counter_if(input logic clk);
  logic load_valid;
  logic [3:0] load_value;
endinterface

module counter(
  input clk,
  input resetn,
  input [3:0] load_value,
  input load_valid,
  output reg [3:0] q
);
  always @(posedge clk or negedge resetn) begin
    if (!resetn)
      q <= 4'd0;
    else if (load_valid)
      q <= load_value;
    else
      q <= q + 4'd1;
  end
endmodule

仿真详细流程

在Linux终端中依次输入以下命令启动VCS:

vcs -full64 -kb -debug_access+all -sverilog 文件名
./simv -gui

在VCS的dve中依次输入以下命令启动仿真:

dump -deltaCycle on
run

仿真输出如下:

仿真波形如下:

我对于counter计数到6的这个时钟上升沿有疑问,不确定在这个posedge clk边沿,counter会执行q<=q+4'd1,还是q<=load_value。因此,调用Delta Cycle查看

结果如下:

可见,在该时钟上升沿,q的+1操作先于load_value的变化,因此counter会在当前clk周期变为6,在下一个clk上升沿变为12(load_value的值)。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号