MCDF-实验2——硬件验证方式与软件验证方式的过渡

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本次实验使用接口、类和包,完成软件验证方式

提示:以下是本篇文章正文内容,下面案例可供参考

一、接口的使用——tb1.sv

定义接口和内部端口,并且为了消除竞争需要在其中声明一个时钟块,确保时钟驱动信号间的时序关系。

输入端口(采样信号):ch_ready,ch_margin
输出端口(驱动信号):ch_valid,ch_data
时钟块触发事件:clk上升沿
接口信号的赋值必需采用非阻塞赋值<=(同步驱动)

在这里插入图片描述


数据发送组件chnl_initiator:

相较于上个实验的的tb4.sv,chnl_initiator的端口更简单,使用了接口来代替。
在这里插入图片描述

数据产生组件chnl_generator:

相较于上一个实验,数据存放由数组改为了队列chnl_arr[$],定义了两个变量和两个方法
变量:
id:用来标识slave_channel
num:用来递增数据
方法:
initialize:初始化变量
get_data:产生递增数据
在这里插入图片描述
测试代码:

initial begin
// 初始化验证组件的initial块
chnl0_gen.initialize(0);
chnl1_gen.initialize(1);
chnl2_gen.initialize(2);
chnl0_init.set_name(“chnl0_init”);
chnl1_init.set_name(“chnl1_init”);
chnl2_init.set_name(“chnl2_init”);
chnl0_init.set_idle_cycles(0);
chnl1_init.set_idle_cycles(0);
chnl2_init.set_idle_cycles(0);
end

>  **//用来测试的initial块**
>     initial begin
>     @(posedge rstn);
>     repeat(5) @(posedge clk);
>     repeat(100) begin **//写入100个**
>       chnl0_init.chnl_write(chnl0_gen.get_data());      
>     end
>     chnl0_init.chnl_idle();    
>     end 
>     initial begin
>     @(posedge rstn);
>     repeat(5) @(posedge clk);
>     repeat(100) begin
>       chnl1_init.chnl_write(chnl1_gen.get_data());  
>     end
>     chnl1_init.chnl_idle();    
>     end
>     initial begin
>     @(posedge rstn);
>     repeat(5) @(posedge clk);
>     repeat(100) begin
>       chnl2_init.chnl_write(chnl2_gen.get_data());
>     end
>     chnl2_init.chnl_idle();    
>     end

仿真波形如下:并行发送数据,而且valid与data均与clk在同一变化沿。时钟信号与数据信号没有延迟。
在这里插入图片描述

1.使用时钟块来做数据驱动

即在写入数据的任务中使用时钟块索引为ch_valid和ch_data赋值

在这里插入图片描述
仿真结果:驱动信号在时钟上升沿之后的1ns处变化。
在这里插入图片描述

2.设置空闲周期

加入变量idle_cycles,用来设置有效数据间隔;首先找到设置空闲周期的任务chnl_idle()它是等待下一时钟上升沿执行的,即空闲1个周期,我们需要设置变量来设置空闲周期数目。使用repeat(idle_cycles)可以实现这一目的。
且使用函数set_idle_cycles设置变量大小
在这里插入图片描述在这里插入图片描述
只需将变量设置为0即可实现连续发送。
在这里插入图片描述

二、设置测试和仿真的结束——tb2.sv

使用fork…join并行发送数据,将不同的test组装到task中,以此区分不同的测试。

1.使用task封装tb1.sv中的初始化过程块并将三个测试过程块改为并行线程

参考basic_test改写burst_test,要求idle_cycles==0,且同时发送500个数据
在这里插入图片描述
在这里插入图片描述

burst_test():
在这里插入图片描述
在这里插入图片描述

仿真结果如下:
在这里插入图片描述

  • 【问题】之前没有注意到ready的波形有些问题,在仿真运行到一定时间时,ready开始每隔一个时钟周期拉高(参考slave_fifo设计代码,实际上是余量为0,即FIFO满状态时ready会拉低,然后一取一放导致ready一高一低),导致有些数据处于ready==0的状态。翻看之前的tb1.sv仿真结果也有这样的问题。(实验1中发送的数据少且不紧凑,所以没发现😭)
    也就是说上一个数据还没等到ready拉高,就开始同步下一个数据了。需要添加@语句,等待时钟的下降沿触发为什么?
    在这里插入图片描述

