Hello World Link to heading
The simplest UVM testbench starts with run_test
.
initial begin
run_test("test01");
end
run_test
is defined on src/base/uvm_globals.svh
where it constructs uvm_root
and run run_test from top.run_test()
task run_test (string test_name="");
uvm_root top;
top = uvm_root::get();
top.run_test(test_name);
endtask
Creating uvm_root Link to heading
uvm_root is created by calling uvm_root::get
static method. Beside creating the singelton uvm_root
, it creates the uvm_domain
which is also a singelton.
// src/base/uvm_root.svh
function uvm_root uvm_root::get();
if (m_inst == null) begin
m_inst = new();
void'(uvm_domain::get_common_domain());
m_inst.m_domain = uvm_domain::get_uvm_domain();
end
return m_inst;
endfunction
get_common_domain
initializes m_common_domain
and register phases (build, connect, etc.).
static function uvm_domain get_common_domain();
uvm_domain domain;
uvm_phase schedule;
if (m_common_domain != null)
return m_common_domain;
domain = new("common");
domain.add(uvm_build_phase::get());
domain.add(uvm_connect_phase::get());
domain.add(uvm_end_of_elaboration_phase::get());
domain.add(uvm_start_of_simulation_phase::get());
domain.add(uvm_run_phase::get());
domain.add(uvm_extract_phase::get());
domain.add(uvm_check_phase::get());
domain.add(uvm_report_phase::get());
domain.add(uvm_final_phase::get());
m_domains["common"] = domain;
// for backward compatibility, make common phases visible;
// same as uvm_<name>_phase::get().
build_ph = domain.find(uvm_build_phase::get());
connect_ph = domain.find(uvm_connect_phase::get());
end_of_elaboration_ph = domain.find(uvm_end_of_elaboration_phase::get());
start_of_simulation_ph = domain.find(uvm_start_of_simulation_phase::get());
run_ph = domain.find(uvm_run_phase::get());
extract_ph = domain.find(uvm_extract_phase::get());
check_ph = domain.find(uvm_check_phase::get());
report_ph = domain.find(uvm_report_phase::get());
m_common_domain = domain;
domain = get_uvm_domain();
m_common_domain.add(domain,
.with_phase(m_common_domain.find(uvm_run_phase::get())));
return m_common_domain;
endfunction
In this snippet from get_common_domain
, there is get_uvm_domain
that fills the run phases (reset, main, etc).
domain = get_uvm_domain();
m_common_domain.add(domain,
.with_phase(m_common_domain.find(uvm_run_phase::get())));
I traced get_uvm_domain
, but i will focus on common uvm phases. The phase are defined in src/base/uvm_common_phases.svh
.
The key task is exec_task
which calls comp.run_phase(phase)
.
// src/base/uvm_common_phases.svh
class uvm_run_phase extends uvm_task_phase;
virtual task exec_task(uvm_component comp, uvm_phase phase);
comp.run_phase(phase);
endtask
...
endclass
Running the test Link to heading
top.run_test
is defined in src/base/uvm_root.svh
task uvm_root::run_test(string test_name="");
uvm_root:run_test
first parses +UVM_TESTNAME and eventually creates uvm_test_top
object
$cast(uvm_test_top, factory.create_component_by_name(test_name,
"", "uvm_test_top", null));
and then calls uvm_phase::m_run_phases
, well to run the phases.
// phase runner, isolated from calling process
fork begin
// spawn the phase runner task
phase_runner_proc = process::self();
uvm_phase::m_run_phases();
end
join_none
#0; // let the phase runner start
wait (m_phase_all_done == 1);
at this point uvm_phase::m_run_phases
calls get_common_domain
to return the m_common_domain
that was already constructed in the top (uvm_root
).
task uvm_phase::m_run_phases();
uvm_root top = uvm_root::get();
// initiate by starting first phase in common domain
begin
uvm_phase ph = uvm_domain::get_common_domain();
void'(m_phase_hopper.try_put(ph));
end
forever begin
uvm_phase phase;
m_phase_hopper.get(phase);
fork
begin
phase.execute_phase();
end
join_none
#0; // let the process start running
end
endtask
jumping to execute_phase
which is a beast and worthy of another post on it own.
but the key point here is calls to task_phase.traverse
task_phase.traverse(top,this,UVM_PHASE_EXECUTING);
and in src/base/uvm_task_phase.svh
, traverse is called. traverse
calls m_traverse
recursively
virtual function void traverse(uvm_component comp,
uvm_phase phase,
uvm_phase_state state);
phase.m_num_procs_not_yet_returned = 0;
m_traverse(comp, phase, state);
endfunction
function void m_traverse(uvm_component comp,
uvm_phase phase,
uvm_phase_state st
ph.execute(comp, phase);
...
endfunction
in execute
, exec_task
is called with component and phase. which links us to other side of the thread.
virtual function void execute(uvm_component comp,
uvm_phase phase);
fork
begin
uvm_sequencer_base seqr;
process proc;
// reseed this process for random stability
proc = process::self();
proc.srandom(uvm_create_random_seed(phase.get_type_name(), comp.get_full_name()));
phase.m_num_procs_not_yet_returned++;
if ($cast(seqr,comp))
seqr.start_phase_sequence(phase);
exec_task(comp,phase);
phase.m_num_procs_not_yet_returned--;
end
join_none
endfunction
endclass