ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(17)UVM sequencer和driver

2021-11-18 09:31:07  阅读:224  来源: 互联网

标签:sequence REQ driver item uvm sequencer UVM


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#(REQ,RSP)seq_item_port
  • uvm_analysis_port_port#(RSP)rsp_port

而sequencer一侧则为请求的响应端,在sequencer一侧例化了对应的两种端口:

  • uvm_seq_item_pull_imp#(REQ,RSP,this_type)seq_item_export
  • uvm_analysis_export #(RSP) rsp_export

sequencer中存放有response的fifo,没有request的fio

  • 通常情况下,用户可以通过匹配的第一对TLM端口完成item的完整传送,即driver::seq_item_port和sequencer::item_port_export
  • 这一对端口在连接时同其他端口连接方式一样,即通过driver::seq_item_port.connect(sequencer::seq_item_export)完成。
  • 这一类端口功能主要用来实现driver与sequencer的request获取和response返回。

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

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

读者在这里需要了解关于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可以选择填入,返回相应的状态值。
  • driver也可以通过put_response()或者put()方法来单独返送response。此外发送response还可以通过成对的uvm_driver::rsp_port和sequencer::rsp_export端口来完成,方法为uvm_driver::rsp_port::write(RSP)。

三、事务传输实例

class bus_trans extends uvm_sequence_item;
	rand bit data;
	`uvm_object_utils_begin
		`uvm_field_int(data,UVM_ALL_ON)
	`uvm_object_utils_end
	...
endclass

class flat_seq extends uvm_sequence;
	`uvm_object_utils(flat_seq)
	...
	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会做两件事情:1.它利用工厂type_id:create这种形式创建了item 2.告诉你创建的这个item挂载到哪里,要被挂载到一个具体的sequencer上面。
  2. 每一个sequence最终都要挂载到一个sequencer上面,sequence上要产生的很多item,它们也要挂载到sequencer上面。
  3. 默认情况下,sequence挂载的sequencer,也就是它产生的item要挂载的sequencer。
  4. m_sequencer是uvm_sequence的成员变量。它从哪里来呢?如果你在顶层test这个层次,如果你已经把sequence通过一个挂载的形式,那么sequence就知道了它的m_sequencer,它就有了一个具体的值,这个具体的值就是它所挂载的sequencer示例的句柄名称
  5. 如果使用new,没有挂载。接下来sequence在用start_item或finish_item的时候,它内部会做一个处理。如果给item没有做任何sequencer的指定,默认会指定当前所在的sequence挂载的sequencer。
    create_item只会返回一个父类的句柄,返回sequence_item这样一个句柄
  6. randomize是一个虚方法,这里有一个细节,如果没有父类句柄到子类句柄的转化,即void’($cast(req,tmp)),仍可以用tmp.randomize();对子类成员变量进行随机化,只不过这里面要对data做限制,所以必须要用void’($cast(req,tmp))。
  7. start_item会立即返回,如果只有一个sequence要挂载到sequencer上面。因为只有一个时,它会立即得到仲裁。
  8. sequencer要放行的条件是什么?要做仲裁的条件是什么?1.driver要做get_next_item,driver已经问我要货车了 2.item要放到我这里,item是怎么放到我这里的呢?sequence要调用一个start_item, start_item是在关卡前等待放行,在放行(仲裁)之前要做randomize
  9. finish_item是真正得在等待仲裁了。真正的finish_item要等到driver给它一个item_done才可以。
  10. get_response要不要做,取决于item_done中rsp有没有返回。它得到的是一个父类句柄,它是一个固定类型。
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.print()),UVM_LOW)
	endtask
endclass

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

seq通过start挂载到e.sqr,一旦挂载,sequence就知道它的m_sequencer了,一旦挂载上去,它的body任务就会自动执行。挂载并不意味着会随机化sequence。

四、事务传输过程分析

在定义sequencer时,默认了REQ类型为uvm_sequence_item类型,这与稍后定义driver时采取默认REQ类型保持一致。

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

  • 通过方法create_item()创建request item对象。
  • 调用start_item()准备发送item。
  • 在完成发送item之前对item进行随机处理。
  • 调用finish_item()完成item发送。
  • 有必要的情况下可以从driver那里获取response item。

start_item是立即返回的,finish_item是阻塞的。

在定义driver时,它的主任务driver::run_phase()也应通常做出如下处理:

  • 通过seq_item_port.get_next_item(REQ)从sequencer获取有效的request item。
  • 从request item中获取数据,进而产生数据激励。
  • 对request item进行克隆生成新的对象response item。
  • 修改response item中的数据成员,最终通过seq_item_port.item_done(RSP)将response item对象返回给sequence。

对于uvm_sequence::get_response(RSP)和uvm_driver::item_done(RSP)这种成对的操作,是可选的而不是必须的,即用户可以选择uvm_driver不返回response item,同时sequence也无需获取response item。

在高层环境中,应该在connect phase中完成driver到sequencer的TLM端口连接。在完成了flat_seq、sequencer、driver和env的定义之后,到了test1层,除了需要考虑挂起objection防止提前退出,便可以利用uvm_sequence类的方法uvm_sequence::start(SEQUENCER)来实现sequence到sequencer的挂载。

五、通信时序

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

  • 对于sequence而言,无论是flat sequence还是hierarchical sequence,进一步切分的话,流向sequencer的都是sequence item,所以就每个item的“成长周期”来看,它起始于create_item(),继而通过start_item()尝试从sequencer获取可以通过的权限。
  • 对于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一侧获取返回的数据对象。

create_item->start_item(randomization) +get_next_item->finish_item(此时sequence中的item穿过sequencer到达driver一侧)->(可选的get_response)->(可选的put_response)->item_done(rsp)(括号内的rsp依然可选)->整个握手结束
当出现item_done标志着整个握手技术,item_done是非阻塞的。

六、握手建议

  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类型。

关注作者

  • 自述
    作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。
  • 经历
    曾获得国家奖学金,“高教社杯”数学建模国家二等奖等
  • 陆续更新:
    1.与UVM验证相关的system verilog后续内容;
    2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
    3.保研与竞赛经历等
  • 微信公众号
    欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。

标签:sequence,REQ,driver,item,uvm,sequencer,UVM
来源: https://blog.csdn.net/qq_42419590/article/details/121353285

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有