SV实验2 随机约束、测试控制和环境构建

博客介绍了测试平台相关内容。在随机约束方面,原package独立放置,数据类声明更多随机成员变量,用特定仿真命令产生不同随机seed,将数据产生和测试控制主动权交generator。测试控制更灵活,随机化可分层,测试选择可由仿真命令控制。测试平台结构加入monitor和checker组件。


SV实验3的学习记录,MCDT部分的Testbench已逐渐完整,包含initiator、generator、agent、monitor、checker和test等组件。

随机约束

  • 原package独立放置在chnl_pkg.sv文件
  • 数据类chnl_trans声明了更多随机成员变量
  class chnl_trans;
    rand bit[31:0] data[];
    rand int ch_id;
    rand int pkt_id;
    rand int data_nidles;
    rand int pkt_nidles;
    bit rsp;  // default = 0,用于标明数据是否成功发送
    local static int obj_id = 0;
    constraint cstr{
      soft data.size inside {[4:8]};
      // 动态数组约束;标明数据通道方便调试,保证没有数据重复
      foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
      soft ch_id == 0;  // 软约束
      soft pkt_id == 0;
      data_nidles inside {[0:2]};
      pkt_nidles inside {[1:10]};
    };

    function new();
      this.obj_id++;
    endfunction

    function chnl_trans clone();  // 深拷贝
      chnl_trans c = new();
      c.data = this.data;
      c.ch_id = this.ch_id;
      ...
      return c;
    endfunction

    function string sprint();  // 打印所有成员变量值
      string s;
      s = {s, $sformatf("obj_id = %0d: \n", this.obj_id)};
      foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
      ...
      return s;
    endfunction
  endclass: chnl_trans
  • 采用"vsim -novopt -solvefaildebug -sv_seed random work.tb"仿真命令产生不同随机seed

  • 对数据产生和测试控制的主动权交于generator

  class chnl_generator;
    rand int pkt_id = -1;
    rand int ch_id = -1;
    rand int data_nidles = -1;
    rand int pkt_nidles = -1;
    rand int data_size = -1;
    rand int ntrans = 10;

    mailbox #(chnl_trans) req_mb;  // 声明参数化的mailbox
    mailbox #(chnl_trans) rsp_mb;

    constraint cstr{
      soft ch_id == -1;  // 若generator被随机则覆盖trans的随机,由send_trans()实现
      soft pkt_id == -1;
      soft data_size == -1;
      soft data_nidles == -1;
      soft pkt_nidles == -1;
      soft ntrans == 10;
    }

    function new();
      this.req_mb = new();  // 例化mailbox
      this.rsp_mb = new();
    endfunction

    task run();
      repeat(ntrans) send_trans();
      run_stop_flags.put();
    endtask

    // generate transaction and put into local mailbox
    task send_trans();
      chnl_trans req, rsp;
      req = new();
      // 对trans数据做随机化,并添加了一些外部约束,"->"操作符类似"case"
      assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id; 
                                 local::pkt_id >= 0 -> pkt_id == local::pkt_id;
                                 local::data_nidles >= 0 -> data_nidles == local::data_nidles;
                                 local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
                                 local::data_size >0 -> data.size() == local::data_size; 
                               })
        else $fatal("[RNDFAIL] channel packet randomization failure!");
      this.pkt_id++;  // 每发送一次trans加1
      $display(req.sprint());
      this.req_mb.put(req);  // mailbox将数据传递给initiator
      this.rsp_mb.get(rsp);
      $display(rsp.sprint());
      assert(rsp.rsp)  // 断言检查数据是否成功发送
        else $error("[RSPERR] %0t error response received!", $time);
    endtask

    function string sprint(); 
    ...

    function void post_randomize();
      string s;
      s = {"AFTER RANDOMIZATION \n", this.sprint()};
      $display(s);
    endfunction
  endclass: chnl_generator
  • generator和initiator间的数据传递
  class chnl_initiator;
    local string name;
    local virtual chnl_intf intf;
    mailbox #(chnl_trans) req_mb;  // initiator内不用再例化mailbox,仅声明悬空句柄
    mailbox #(chnl_trans) rsp_mb;
  
    function new(string name = "chnl_initiator");
      this.name = name;
    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 run();
      this.drive();
    endtask

    task drive();
      chnl_trans req, rsp;
      @(posedge intf.rstn);
      forever begin  		// 永动
        this.req_mb.get(req);
        this.chnl_write(req);
        rsp = req.clone();  // 拷贝生成新对象
        rsp.rsp = 1;		// 标注trans已成功发送
        this.rsp_mb.put(rsp);
      end
    endtask
  
    task chnl_write(input chnl_trans t);
      foreach(t.data[i]) begin
        @(posedge intf.clk);
        intf.drv_ck.ch_valid <= 1;
        intf.drv_ck.ch_data <= t.data[i];
        @(negedge intf.clk);
        wait(intf.ch_ready === 'b1);
        $display("%0t channel initiator [%s] sent data %x", $time, name, t.data[i]);
        repeat(t.data_nidles) chnl_idle();
      end
      repeat(t.pkt_nidles) chnl_idle();
    endtask
    
    task chnl_idle();
      @(posedge intf.clk);
      intf.drv_ck.ch_valid <= 0;
      intf.drv_ck.ch_data <= 0;
    endtask
  endclass: chnl_initiator

更加灵活的测试控制

  • 随机化可分层,test层通过do_config() 对generator做随机化,并进一步随机化chanl_trans对象
    tb2
  class chnl_root_test;
    chnl_generator gen[3];
    chnl_agent agents[3];
    mcdt_monitor mcdt_mon;
    chnl_checker chker;
    protected string name;
    event gen_stop_e;

    function new(string name = "chnl_root_test");
      this.name = name;
      this.chker = new();
      foreach(agents[i]) begin  //先例化再连接
        this.agents[i] = new($sformatf("chnl_agent%0d",i));
        this.gen[i] = new();
        // Connect the mailboxes handles of gen[i] and agents[i].init
        this.agents[i].init.req_mb = this.gen[i].req_mb;  // 赋值悬空句柄
        this.agents[i].init.rsp_mb = this.gen[i].rsp_mb;
        this.agents[i].mon.mon_mb = this.chker.in_mbs[i];
      end
      this.mcdt_mon = new();
      this.mcdt_mon.mon_mb = this.chker.out_mb;
      $display("%s instantiated and connected objects", this.name);
    endfunction

    virtual task gen_stop_callback();
      // empty
    endtask

    virtual task run_stop_callback();
      $display("run_stop_callback enterred");
      // by default, run would be finished once generators raised 'finish'
      // flags 
      $display("%s: wait for all generators have generated and tranferred transcations", this.name);
      run_stop_flags.get(3);
      $display($sformatf("*****************%s finished********************", this.name));
      $finish();
    endtask
    
    virtual task run();
      $display($sformatf("*****************%s started********************", this.name));
      this.do_config();
      fork
        agents[0].run();
        agents[1].run();
        agents[2].run();
        mcdt_mon.run();
        chker.run();
      join_none  // agent的run()无法结束,故采用fork-join_none

      // run first the callback thread to conditionally disable gen_threads
      fork
        this.gen_stop_callback();
        @(this.gen_stop_e) disable gen_threads;
      join_none

      fork : gen_threads
        gen[0].run();
        gen[1].run();
        gen[2].run();
      join  // 此处为等待所有generator发送完激励,故采用fork-join

      run_stop_callback(); // wait until run stop control task finished

    endtask

    virtual function void set_interface(virtual chnl_intf ch0_vif 
                                        ,virtual chnl_intf ch1_vif 
                                        ,virtual chnl_intf ch2_vif 
                                        ,virtual mcdt_intf mcdt_vif
                                      );
      agents[0].set_interface(ch0_vif);
      agents[1].set_interface(ch1_vif);
      agents[2].set_interface(ch2_vif);
      mcdt_mon.set_interface(mcdt_vif);
    endfunction

    virtual function void do_config();
    endfunction

  endclass
  
  class chnl_basic_test extends chnl_root_test;
    function new(string name = "chnl_basic_test");
      super.new(name);
    endfunction
    
    virtual function void do_config();
      super.do_config();
      //对generator随机化
      assert(gen[0].randomize() with {ntrans==100; data_nidles==0; pkt_nidles==1; data_size==8;})
        else $fatal("[RNDFAIL] gen[0] randomization failure!");

      assert(gen[1].randomize() with {ntrans==50; data_nidles inside {[1:2]}; pkt_nidles inside {[3:5]}; data_size==16;})
        else $fatal("[RNDFAIL] gen[1] randomization failure!");

      assert(gen[2].randomize() with {ntrans==80; data_nidles inside {[0:1]}; pkt_nidles inside {[1:2]}; data_size==32;})
        else $fatal("[RNDFAIL] gen[2] randomization failure!");
    endfunction
  endclass: chnl_basic_test
  • 测试选择可由仿真时添加命令"+TESTNAME=testname"控制
    tb3
  chnl_root_test tests[string];  // 句柄关联数组
  ...
  // 每次只跑一个测试
  initial begin 
    basic_test = new();
    burst_test = new();
    fifo_full_test = new();
    tests["chnl_basic_test"] = basic_test;  //index为类型
    tests["chnl_burst_test"] = burst_test;
    tests["chnl_fifo_full_test"] = fifo_full_test;
    if($value$plusargs("TESTNAME=%s", name)) begin  // 获取自定义参数
      if(tests.exists(name)) begin
        tests[name].set_interface(chnl0_if, chnl1_if, chnl2_if);
        tests[name].run();
      end
      else begin
        $fatal($sformatf("[ERRTEST], test name %s is invalid, please specify a valid name!", name));
      end
    end
    else begin
      $display("NO runtime optiont TEST=[testname] is configured, and run default test chnl_basic_test");
      tests["chnl_basic_test"].set_interface(chnl0_if, chnl1_if, chnl2_if);
      tests["chnl_basic_test"].run(); 
    end
  end

测试平台的结构

  • 加入monitor和checker组件;checker置于test层中,而不是agent中
    tb4
  typedef struct packed {  // 定义一个监测到数据的结构体
    bit[31:0] data;
    bit[1:0] id;
  } mon_data_t;

  class chnl_monitor;
    local string name;
    local virtual chnl_intf intf;
    mailbox #(mon_data_t) mon_mb;  // 悬空句柄
    function new(string name="chnl_monitor");
      this.name = name;
    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 run();
      this.mon_trans();
    endtask

    task mon_trans();
      mon_data_t m;
      forever begin
        @(posedge intf.clk iff (intf.mon_ck.ch_valid==='b1 && intf.mon_ck.ch_ready==='b1));
        // 将数据放入mon_mb并使用$display()打印monitor名称数据值
        m.data = intf.mon_ck.ch_data;
        mon_mb.put(m);
        $display("%0t %s monitored channle data %8x", $time, this.name, m.data);
      end
    endtask
  endclass
  
  class mcdt_monitor;
    ...
    task mon_trans();
      mon_data_t m;
      forever begin
        @(posedge intf.clk iff intf.mon_ck.mcdt_val==='b1);
        m.data = intf.mon_ck.mcdt_data;
        m.id = intf.mon_ck.mcdt_id;
        mon_mb.put(m);
        $display("%0t %s monitored mcdt data %8x and id %0d", $time, this.name, m.data, m.id);
      end
    endtask
  endclass
  
  class chnl_agent;
    local string name;
    chnl_initiator init;
    chnl_monitor mon;
    virtual chnl_intf vif;
    function new(string name = "chnl_agent");
      this.name = name;
      this.init = new({name, ".init"});
      this.mon = new({name, ".mon"});  // 例化monitor
    endfunction

    function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      init.set_interface(vif);
      mon.set_interface(vif);
    endfunction
    task run();
      fork
        init.run();  // 注意initiator的run()永远无法结束
        mon.run();
      join
    endtask
  endclass: chnl_agent
  
  class chnl_checker;
    local string name;
    local int error_count;  // 每报错一次加1
    local int cmp_count;    // 每比较一次加1
    mailbox #(mon_data_t) in_mbs[3];
    mailbox #(mon_data_t) out_mb;

    function new(string name="chnl_checker");
      this.name = name;
      foreach(this.in_mbs[i]) this.in_mbs[i] = new();
      this.out_mb = new();
      this.error_count = 0;
      this.cmp_count = 0;
    endfunction

    task run();
      this.do_compare();
    endtask

    task do_compare();
      mon_data_t im, om;
      forever begin
        // 先从out_mb拿数据,再与对应id的in_mb比较  
        out_mb.get(om);
        case(om.id)
          0: in_mbs[0].get(im);
          1: in_mbs[1].get(im);
          2: in_mbs[2].get(im);
          default: $fatal("id %0d is not available", om.id);
        endcase

        if(om.data != im.data) begin
          this.error_count++;
          $error("[CMPFAIL] Compared failed! mcdt out data %8x ch_id %0d is not equal with channel in data %8x", om.data, om.id, im.data);
        end
        else begin
          $display("[CMPSUCD] Compared succeeded! mcdt out data %8x ch_id %0d is equal with channel in data %8x", om.data, om.id, im.data);
        end
        this.cmp_count++;
      end
    endtask
  endclass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小破同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值