MCDF实验2

接口的使用

实验用于验证组件和DUT之间通过接口连接

tb1.1:可以看到之前的实验 channel initiator 发送的数据例如 valid 和 data 与时钟 clk 均在同一个变化沿,没有任何延迟,这种0延迟的数据发送不利于波形查看和阅读,因此在已有代码的基础上使用 intf.ck 的方式来做数据驱动,并且再观察波形,查看驱动的数据与时钟上升沿的延迟是多少?

答:此处实验并没有更改代码,有延迟1ns,不加也没发现需要

tb1.2:为了更好地控制相邻数据之间的空闲间隔,引入一个变量 idle_cycles, 它表示相邻有效数据之间的间隔。已有代码会使得有效数据之间保持固定的一个空闲周期,我们需要使用 idle_cycles 变量, 来灵活控制有效数据之间的空闲周期。通过这个方法,在 tb 的 initial 块中我们通过方法 set_idle_cycles() 使得三个 channel initiator 的空闲周期变为 0, 即可以实现有效数据的连续发送。

仿真的结束

通过fork-join的功能和方法,实现三个chnl_initiator同时发送数据的要求。同时将不同test也组装到task中,以此区分不同的测试内容。

fork join :内部是并行执行

1.fork join:当fork join内部全部执行完之后,才会继续往下顺序执行,不用同时执行完,只要都执行过就可以;

2.fork join_none:fork join_none语句块执行的同时,父线程会继续执行,即全部并行执行;

3.fork join_any:执行到fork join_any内部语句时,当有任意一个语句执行完,那么后续的父类线程也会继续顺序执行。

4.可以通过 wait fork ,等待所有的点火的线程都执行完。点火的线程在task推出后其实还有可能在后他执行。

5.使用disable forkname,停止指定的线程,会迭代停止内部的线程,同时也好注意会不会停止调用的其他不想停的同名线程。

6.disable fork,会停止当前以及外层线程

要求2.1

要求2.2

    fork: fork_all_run
        forever chnl0_init.chnl_write(chnl0_gen.get_data());
        forever chnl1_init.chnl_write(chnl1_gen.get_data());
        forever chnl2_init.chnl_write(chnl2_gen.get_data());
    join_none
    fork
      wait(chnl0_init.intf.ch_margin == 0);
      wait(chnl1_init.intf.ch_margin == 0);
      wait(chnl2_init.intf.ch_margin == 0);
    join
    disable fork_all_run; //????can not understand
    fork  //close initial
        wait(chnl0_init.chnl_idle());
        wait(chnl1_init.chnl_idle());
        wait(chnl2_init.chnl_idle());
    join
    fork  //wait for transmission to complete
        wait(chnl0_init.intf.ch_margin == 'h20);
        wait(chnl1_init.intf.ch_margin == 'h20);
        wait(chnl2_init.intf.ch_margin == 'h20);
    join

 

类的例化和类的成员

将之前硬件盒子(module)中的数据内容移植到软件盒子(class)

 

 

 

// USER TODO 3.4
// check if the object use is correct?
class chnl_generator;
  chnl_trans trans[$];  
  int num;
  int id;
  function new(int n);
    this.id = n
    this.num = 0;z
//    t = new();    // only one t if instantiate it here
  endfunction
  function chnl_trans get_trans();
    chnl_trans t = new();  //each run yield a new t
    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

 

trans[$]用于存放句柄。在function new(int n)中创建实例,三个chnl在初始化时只创建了一个实例,而在get_trans()函数中只有一个实例的话,不论data产生了多少,最终句柄都指向一个对象,只有一个数字,因为数据不断被覆盖直至最后一个。

正确的是应该在get_trans()内创建实例,每次调用产生数据都会创建一个对象,并将句柄保存至trans[$]中,如果repeat(500),那么会产生500个句柄和500个不同的数据。

包的定义和类的继承

 

 

 程序经过不断的抽象,理解起来难了

  • 通过agent将generator和initiator整合起来,整合的同时需要注意属性、方法的传递和如何和接口进行交互。
  • 最外面通过root-test对接口进行连接,对agent进行整体的测试。测试时只需要对最外面的root-test进行配置和运行。

整体结构需要仔细理解

 

 

 

 

有一些不明白的:

 class chnl_initiator;
    local string name;
    local int idle_cycles;
    local virtual chnl_intf intf;   //为什么属性要声明为virtual
    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);
      // USER TODO 1.1
      // Please use the clocking drv_ck of chnl_intf to drive data
      intf.drv_ck.ch_valid <= 1;
      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);
      // USER TODO 1.2
      // Apply variable idle_cycles and decide how many idle cycles to be
      // inserted between two sequential data
      repeat(this.idle_cycles) chnl_idle();
    endtask
    
    task chnl_idle();
      @(posedge intf.clk);
      // USER TODO 1.1
      // Please use the clocking drv_ck of chnl_intf to drive data
      intf.drv_ck.ch_valid <= 0;
      intf.drv_ck.ch_data <= 0;
    endtask
  endclass: chnl_initiator

 

posted @ 2023-02-05 22:13  天下大任望君莫辞  阅读(86)  评论(0编辑  收藏  举报