今天开始来探讨一下数据传输类IP的延迟控制的话题,下面是简单的顶层连接示意图:
这类IP比较单纯,上游发送数据和command,或者数据通过memory交互,上游只发送descriptor信息到FIFO,design读取descriptor,解析之后去对应的address读取要发送的data,然后结果一系列decode、ecc或者加解密的操作,最后design把要输出的数据或者descriptor信息写到output FIFO,然后依次往下级送。 对于这类IP的验证工作,在纯黑盒验证的情况下,除了数据、descriptor format相关的feature外,特别重要的一环就是如何控制发包的间隔,以及下游的反压。根据笔者的经验,一般有如下几种做法:(读者有其他方法,欢迎在评论区提出,相互学习探讨)
1. 最简单最省事最无脑的方法:mst端发次发包中间随机delay若干cycle,下游反压也是,随机的toggle ready信号,纯靠rand去撞各个组合场景。这种方法是很简单并且理论上也有概率撞到各种组合,但是绝大部分场景都很类似并且简单。尤其是对于对于上游是通过mem和dut交互的时候,上游只需要发送descriptor,data是backdoor写到mem的,发送端很快就发完一包,而design解析descriptor之后还需要去对应的address读取data,一般数据量比起descriptor大的多,这就会导致,输出端天然比输入端慢很多,如果不额外加入控制,通常发送端很快就把包发完了,在waveform上看到的情况就是,波形的后半段,甚至绝大部分都是只有design往下游发,而上游给design发包在很短时间内就完成了。
2.由此就自然而然产生了第二种控制方式,那就是在1的基础上加上一下额外信息来帮忙产生delay或者控制发包的时机,这里一般也有两种方式:
2.1. 借助input fifo,根据read/write pointer或者seq自己记录的pointer来控制发包时间,第一种方式比较像是~full就发,现在可以改成比如full之后等空或者等到很少descriptor没有被design读走之后再发,或者设定一个threshold。当input fifo中的有效descriptor大于threshold的时候就停止发包,这样产生的效果就是当输入端过快的时候会停下来等输出端,减小design只有发送没有接收的窗口。
2.2 如果design处理完一个包之后会有irq产生,像上图中那样,发送端可以参考这个irq,比如收到irq之后才发送下一包,这样也方便造一包一包处理的case,我们可以进一步优化一下,不如每次发包的时候随机一下,可能看到irq产生 了再发下一包,也可能随机Dealy若干cycle,也可能参考2.1中的方法。
3. 真实情况通常是一段时间流量快一段时间流量一般,一段时间流量慢,通过简单的delay控制其实很难达到这样的效果,尤其再叠加上如果design有多个通路或者多组interface,怎么样打到不同通道之间的组合情况更是一件很难的事。下面给出一种思路:
首先是delay mode 这个进程,这个进程只进行delay mode的随机,图中给了三种mode,可以根据具体情况增减mode,每种mode对应的delay cycle数也完全自定义 ,每种mode的持续时间也是rand的,持续时间结束之后rand选择下一种mode和持续时间。另外一个进程就是我们正常发包的进程,每次发包前随机产生一个delay,这个Dealy随着mode的不同,产生出来的delay的区间就不同。举个例子,mode进程是 fast_mode(这种mode下产生的delay都是0,持续100T)->normal mode(这种mode下产生的delay是1~10T,持续200T)->slow_mode(这种mode下产生的delay是10~200T,持续500T),我们来看这段800T的窗口,前100t就能 发包100包(假设drive 1T完成handshake),后面200T时间内都是normal mode,有可能只能发50包,后面500t都是slow_mode,可能只发了10包。这样在波形上看到的情况就是波形最前面发包快,然后速度慢慢降低,配合上mode的rand,就能非常容易撞到各种组合情况。当然这种方式的code实现也会复杂很多,但是一旦实现出来,就能充当一个common的delay control组件,其他人也能用,一本万利。