标准验证环境与处理器验证的代码实现(一)
背景:
标准的UVM验证环境其实在各个用户实现过程中也不标准,这里只是提供一套相对容易复用的环境实现模板,后面可能会考虑采用脚本进行实现。
另外,处理器验证和一般的电路模块验证也有很大区别。这个专栏也会根据这些差异点,做一些差异原因与处理方案的描述,内容十分基础,有待深入讨论。
这里先介绍一下agent与sequence
Agent:
Agent模块,代理各个重要模块。常见的“重要模块”就是sequence、sequencer、driver、monitor,另外interface的连接也可以在agent中完成。
Agent的功能原本可以在env中实现,但是会导致复用性变差,这一点会在后续env里解释。
agent中实例化了sequencer、driver、monitor,以及一个或数个interface
在build phase阶段,需要将这些模块注册到factory中:
根据当前agent是否为active,决定是否注册sqr与drv
passive mod只启用mon,不发送激励
drv和mon需要的interface也在这里采用config_db传递(传空也无妨)
active mod下,在connect phase中将drv的port连接到sqr的export上
sequence
sequence中可以对不同类型的激励进行管理、配置,建议从base_seq写起,实现基础的配置功能,然后再继承到各个子类sequence中,进行特殊功能的实现。
phase的objection一般建议在sequence中实现,这里放在了pre_body和post_body中进行raise与drop;
sequence是一个object,而非component,需要挂载到sqr上传递给drv,一般在main_phase中进行这个传递过程;
seq的body中实现transaction的配置与打包,trans一般就是UVM中激励传输的标准粒度(当然也可以直接做点对点的信号传输,适合激励量较少的情况)
driver
driver的目的是获取sequence,解包transaction,并将解包结果传递给DUT,这一过程一般通过直接对interface上的信号赋值实现。
一般激励过程会在drv的main_phase中进行,这也是个经典问题“什么时候用main_phase,什么时候用run_phase”,main_phase是可以重新跳转回reset_phase的,而run_phase则与之并行。在需要DUT运行过程中重启的情况中,main_phase -> reset_phase是一个十分有利于维护的跳转实现方案。
DUT不同,drv的实现方式也会有很大的差异,这里只给了一个极简的参考,不用被这个方案束缚。
处理器验证的差异点
其实没有用到sqr->drv的传输,driver的目的是主动发起transaction的获取请求,并进行解包。这一过程会不断循环,以模拟对DUT不断传输的输入信号。
而处理器验证的情况与上述过程存在一定差异。
首先是激励的传入,处理器的输入通常不是实时的,而是在一开始就准备好的一段指令流。这段指令流可以是特定的手写汇编码,也可以是随机指令生成器产生的一段指令流,通过DUT的取指通路传入到处理器当中进行操作。个人理解,现在的处理器通常是乱序、并行执行的,且CPU也是从DDR通过cache读取一长段指令流,以减少交互次数,所以按单笔指令进行激励的验证方式就显得十分突兀,无法模拟出正常的行为。
另外,指令流的传输并不需要借助interface逐个赋值,通常是写到DDR或者cache的memory模型中,一般采用文本交互。文本交互的时间、资源消耗是不低的,频繁进行文本交互无疑会大幅拖慢仿真。
综上,处理器的激励一般是不需要drv这个永动机来驱动的,而是在初始化阶段完成的,drv在这样的验证环境中就显得可有可无了。