一、概述
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 #(RSP) rsp_port;
而sequencer
一侧则为请求的响应端,在sequencer
一侧例化了对应的两种端口:
uvm_seq_item_pull_imp #(REQ, RSP, 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
返回。
这一种类型的TLM端口支持如下方法:
//采取blocking的方式等待从sequence获取下一个item
task get_next_item(output REQ req_arg)
//采取blocking的方式等待从sequence获取下一个item,如果立即返回的结果req_arg为null,则表示sequence还没有准备好
task try_next_item(output REQ req_arg)
//用来通知sequence当前的sequence item已经消化完毕,可以选择性地传递RSP参数,返回状态值。
function void item_done(input RSP rsp_arg=null)
//等待当前的sequence直到产生下一个有效的item
task wait_for_sequences()
//如果当前的sequence准备好而且可以获取下一个有效item,则返回1,否则返回0
function bit has_do_available()
//采取nonblocking方式发送response,如果成功返回1,否则返回0
function void put_response(input RSP rsp_arg)
//采用get方式获取item
task get(output REQ req_arg)
//采用peek方式获取item
task peek(output REQ req_arg)
//采取blocking方式将response发送回sequence
task put(input RSP rsp_arg)
关于REQ
和RSP
类型的一致性,由于uvm_sequencer
与uvm_driver
实际上都是参数化的类:
uvm_sequencer #(type REQ=uvm_sequence_item, RSP=REQ)
//这有一个潜在的类型转换要求,即driver得到REQ对象在进行下一步处理时,
//需要进行动态的类型转换,将REQ转换为uvm_sequence_item的子类型才可以从中获取有效的成员数据。
uvm_driver #(type REQ=uvm_sequence_item, RSP=REQ)
通常情况下RSP
类型与REQ
类型保持一致,这么做的好处是为了便于统一处理,方便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
端口来完成,方法为uvm_driver::rsp_port::write(RSP)
。
三、事务传输示例
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_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
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", rsp.sprint()), UVM_LOW)
void'($cast(rsp, req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
rsp.data += 100;
seq_item_port_done(rsp);
`uvm_info("DRV", $sformatf("sent a item \n %s", rsp.sprint()), 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
输出结果:
四、事务传输过程分析
在定义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
。
在定义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端口连接,比如在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
的挂载。
五、通信时序
- 无论是
sequence
还是driver
,它们通话的对象都是sequencer
。当多个sequence
试图挂载到同一个sequencer
上时,涉及sequencer
的仲裁功能。 - 对于
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
一侧获取返回的数据对象。
---------------------
作者:煎丶包
来源:CSDN
原文:https://blog.csdn.net/qq_39794062/article/details/114267840
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件