在UVM框架下,测试激励从产生到最终作用于DUT接口的这一过程,遵守Sequence-Sequencer-Driver这一基本结构。在这一基本结构之上,可能会根据实际应用扩展出来multi sequence、virtual sequence、virtual sequencer等更加复杂的激励传递结构。
尽管如此,driver和sequencer依然遵循着基本的握手机制(handshake),以保证测试激励能够按照预期作用于DUT上。而握手机制的实现,实际上就是对Sequence API的合理使用。
01 Sequencer与Driver的连接
Sequencer和Driver的连接通过TLM端口来完成。
UVM框架下,driver使用uvm_driver参数化类来实现,sequencer使用uvm_sequencer参数化类来实现。uvm_driver和uvm_sequencer的类参数都是用来指定请求和响应sequence_item的具体类型,在默认情况下,请求和响应sequence_item的类型为uvm_sequence_item。
uvm_driver是uvm_component的子类,其主要的扩展就有成员变量TLM端口seq_item_port。而uvm_sequencer也同样有一个重要的成员变量TLM端口,叫seq_item_export。我们可以在UVM平台的build_phase中,通过connect函数将两者端口连接在一起,就完成了sequencer和driver的连接。
在Driver中,我们可以通过seq_item_port端口来调用很多跟Sequencer交互的API,而这些API的具体实现,都是在Sequencer中实现。这也符合TLM的基本用法。
除此之外,Sequencer中拥有两个FIFO:请求FIFO m_req_fifo和响应FIFO sqr_rsp_analysis_fifo,分别用来存放Sequence下发给Driver的sequence_item和Driver返回给Sequence的sequence_item。
02 使用模型1
虽然Sequencer提供了很多API供我们使用,但在实际使用中,我们依然推荐使用本文将介绍的两种交互方法来完成Sequencer和Driver的握手机制,有时候也称为使用模型。
第一种使用模型是使用get_next_item()和item_done()组合。下面附图是该使用模式的示意图。
图中Sequence产生sequence_item的方式采用最基本的start_item()和finish_item()。
另外,这里按照抽象层级将对接口信号的驱动函数单独放在BFM(Bus Functional Model)中实现,虽然在有些设计中,BFM提供的驱动函数会和Driver的代码写在一起。
按照这种使用模式,Driver在get_next_item()并完成驱动之后,一定要使用item_done()来完成握手,否则Sequence在调用finish_item()之后就会一直被卡在那里。
03 使用模型2
第二种使用模型是使用get()和put()组合。下面附图是该使用模式的示意图。
相比于第一种模式,该模式会复杂一丢丢。好处是,如果没有响应返回,图中的下半部分是可以裁掉的,也就是sequence中不要调用get_response()去等待响应item,driver中也不需要set_id_info()和put()返回响应item。坏处是,如果裁掉响应部分的握手,sequence也就不知道driver什么时候完成对上一个sequence_item的处理。
图中set_id_info()方法用于为响应item设置ID,目的是在多sequence的情况下,让sequencer知道当前返回的sequence_item应该返回给哪个sequence。简单点说,设置id是为了辅助路由。
实际上,在sequence下发item调用start_item()时,Sequencer就会为其分配id信息,所以在返回响应item的时候,我们只要将请求item的id信息通过set_id_info()拷贝给响应item就可以了。这一部分的具体实现在UVM源码uvm_sequence_base.svh文件中。
关于示例代码,我的理解是,搞懂了这些机制之后,翻开UVM类库自带的示例代码(UVM-1.2/examples),读一读代码就可以了,有必要的话再自己简单搭个环境测试一下。
原作者:JKZHAN