driver返回sequence response有三种方式:
driver将数据包下发dut后,先构造一个rsp,再获取req的id信息,最后将rsp反馈给sequence
1. 通过seq_item_port.item_done(rsp)返回response。当存在多个response时,将response作为item_done参数的方式就不适用了,由于一个transaction只能对应一个item_done,所以使用多次item_done(rsp)是会出错的
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
rsp = new("rsp"); //
rsp.set_id_info(req); //
seq_item_port.item_done(rsp);//
end
2. driver也可以通过put_response(rsp)函数或put(rsp)函数,put_response()是一个blocking方法,所以sequence必须有一个对应的get_response(rsp)
3. 使用uvm_driver的内置analysis port, rsp_port.write(rsp);
为了保证request_item和response_item两个对象的独立性,建议使用clone()的方式单独创建response_item。
sequence_item的ID赋值:
sequence_item的ID没有经过域的自动化声明,在克隆时只会复制数据而不会复制id,所以在提供response之前,必须将response transaction与request transaction通过id号相对应,有两种方法:
1. 通过rsp.set_id_info(req)方法,将引用项(req)中的序列号(sequence_id)和事务号(transaction_id)复制到调用项(rsp)中
2. 通过rsp.set_sequence_id( req.get_sequence_id() )方法,获取req中的序列号给rsp打上id号
response机制:
原理是driver将rsp推送给sequencer,而sequencer内部维持一个队列,当有新的response进入时,就推入此队列。但是此队列的大小并不是无限制的,默认情况下,其大小为8,当队列中有8个response时,如果driver再次向此队列推送新的response,UVM就会给出如下错误提示:
UVM_ERROR@1753500000:uvm_test_top.env.i_agt.sqr@@case0_sequence[uvm_test_top.env.i_agt.sqr.case0_sequence] Response queue overflow, response was dropped
因此,如果在driver中每个transaction后都发送一个response,而sequence又没能及时get_response,sequencer中的response队列就存在溢出的风险
另类的response:
1. 使用response handler功能
前面讲述的get_response和put_response是一一对应的。当在sequence中启动get_response时,进程就会阻塞在那里,一直到response_queue中被放入新的记录。如果driver能够马上将response通过put_response的方式传回sequence,那么sequence被阻塞的进程就会得到释放,可以接着发送下一个transaction给driver。但是假如driver需要延时较长的一段时间才能将transaction传回,在此期间,driver希望能够继续从sequence得到新的transaction并驱动它,但是由于sequence被阻塞在了那里,根本不可能发出新的transaction。
发生上述情况的主要原因为sequence中发送transaction与get_response是在同一个进程中执行的,假如将二者分离开来,在不同的进程中运行将会得到不同的结果。在这种情况下需要使用response_handler:
//my_case0.sv
3 class case0_sequence extends uvm_sequence #(my_transaction);
…
10 virtual task pre_body();
11 use_response_handler(1); //打开response的response_handler功能
12 endtask
13
14 virtual function void response_handler(uvm_sequence_item response); //当打开response_handler功能后,用户需要
15 if(!$cast(rsp, response)) //重载虚函数response_handler。此函数的参数是一个uvm_sequence_item类型的指针,需要
16 `uvm_error("seq", "can't cast")//首先将其通过cast转换为my_transactiono类型,之后就可以根据rsp的值来决定后续
17 else begin //sequence的行为。
18 `uvm_info("seq", "get one response", UVM_MEDIUM)
19 rsp.print();
20 end
21 endfunction
22
23 virtual task body();
24 if(starting_phase != null)
25 starting_phase.raise_objection(this);
26 repeat (10) begin
27 `uvm_do(m_trans)
28 end
29 #100;
30 if(starting_phase != null)
31 starting_phase.drop_objection(this);
32 endtask
33
34 `uvm_object_utils(case0_sequence)
35 endclass
由于response handler功能默认是关闭的,所以要使用response_handler,首先需要调用use_response_handler函数,打开sequence的response handler功能。当打开response handler功能后,用户需要重载虚函数response_handler。此函数的参数是一个uvm_sequence_item类型的指针,需要首先将其通过cast转换变成my_transaction类型,之后就可以根据rsp的值来决定后续sequence的行为
要注意一旦采用response_handler机制后,在当前sequence里一定不能用get_response,它会get不到response,一直block住。如果transaction_id不为-1,get_response会在response_queue里检索,直到找到1个匹配对应transaction_id的response为止
2. 使用原transaction
无论是put/get_response或者response_handler,都是新建了一个transaction,并将其返回给sequence。事实上,当一个uvm_do语句执行完毕后,其第一个参数并不是一个空指针,而是指向刚刚被送给driver的transaction。利用这一点,可以实现一种另类的response:
//my_driver.sv
22 task my_driver::main_phase(uvm_phase phase);
…
27 while(1) begin
28 seq_item_port.get_next_item(req);
29 drive_one_pkt(req);
30 req.frm_drv = "this is information from driver"; //
31 seq_item_port.item_done();
32 end
33 endtask
driver中向req中的成员变量赋值,而sequence则检测这个值:
//my_case0.sv
3 class case0_sequence extends uvm_sequence #(my_transaction);
…
10 virtual task body();
…
13 repeat (10) begin
14 `uvm_do(m_trans)
15 `uvm_info("seq", $sformatf("get information from driver: %0s", m_trans.frm_drv), UVM_MEDIUM) //
16 end
…
20 endtask
21
22 `uvm_object_utils(case0_sequence)
23 endclass
其他
- 关于本文,您有什么想法均可在评论区留言交流。
- 自身能力不足,如有错误还请多多指出!