MCDF--lab02

写在前面 

实验2 的部分我们将主要回顾之前接口、仿真结束、类和包的使用。在这一个试验中,我们将逐渐从使用硬件盒子验证过渡到使用接口和软件盒子(class) 来验证设计。而这一个实验之所以重要也是因为它是硬件验证方式与软件验证方式之间的过渡,同时作为验证环境的启蒙,我们在本实验的最后一个小实验中也能够初步体会到类的继承和层次包含关系,而这些都将作为日后学习高阶UVM 知识的重要基础。好了,接下来让我们进入这次的4 个小实验部分。
请下载lab2 中的四个实验文件tb1.sv、tb2.sv、tb3.sv 和tb4.sv。

链接:https://pan.baidu.com/s/1AeOkdzomOoCv01aPmejOEQ?pwd=ha48 
提取码:ha48

tb1 接口的使用 

tb1.sv 的代码部分承接的是上一次实验的最后代码部分,在使用接口之前我们需要定义接口chnl_intf 和内部的端口,同时我们也声明了一个时钟块(clocking_block),它的功能是为了消除可能存在的竞争(race)问题,确保时钟驱动数据之间有一定的延迟,以便于DUT顺利采样。因此更新后的代码,大家可以发现例化的实例包括了只产生数据的channel_generator,只负责发送数据的channel_initiator以及作为验证组件和DUT之间的接口chnl_intf.而例化之后,在initial 块中需要通过调用组件的方法完成初始化、名字设置和空闲周期的设置。这里初始化是为了设置ID,名字设置时为了稍后打印时容易区分各个组件,而空闲周期的设置则关系到我们接下来的实验要求: 

■要求1.1
首先观察波形,大家可以发现channel_initiator 发送的数据例如valid 和data与时钟clk 均在同一个变化沿,没有任何延迟。这种0延迟的数据发送不利于波形查看和阅读,因此要求大家在已有代码的基础上使用intf.ck 的方式来做数据驱动,并且再观察波形,查看驱动的数据与时钟上升沿的延迟是多少。 

不添加clocking block的波形:

数据发送在clock上升沿触发的时刻,可能会产生竞争; 

 添加clocking block的波形:

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

tb2 仿真的结束 

tb2.sv 中主要学习fork-join 的基本功能和使用方法,了解它的并行运行特性,以此来实现三个chnl_initiator 同时发送数据的要求。同时我们又将不同的test 也组装到task 中,以此来区分不同的测试内容,这是由于每一个测试任务的测试目的和要求都不相同,具体要求可以在代码中查找。
tb2.sv 需要首先移植tb1.sv 的要求内容,接下来再完成新的实验要求。

■要求2.1
可以参考task basic test(),来完成burst test()。它的要求是使得每个chnl_initiator 的idle_ cycles 设置为0,同时发送500个数据,最后结束测试。

 波形显示:

■要求2.2
参考task basic test()来完成task fifo. full test()。它的要求是无论采取什么数值的idle_cycles, 也无论发送多少个数据,只要各个chnl initiator 的不停发送使得对应的channel 缓存变为满标志(ready 拉低),那么可以在三个channel 都拉低过ready 时(不必要同时拉低,先后拉低即可),便可以立即结束测试。 

波形显示:

tb3 类的例化和类的成员

在这一部分, 我们便将之前用来封装验证功能的硬件盒子(module) 中的数据和内容移植到软件盒子(class) 中来,大家可以通过前后代码的相同点和不同点来比较实用类的时候,需要注意什么地方,同时也可以基本掌握类的例化,类的成员变量访问权限以及类的成员方法如何定义和使用。.

■要求3.1.
在将module chnl_initiator 和module chnl_generator 分别改造为class chnl_initiator 和 class chnl_generator 后,大家也可以发现我们同时定义了一个用来封装发送数据的类chnl_trans。要求3.1 需要在iniital 块中分别例化3个已经声明过的chnl_initiator 和3 个chnl_generator。

 因为class是动态的生命周期,所以在顶层环境中需要进行例化。

■要求3.2.
由于每一个chnl_initiator 都需要使用接口chnl_intf 来发送数据,在发送数据之前我们需要确保chnl_initiator 中的接口不是悬空的,即需要由外部被传递。所以接下来的实验要求需要通过调用chnl_iniaitor 中的方法来完成接口的传递。

在module中是存在端口的,所以我们经常见到module xx(interface vif)这样的声明,但是在class中,因为class是软件的形式,所以它不可以这样声明,我们只能通过在其内部定义函数,方法的形式解决端口传递问题。 

 ■要求3.3.
接下来就可以调用已经定义过的三个test 任务来展开测试了。

将测试用例定义为task方法,在顶层环境中直接进行调用。 

■要求3.4.
最后是关于类的例化问题,请同学们观察chnl_generator 在例化chnl_trans t 时,有没有不恰当的地方,如果有请指出来现有的代码会造成什么样的潜在问题呢?

tb4 包的定义和类的继承

到了tb4.sv,我们又进一步引入了新的类chnl _agent、chnl_root_test、chn_basic_test、chnl_burst_test 和chnl_fifo_full_test,同时将所有的类( 都是与channel 相关的验证组件类),
封装到专门包裹软件类的容器package chnl_pkg 中且完成编译。因此编译后的chnl .pkg. 会
被默认编译到work 库中,与其它的module 是一同并列放置的。
关于chnl_agent , 我们将它作为一个标准组件单元, 它应该包括generator 、driver(initiator)和monitor。在tb4.sv 中,我们暂时只有chnl_generator 和chnl_initiator, 因此将它们在agent 中例化。同时,我们也将之前用task 来实现的测试任务也由类来实现。
可以发现,父类是chnl_root_test,而我们已经先移植了chnl_basic_test,接下来需要我们们实现另外两个类。

