【PCIe】CDNS PCIe VIP 杂记 -- Callback-CSDN博客
前言:之前针对CDNS PCIe VIP Calback部分进行了学习,并写了上面的博客。学习就是为了应用,本文就将记录一些我实际用到的我觉得比较典型的例子。仅供学习参考。
需求:
PCIe VIP作为EP,DUT是RC。EP发了一笔memory write,后对同一个地址进行memory read,想要check都回来的read data和write data是否一致。
注:实际按照CDNS PCIe VIP UG里讲的使用方法,VIP的使用一般都是成对出现的,即一个active agent(这里是EP本P)和一个passive agent(这里是DUT RC的monitor)作为monitor。因此,即使不做上述需求的check,这里的active和passive agent也是会进行比对的。从实际仿真结果也证明如此。
分析&实现:
对于read data:
基于callback的学习认知,可以从callback入手。之前的博文里也说了,对于read data可以从callback:TL_TX_completion_queue_exit里从payloadAccumulated[]获得。
第一种方法:
在CbF里直接从payloadAccumulated[]中获取read data。具体实现code如下。
基于我之前博文讲的,这种方法的实现其实是在monitor重载CbF function函数的。
但是这种方法,因为在function实现的,不能在仿真过程中实时进行比对。【当然,针对单一test测试,所有操作包括check都可以在monitor里实现,但并不通用】至少目前为止,我自己并没有找到合适好用的方法。因此,此方法比较适合monitor。
第二种方法:
在sequence里实现,即在发送完memory read request traffic后,并行等待TL_TX_completion_queue_exit callbck trigger后,进一步获取read data。具体实现code如下。
对于write data:
和read data类似,我们也可以从相应的callback函数获取,具体如下。
第一种方法:
根据这张图https://img-blog.csdnimg.cn/direct/7ab567150aa24a8496bfbe89c53d7fc2.png看,可以从callback:TL_TX_packet或者TL_transmit_queue_exit里获取。
由于我knowledge的limitation,目前我觉得这种只适合进行monitor使用。
第二种方法:
同样在sequence里进行实现。在实现过程中,我也尝试过从调用callback着手,但是如下这种实现方式会造成仿真hang。
之前一直找不到原因,后来对callback打印信息的capture,发现:比如我这里连续发了2笔write和2笔read,VIP model是会将这4笔traffic同时放到transmit queue里的。从实际仿真的时间戳也可以看出来。因此,我上面的while是会漏capture的。【除非就是每发一笔就等一笔,但这样就和我之前讲的一样,会造成仿真一笔一笔发送-完成,这在大量traffic的仿真很不现实也不通用】
因此,就只能【目前基于我自己的knowledge】在发traffic的时候获取地址和write data,这很容易办到。打印信息也如上。因此在sequence里做data check也就顺利成章了。
=============================分割线=======================================
更新一下完整的check方法:
上面的例子里,只是对连续两笔memory wr-rd,而且只是分别将write和read的data在sequence种获取/打印出来,并没有进行check。(当然,如之前提到的,passive monitor也会对同一个地址的读写进行backdoor check的)
思路分析:
1. 需要对同一个地址(address)的wr_data和rd_data进行对比。因此是两个信息,address和data。获取方式如上所述,read从callback中获得,write在gen traffic的时候获得。
2. 因为sequence在gen trafficd的时候,是一次性gen出来的,从时间戳来看是同一时间的,因此想进行一笔一笔check不现实。因此,想到的方法就是先将所有gen出来的write traffic存到一个结构体(包括地址和wr_data)类型的数组中。对于read traffic,通过fork_join方式,在gen traffic的同时,按照上面的方法,去TL_TX_completion_queue_exitCbF里去获得地址和rd_data,同样存在一个结构体类型的数组中。在最后进行两个数组的地址和数据比对。
具体实现:
1. 定义一个结构体类型变量,并声明write和read的数组变量。
2. 定义相关要用到的变量:
其中:tlp_cpl之所以声明成为denaliPcieTlpMemPacket类型,是为了下面调用callback的时候$cast成功,因为只有这个类型的Tlp有地址。
3. 在sequence body()中new出两个动态数组,分配空间。
4.发送wr/rd traffic,同时获得write traffic的地址和data,存入数组中。
5. 获取read traffic的address和data,并存入read pkt 数组。
最后一步是进行check。调用virtual task mem_datachk();
6. virtual task mem_datachk();定义在base sequence里。