在公司写代码的时候发现前辈有一段这样的代码:
cpp
....//其他transaction
`uvm_create(trans);
....
....
`uvm_send(trans);
tmp_id = trans.get_transaction_id();
get_response(rsp,tmp_id);
如果前面有其他transaction,这段代码里的get_response不带id的话,就会错误地get到前面transaction的response,有点好奇原理,就去看了看源码。
先从driver入手,如果要返回response的话,要先new一个rsp,然后set_id_info,再put_response:
cpp
//参考张强白皮书
seq_item_port.get_next_item(req);
drive_one_pkt(req);
rsp = new("rsp");
rsp.set_id_info(req);
seq_item_port.put_response(rsp);
seq_item_port.item_done();
先看set_id_info这个函数,在uvm_sequence_item.svh里面,作用就是将req的两个id set到rsp里,我们主要关注transaction_id,继续往后走。
cpp
function void set_id_info(uvm_sequence_item item);
if (item == null) begin
uvm_report_fatal(get_full_name(), "set_id_info called with null parameter", UVM_NONE);
end
this.set_transaction_id(item.get_transaction_id());
this.set_sequence_id(item.get_sequence_id());
endfunction
get_transaction_id在uvm_transaction.svh里面,返回了m_transaction_id,这个id是uvm_transaction里面的一个local integer变量,默认值为-1。这个id的更改在其他地方,稍后再讲解。
cpp
function void uvm_transaction::set_transaction_id(integer id);
m_transaction_id = id;
endfunction
function integer uvm_transaction::get_transaction_id();
return (m_transaction_id);
endfunction
然后看put_response函数,在uvm_sequence.svh里,调用了put_base_response函数。
cpp
virtual function void put_response(uvm_sequence_item response_item);
RSP response;
if (!$cast(response, response_item)) begin
uvm_report_fatal("PUTRSP", "Failure to cast response in put_response", UVM_NONE);
end
put_base_response(response_item);
endfunction
put_base_response在uvm_sequence_base.svh里,作用是将response压入队列中,队列默认长度为8,即response_queue_depth=8,当response在队列中的数量为8还继续压入队列后就会报错,一般发生在没有取response的情况下。
cpp
virtual function void put_base_response(input uvm_sequence_item response);
if ((response_queue_depth == -1) ||
(response_queue.size() < response_queue_depth)) begin
response_queue.push_back(response);
return;
end
if (response_queue_error_report_disabled == 0) begin
uvm_report_error(get_full_name(), "Response queue overflow, response was dropped", UVM_NONE);
end
endfunction
接下来到sequence端,我们看看transaction的id和response是怎么get的。transaction_id,在uvm_transaction.svh里,就是返回m_transaction_id。get_response在uvm_sequence.svh里,调用了get_base_response,这里的参数列表里就有transaction_id了。
cpp
virtual task get_response(output RSP response, input int transaction_id = -1);
uvm_sequence_item rsp;
get_base_response( rsp, transaction_id);
$cast(response,rsp);
endtask
get_base_response在uvm_sequence_base.svh里,可以看到在没有指定transaction_id(id为默认值)的时候,返回的response是从队列直接pop的,指定了以后就遍历队列找id对应的response,这里就是最前面代码的机制。
cpp
virtual task get_base_response(output uvm_sequence_item response, input int transaction_id = -1);
int queue_size, i;
if (response_queue.size() == 0)
wait (response_queue.size() != 0);
if (transaction_id == -1) begin
response = response_queue.pop_front();
return;
end
forever begin
queue_size = response_queue.size();
for (i = 0; i < queue_size; i++) begin
if (response_queue[i].get_transaction_id() == transaction_id)
begin
$cast(response,response_queue[i]);
response_queue.delete(i);
return;
end
end
wait (response_queue.size() != queue_size);
end
endtask
但是到目前为止,response阶段没有看到id是怎么来的,我们从transaction的create和send阶段找一下。这些宏在uvm_sequence_defines.svh里,可以看到create阶段不涉及id的操作,send里面调了函数,可能会有。因为我们send的是transaction,是uvm_sequence_item类型,uvm_sequence_base是其子类,所以在uvm_send_pri里case不会成功,首先进入start_item里。
cpp
`define uvm_create(SEQ_OR_ITEM) \
`uvm_create_on(SEQ_OR_ITEM, m_sequencer)
`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
begin \
uvm_object_wrapper w_; \
w_ = SEQ_OR_ITEM.get_type(); \
$cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
end
`define uvm_send(SEQ_OR_ITEM) \
`uvm_send_pri(SEQ_OR_ITEM, -1)
`define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
begin \
uvm_sequence_base __seq; \
if (!$cast(__seq,SEQ_OR_ITEM)) begin \
start_item(SEQ_OR_ITEM, PRIORITY);\
finish_item(SEQ_OR_ITEM, PRIORITY);\
end \
else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
end
start_item在uvm_sequence_base.svh里,貌似也不涉及id的操作,再看finish_item,也在vm_sequence_base.svh里。finish_item调用了sequencer的send_request函数,进去看看。
cpp
virtual task start_item (uvm_sequence_item item,
int set_priority = -1,
uvm_sequencer_base sequencer=null);
uvm_sequence_base seq;
if(item == null) begin
uvm_report_fatal("NULLITM",
{"attempting to start a null item from sequence '",
get_full_name(), "'"}, UVM_NONE);
return;
end
if($cast(seq, item)) begin
uvm_report_fatal("SEQNOTITM",
{"attempting to start a sequence using start_item() from sequence '",
get_full_name(), "'. Use seq.start() instead."}, UVM_NONE);
return;
end
if (sequencer == null)
sequencer = item.get_sequencer();
if(sequencer == null)
sequencer = get_sequencer();
if(sequencer == null) begin
uvm_report_fatal("SEQ",{"neither the item's sequencer nor dedicated sequencer has been supplied to start item in ",get_full_name()},UVM_NONE);
return;
end
item.set_item_context(this, sequencer);
if (set_priority < 0)
set_priority = get_priority();
sequencer.wait_for_grant(this, set_priority);
`ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
void'(sequencer.begin_child_tr(item, m_tr_handle, item.get_root_sequence_name()));
`endif
pre_do(1);
endtask
virtual task finish_item (uvm_sequence_item item,
int set_priority = -1);
uvm_sequencer_base sequencer;
sequencer = item.get_sequencer();
if (sequencer == null) begin
uvm_report_fatal("STRITM", "sequence_item has null sequencer", UVM_NONE);
end
mid_do(item);
sequencer.send_request(this, item);
sequencer.wait_for_item_done(this, -1);
`ifndef UVM_DISABLE_AUTO_ITEM_RECORDING
sequencer.end_tr(item);
`endif
post_do(item);
endtask
send_request在uvm_sequencer_param_base里,可以看到req的transaction_id就是在这里设置的,值为m_next_transaction_id,且每次设置完就自加1,这个值在uvm_sequence_base.svh里,初值为1。
cpp
function void uvm_sequencer_param_base::send_request(uvm_sequence_base sequence_ptr,
uvm_sequence_item t,
bit rerandomize = 0);
REQ param_t;
if (sequence_ptr == null) begin
uvm_report_fatal("SNDREQ", "Send request sequence_ptr is null", UVM_NONE);
end
if (sequence_ptr.m_wait_for_grant_semaphore < 1) begin
uvm_report_fatal("SNDREQ", "Send request called without wait_for_grant", UVM_NONE);
end
sequence_ptr.m_wait_for_grant_semaphore--;
if ($cast(param_t, t)) begin
if (rerandomize == 1) begin
if (!param_t.randomize()) begin
uvm_report_warning("SQRSNDREQ", "Failed to rerandomize sequence item in send_request");
end
end
if (param_t.get_transaction_id() == -1) begin
param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);
end
m_last_req_push_front(param_t);
end else begin
uvm_report_fatal(get_name(),$sformatf("send_request failed to cast sequence item"), UVM_NONE);
end
param_t.set_sequence_id(sequence_ptr.m_get_sqr_sequence_id(m_sequencer_id, 1));
t.set_sequencer(this);
if (m_req_fifo.try_put(param_t) != 1) begin
uvm_report_fatal(get_full_name(), "Concurrent calls to get_next_item() not supported. Consider using a semaphore to ensure that concurrent processes take turns in the driver", UVM_NONE);
end
m_num_reqs_sent++;
// Grant any locks as soon as possible
grant_queued_locks();
endfunction
这下搞清楚了,transaction_id初值为1,且一个sequence里面每个transaction_id的值均不相同,每发一个req,id都会加一,通过这个id可以来区分不同的transaction!