Have you ever wondered what set_id_info
does? If you have, read on.
UVM docs describe set_id_info
as:
function void set_id_info( uvm_sequence_item item )
Copies the sequence_id and transaction_id from the referenced item into the calling item. This routine should always be used by drivers to initialize responses for future compatibility.
Basically, set_id_info
is called as a part of req
/rsp
protocol. driver can have something like the following
seq_item_port.get(req);
rsp = new();
rsp.set_id_info(req);
...
seq_item_port.put(rsp);
Jumping right into src/seq/uvm_sequence_item.svh
, it’s obvious that set_id_info
copies transaction id and sequence id. so, why is this important anyway?
Let’s start by looking at set_transaction_id
and get_transaction_id
.
// Function: set_id_info
//
// Copies the sequence_id and transaction_id from the referenced item into
// the calling item. This routine should always be used by drivers to
// initialize responses for future compatibility.
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
In src/base/uvm_transaction.svh
, set_transaction_id
and get_transaction_id
are setter and getter for m_transaction_id
.
// set_transaction_id
function void uvm_transaction::set_transaction_id(integer id);
m_transaction_id = id;
endfunction
Looking for callers of set_transaction_id
, it’s called from uvm_sequencer_param_base::send_request
if (param_t.get_transaction_id() == -1) begin
param_t.set_transaction_id(sequence_ptr.m_next_transaction_id++);
end
so, m_next_transaction_id
is moving counter for the transaction items in the sequence.
class uvm_sequence_base extends uvm_sequence_item;
protected uvm_sequence_state m_sequence_state;
int m_next_transaction_id = 1;
At this point we know what is transaction id and what sets it.
But who uses it? Let’s trace get_transaction_id
then. I can see two places where transaction id is used.
First place, get_response
can be called from sequence to get the rsp after sending req
`uvm_create(req)
...
...
...
`uvm_send(req)
get_response(rsp);
and get_response
calls get_base_response
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
and get_base_response
gets response from the queue if it can match id.
virtual task get_base_response(output uvm_sequence_item response, input int transaction_id = -1);
int queue_size, i;
....
....
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
Second place, for driver/sequence protocol, driver calls item_done
to unblock sequence (blocked at finish_item).
And item_done
sets m_wait_for_item_transaction_id
function void uvm_sequencer::item_done(RSP item = null);
...
...
m_wait_for_item_transaction_id = t.get_transaction_id();
on sequence side, finish_item
calls wait_for_item_done
virtual task finish_item (uvm_sequence_item item,
int set_priority = -1);
...
...
sequencer.wait_for_item_done(this, -1);
and wait_for_item_done
blocks until the right transaction id comes up.
task uvm_sequencer_base::wait_for_item_done(uvm_sequence_base sequence_ptr,
int transaction_id);
...
...
wait ((m_wait_for_item_sequence_id == sequence_id &&
m_wait_for_item_transaction_id == transaction_id));