IC验证学习笔记(MCDF)实验2-接口、类。从使用硬件盒子过渡到使用接口和软件盒子(class)来验证设计。

接口interface
功能:用来设计和验证,在验证时让连接更简洁 

和module相似,可以定义端口,双向信号,可以用initial和always,可以定义function和task

可以在软硬件环境传递,可以作为module的端口列表,可以作为软件方法的形式参数(virtual)

把接口看成一个插排,DUT和TB之间的数据驱动关系都可以用插排完成

        将验证组件和DUT之间通过接口来连接,所以验证组件chnl_initiator的端口变得非常干净,即chnl_intf。在使用接口之前需要定义接口chnl_intf和内部的接口,同时也要声明一个时钟块,它的功能是为了消除可能存在的竞争问题,确保时钟驱动数据之间有一定的延迟,以便于DUT顺利采样。
        引入接口后的代码,例化的实例包括了只产生数据的channel_generator,只负责发送数据的chnl_initiator以及作为验证组件和DUT之间的接口chnl_intf。
tb3代码改造:
接口和时钟块不变
变量限定为local
端口列表的接口放下来用virtual
初始化用new函数
类里面的变量都是动态变量,不用加automatic了
用到的方法和变量都加上this表示这个当前类的,防止同名问题
用函数将接口给到initiator中
单独的一个类ch_trans来封装发送出去的数据,输送数据的task的输入变成这个类的句柄,而不是原来的标量data,里面的data需要加上句柄索引
队列从存放数据变为存放句柄
生成数据的函数最终返回句柄t
例化initiator时不用再加入接口参数,例化generator不用括号
在initial块中对三个通道的initiator和generator进行初始化和连接到接口上,在三个test中就不用了再做这一步骤了(原本是每个test都需要调用函数来进行初始化)

tb4改造思路:
所有的class都封装在package里面,包括trans, gen,init,agent,root_test,basic_test,burst_test, full_test
trans, initiator, generator不用变动
agent中包含init和gen的句柄,标志数据个数的变量ntrans,通过将接口代入set_interface和initiator连接,将ntrans作为参数和generator连接,分别是发送和产生数据。
将3个test中相同的部分放到root_test中,包括设置数据个数ntrans和名字name,声明3个agent句柄,例化每个agent,运行起来run,需要将接口连接到3个句柄上
3个test作为子类继承root_test
3个test只需要设置ntrans和name和idle_cycle即可,并分别给到父类中的new函数。
第3个test还是需要用fork_join_none继续写
以上就是包的内容
在tb这个module中,依然是例化mcdt+产生时钟信号+产生复位信号+例化接口。
在tb这个module中,要先用import引入所有类(module是硬件盒子),然后才能声明3个test的句柄,并例化。然后将3个接口作为参数给到3个句柄中的set_interface,由于3个test中没有这个函数,所以往父类root_test中寻找,给到agent的set_interface,再给到initiator的set_interface,由此完成了信号的层层传递。
最后调用3个句柄中的run,也就是各自test中的run,跑起来了

module class区别

         module可以声明端口,将接口的实例intf直接传递进去。而class没有端口,不能直接像module一样这么写,而是将接口作为class的成员变量写出,并且在之后通过set_interface将指针传递到组件里。并且class里的idle_cycles初始化是放在new里面,而module没有new。

         module里默认生命周期是static,要声明为动态的就需要写明是automatic,而class里默认就是automatic,所以函数和任务都不用注明;

        module里不能封装(local、protected)、而class可以

        module里不能封装(local、protected)、而class可以

        组件需要在module里例化(不用在initial里例化),而在class里则是需要在initial块里实例化

@(negedge intf.clk)的作用:避免采样时的竞争问题。

        仿真的时候会存在时钟和信号的时序竞争问题。
        默认情况下,时钟对组合逻辑添加一个无限小的时间延迟,称为delta cycle。delta cycle比最小时间精度还要小,是一个相对的概念。
        注意,valid和data是时序逻辑,而ready是组合逻辑,因此存在一个delta cycle。
valid、data和ready在同一个delta cycle,就是在上升沿处。如果不加@(negedge intf.clk),本来ready是打算变为1的,结果因为delta cycle的存在使得ready采样采到了0,也就是采错了。
        解决方法:加延迟。比如像代码里在clk下降沿再采,其实就是添加了一个延迟。另一个方法是直接添加延迟#1 ps,但不推荐,一般建议不加固定的延时,因为不利于维护和看代码。(在clocking块里就要直接添加固定的延迟)
为什么valid、data用了时钟块,而ready却没用?

        valid、data是时序逻辑部分,而ready是组合逻辑(因为ready信号要立即给valid和data反馈,接受还是不接受,而不是等到下一拍,因此是ready采用组合逻辑)
时钟块是为了模拟时序逻辑里的建立保持时间,只在时序逻辑中出现,组合逻辑不用。
要观测实时的信号就不用clocking

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值