UVM has a builtin transaction recorder as part of uvm_transaciton
. This is deepdive into how it works.
How to use transaction recorder Link to heading
First, The recording should be enabled. In this example, this is part of sequence body where enable_recording
is called with stream name
.
pkt = apb_rw::type_id::create("apb_rw");
pkt.enable_recording("packet_stream");
start_item(pkt);
pkt.randomize();
finish_item(pkt);
Next, run_phase
can call accpet_tr
, begin_tr
and end_tr
to log the transactions in the stream.
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(pkt);
accept_tr(pkt);
begin_tr(pkt);
// driver signals
//...
end_tr(pkt);
seq_item_port.item_done();
end
endtask
The output is file tr_db.log
with streams and transactions.
CREATE_STREAM @0 {NAME:packet_stream T:TVM SCOPE: STREAM:1}
BEGIN @0 {TXH:2 STREAM:1 NAME:apb_rw TIME=0 TYPE="Begin_End, Link" LABEL:"" DESC=""}
SET_ATTR @0 {TXH:2 NAME:accept_time VALUE:0 RADIX:UVM_TIME BITS=64}
END @0 {TXH:2 TIME=0}
FREE @0 {TXH:2}
BEGIN @0 {TXH:3 STREAM:1 NAME:apb_rw TIME=0 TYPE="Begin_No_Parent, Link" LABEL:"" DESC=""}
SET_ATTR @0 {TXH:3 NAME:accept_time VALUE:0 RADIX:UVM_TIME BITS=64}
END @0 {TXH:3 TIME=0}
FREE @0 {TXH:3}
uvm_component infrastructure Link to heading
Transaction recording methods are called in the components. They delegate the calls to uvm_transaction
.
For example, uvm_component::accept_tr
calls tr.accept_tr
function void uvm_component::accept_tr (uvm_transaction tr,
time accept_time=0);
uvm_event e;
tr.accept_tr(accept_time);
do_accept_tr(tr);
e = event_pool.get("accept_tr");
if(e!=null)
e.trigger();
endfunction
And uvm_component::begin_tr
calls tr.begin_tr
or tr.begin_child_tr
depending the type of transaction.
tr_h = 0;
if(has_parent)
link_tr_h = tr.begin_child_tr(begin_time, parent_handle);
else
link_tr_h = tr.begin_tr(begin_time);
uvm_transaction infrastructure Link to heading
In uvm_transaction
, enable_recording
does the following:
- picks up the
uvm_default_recorder
if recorder is not passed. - creates stream in
m_recorder
- sets
record_enable
if (recorder == null)
recorder = uvm_default_recorder;
m_recorder = recorder;
this.stream_handle = m_recorder.create_stream(stream, "TVM", scope);
record_enable = 1;
And for begin_tr
and other function, uvm_transaction delegates uvm_recoder
.
if(!has_parent)
tr_handle = m_recorder.begin_tr("Begin_No_Parent, Link",
stream_handle, get_type_name(),"","",this.begin_time);
else begin
tr_handle = m_recorder.begin_tr("Begin_End, Link",
stream_handle, get_type_name(),"","",this.begin_time);
if(parent_handle)
m_recorder.link_tr(parent_handle, tr_handle, "child");
end
uvm_recorder infrastructure Link to heading
Finally, The last doll of this programmatic “Russian dolls” uvm_recorder
!
uvm_default_recorder
is a singleton defined in uvm_object_globals.svh
uvm_recorder uvm_default_recorder = new();
And uvm_recoder
calls $fdisplay
to print to log file.
virtual function integer begin_tr(string txtype,
integer stream,
string nm,
string label="",
string desc="",
time begin_time=0);
if (open_file()) begin
m_handles[++handle] = 1;
$fdisplay(file,"BEGIN @%0t {TXH:%0d STREAM:%0d NAME:%s TIME=%0t TYPE=\"%0s\" LABEL:\"%0s\" DESC=\"%0s\"}",
$time,handle,stream,nm,begin_time,txtype,label,desc);
return handle;
end
return -1;
endfunction