MCDT实验(systemverilog部分)学习笔记

该学习笔记基于路科SV的实验3,MCDT的testbench的搭建。
搭建TB的一个核心:
1)验证结构的层次(组件的例化new());
2) 在run()之前,完成接口的连接(set_interface)、组件的连接;
3)环境运行run().

MCDT为MCDF的简化版,此时只有channle和arbiter,没有register和formatter。
TB1实验:
在这里插入图片描述
放大看generator与initiator之间用mailbox传递数据的结构简图如下
在这里插入图片描述
generator组件产生数据后,利用mailbox传递给initiator组件,之后initiator通过interface接口将数据传递给DUT中的channel slave。
从实验3开始,将chnl_pkg.sv与tb.sv文件分开。(chnl_pkg.sv中存放的是各种class的集合,tb.sv中完成interface的定义,接口的例化和各个测试用例的例化和运行)。
在chnl_pkg中:
1.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;
    local static int obj_id = 0;//obj_id初始化。
 
    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 {[1:10]};
    };

    function new();
      this.obj_id++;//看别人讨论说,new的时候可以放一些想在初始化的时候设置的内容
    endfunction

    function chnl_trans clone();//数据的克隆
      chnl_trans c = new();
      c.data = this.data;
      c.ch_id = this.ch_id;
      c.pkt_id = this.pkt_id;
      c.data_nidles = this.data_nidles;
      c.pkt_nidles = this.pkt_nidles;
      c.rsp = this.rsp;
      return c;
    endfunction

    function string sprint();//数据的打印
      string s;
      s = {s, $sformatf("=======================================\n")};
      s = {s, $sformatf("chnl_trans object content is as below: \n")};
      s = {s, $sformatf("obj_id = %0d: \n", this.obj_id)};
      foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
      s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
      s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
      s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
      s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
      s = {s, $sformatf("rsp = %0d: \n", this.rsp)};
      s = {s, $sformatf("=======================================\n")};
      return s;
    endfunction
  endclass: chnl_trans

2.chnl_generator产生数据的类的定义:(完成mailbox的例化,以及sent_trans的任务)

 class chnl_generator;
    int pkt_id;
    int ch_id;
    int ntrans;//决定传输多少个数据。
    int data_nidles;
    mailbox #(chnl_trans) req_mb;//声明req_mb(mailbox)传输的参数是chnl_trans.
    mailbox #(chnl_trans) rsp_mb;//声明rsp_mb(mailbox)传输的参数是chnl_trans.

    function new(int ch_id, int ntrans);
      this.ch_id = ch_id;
      this.pkt_id = 0;
      this.ntrans = ntrans;
      this.req_mb = new();//mailbox做例化
      this.rsp_mb = new();//mailbox做例化
    endfunction

    task run();//使得generation可以运行起来。
      repeat(ntrans) send_trans();
    endtask

    // generate transaction and put into local mailbox
    task send_trans();//关键任务
      chnl_trans req, rsp;
      req = new();//数据类的例化
      assert(req.randomize with {ch_id == local::ch_id; pkt_id == local::pkt_id;})//随机化
        else $fatal("[RNDFAIL] channel packet randomization failure!");//随机化
      this.pkt_id++;
      $display(req.sprint());
      this.req_mb.put(req);//使用mailbox传输trans
      this.rsp_mb.get(rsp);//使用mailbox传输trans
      $display(rsp.sprint());
      assert(rsp.rsp)//断言,在req中rsp=0,在rsp中rsp=1,其余数据rsp与req相同
        else $error("[RSPERR] %0t error response received!", $time);
    endtask
  endclass: chnl_generator

3.chnl_initiator类的定义:(完成mailbox的握手,以及将数据通过interface传输给DUT)

class chnl_initiator;
    local string name;
    local virtual chnl_intf intf;//class中的接口需要加virtual.
    mailbox #(chnl_trans) req_mb;//定义参数为chnl_trans的req_mb的mailbox.
    mailbox #(chnl_trans) rsp_mb;//定义参数为chnl_trans的rsp_mb的mailbox.
  
    function new(string name = "chnl_initiator");
      this.name = name;
    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 run();
      this.drive();//task可以嵌套别的task.
    endtask

    task drive();
      chnl_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        this.req_mb.get(req);//init从mailbox中获取数据;
        this.chnl_write(req);//init将数据再发送给DUT中的chnnal slave.
        rsp = req.clone();
        rsp.rsp = 1;
        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];
        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

4.chnl_agent类中包含generator和initiator的例化:(mailbox句柄的连接,以及generator和initiator的run)

class chnl_agent;
    chnl_generator gen;
    chnl_initiator init;
    local virtual chnl_intf vif;
    function new(string name = "chnl_agent", int id = 0, int ntrans = 1);
      this.gen = new(id, ntrans);
      this.init = new(name);
    endfunction
    function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      init.set_interface(vif);
    endfunction
    task run();
      this.init.req_mb = this.gen.req_mb;//把gen里面不悬空的句柄赋值给init里面悬空(即没有例化)的句柄。
      this.init.rsp_mb = this.gen.rsp_mb;//把gen里面不悬空的句柄赋值给init里面悬空(即没有例化)的句柄。
      fork
        gen.run();
        init.run();
      join_any
    endtask
  endclass: chnl_agent

总结:
**class :类名
数据类型名称
mailbox 名字
function new()
设置(this……)
mailbox的实例化
endfunction
task run()
repeat(ntrans) send_trans();//任务嵌套了别的任务
endtask

task send_trans();//被嵌套的任务
chnl_trans req, rsp;
req = new();
this.req_mb.put(req);//使用mailbox传输trans。
this.rsp_mb.get(rsp);//使用mailbox传输trans。
endtask

endclass: 类名**
TB2实验:
为了方便对gen随机化,将TB1中的gen从agent中提出来,单独放在test层次中,再利用mailbox进行gen和agent中的init数据通信。
在这里插入图片描述
TB3实验:
在实验2的基础上,增加了checker和monitor模块,结构如上图所示。
channel monitor监控的是channel initiator与slave之间interface中的数据,并且channel monitor所得数据会送给mcdt checker;而mcdt monitor监控的则是arbiter的输出数据。
在channel monitor中很关键的一个task是:

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
        mon_mb.put(m);//将mon接收到的数据通过mailbox传出
        $display("%0t %s monitored channle data %8x", $time, this.name, m.data);
      end
    endtask

在mcdt monitor中很关键的一个task是:

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

在mcdt checker中:

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.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
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值