> 文章列表 > 一、UVM Sequencer 和Driver

一、UVM Sequencer 和Driver

一、UVM Sequencer 和Driver

需要注意的几个重点:

1、端口和方法:

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

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

2、区分几种常见的sequence定义方式:---flat sequence

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有没有返回;得到一个父类句柄,所以还需要一个类型转换;

3、在定义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

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


4、在高层环境中,应该在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一样看不见摸不着。


通信时序

  1.  无论是sequence还是driver,他们的通话对象都是sequence人。当多个sequence试图要挂载到同一个sequencer上时,涉及sequencer的仲裁功能。
  2. 抽取去这三个类的主要方法,利用时间箭头演示出完整的TLM通信过程。
  3. 对于sequence而言,无论是flat sequence还是hierarchical sequence,进一步切分的话,流向sequencer的都是sequence item,所以就每个item的“成长周期”来看,它起始于create_item(),继而通过start_item()尝试从sequencer获取可以通过的权限。
  4. 而driver一侧将一直处于“吃不饱”的状态,如果它没有了item可以使用,将调用get_next_item()来尝试从sequencer一侧获取item。
  5. 在sequencer将通过权限交给某一个底层的sequence前,目标sequence中的item应该完成随机化,继而在获取sequencer的通过权限后,执行finish_item()。
  6. 接下来sequence中的item将穿过sequencer到达driver一侧,这个重要节点标志着sequencer第一次充当通信桥梁的角色已经完成。
  7. driver在得到新的item之后,会提取有效的数据信息,将其驱动到与DUT连接的接口上面。
  8. 在完成驱动后,driver应当通过item_done()来告知sequence已经完成数据传送,而sequence在获取该消息后,则表示driver与sequence双方完成了这一次item的握手传输。
  9. 在这次传递中,driver可以选择将RSP作为状态返回值传递给sequence,而sequence也可以选择调用get_response(RSP)等待从driver一侧获取返回的数据对象