MCDF实验_lab4(4)

一、框架图

在这里插入图片描述

二、代码分析

按模块分布的package:
-chnl_pkg: //同lab3
	chnl_trans;
	chnl_generator;
	chnl_agent->(chnl_driver, chnl_monitor); 
-reg_chnl:
	reg_trans;
	reg_generator;
	reg_agent->(reg_driver, reg_monitor); 
-arb_pkg: 
	arb_trans;
	arb_generator;
	arb_agent->(arb_driver, arb_monitor)
-fmt_chnl:
	fmt_trans;
	fmt_generator;
	fmt_agent->(fmt_driver, fmt_monitor); 

reg_chnl

reg_trans,传输的数据包
类名:reg_trans
功能:reg与dut传输的数据包
函数:clone复制一个相同的对象
	 sprint打印所有属性
class reg_trans;
   rand bit[7:0] addr;//`WRITE, `READ, `IDLE
   rand bit[1:0] cmd;//`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR, `SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR
   rand bit[31:0] data;
   bit rsp;

   constraint cstr {
     soft cmd inside {`WRITE, `READ, `IDLE};
     soft addr inside {`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR, `SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};//0x00,04,08;0x10,14,18
     addr[7:4]==0 && cmd==`WRITE -> soft data[31:6]==0;//读写寄存器,写时,高26位为0
     soft addr[7:5]==0;
     addr[4]==1 -> soft cmd == `READ;
   };

   function reg_trans clone();
   ...
   endfunction
   function string sprint();
   ...
   endfunction
endclass
reg_generator
类名:reg_generator激励产生器
功能:产生激励,自己控制发送的指令、地址、数据,把数据发送给请求mailbox
函数:  new();//构造函数,创建请求mailbox和响应mailbox实例
	    task run();//执行send_trans()
		task send_trans();
			//a. 利用randomize with产生transaction的随机变量
			//b. 将该数据包发送到请求mailbox
			//c. 从req_mailbox中获取响应的trans,如果该响应的命令为读,将该响应的数据保存
			//d. 通过判断其rsp来确定是否发送成功
		string sprint();//将generator的产生的随机数据打印
		void post_randomize();//打印随机化后的值		
class reg_generator;
    rand bit[7:0] addr = -1;
    rand bit[1:0] cmd = -1;
    rand bit[31:0] data = -1;
    
    mailbox #(reg_trans) req_mb;
    mailbox #(reg_trans) rsp_mb;
    reg_trans reg_req[$];
    
    constraint cstr{
      soft addr == -1;
      soft cmd == -1;
      soft data == -1;
    }

    function new();
      this.req_mb = new();
      this.rsp_mb = new();
    endfunction

    task start();
      send_trans();
    endtask
    
    // generate transaction and put into local mailbox
    task send_trans();
      reg_trans req, rsp;
      req = new();
      assert(req.randomize with {local::addr >= 0 -> addr == local::addr;
                                 local::cmd >= 0 -> cmd == local::cmd;
                                 local::data >= 0 -> data == local::data;
                               })
        else $fatal("[RNDFAIL] register packet randomization failure!");
      $display(req.sprint());
      this.req_mb.put(req);
      this.rsp_mb.get(rsp);
      $display(rsp.sprint());
      if(req.cmd == `READ) 
        this.data = rsp.data;
      assert(rsp.rsp)
        else $error("[RSPERR] %0t error response received!", $time);
    endtask

    function string sprint();
    ...
    endfunction
    function void post_randomize();
    ...
    endfunction
  endclass
reg_driver
类名:reg_driver激励发送器
功能:将数据包发送给dut
函数:	new(string name = "chnl_initiator");//构造函数,初始化该激励发送器的name
		void set_interface(virtual chnl_intf intf);//设置接口,接口与dut之间传输数据
		task run();//运行,通过调用drive函数
		task do_drive();//从req_mailbox获取对应的数据包,将该数据包发送给dut,将该数据包的响应rsp设置为1,输入req_mailbox
		task do_reset();//复位
		task chnl_write(input chnl_trans t);//接口时钟上升沿将数据包和数据有效位发送给dut
		task chnl_idle();//发送状态空
class reg_driver;
    local string name;
    local virtual reg_intf intf;
    mailbox #(reg_trans) req_mb;
    mailbox #(reg_trans) rsp_mb;

    function new(string name = "reg_driver");
      this.name = name;
    endfunction
  
    function void set_interface(virtual reg_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();
      fork
        this.do_drive();
        this.do_reset();
      join
    endtask

    task do_reset();
      forever begin
        @(negedge intf.rstn);
        intf.cmd_addr <= 0;
        intf.cmd <= `IDLE;
        intf.cmd_data_m2s <= 0;
      end
    endtask

    task do_drive();
      reg_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        this.req_mb.get(req);
        this.reg_write(req);
        rsp = req.clone();
        rsp.rsp = 1;
        this.rsp_mb.put(rsp);
      end
    endtask
  
    task reg_write(reg_trans t);
      @(posedge intf.clk iff intf.rstn);
      case(t.cmd)
        `WRITE: begin 
                  intf.drv_ck.cmd_addr <= t.addr; 
                  intf.drv_ck.cmd <= t.cmd; 
                  intf.drv_ck.cmd_data_m2s <= t.data; 
                end
        `READ:  begin //read
                  intf.drv_ck.cmd_addr <= t.addr; 
                  intf.drv_ck.cmd <= t.cmd; 
                  repeat(2) @(negedge intf.clk);//下一个时钟的下降沿采样就一定是当前的data
                  t.data = intf.cmd_data_s2m; 
                end
        `IDLE:  begin 
                  this.reg_idle(); 
                end
        default: $error("command %b is illegal", t.cmd);
      endcase
      $display("%0t reg driver [%s] sent addr %2x, cmd %2b, data %8x", $time, name, t.addr, t.cmd, t.data);
    endtask
    
    task reg_idle();
      @(posedge intf.clk);//等一拍
      intf.drv_ck.cmd_addr <= 0;
      intf.drv_ck.cmd <= `IDLE;
      intf.drv_ck.cmd_data_m2s <= 0;
    endtask
  endclass
reg_monitor
reg_agent

fmt_pkg

  • 整形器按照数据包的形式发送数据,数据包的长度有 4、8、16和 32。
  • 整形器必须完整发送某个通道的数据包后,才可以准备发送下一个数据包,在发送数据包期间,fmt_chid和 fmt_length应该保持不变,直到数据包发送完毕
  • DUT的formatter发送请求,fmt_pkg要将formatter的数据接收并按一定的速度消化,所以需要在内部放一个fifo,模拟接收数据与消耗数据
fmt_trans
类名:fmt_trans
功能:fmt的transaction
属性:fifo深度和消耗速度,数据包的长度,数据,通道号与响应	
函数:function fmt_trans clone();//克隆
	  function string sprint();//打印
	  function bit compare(fmt_trans t);//比较函数
fmt_driver
类名:fmt_driver
功能:配置fifo的深度和消耗速度,接收formatter数据,模拟消耗数据
属性:fifo接收数据的buffer	
函数:run();
		do_receive();//从formatter接受数据
		do_consume();//消耗数据
		do_config();//配置fifo
		do_reset();//复位
class fmt_driver;
    local string name;
    local virtual fmt_intf intf;
    mailbox #(fmt_trans) req_mb;
    mailbox #(fmt_trans) rsp_mb;

    local mailbox #(bit[31:0]) fifo;//模拟fifo buffer
    local int fifo_bound;
    local int data_consum_peroid;//数据消耗周期
  
    function new(string name = "fmt_driver");//设置这个fifo
      this.name = name;
      this.fifo = new();//指向无限大的信箱
      this.fifo_bound = 4096;
      this.data_consum_peroid = 1;
    endfunction
  
    function void set_interface(virtual fmt_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();
      fork
        this.do_receive();
        this.do_consume();
        this.do_config();
        this.do_reset();
      join
    endtask
	//得到gene随机后的fmt_fifo_t和fmt_bandwidth_t值,配置内部fifo
    task do_config();
      fmt_trans req, rsp;
      forever begin
        this.req_mb.get(req);
        case(req.fifo)
          SHORT_FIFO: this.fifo_bound = 64;
          MED_FIFO: this.fifo_bound = 256;
          LONG_FIFO: this.fifo_bound = 512;
          ULTRA_FIFO: this.fifo_bound = 2048;
        endcase
        this.fifo = new(this.fifo_bound);//指向固定深度的mailbox
        case(req.bandwidth)
          LOW_WIDTH: this.data_consum_peroid = 8;//数据消耗速度,发一个数据,等8个周期
          MED_WIDTH: this.data_consum_peroid = 4;
          HIGH_WIDTH: this.data_consum_peroid = 2;
          ULTRA_WIDTH: this.data_consum_peroid = 1;//一个周期发一个数据
        endcase
        rsp = req.clone();
        rsp.rsp = 1;
        this.rsp_mb.put(rsp);
      end
    endtask
	//复位
    task do_reset();
      forever begin
        @(negedge intf.rstn) 
        intf.fmt_grant <= 0;//复位后,响应信号为0
      end
    endtask
	//模拟从formatter接受数据
    task do_receive();
      forever begin
        @(posedge intf.fmt_req);//req拉高了
		@(posedge intf.clk  iff(this.fifo_bound-this.fifo.num()) >= intf.fmt_length);//fifo余量>=formatter长度       
        intf.drv_ck.fmt_grant <= 1;//将grant给dut,当前可以接收数据
        @(posedge intf.fmt_start);
        fork
          begin
            @(posedge intf.clk);//grant保持两拍,开始一拍后置0
            intf.drv_ck.fmt_grant <= 0;
          end
        join_none
        repeat(intf.fmt_length) begin
          @(negedge intf.clk);
          this.fifo.put(intf.fmt_data);
        end
      end
    endtask
	//消耗数据	
    task do_consume();
      bit[31:0] data;
      forever begin
        void'(this.fifo.try_get(data));//无论有无数据,一直在里面拿一个数据
        repeat($urandom_range(1, this.data_consum_peroid)) @(posedge intf.clk);//拿一次数据等一段随机时间
      end
    endtask
  endclass
fmt_generator
类名:fmt_generator
功能:随机生成fifo的大小和消耗速度
属性:fifo,bandwidth为随机数,fifo的大小和位宽	
	  请求mailbox和响应mailbox
函数:function new();//实例化两个mailbox
	  task start();//调用send_trans()函数
	  task send_trans();//随机化fmt_trans,将该trans发送给driver,并接收该trans
	  function string sprint();//打印
	  function void post_randomize();//打印随机化后的值
fmt_monitor
类名:fmt_monitor
功能:获取数据包的长度,数据,通道号,最终送给checker作最终的数据比较。	
函数:task mon_trans();//从接口上获取数据包的长度,数据,通道号	
fmt_agent

mcdf_pkg

在这里插入图片描述

//mcdt的transaction
typedef struct packed {
    bit[2:0] len;//长度
    bit[1:0] prio;//优先级
    bit en;//使能
    bit[7:0] avail;//mcdt的margin,只读
  } mcdf_reg_t;
  typedef enum {RW_LEN, RW_PRIO, RW_EN, RD_AVAIL} mcdf_field_t;
mcdf_refmod
类名:mcdf_refmod
功能:将每个通道输入的数据按通道按寄存器输入的格式打包输出
属性: reg_mb 捕捉reg_trans的参数
	  regs[i]存储捕捉到的i通道的寄存器的参数
	  in_mbs[i] 发送的数据
	  out_mbs[i] 打包的数据
函数: task run();
	  task do_reg_update();//捕捉寄存器读写,用来更新寄存器内部的信号值,通过寄存器的值进行打包
	  task do_packet();//利用通道内对应寄存器的值对通道打包,
  class mcdf_refmod;
    local virtual mcdf_intf intf;
    local string name;
    mcdf_reg_t regs[3];//chnl 0,1,2的寄存器,RW和R
    mailbox #(reg_trans) reg_mb;
    mailbox #(mon_data_t) in_mbs[3];
    mailbox #(fmt_trans) out_mbs[3];

    function new(string name="mcdf_refmod");
      this.name = name;
      foreach(this.out_mbs[i]) this.out_mbs[i] = new();//例化
    endfunction

    task run();
      fork
        do_reset();
        this.do_reg_update();//模拟寄存器,对寄存器进行配置
        do_packet(0);//打包
        do_packet(1);
        do_packet(2);
      join
    endtask
	//捕捉寄存器读写,用来更新寄存器内部的信号值,通过寄存器的值进行打包
    task do_reg_update();
      reg_trans t;
      forever begin
        this.reg_mb.get(t);//捕捉reg_trans的数据
        if(t.addr[31:4] == 0 && t.cmd == `WRITE) begin//先判断为读操作,再根据操作的通道号对regs赋值
          this.regs[t.addr[3:2]].en = t.data[0];//使能
          this.regs[t.addr[3:2]].prio = t.data[2:1];//优先级
          this.regs[t.addr[3:2]].len = t.data[5:3];//数据包长度 
        end
        else if(t.addr[7:4] == 1 && t.cmd == `READ) begin//只读
          this.regs[t.addr[3:2]].avail = t.data[7:0];
        end
      end
    endtask
	//利用通道内对应寄存器的值对通道打包
    task do_packet(int id);
      fmt_trans ot;//package格式
      mon_data_t it;//单一数据
      forever begin
        wait(this.in_mbs[id].num() > 0);//有数据可以打包
        ot = new();
		ot.length = 4 << (this.get_field_value(id, RW_LEN) & 'b11);//reg长度解码表0->4,1->8,2->16,3->32,其他->32
        ot.data = new[ot.length];
        ot.ch_id = id;
        foreach(ot.data[m]) begin
          this.in_mbs[id].get(it);
          ot.data[m] = it.data;
        end
        this.out_mbs[id].put(ot);//拿完了放进去
      end
    endtask

    function int get_field_value(int id, mcdf_field_t f);
      case(f)
        RW_LEN: return regs[id].len;
        RW_PRIO: return regs[id].prio;
        RW_EN: return regs[id].en;
        RD_AVAIL: return regs[id].avail;
      endcase
    endfunction 
	//reset
    task do_reset();
      forever begin
        @(negedge intf.rstn); 
        foreach(regs[i]) begin
          regs[i].len = 'h0;
          regs[i].prio = 'h3;
          regs[i].en = 'h1;
          regs[i].avail = 'h20;
        end
		//清空各个mailbox
		foreach(out_mbs[i])  out_mbs[i].delete();
      end
    endtask

    function void set_interface(virtual mcdf_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction
    
  endclass
checker
类名:mcdf_checker
功能:将每个通道输入的数据按通道按寄存器输入的格式打包输出
属性:err_count 误码数据个数计数
	  total_count 全部数据个数计数
	  chnl_count 通道数据个数计数
	  
函数:new(string name="mcdf_checker");//实例化mailbox和referance model,连接mailbox
	  task run();
	  do_compare();//直接从输出拿数据,根据id和输入进行比较
      do_report();//报告,输出所有数据
class mcdf_checker;
    local string name;
    local int err_count;
    local int total_count;
    local int chnl_count[3];
    local virtual mcdf_intf intf;
    local mcdf_refmod refmod;
    mailbox #(mon_data_t) chnl_mbs[3];
    mailbox #(fmt_trans) fmt_mb;
    mailbox #(reg_trans) reg_mb;
    mailbox #(fmt_trans) exp_mbs[3];

    function new(string name="mcdf_checker");
      this.name = name;
	  //实例化mailbox
      foreach(this.chnl_mbs[i]) this.chnl_mbs[i] = new();//实例化mailbox
      this.fmt_mb = new();
      this.reg_mb = new();
      this.refmod = new();
	  //连接mailbox
      foreach(this.refmod.in_mbs[i]) begin
        this.refmod.in_mbs[i] = this.chnl_mbs[i];
        this.exp_mbs[i] = this.refmod.out_mbs[i];
      end
      this.refmod.reg_mb = this.reg_mb;
      this.err_count = 0;
      this.total_count = 0;
      foreach(this.chnl_count[i]) this.chnl_count[i] = 0;
    endfunction

    function void set_interface(virtual mcdf_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
        this.refmod.set_interface(intf);
    endfunction

    task run();
      fork
        this.do_compare();
        this.refmod.run();
      join
    endtask

    task do_compare();
      fmt_trans expt, mont;
      bit cmp;
      forever begin
        this.fmt_mb.get(mont);//从输出拿数据,根据id进行比较
        this.exp_mbs[mont.ch_id].get(expt);
        cmp = mont.compare(expt);   
        this.total_count++;//每比较一次+1
        this.chnl_count[mont.ch_id]++;//每通道比较一次+1
        if(cmp == 0) begin
          this.err_count++;
          rpt_pkg::rpt_msg("[CMPFAIL]", 
            $sformatf("%0t %0dth times comparing but failed! MCDF monitored output packet is different with reference model output", $time, this.total_count),
            rpt_pkg::ERROR,
            rpt_pkg::TOP,
            rpt_pkg::LOG);
        end
        else begin
          rpt_pkg::rpt_msg("[CMPSUCD]",
            $sformatf("%0t %0dth times comparing and succeeded! MCDF monitored output packet is the same with reference model output", $time, this.total_count),
            rpt_pkg::INFO,
            rpt_pkg::HIGH);
        end
      end
    endtask

    function void do_report();//总结
      string s;
      s = "\n---------------------------------------------------------------\n";
      s = {s, "CHECKER SUMMARY \n"}; 
      s = {s, $sformatf("total comparison count: %0d \n", this.total_count)}; 
      foreach(this.chnl_count[i]) s = {s, $sformatf(" channel[%0d] comparison count: %0d \n", i, this.chnl_count[i])};
      s = {s, $sformatf("total error count: %0d \n", this.err_count)}; 
      foreach(this.chnl_mbs[i]) begin
        if(this.chnl_mbs[i].num() != 0)
          s = {s, $sformatf("WARNING:: chnl_mbs[%0d] is not empty! size = %0d \n", i, this.chnl_mbs[i].num())}; 
      end
      if(this.fmt_mb.num() != 0)
          s = {s, $sformatf("WARNING:: fmt_mb is not empty! size = %0d \n", this.fmt_mb.num())}; 
      s = {s, "---------------------------------------------------------------\n"};
      rpt_pkg::rpt_msg($sformatf("[%s]",this.name), s, rpt_pkg::INFO, rpt_pkg::TOP);
    endfunction
  endclass

Makefile

仿真运行指令
vsim -l sim.log -c -voptargs=+acc -classdebug -solvefaildebug -sv_seed 0
+TESTNAME=mcdf_data_consistence_basic_test -
|mcdf_data_consistence_basic_tast.log work.tb
-classdebug,这是为了提供更多的 SV类调试功能
-solvefaildebug,这是为了在 SV随机化失败之后有更多的信息提供出来
-sv_seed 0,暂时给固定的随机种子 0
+TESTNAME=mcdf_data_consistence_basic_test,这是指定仿真选择的调试
-| mcdf_data_consistence_basic_test_sim.log,这是让仿真的记录保存在特定的测	试文件名称中
收集覆盖率指令
-vsim -i -classdebug -solvefaildebug -coverage -coverstore COVERAGE_STORAGE_PATH -sv_seed random +TESTNAME=mcdf_data_consistence_basic_test -
|mcdf_data_consistence_basic_tast.log work.tb
-coverage: 会在仿真时产生代码覆盖率数据, 功能覆盖率数据则默认会生成,与此选项无关。
-coverstore COVERAGE STORAGE_PATH: 在仿真结束时,生成覆盖率数据并存储到COVERAGE_STORAGE PATH
-testname TESTNAME: 本次仿真的 test名称,你可以使用同+TESTNAME选项-样的test名称。这样在仿真结束后,将在COVERAGE_ STORAGE_ PATH下产生一个覆盖率数据文件"{TESTNAME}_ {SV_ SEED}.data" 。由于仿真时我们传入的种子是随机值,因此我们每次提交测试,在测试结束后都将产生一个独一无二的覆盖率数据。例如mcdf_ full_ random_test_1988811153.data。
合并覆盖率
  • 运行不同的仿真,或者运行同一个test,它们都会生成独一无二的数据库。接下来,你就可以将之前统一在COVERAGE STORAGE_PATH :下面生成的xxx.data覆盖率数据做合并了。你可以在Questasim的仿真窗口中敲入命令。
    vcover merge -out merged coverage.ucdb C:/questasim64 10.6c/examples
    这个命令即是将你之前产生的若干个xxxx.data的覆盖率合并在一起,生成一个合并在一起的覆盖率文件。所以,在测试前期,你提交的测试越多,那么理论上覆盖率的增长也就越明显。
  • 接下来,你可以点击File -> Open来打开这个合并后的UCDB覆盖率数据库(注意选择文件类型UCDB就可以看到这个文件了)。当你打开这个数据库之后,你可以发现合并后的数据库要比
    之前单独提交的任何一个测试在仿真结束时的该次覆盖率都要高。例如你可以在covergroups窗
    口栏中查看功能覆盖率,也可以在Analysis窗口中查看代码覆盖率。
分析覆盖率

你可以依旧使用Questasim来打开UCDB利用工具来查看覆盖率,或者更直观的方式是在打开
当前覆盖率数据库的同时,生成HTML报告。选择Tools -> Coverage Report-> HTML
Covergroups
Statements
Branches
Toggles

参考文献

https://blog.csdn.net/Turn_vs/article/details/125071985
https://blog.csdn.net/qq_41186941/article/details/122852006
https://blog.csdn.net/dinghj3/article/details/122312429

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小鱼爱学习,每天好心情

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

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

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

打赏作者

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

抵扣说明:

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

余额充值