(8)UVM Sequencer和Driver

概述

driver同sequencer之间的TLM通信采取了get模式,即由driver发起请求,从sequencer一端获得item,再由sequencer将其传递至driver。

作为driver,只要它可以从sequencer获取item,它就可以一直工作下去
sequencer和item只应该在合适的时间点产生需要的数据,而至于怎么处理数据,则会由driver来实现

端口和方法

在这里插入图片描述
为了便于item传输,UVM专门定义了匹配的TLM端口供sequencer和driver使用:

  • uvm_seq_item_pull_port #(type REQ=int,type RSP=REQ)
  • uvm_seq_item_pull_export #(type REQ=int,type RSP=REQ)
  • uvm_seq_item_pull_imp #(type REQ=int,type RSP=REQ,type imp=int)
    一般情况下只会使用第一种声明方法

由于driver是请求发起端,所以在driver一侧例化了下面两种端口:

  • uvm_seq_item_pull_port #(REP,RSP)seq_item_port
  • uvm_analysis_port #(RSP)rsp_port
    analysis是一种广播

而sequencer一侧则为请求的响应端,在sequencer一侧例化了对应的两种端囗:
uvm_seq_item_pull_imp #(REQ,RSP,this_type)seq_item_export
uvm_analysis_export #(RSP)rsp_export
export作为一个中间节点(原因在于sequencer是一个中点,自身中间有自己定义的fifo存放requeset)

这一种类型的TLM端口支持如下方法:

  • task get_next_item(output REQ req_arg):采取blocking的方式等待从sequence获取下一个item。
  • task try_next_item(output REQ req_arg):采取nonblocking的方式从sequencer获取item,如果立即返回的结果req_arg为null,则表示sequence还没有准备好。
  • function void item_done(input RSP rsp_arg=null):用来通知sequence当前的sequence item已经消化完毕,可以选择性地传递RSP参数,返回状态值。
  • task wait_for_sequences():等待当前的sequence直到产生下一个有效的item。
  • function bit has_do_available():如果当前的sequence准备好而且可以获取下一个有效的item,则返回1,否则返回0。
  • function void put_response(input RSP rsp_arg):采取nonblocking方式发送response,如果成功返回1,否则返回0。
  • task get(output REQ req_arg):采用get方式获取item。
  • task peek(output REQ req_arg):采用peek方式获取item。
  • task put(input RSP rsp_arg):采取blocking方式将response发送回sequence。
    一般用到的就是get_next_item和item_done,且成对出现;

在这里需要了解关于REQ和RSP类型的一致性,由于uvm_sequencer与uvm_driver实际上都是参数化的类:

  • uvm_sequencer#(type REQ=uvm_sequence_item,RSP=REQ)
  • uvm_driver#(type REQ=uvm_sequence_item,RSP=REQ)
  • 自定义sequencer或者driver的时候,可以使用缺省类型type REQ=uvm_sequence_item,以及RSP与REQ类型保持一致。

有一个潜在的类型转换要求,即driver得到REQ对象在进行下一步处理时,需要进行动态的类型转换,将REQ转换为uvm_sequence_item的子类型才可以从中获取有效的成员数据
另外一种可行的方式是在自定义sequencer和driver时就标明了其传递的具体item类型,这样就不用再进行额外的类型转换了。

通常情况下RSP类型与REQ类型保持一致,这么做便于统一处理,方便item对象的拷贝、修改等操作。

driver消化完当前的request后,可以通过item_done(input RSP rsp_arg=null)方法来告知sequence此次传输已经结束,参数的RSP可以选择填入,返回相应的状态值
也可以通过put_response()或者put()方法来单独发送response。此外发送response还可以通过成对的uvm_driver::rsp_port和uvm_driver::rsp_export端口来完成,方法为uvm_driver::rsp_port::write(RSP)

实例

抓住transaction在哪里?sequence在哪里?之间做了哪些事情?sequencer和driver的关系?几个问题。

flat_seq作为动态创建的数据生成载体,它的主任务flat_seq::body()做了如下的几件事情:

  • 通过方法create_item()创建request item对象。
  • 调用start_item()准备发送item。
  • 在完成发送item之前对item进行随机处理。
  • 调用finish_item()完成item发送。
  • 有必要的情况下可以从driver那里获取response item。
