主要是了解sequence item、sequence、sequencer、driver 的作用,以及之间是怎么相互联系的。
在上面这张图中,sequence会产生不同目标数量的sequence item对象,不同主要依托于SV的随机化,是的每个sequence item 的数据内容都不同;产生的sequence item经过sequencer在流向driver,driver对得到的每个sequence item进行数据解析,将数据写入接口上,写入的时候需要注意时序(接口协议),对DUT形成有效激励。
在必要的时候,driver在解析完每个sequence item后,会将最后的状态信息写回sequence item 返回给sequencer,然后最终抵达sequence对象一侧。
sequence item是driver与DUT每一次互动的最小粒度内容,driver往往是一个Sequence item消化完,报告 给sequencer和sequence,同时再请求消化下一个sequence item。
uvm_sequence_item 和uvm_sequence都是基于uvm_object,这是不同于uvm_component的地方,所以可以在uvm环境中的任何地方进行创建和配置。uvm_component只应当在build_phase阶段进行创建和配置。
这里的sequence指的是uvm_sequence类,而item指的是 uvm_sequence_item类,我们简称其为sequence和item。对于激励生成和场景控,是由sequence来编织的,而对于激励所要的具体数据和控制要求,则是从item的成员数据得来的。
item是基于uvm_object类,这表明了它具备UVM核心基类所必要的数据操作方法,例如:copy()、clone()、compare()、record()。item通常应该具备控制类、负载类、配置类和调试类这些数据类型。
如果需要用来做驱动,那么用户应考虑定义为rand类型,同按照驱动协议给出合适的constraint 。
由于item本身的数据属性,为了充分利用UVM域声明的特性,我们建议将必要的数据成员都通过'uvm_field_XXX宏来声明,以便日后uvm"object 基本数据方法自动实现。
UVM要求item的创建和随机化都应该发生在sequence的body(任 务中,而不是在equencer或者driver中。
按照item对象的生命周期来区分,它的生命应该开始于sequence的body中,而后经历了随机化并穿越sequencer最终到达driver,直到被driver 消化之后,它的生命一般来讲才会结束。之所以要突出这一点,是因为一些用户在使用中会不恰当地直接操作item对象,直接修改其中的数据,或将它的句柄发送给其它组件使用,这会无形中修改item的数据基因或者延长item对象的寿命。这种不合适的对象操作方式是需要注意的, 可以取代的方式则是合理利用copy()和clone()等数据方法。
扁平类(flat sequence):这一类往往只用来组织更细小的粒度,即item实例构成的组
除此之外还需要更多的信息来完善他所需要具备的激励场景,包含:sequence item +constraint 、item的时序信息、在需要我握手的时候等待monitor做出反应。
flat_seq类可以看作是一个更长的数据包,数据包的具体内容、长度、 地址等信息都包含在flat_seq中。在生成item过程中,通过将自身随机变量作为constraint内容来限定item随机变量,这是flat Sequence的大致处理方法.
层次类(hierarchical sequence):这一类是由更高层的sequence用来组织底层的sequence,进而让这些sequence或者按照顺序方式,或者按照并行方式挂载到同一sequencer上。
Hierarchical sequence区别于flat sequence的地方在于,它可以使用其他sequence,当然还有item,这么做是为了创建更丰富的激励场景通过层次嵌套关系,可以让hierarchical sequence使用其它 hierarhical sequence、flat sequence和sequence item,这也 就意味着,如果底层的sequemce item和flat sequence的粒度得当,那么用户就可以充分复用这些sequence item来构成形式更加多样的hierarchical sequence
虚拟类(virtual sequence):这一类则是最终控制整个测试场景的方式,鉴于整个环境 往往存在不同种类的sequencer和其对应的sequence,我们需要一个虚拟的sequence 来协调顶页层的测试场景。之所以称这个方式为virtual sequence是因为该序列本身并不会固定挂载于某一种sequencer类型上。而是将其内部不同类型sequence最终挂载到不同的目标Seguencer上面。这也是virtual sequence不同于hierarchical sequence的最大一点。
driver同sequencer之间的TLM通信采取了get模式,即由driver 发起请求,从sequencer一端获得item,再由sequemncer将其传递至driver,为什么不用put模式,是因为get模式更加有效率。sequencer和item只应该在合适的时间点产生需要的数据,而于怎么处理数据,则会由driver来实现
为了方便item的传输,ssequencer和driver拥有自己专门的TLM端口:
uvm_seq_item_pull_port#(type REQ=int, type RSP=REQ)
uvm_seq_item_pul_export#(type REQ=int, type RSP=REQ)
uvm_seq_item_pulL_imp #(type REQ=int, type RSPQ, type imp=int)
get模式,由于driver是请求端,所以在driver端例化了两种端口:
uvm_seq_item_pull_port#(REQ,PS)seq_item_port
uvm_analysis_port #(RSP)rsp_port
对应的sequencer一端作为响应端,相同的也对应了两种的端口:
uvm_seq_item_pull_imp #(REQ,RS, this_type) seq_item _export
uvm_analysis_export #(RSP) rsp_export
一般情况下,用户可以通过匹配第一对TLM端口完成item完整的传送:driver::seq_item_port 和sequencer::seq_item_export。顶层中连接方法:driver::seq_item_port.connect(sequencer::seq_item_export),主要实现的腮红能是driver与sequencer的request的获取和response的返回
端口支持的方法如下:
由于uvm_sequencer与uvm_driver实际上都是参数化的类,用户在自定义sequencer或者driver的时候, 它们可以使用缺省类型type
REQ=uvm_sequence_item, 以及RSP与REQ类型保持一致。
uvm_sequencer#(type REQ=uvm_sequence_item, RSP=REQ)
uvm_driver#(type REQ=uvm_sequence_item, RSP=REQ)
这有一个潜在的类型转换要求, 即driver得到REQ对象在进行下一步处理时, 需要进行动态的类型转换, 将REQ转换为uvm_sequence_item的子类型才可以从中获取有效的成员数据。
另外一种可行的方式是在自定义sequencer和driver时就标明了其传递的具体item类型, 这样就不用再进行额外的类型转换了。
这样做的好处就是便于统一管理,方便item对象的拷贝、修改和操作。
driver消化完当前的request后,可以通过 item_done(input RSP rsp_arg=null)方法来告知sequence 此次传输已经结束,参数中的RSP可以选择填入,返回相应的状态值;
driver也可以通过put_response()或者put()方法来单独发送response 。此外发送response还可以通过成对的uvm_driver::rsp_port和uvm_driver::rsp_export端口来完成,方法为um_driver::rsp_port::write(RSP)。
在定义sequencer时, 默认了REQ类型为uvm_sequence_item类型, 这与稍后定义driver时采取默认REO类型保持一致。
flat_seq作为动态创建的数据生成载体它的主任务flat_seq::body(做了如下的几件事情:
1:通过方法create_item() 创建request item对象。
2:调用start_item() 准备发送item。
3:在完成发送item之前对item进行随机处理。
4:调用finish_item() 完成item发送。
5:有必要的情况下可以从driver那里获取response item。
高层环境中还应该进行连接。
sequencer作为sequence和driver之间的桥梁,可以通过sequencer的仲裁功能来解决多个sequence师徒挂载到sequencer的情况。
图中微米可以看出,流向sequencer的都是sequence item,item产生于create_item(),然后通过start_item(),完成随机化以后在获取sequencer的通过权以后,就会执行finish_item(),接下来sequence中的item将穿过sequence人到达driver一侧。driver提取有效信息之后将驱动到DUT上面,完成驱动之后会通过item_done来告诉sequence已经完成了一次传输,完成了一次握手,可以选择将RSP返回给sequence,sequence 也可以调用get_response来等待从driver中获取返回的数据对象。
在多个sequence同时向sequencer发送item的时候,就需要用ID来进行信息表明了,ID信息在sequence创建item的时候就已经赋值了。