■要求4.1
由于我们将各个类首先封装在了package chnl_pkg 中,因此在module tb4 中要声明类的句柄,首先应该从chnl_pkg 中引入其中定义的类。

将package中的class引用到下边使用import package_name::*

■要求4.2
可以参考之前已经实现的burst_test()和fifo_full_test()任务,以及已经实现的类chnl_basic_test,按照同样的要求来实现两个新的类chnl_burst_test 和chnl_fifof_full_test。

class chnl_root_test;
    chnl_agent agent[3];
    protected string name;
    function new(int ntrans = 100, string name = "chnl_root_test");
      foreach(agent[i]) begin
        this.agent[i] = new($sformatf("chnl_agent%0d",i), i, ntrans);
      end
      this.name = name;
      $display("%s instantiate objects", this.name);
    endfunction
    task run();
      $display("%s started testing DUT", this.name);
      fork
        agent[0].run();
        agent[1].run();
        agent[2].run();
      join
      $display("%s waiting DUT transfering all of data", this.name);
      fork
        wait(agent[0].vif.ch_margin == 'h20);
        wait(agent[1].vif.ch_margin == 'h20);
        wait(agent[2].vif.ch_margin == 'h20);
      join
      $display("%s: 3 channel fifos have transferred all data", this.name);
      $display("%s finished testing DUT", this.name);
    endtask
    function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif);
      agent[0].set_interface(ch0_vif);
      agent[1].set_interface(ch1_vif);
      agent[2].set_interface(ch2_vif);
    endfunction
  endclass

  // each channel send data with idle_cycles inside [1:3]
  // each channel send out 200 data
  // then to finish the test
  class chnl_basic_test extends chnl_root_test;
    function new(int ntrans = 200, string name = "chnl_basic_test");
      super.new(ntrans, name);
      foreach(agent[i]) begin
        this.agent[i].init.set_idle_cycles($urandom_range(1, 3));
      end
      $display("%s configured objects", this.name);
    endfunction
  endclass: chnl_basic_test

  // USER TODO 4.2
  // Refer to chnl_basic_test, and extend another 2 tests
  // chnl_burst_test, chnl_fifo_full_test
  // each channel send data with idle_cycles == 0
  // each channel send out 500 data
  // then to finish the test
  class chnl_burst_test extends chnl_root_test;
    //USER TODO
    function new(int ntrans = 500, string name = "chnl_burst_test");
      super.new(ntrans, name);
      foreach(agent[i]) begin
        this.agent[i].init.set_idle_cycles(0);
      end
      $display("%s configured objects", this.name);
    endfunction
  endclass: chnl_burst_test

  // USER TODO 4.2
  // The test should be immediately finished when all of channels
  // have been reached fifo full state, but not all reaching
  // fifo full at the same time
  class chnl_fifo_full_test extends chnl_root_test;
    // USER TODO
    function new(int ntrans = 1_000_000, string name = "chnl_fifo_full_test");
      super.new(ntrans, name);
      foreach(agent[i]) begin
        this.agent[i].init.set_idle_cycles(0);
      end
      $display("%s configured objects", this.name);
    endfunction
    task run();
      $display("%s started testing DUT", this.name);
      fork: fork_all_run
        agent[0].run();
        agent[1].run();
        agent[2].run();
      join_none
      $display("%s: 3 agents running now", this.name);

      $display("%s: waiting 3 channel fifos to be full", this.name);
      fork
        wait(agent[0].vif.ch_margin == 0);
        wait(agent[1].vif.ch_margin == 0);
        wait(agent[2].vif.ch_margin == 0);
      join
      $display("%s: 3 channel fifos have reached full", this.name);

      $display("%s: stop 3 agents running", this.name);
      disable fork_all_run;
      $display("%s: set and ensure all agents' initiator are idle state", this.name);
      fork
        agent[0].init.chnl_idle();
        agent[1].init.chnl_idle();
        agent[2].init.chnl_idle();
      join

      $display("%s waiting DUT transfering all of data", this.name);
      fork
        wait(agent[0].vif.ch_margin == 'h20);
        wait(agent[1].vif.ch_margin == 'h20);
        wait(agent[2].vif.ch_margin == 'h20);
      join
      $display("%s: 3 channel fifos have transferred all data", this.name);

      $display("%s finished testing DUT", this.name);
    endtask
  endclass: chnl_fifo_full_test

这里很好的使用了类的继承,首先声明一个基类,作为基本的测试用例,后边新的测试用例根据需要继承基类,添加新的条件。

■要求4.3
例化三个测试环境。在4.2 中继承了三个chnl_root_test 的子类,并在import package之后,创建了对应的句柄。现在要例化三个test,即创建对应的对象,并将句柄指向这些对象。 

在顶层环境中,声明所有的测试用例的句柄,并进行例化。

■要求4.4
将每个通道的接口,赋值给对应的chnl_initiator。

进行端口连接; 

■要求4.5
开始运行。 

 运用SV的系统函数运行

 写在最后

这部分内容重点是进行相邻tb文件的代码对比,通过比较了解module和class的区别和联系,另外一点就是要根据最后tb4代码画出验证结构图,最后通过验证结构图再去理解代码,以上两点是博主的一点小感悟和学习方法,分享给大家,想自学小项目的可以下载代码文件练习起来了,欢迎一起讨论,共同进步。

 结构图:

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

创芯人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值