【SV练习】 MCDF实验3

系统框图:

总体上看,实验2 转到实验3,把更多的验证环境好而组建都放置到package里面,chnl_trans的内容丰富了很多,变得更复杂了。实验二里面的chnl_trans是在generator里面完成赋值的,实验三赋值、随机化则是自己完成的

知识点:

信箱:

旗语: 主要是可以实现对同一资源的访问控制,semaphore是SV内建的类,因此需要句柄,方法包含new(),get(),put(),try_get()。旗语想当一个篮子,想要获取资源必须从篮子里面获取一把后者多把钥匙,获取完资源后必须把钥匙放回篮子里面去。另外一个线程想要获取资源的话,必须等待篮子里面拥有足够多的钥匙才可以,否则会是一直阻塞的状态。

断言: 断言分为立即断言和并发断言,断言属性类似于if语句中的条件表达式,用于检查表达式中的值是否为真。        

随机约束

主要是数据空闲周期和包数据周期,data.size被限制在[4:8],data_nidles 限制在[0:2],pkt_nidle限制在[3:5]

    data.size inside {[4:8]};

/*...................................*/

    constraint cstr{
      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 {[3:5]};
    };

/*...................................*/

    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];
        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

仿真结束:原本在 chnl_root_test 类中用来结束仿真的$finish()变迁到 generator 中。

agent部分不再控制发送数据的多少,只是控制运行generate和initiator,发送ntrans的数量在generate里面控制,initiator里面因为forever的存在是一直运行的。如果放在165行代码的位置,会提前结束,发送完一次数据就会结束仿真。

165处会有个breakpoint,生成的对象只有1184个,$finish 在这个位置中,程序会提早结束,因为fork jion_any中有一个程序结束就会导致程序直接结束。

解决方法:initiator是不会结束的,所以需要使用generate来结束控制agent结束,从而使得整个结束掉。所以在chnl_root_test中在三个agent运行结束以后finish

 Could we put c.obj_id = this.obj_id here? and why?

obj_id是一个local静态变量,处于一个独立了的空间,句柄C和this都是通过访问静态变量来获得obj_id的值,相当于是自己给自己赋值,所以有没有这句话是一样的。

restart之后两个数据是一样的,因为默认产生随机数的种子是相同的。运行仿真器命令vsim -novopt -solvefaildebug -sv_seed 0 work.tb1结果也是一样的,运行vsim -novopt -solvefaildebug -sv seed random work.tb1,结果不一样。

obj. id 值是1200,每次new一次就会自增一次,chnl_basic_test中的ntrans有200个数据,initiator中会存在一次克隆,相当于乘2,3个agent,相当于乘3。200*2*3=1200;一个agent里面最多三个对象存在,三个agent里面最多九个。
    function new();
      this.obj_id++;
    endfunction

mailbox部分:

generate首先生成一个req然后放到req_mb里面,然后传递给initiator,initiator会条用clone产生一个rsp,使得rsp.rsp=1,再放到rsp_mb里面。然generate还会从rsp_mb里面拿回一个rsp的句柄。在initiator里面并没有例化这两个信箱,所以req_mb和rsp_mb都是句柄,句柄都是指向generator里面,在agent里面赋值句柄,把generator两个不悬空的句柄赋值给initiator中悬空的句柄

更加灵活的控制:

实现不同的test,需要对generator里面作出不同的控制,控制对象就是chnl_trans。可以达到在顶层里面控制chnl_trans的效果。在chnl_root_test里面声明initiator和generator的句柄,然后利用信箱进行连接。

    function new(string name = "chnl_root_test");
      foreach(agent[i]) begin
        this.agent[i] = new($sformatf("chnl_agent%0d",i));
        this.gen[i] = new();
		this.agent[i].init.req_mb = this.gen[i].req_mb;
		this.agent[i].init.rsq_mb = this.gen[i].rsq_mb;	
      end
      this.name = name;
      $display("%s instantiate objects", this.name);
    endfunction

参考gen[0]书写gen[1] 、gen[2] 

  virtual function void do_config();
      super.do_config();
      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==6;})
        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!");

分别对chnl_burst_test和chnl_fifo_ fullt_test中的config进行配置

仿真的参数传递选择测试的种类:对比tb1和tb3

  //更改测试的话,需要重新更改代码,重新编译
  import chnl_pkg1::*;

  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();
    basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
    basic_test.run(); 
  end

结合系统框图 ,根据脚本命令选择测试类型,如果没有声明的话则会自动选择basic_test测试

  import chnl_pkg2::*;

  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;
  chnl_root_test tests[string];
  string name;

  initial begin 
    basic_test = new();
    burst_test = new();
    fifo_full_test = new();
    tests["chnl_basic_test"] = basic_test;
    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
endmodule

测试平台的结构:

增加了组件monitor和checker。

在 chnl_monitor 类和 mcdt_monitor 类各自的 mon_trans()方法中需要采集正确的数据,将它们写入 mailbox 缓存,同时将捕捉的数据也打印出来,便于我们的调试。
就是通过接口把各自的mon_trans数据分别送进邮箱。
    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));
        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
    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
在 chnl_agent 中,参考如何例化的 initiator 对象,也对 chnl_monitor 对象开始例化、传递虚接口和使其运行。
例化initiator对象还是通过new函数,参考initiator写法,分别进行例化和接口传递和运行
  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"});
    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();
        mon.run();
      join
    endtask
  endclass: chnl_agent
在 chnl_checker 的任务 do_compare()中, 同学需要从 checker 自己的数据缓存mailbox 中分别取得一个输出端的采集数据和一个输入端的采集数据,继而将它们的内容进行比较。
输出是mcdt输出的数据,输入是三个channel输入的数据,也就是三个agent,对于输出跟哪个agent的数据比较则是根据数据的ID来判断。
task do_compare();
      mon_data_t im, om;
      forever begin
        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
在顶层环境 chnl_root_test 中。同学们先要对 mcdt_monitor 和 chnl_checker 进行例化。传递虚接口,并且将 chnl_monitor. mcdt_monitor 的邮箱句柄分别指向chnl_checker 中的邮箱实例。别忘了,在例化和连接之后要运行
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();
        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

为什么约束值都是-1,-1可以使得随机化的初始条件不成立,实现随机化中递进的层层控制。local::ch_id指类chnl_generator的变量,而ch_id指当前req的变量,local::只在randomize里出现。

  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;
   task send_trans();
      chnl_trans req, rsp;
      req = new();
      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!");

 运行之前,首先配置我们的generate,然后运行agent,在运行generate,generate在运行之前的随机化发生在do_config里面

    virtual task run();
      $display($sformatf("*****************%s started********************", this.name));
      this.do_config();
      fork
        agent[0].run();
        agent[1].run();
        agent[2].run();
      join_none
      fork
        gen[0].run();
        gen[1].run();
        gen[2].run();
      join
      $display($sformatf("*****************%s finished********************", this.name));
      // USER TODO 1.3
      // Please move the $finish statement from the test run task to generator
      // You woudl put it anywhere you like inside generator to stop test when
      // all transactions have been transfered
      $finish();
    endtask

参考:路科验证MCDF_svlab3笔记_Hardworking_IC_boy的博客-CSDN博客_路科验证

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值