UVM序列篇之一:新手上路

时间:2022-08-24 23:54:46

      声明:本人所有权属路科验证,本人仅为个人学习方便将文章整理至此。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

有了UVM的世界观,知道这座城市的建筑设计理念,也跟着码师们(实在不忍心用码农……)一起修建了各成独立环境的组件群落。读者们在经过一番实践,经过上一章讲的组件之间的通信方式,开辟了各个建筑之间的道路、桥梁和河道以后,就可以进入紧张繁忙的物流期了。如果城市里面没有交通,那么显然不会有多热闹。

在本章中,我们将主要围绕下面几个核心词来阐述它们的作用、主要分类以及之间的互动关系:

  • sequence item

  • sequence

  • sequencer

  • driver

如果按照交通道路的车流来打比方,sequence就是道路,sequence item是道路上行驶的货车,sequencer是目的地的关卡,而driver便是最终目的地卸货的地方。从软件实施的层面来讲,这里的货车是从sequence一端出发的,再经过了sequencer,最终抵达了driver,经过driver的卸货,每一辆的货车也就完成了它的使命。而driver对每一件到站的货物,经过它的扫面处理,将它分解为更小的信息量,提供给DUT。在这个过程中,不同的角色中有下面的这些软件互动:

  1. sequence对象自身会产生目标数量的sequence item对象。借助于SV的随机化和sequence item对随机化的支持,使得产生的每个sequence item对象中的数据内容都不相同。
  2. 产生的sequence item会经过sequencer再流向driver。
  3. driver得到了每一个sequence item,经过数据解析,再将数据按照与DUT的物理接口协议写入到接口上,对DUT形成有效激励。
  4. 如果有必要的时候,driver在每解析并且消化完一个sequence item,它也会将最后的状态信息同sequence item对象本身再度返回给sequencer,最终抵达sequence对象一侧。这么做的目的在于,有的时候sequence需要得知driver与DUT互动的状态,这就需要driver仍然有一个回路再将处理了的sequence item对象和状态信息写回到sequence一侧。

UVM序列篇之一:新手上路

贯穿本章的几个重要概念,在上面的互动中可以看到,sequence item是每一次driver与DUT互动的最小粒度内容。例如,DUT如果是一个slave端,driver扮演master去访问DUT的寄存器,那么sequence item的需要定义的数据信息至少包括访问地址、命令码、数据和状态值,这样的信息在driver取得以后,会用时序的方式在interface一侧激励送给DUT。按照一般总线做寄存器访问的方式,这样的访问在时序上大致会保持几个时钟周期,直至数据传送完毕,而由driver再准备发起下一次的操作。用户除了可以在声明sequence item时,添加必要的成员变量,也可以添加对这些成员变量进行操作的成员方法。这些添加了的成员变量,需要充分考虑在通过sequencer传递到driver前是否需要随机化,例如上面提到的例子中的访问地址和数据等等,都应该通过SV关键词rand声明为可以随机化的变量,以便后期进行随机化处理。

那么对于一个sequence而言,它会产生多个sequence item,也可以产生多个sequence。从产生层次来看,sequence item是最小粒度,它可以由sequence生成,而相关sequence也可以进一步组织和实现层次化,最终由更上层的sequence进行调度。这么看来,sequence可以看做是产生激励内容的载体。

在sequence与driver之间起到桥梁作用的是sequencer。由于sequencer与driver均是component组件,它们之间的通信也是通过TLM端口实现的。

在上一章中提到过,TLM端口在例化中需要对通信参数进行指定,这里的通信参数即sequence item种类。由于这一限制,使得sequencer到driver的传输数据类型不能改变,同时与sequencer挂接的sequence内创建的sequence item类型也应为指定类型。这就跟一个投币机的原理有一些类似,如果顾客投递的是1块钱,那么它会被识别到相应的储币区域,而如果顾客投递的是5毛钱,那么它肯定会被区别对待的。  

