SV实验1 基于SV的Testbench

Testbench由硬件环境转变为软件环境。

实验一

  • Testbench结构框图
    image
  • 时钟信号和复位信号放在task内由两个并行initial块调用,时钟周期可传参指定
  • 为发送更多数据采用动态数组产生数据;为实现更紧凑高速的数据发送采用并行发送
  • 将原有的激励方法chnl_write()、chnl_idle() 等封装在新的模块chnl_initiator
module chnl_initiator(
  input               clk,
  input               rstn,
  output logic [31:0] ch_data,
  output logic        ch_valid,
  input               ch_ready,
  input        [ 5:0] ch_margin
);

string name;

function void set_name(string s);  // 设置实例名称,方便仿真时打印信息和调试
  name = s;
endfunction

task chnl_write(input logic[31:0] data);  // 实现一次有效写数据,不需再传id参数
  @(posedge clk);
  ch_valid <= 1;
  ch_data <= data;
  @(negedge clk);
  wait(ch_ready === 'b1);  // 等待ch_ready拉高
  $display("%t channel initial [%s] sent data %x", $time, name, data);
  chnl_idle();
endtask

task chnl_idle();  // 实现一个时钟周期的idle
  @(posedge clk);
  ch_valid <= 0;
  ch_data <= 0;
endtask

endmodule

实验二

  • Testbench结构框图

image
软件和硬件分开,采用interface连接;软件部分封装进package,硬件封装进library。

  • 采用interface连接验证组件和DUT
interface chnl_intf(input clk, input rstn);
  logic [31:0] ch_data;
  logic        ch_valid;
  logic        ch_ready;
  logic [ 5:0] ch_margin;
  clocking drv_ck @(posedge clk);  // 声明时钟块控制时钟偏移,消除可能的竞争问题
    default input #1ns output #1ns;
    output ch_data, ch_valid;
    input ch_ready, ch_margin;
  endclocking
endinterface
  • 将module initiatorgenerator改造为class,并定义了一个用来封装发送数据的类chnl_trans
  class chnl_trans;
    int data;
    int id;
    int num;
  endclass: chnl_trans
  
  class chnl_initiator;
    local string name;  // SV类中成员默认public
    local int idle_cycles;
    local virtual chnl_intf intf; // class无端口,interface作为成员变量加virtual声明为虚接口
  
    function new(string name = "chnl_initiator");  // class内无需声明automatic
      this.name = name;
      this.idle_cycles = 1;
    endfunction
  
    function void set_idle_cycles(int n);
      this.idle_cycles = n;
    endfunction
  
    function void set_name(string s);
      this.name = s;
    endfunction
  
    function void set_interface(virtual chnl_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction
  
    task chnl_write(input chnl_trans t);
      @(posedge intf.clk);
      intf.drv_ck.ch_valid <= 1;  // 用interface的clocking块drv_ck驱动数据
      intf.drv_ck.ch_data <= t.data;
	      @(negedge intf.clk);
      wait(intf.ch_ready === 'b1);
      $display("%t channel initiator [%s] sent data %x", $time, name, t.data);
      repeat(this.idle_cycles) chnl_idle();  // idle_cycles定义了插入数据间的idle周期
    endtask
    
    task chnl_idle();
      @(posedge intf.clk);
      intf.drv_ck.ch_valid <= 0;
      intf.drv_ck.ch_data <= 0;
    endtask
  endclass: chnl_initiator
 
  class chnl_generator;
    chnl_trans trans[$];
    int num;
    int id;
    function new(int n);
      this.id = n;  // 每个initiator的id不同,从generator中拿到的数据也不同
      this.num = 0;
    endfunction
    function chnl_trans get_trans();
      chnl_trans t = new();
      t.data = 'h00C0_0000 + (this.id<<16) + this.num;
      t.id = this.id;
      t.num = this.num;
      this.num++;
      this.trans.push_back(t);
      return t;
    endfunction
  endclass: chnl_generator
  • 引入agent、root_test、basic_test、burst_testfifo_full_test等class,封装进package编译
package chnl_pkg;
  class chnl_agent;
    chnl_generator gen;
    chnl_initiator init;
    local int ntrans;
    virtual chnl_intf vif;
    function new(string name = "chnl_agent", int id = 0, int ntrans = 1);
      this.gen = new(id);  // 在agent内例化generator和initiator
      this.init = new(name);
      this.ntrans = ntrans;
    endfunction
    function void set_ntrans(int n);
      this.ntrans = n;
    endfunction
    function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      init.set_interface(vif);
    endfunction
    task run();  // 运行各组件
      repeat(this.ntrans) this.init.chnl_write(this.gen.get_trans());
      this.init.chnl_idle(); // set idle after all data sent out
    endtask
  endclass: chnl_agent
  
  class chnl_root_test;
    chnl_agent agent[3];
    protected string name;
    function new(int ntrans = 100, string name = "chnl_root_test");
      foreach(agent[i]) begin
        this.agent[i] = new($sformatf("chnl_agent%0d",i), i, ntrans);
      end
      this.name = name;
      $display("%s instantiate objects", this.name);
    endfunction
    task run();
      $display("%s started testing DUT", this.name);
      fork
        agent[0].run();
        agent[1].run();
        agent[2].run();
      join
      $display("%s waiting DUT transfering all of data", this.name);
      fork	// 3个anent并行运行
        wait(agent[0].vif.ch_margin == 'h20);
        wait(agent[1].vif.ch_margin == 'h20);
        wait(agent[2].vif.ch_margin == 'h20);
      join
      $display("%s: 3 channel fifos have transferred all data", this.name);
      $display("%s finished testing DUT", this.name);
    endtask
    function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif);
      agent[0].set_interface(ch0_vif);
      agent[1].set_interface(ch1_vif);
      agent[2].set_interface(ch2_vif);
    endfunction
  endclass

  // each channel send data with idle_cycles inside [1:3]
  // each channel send out 200 data
  class chnl_basic_test extends chnl_root_test;  // 继承实现代码复用
    function new(int ntrans = 200, string name = "chnl_basic_test");
      super.new(ntrans, name);
      foreach(agent[i]) begin
        this.agent[i].init.set_idle_cycles($urandom_range(1, 3));
      end
      $display("%s configured objects", this.name);
    endfunction
  endclass: chnl_basic_test

  //省略其他test子类...

endpackage: chnl_pkg
  • 接口句柄由set_interface() 获取并经由test->agent->initiator层层传递;理解组件连接、环境运行的层次结构。
    image
  import chnl_pkg::*;  //导入package中定义的class

  chnl_intf chnl0_if(.*);
  chnl_intf chnl1_if(.*);
  chnl_intf chnl2_if(.*);

  chnl_basic_test basic_test;
  chnl_burst_test burst_test;
  chnl_fifo_full_test fifo_full_test;
  
  initial begin 
    // 测试环境例化
    basic_test = new();
    burst_test = new();
    fifo_full_test = new();

    // set_interface()收集各interface句柄并传递给各initiator对象
    basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
    burst_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
    fifo_full_test.set_interface(chnl0_if, chnl1_if, chnl2_if);

    // 开始定向测试序列
    basic_test.run(); 
    burst_test.run();
    fifo_full_test.run();
    $display("*****************all of tests have been finished********************");
    $finish();
  end
  

吐槽一下csdn不支持Verilog语法高亮,所以就先用Java替代啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小破同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值