class bus_trans extends uvm_sequence_item; 
	rand int data; 
	`uvm_object_utils_begin(bus_trans)
		`uvm_field_int(data,UVM_ALL_ON)
	`uvm_object_utils_end 
	...
endclass 
class flat_seq extends uvm_sequence; 
	`uvm_object_utils(flat_seg)
	...
	task body(); 
		uvm_sequence_item tmp; 
		bus_trans req,rsp;
		tmp=create_item(bus_trans::get_type(),m_sequencer,"req"); 
		void'($cast(req, tmp)); 
		start_item(req); 
		req.randomize with {data==10;}; 
		`uvm_info("SEQ",$sformatf("sent a item \n %s", req.sprint()), UVM_LOW)
		finish_item(req); 
		get_response(tmp); 
		void'($cast(rsp, tmp)); 
		`uvm_info("SEQ",$sformatf("got a item \n %s", rsp.sprint()), UVM_LOW)
	endtask 
endclass

1、create_item可以和new做替换;但他完成了利用工厂创建item;同时告诉接下来这个item要挂载到sequencer上;即便没有挂载,接下来也可以在下面代码默认匹配;所以用new也可以;
2、然后类型转换;create_item返回的是父类的句柄,需要转换成子类的句柄;因为要访问子类里面的成员变量;
3、start_item:表示接下来要敲门了,但sequencer没有放行;因为要等driver的仲裁;Finish_item需要driver返回一个item_done;
4、Get_response看response有没有返回;得到一个父类句柄,所以还需要一个类型转换;
//-----------------------------------------------------------------------------------------------------------------------
在定义sequencer,默认了REQ类型为uvm_sequence_item类型,这与稍后定义driver时采取默认REQ类型保持一致。
在定义driver时,它的主任务driver::run_phase()也应通常做出如下处理:

  • 通过seq_item_pot.get_next item(REQ)从sequencer获取有效的request item。
  • 从request item中获取数据,进而产生数据激励//虽在下面代码中没有体现,但却应有。
  • 对request item进行克隆生成新的对象response item。
  • 修改responseitem中的数据成员,最终通过seq_item_port.item_done(RSP)将response item对象返回给sequence。
class sequencer extends uvm_sequencer; 
	`uvm_component_utils(sequencer)
	...
endclass 
class driver extends uvm_driver; 
	`uvm_component_utils(driver)
	...
	task run_phase(uvm_phase phase); 
		REQ tmp; 
		bus_trans req, rsp; 
		seq_item_port.get_next_item(tmp); 
		void'($cast(req, tmp)); 
		`uvm_info("DRV",$sformatf("got a item \n %s", req.sprint()), UVM_LOW)
		void'($cast(rsp, req.clone())); 
		rsp.set_sequence_id(req.get_sequence_id()); 
		rsp.data+=100; 
		seq_item_port.item_done(rsp);
		`uvm_info("DRV",$sformatf("sent a item \n %s", rsp.sprint()), UVM_LOW)
	endtask 
endclass

1、sequencer只要声明做个注册就可以了;
2、通过seq_item_port得到一个item,需要转换成一个子类的句柄;子类句柄做一个克隆,然后会返回一个object,然后需要做一个转换,转换为一个子类的句柄;
3、set_sequence_id就是在返回的时候打一个标签;看返回到哪个seq;是seq1还是seq2;
对于uvm_sequence::get_response(RSP)和uvm_driver::item_done(RSP)这种成对的操作,是可选的而不是必须的,即用户可以选择uvm_driver不返回response item,同时sequence也无需获取response item。
//-----------------------------------------------------------------------------------------------------------------------
在高层环境中,应该在connect phase中完成driver到sequencer的TLM端口连接,比如下例在env::connect_phase()中通过drv.seq_item_port.connect(sqr.seq_item_export)完成了driver与sequencer之间的连接。
在完成了flat_seq、sequencer、driver和env的定义之后,到了test1层,除了需要考虑挂起objection防止提前退出,便可以利用uvm_sequence类的方法uvm_sequence::start(SEQUENCER)
来实现sequence到sequencer的挂载。
seq.start(e.sqr);实现了seq和对应sqr的挂载。