在激励驱动链的最后一道关卡是driver。这个家伙胃口还蛮挑剔的。它就跟投币机一样只认了同一个类型的“钢镚儿”。对于常见的用法,driver往往是一个sequence item消化完,报告给sequencer和sequence,同时再请求消化下一个sequence item。所以,driver看起来永远喂不饱,同时还又对食物很挑剔。在消化每一个sequence item之前,该item中的数据是已经随机化好的,所以每个item一般都是各不相同的。driver自己并不会轻易修改item中的值,它会把item中的数据按照与DUT连接接口的物理协议按照时序关系驱动到端口上面。例如对于一个标准的写操作,driver不但需要按照时序依次驱动地址总线、命令码总线和数据总线,还应该等待从端的返回信号和状态值,这样才算完成了一次数据写传输。又如果是一个读操作,driver还应该在驱动完地址总线和命令码总线之后,等待返回信号、状态值和读出的数据,并且在有需要的情况下,将读出的数据再写回到sequence item中,通过sequencer最后返回给sequence。

从类的继承性来看,uvm_sequence_item和uvm_sequence是基于uvm_object,这不同于uvm_component可以在build阶段作为UVM环境的“不动产”创建和配置,而是可以在任何的阶段创建。这种类的继承带来的UVM应用区别在于:

  • 由于无法判定环境在run阶段运行什么时间点会创建sequence和挂载(attach)到sequencer上面,所以无法通过简单的UVM结构来识别sequence的运行阶段。
  • 正是由于uvm_object独立于build阶段之外,这使得用户可以有选择地、动态地在合适的时间点挂载想要的sequence和item。
  • 考虑到uvm_sequence和uvm_sequence_item并不处于UVM结构当中,所以顶层在做配置时,无法按照层次关系直接配置到sequence中。而如果sequence一旦活动起来,那么它必须挂载到一个sequencer上,这样sequence可以依赖于sequencer的结构关系,间接通过sequencer来获取顶层的配置和更多的顶层信息。

UVM序列篇之一:新手上路

在本文的最后,需要额外提出的一点是,从常规的认知方式来看,用户可能更愿意将时序控制的权利赋予sequence。这种从sequence产生item,继而将item通过sequencer推送给driver的方式,实际上有一些“越俎代庖”的嫌疑。毕竟如果按照责任明确划分的话,sequence应该是只负责生成item的内容,而不应该控制item消化的方式和时序,而驱动激励的时序这一任务应当由driver来完成。读到这里,也许一些读者可能会困惑,毕竟实际应用中,我们可以添加一些实用的时间延迟或者事件触发来控制item的生成和传送,但是请注意,item的生成和传送,并不是直接表示了最终的驱动时序,决定这一点的还包括sequencer和driver。

sequencer之所以作为一个“路由”管道,停在sequence和driver之间,看重的是它的两个特点:

  • sequencer作为一个组件,它可以通过TLM端口与driver传送item对象。

  • sequencer在面向多个并行sequence时,它有充分的仲裁机制来实现合理分配item传送来模拟并行item数据传送至driver的测试场景。

那么是否sequencer应该从sequence一侧得到item数据,再推送给driver呢?或者,在sequence、sequencer与driver之间发生的数据传送请求应由谁首先发出?而数据流向又是从谁到谁呢?关于这一个具体的TLM传输机制,我们将会在其后的《sequencer和driver》中详细介绍。而在这里,希望读者首先认清的是,数据传送机制采用的是get模式,而不是put模式。我们在TLM传输中为读者们就介绍过典型的两种数据传送场景,如果是put模式,那么应该是sequencer将数据put到driver,而如果是get模式,那么应当是driver从sequencer获取item。之所以选择get模式,UVM是基于下面的考虑:

  • 如果是get模式,那么当item从sequence产生,穿过sequencer到达driver时,我们就可以结束该传输(假如不需要返回值的话)。而如果是put模式,则必须是sequencer将item传送至driver,同时必须收到返回值才可以发起下一次的传输。这从效率上看,是有差别的。

  • 如果需要让sequencer拥有仲裁特性,可以使得多个sequence同时挂载到sequencer上面,那么get模式更符合“工学设计”。这是因为driver作为initiator,一旦发出get请求,它会先通过sequencer,继而获得仲裁后的item。

如果读者对于究竟应该采用get模式还是put模式有疑问的话,不妨想象一个极端的情况,sequence的职责如果仅仅是个“水池”,那么用水的权利应该交给终端的driver,还是交给作为调度站的sequencer呢?

作为一个开着item,或者坐到sequence中浏览观光的新手,读者们可以伴随路桑在下一节《sequence和item》中欣赏这两个对象之间各种悱恻缠绵的关系。

谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。