设置断点,分析仿真过程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
26,27同步数据 —— 28打印语句 —— 29等待时钟下降沿(黄线处) —— 31打印发送成功信息(645ns处)—— 25等待时钟上升沿—— 30等待ready高电平(这里应该是与31同时进行)——接25一直等到时钟上升沿(蓝线之后的那个上升沿处,下一个数据)
29是否有必要。不加29行,波形有误,但找不到添加的原因


2.完成task fifo_full_test()

参考task basic_test(),要求:无论采取什么数值的idle_cycles,无论发送多少个数据,只要各chnl_initiator的不停发送使得channel缓存变满(full,设计代码中规定FIFO满了后ready会拉低),那么在三个channel都拉低过ready时(先后拉低也可以),便结束测试结束测试不是结束仿真,即停止发送数据
首先,该任务与basic_test()区别在于,数据是一直发送的,没有固定数目;
其次,idle_cycles是随机的
最后,测试结束是依靠FIFO为满状态,即余量为0,且三个都到过0,而不是之前的需要等到发送完固定数目的数据

  • 组件初始化化阶段:
    在这里插入图片描述
  • 测试阶段:forever-loop用fork…join_none保证不阻塞,然后等待三个channel_fifo满状态都已经触发过。紧跟着结束测试线程。
    在这里插入图片描述
  • 空闲阶段:在结束测试后一定要设置空闲周期,否则会不断向fifo放入最后一个数据
    在这里插入图片描述
  • 等待阶段:等待存入fifo中的数据全部发送完后结束仿真
    在这里插入图片描述
    在这里插入图片描述
    仿真结果:
    在这里插入图片描述

三、类的使用——tb3.sv

1.改造原有module

  • 需要注意的是class没有端口列表,接口需要声明virtual,
  • 同时注意在函数和任务的参数列表中要声明参数的传递方向
  • 变量声明类型为logic
  • new函数用来对变量初始化
  • class中的方法无需声明automatic这部分知识需要复习
  • 方法中的变量需要加上this区分类属性和方法属性的同名变量
  • 添加了参数类,存放相关数据,队列从存放数据改为存放该类的句柄
  • 由于num,id,data存放在参数类中,因此使用时需用句柄索引
  • 产生数据的函数改为了get_trans,它返回的不再是数据data而是参数类的句柄t

class chnl_initiator
在这里插入图片描述
在这里插入图片描述


chnl_generator
在这里插入图片描述

在这里插入图片描述


2.在测试模块tb3里的initial块中例化相应类

  • 接口例化时其端口列表可以使用通配符
  • 例化启动类以及数据产生类时无需传递参数
  • 接口的传递需单独进行,详见下一步
    在这里插入图片描述

3.给启动类传递外部接口

在这里插入图片描述

4.检查并调用已经定义好的三个test任务

**检查:**可以在创建两个类的对象时,对channel的id和name作初始化,避免在每个task中初始化参数。只留下task前面关于idle_cycles的部分。还有就是chnl_generator内产生数据的函数名不是get_data了已经改为get_trans所以需要更改。
在这里插入图片描述

在这里插入图片描述
测试块
在这里插入图片描述

仿真结果如下:
在这里插入图片描述
从结果可以看到问题出在chnl_generator.get_trans这里。检查后发现创建对象时都是chnl0_gen.
在这里插入图片描述
【考虑:chnl_generator在例化chnl_trans t时是否合适?】
chnl_trans t是在类中例化的,在方法内使用,若方法内出现同名实例是否会找不到指定的实例。
方法内使用则在方法内例化
在这里插入图片描述

四、包的定义和类的继承——tb4.sv

引入class chnl_agent、chnl_root_test、chnl_basic_test、chnl_burst_test、chnl_fifo_full_test,将所有的类封装到软件包package chnl_pkg中。

  • chnl_agent应该包括generator、driver、monitor。tb3中只使用了generator和initiator,因此只需要将它们在agent中例化
  • 三个测试任务也需要用类来实现,利用类的继承改写三个测试任务