class env extends uvm_env; 
	sequencer sqr; 
	driver drv;
	`uvm_component_utils(env)
	...
	function void build_phase(uvm_phase phase); 
		sqr=sequencer::type_id::create("sqr", this); 
		drv=driver::type_id::create("drv", this);
	endfunction 
	function void connect_phase(uvm_phase phase); 
		drv.seq_item_port.connect(sqr.seq_item_export); 
	endfunction 
endclass
class test1 extends uvm_test; 
	env e; 
	`uvm_component_utils(test1)
	...
	function void build_phase(uvm_phase phase); 
		e=env::type_id::create("e", this); 
	endfunction 
	task run_phase(uvm_phase phase); 
		flat_seq seq; 
		phase.raise_objection(phase); 
		seq=new(); 
		seq.start(e.sqr); 
		phase.drop_objection(phase); 
	endtask 
endclass

输出结果:

UVM_INFO @ 0:uvm_test_top.e.sqr@@flat_seq [SEQ] sent a item 
...
UVM_INFO @ 0:uvm test top.e.drv [DRV] got a item 
...
UVM_INFO @ 0:uvm test top.e.drv [DRV] sent a item 
...
UVM_INFO @ 0:uvm_test_top.e.sqr@@flat_seq [SEQ] got a item
...

其中seq_item_export的定义、start_item()等都是内部定义的,虽然对应模块已写好,但却像run_phase一样看不见摸不着。

展示了从item定义,到sequence定义,最后到sequencer与driver的连接,即sequencer和driver之间的item传输过程,帮助理解传输过程的起点、各个节点以及终点。

对于理解driver从sequencer获取item,经过时序处理再返回给sequence的握手过程很有帮助。

通信时序

在这里插入图片描述
无论是sequence还是driver,它们通话的对象都是sequencer。当多个sequence试图要挂载到同一个sequencer上时,涉及sequencer的仲裁功能。

抽取去这三个类的主要方法,利用时间箭头演示出完整的TLM通信过程。

对于sequence而言,无论是flat sequence还是hierarchical sequence,进一步切分的话,流向sequencer的都是sequence item,所以就每个item的“成长周期”来看,它起始于create_item(),继而通过start_item()尝试从sequencer获取可以通过的权限
而driver一侧将一直处于“吃不饱”的状态,如果它没有了item可以使用,将调用get_next_item()来尝试从sequencer一侧获取item。

在sequencer将通过权限交给某一个底层的sequence前,目标sequence中的item应该完成随机化,继而在获取sequencer的通过权限后,执行finish_item()

接下来sequence中的item将穿过sequencer到达driver一侧,这个重要节点标志着sequencer第一次充当通信桥梁的角色已经完成。

driver在得到新的item之后,会提取有效的数据信息,将其驱动到与DUT连接的接口上面

在完成驱动后,driver应当通过item_done()来告知sequence已经完成数据传送,而sequence在获取该消息后,则表示driver与sequence双方完成了这一次item的握手传输。

在这次传递中,driver可以选择将RSP作为状态返回值传递给sequence,而sequence也可以选择调用get_response(RSP)等待从driver一侧获取返回的数据对象

握手建议

1、在多个sequence同时向sequencer发送item时,就需要有ID信息表明该item从哪个sequence,ID信息在sequence创建item时就赋值了
2、在到达driver以后,这个ID也可以用来跟踪它的sequence信息,使得运输和使用更加安全,sequencer可以根据ID信息来分发这些response item返回至正确的sequence源头
3、建议在driver中,通过clone方式单独创建response item,保证request item和response item两个对象的独立性。
4、也许有的用户为了“简便”,在使用了request item之后,就直接修改它的数据并作为要返回给sequence的response item。这么做看来似乎节能环保,但实际上殊不知可能埋下隐患,一方面它延长了本来应该丢进垃圾桶的request item寿命,同时也无法再对request item原始生成数据做出有效记录
5、为了统一起见,可以不在定义sequence或者driver时指定sequence item类型,使用默认类型REQ=uvm_sequence_item,但是需要注意在driver一侧的类型转换,例如对get_next_item(REQ)的返回值REQ句柄做出动态类型转换,待得到正确类型之后再进行接下来的操作。
6、有的时候如果要复用一些验证IP,用户需要修改原有的底层sequence item。从处于验证复用的角度,建议通过继承于原有sequence item的方式定义新的item子类,同时在顶层通过factory override的方式用新的item类型替换原有的item类型。

这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字ic攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值