class_agent

  class chnl_agent;
  //例化generator和initiator
    chnl_generator gen; 
    chnl_initiator init;
  //ntrans是用来定义发送数据的个数
    local int ntrans;
    local virtual chnl_intf vif;
  //new函数初始化变量
    function new(string name = "chnl_agent", int id = 0, int ntrans = 1);
      this.gen = new(id);
      this.init = new(name);
      this.ntrans = ntrans;
    endfunction
  //设置数据个数
    function void set_ntrans(int n);
      this.ntrans = n;
    endfunction
  //连接agnet和initiator的接口
    function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      init.set_interface(vif);
    endfunction
  //发送数据
    task run();
      repeat(this.ntrans) this.init.chnl_write(this.gen.get_trans());
    endtask
  endclass: chnl_agent

class_root_test

  class chnl_root_test;
  //存放3个channel事务类的句柄的队列
    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

1.在顶层模块module tb4中导入软件包

直接使用import + 软件包名字使用通配符导入全部内容
在这里插入图片描述

2.改写测试task为class

  class chnl_burst_test extends chnl_root_test;
    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
  class chnl_fifo_full_test extends chnl_root_test;
    function new(int ntrans = 500, 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; 
        forever agent[0].run();
        forever agent[1].run();
        forever agent[2].run();
      join_none
      $display("fifo_full_test: 3 initiators running now");
      fork
      	wait(agent[0].vif.ch_margin == 'h0);
      	wait(agent[1].vif.ch_margin == 'h0);
      	wait(agent[2].vif.ch_margin == 'h0);
    	join
		$display("fifo_full_test: 3 channel fifos havereached full");
    disable fork_all_run; 
    $display("fifo_full_test: stop 3 initiator running");  
 		$display("fifo_full_test: set and ensure all agents' initiator are idle state");
    fork
      agent[0].init.chnl_idle();
      agent[1].init.chnl_idle();
      agent[2].init.chnl_idle();
    join  
  
     $display("fifo_full_test waiting DUT transfering all of data");
    fork
      wait(agent[0].vif.ch_margin == 'h20);
      wait(agent[1].vif.ch_margin == 'h20);
      wait(agent[2].vif.ch_margin == 'h20);
    join
    $display("fifo_full_test: 3 channel fifos have transferred all data");  
      $display("%s finished testing DUT", this.name);
  endtask
  endclass: chnl_fifo_full_test

3.顶层例化这三个测试类

代码如下(示例):
在这里插入图片描述

4.从test一层传递接口

在这里插入图片描述

5.调用run,开始测试

在这里插入图片描述


编译没有问题,仿真报错
在这里插入图片描述

  • Illegal access to local member agent[2].vif.ch_margin.
    字面意思,vif是在agent中定义的局部变量,不能在外部访问,去掉local即可

问题解决方案!感谢博主!!
仿真结果:没有等到所有数据发送完
在这里插入图片描述
查看波形:最后一个数据发完后没有调用chnl_idle()重置
在这里插入图片描述
修改:在task run内调用写入任务chnl_write后,再调用chnl_idle()
在这里插入图片描述
然后与参考代码的仿真结果对比发现仿真时长不够,仔细查看波形,发现问题出在实验1的疑问,即等待ready拉高前所添加的时钟下降沿触发语句。
在这里插入图片描述
为什么要添加@(negedge clk)
@(negedge clk)在当前发送数据的这一时钟周期内等待ready拉高,若为高则数据发送成功,打印语句。

总结:验证环境结构

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 路科验证v2 mcdf代码是用于验证MCDF(多配置自洽场)方法在路科库中的实现的一种代码。MCDF方法是一种计算分子的电子结构和能量的量子化学方法,它将电子排布在一组给定的分子轨道上,并利用自洽场方法来优化这些轨道和分子的电子能量。MCDF方法在化学计算中被广泛应用,用于预测分子的光谱性质、反应能垒等。 在路科验证v2 mcdf代码中,主要包括以下几个步骤: 1. 初始化:代码首先需要初始化所需的参数,例如分子结构、基组选择等。 2. 核心计算:接下来,代码会使用MCDF方法计算分子的电子结构和能量。这包括求解含时无关Hartree-Fock方程、计算分子的电子密度等。 3. 自洽场迭代:在MCDF方法中,自洽场迭代是一个重要的步骤。代码会通过调整分子轨道和电子密度来达到自洽场的要求,直到达到收敛条件(例如轨道能量变化很小)为止。 4. 结果输出:最后,代码将会输出计算得到的分子的能量、电子结构和其他相关的物理量。这些结果可以用于进一步的分析和研究。 总之,路科验证v2 mcdf代码是用于验证MCDF方法在路科库中的实现的一种代码,通过计算分子的电子结构和能量,为进一步的量子化学计算提供基础。 ### 回答2: 路科验证v2 MCDF代码是一种用于验证机器学习模型性能的工具。MCDF(模型复杂度和数据难度)是一种评估机器学习模型性能的指标,可以帮助我们了解模型的泛化能力以及在不同数据难度下的表现。 路科验证v2 MCDF代码包括几个步骤: 1. 数据准备:首先,需要准备数据集来进行模型验证。可以选择一个已有的数据集,也可以根据需求自己生成一个数据集。数据集的特征和标签应该是清晰明确的,且特征和标签的数量应该是一样的。 2. 模型选择:根据需求选择适合的机器学习模型,例如决策树、支持向量机等。根据选择的模型,确定需要调整的参数。 3. 路科验证:路科验证是一种交叉验证的方法,在训练和验证的过程中,将数据集划分为K个相等大小的折(或区间),其中K-1个折用于训练,剩下的1个折用于验证。该过程会进行K次,每次会选择不同的验证折。通过路科验证可以获得模型在不同数据子集上的表现情况。 4. MCDF计算:MCDF是根据不同的数据难度和模型复杂度计算的。数据难度可以通过调整不同的训练和验证集合比例来获得。模型复杂度则是根据选择的机器学习模型和调整的参数来得到。MCDF计算可以通过统计模型在不同数据子集上的准确率、精确率、召回率等指标来得到。 通过路科验证v2 MCDF代码,我们可以了解到机器学习模型在不同数据子集上的表现,评估模型的泛化能力,了解模型的优势和不足,并根据结果进一步调整模型和参数,提高模型的性能。 ### 回答3: 为了回答你的问题,我首先需要解释一下相关的背景信息。路科验证 (Lucas-Lehmer test) 是一种用于验证 Mersenne 数的质数性质的算法,而 v2 mcdf 代码则是实现了这种验证方法的计算机程序。 路科验证基于费马小定理和二次剩余定理,用于判断形如 2^n - 1 的数是否为质数。具体的算法如下: 1. 初始化:选择一个整数 n,通常要求 n 是质数,并且计算 s = 4 - 2^(n-1)。 2. 迭代计算:对于 i 从 2 到 n-1,重复以下步骤: a. 计算 s = (s^2 - 2) mod (2^n - 1)。 3. 结果验证:若 s 等于零,则 2^n - 1 是一个 Mersenne 质数。 v2 mcdf 代码是一种对路科验证算法的实现,用于在计算机上自动执行验证计算。这个代码可能是一种特定的程序或者函数,其输入为一个数字 n,通过计算得出验证结果。 使用 v2 mcdf 代码进行路科验证的步骤如下: 1. 根据你的需求选择一个合适的 n 值。 2. 利用 v2 mcdf 代码进行验证计算,输入 n,并获得验证结果。 3. 如果验证结果为真,则 2^n - 1 是一个 Mersenne 质数;如果验证结果为假,则不是。 需要注意的是,路科验证算法是一种可以在合理的时间内进行的算法,但对于非常大的 n 值,计算可能会非常耗时。因此,考虑到计算资源和时间限制,选择合适的 n 值进行验证是非常重要的。 希望这个回答能够帮助你了解路科验证和 v2 mcdf 代码的基本原理和使用方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值