Part 4 · TLM & Analysis · Intermediate
analysis_imp_decl Macro Pattern: Multiple Analysis Inputs in One Component
Using uvm_analysis_imp_decl to define distinct write methods for multiple analysis channels and avoid class/type collisions.
Why analysis_imp_decl exists
A component can have more than one analysis input stream: for example expected and actual channels. If both use plain uvm_analysis_imp, they would map to the same write() signature and become ambiguous.
uvm_analysis_imp_decl lets you generate distinct imp types with unique write method names, so one component can consume several streams cleanly.
[TLM] multi-stream need
[MON] expected_model.ap ──► scoreboard expected stream
[MON] dut_monitor.ap ──► scoreboard actual stream
scoreboard needs two independent ingestion functions:
write_exp(tr)
write_act(tr)`uvm_analysis_imp_decl(_exp)
`uvm_analysis_imp_decl(_act)
class alu_scoreboard extends uvm_component;
`uvm_component_utils(alu_scoreboard)
uvm_analysis_imp_exp #(alu_txn, alu_scoreboard) exp_in;
uvm_analysis_imp_act #(alu_txn, alu_scoreboard) act_in;
function new(string name, uvm_component parent);
super.new(name, parent);
exp_in = new("exp_in", this);
act_in = new("act_in", this);
endfunction
endclass[CHECK] semantic clarity
write_exp(tr): expected/reference path ingestion
write_act(tr): observed DUT path ingestion
explicit naming reduces confusion in debug logs and code reviewUse macro-declared imp variants when one component consumes multiple streams.
Name suffixes by stream meaning (_exp, _act, _req, _rsp, etc.).
Distinct write methods improve readability and triage speed.
Complete pattern with two write methods
After declaring imp suffixes, implement corresponding methods in the component. Each method can queue and annotate transactions independently before a matcher thread compares them.
`uvm_analysis_imp_decl(_exp)
`uvm_analysis_imp_decl(_act)
class alu_scoreboard extends uvm_component;
`uvm_component_utils(alu_scoreboard)
uvm_analysis_imp_exp #(alu_txn, alu_scoreboard) exp_in;
uvm_analysis_imp_act #(alu_txn, alu_scoreboard) act_in;
alu_txn exp_q[$];
alu_txn act_q[$];
function void write_exp(alu_txn t);
exp_q.push_back(t.clone());
endfunction
function void write_act(alu_txn t);
act_q.push_back(t.clone());
endfunction
task run_phase(uvm_phase phase);
forever begin
wait (exp_q.size() > 0 && act_q.size() > 0);
compare_pair(exp_q.pop_front(), act_q.pop_front());
end
endtask
endclass[MON][CHECK] pairwise compare flow
expected stream -> exp_q
actual stream -> act_q
matcher thread:
wait both non-empty
pop pair
compare fields
report mismatch/pass[TLM] macro naming convention examples
`uvm_analysis_imp_decl(_req)
`uvm_analysis_imp_decl(_rsp)
`uvm_analysis_imp_decl(_pred)
`uvm_analysis_imp_decl(_obs)
keep suffixes semantic and stable across environmentImplement one write_<suffix>() per analysis_imp_decl suffix.
Queue per-stream independently; compare in a controlled thread.
Use consistent suffix naming conventions project-wide.
Wiring two monitor streams into one scoreboard
The connect_phase should map each producer to the correct input imp. Explicit stream names reduce accidental channel swaps.
class alu_env extends uvm_env;
`uvm_component_utils(alu_env)
ref_model rm;
alu_agent dut_agt;
alu_scoreboard sb;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
rm = ref_model::type_id::create("rm", this);
dut_agt = alu_agent::type_id::create("dut_agt", this);
sb = alu_scoreboard::type_id::create("sb", this);
endfunction
function void connect_phase(uvm_phase phase);
rm.ap.connect(sb.exp_in);
dut_agt.mon.ap.connect(sb.act_in);
endfunction
endclass[MON][TLM][CHECK] dual-source wiring
[MON] ref_model.ap -> sb.exp_in -> write_exp()
[MON] dut_mon.ap -> sb.act_in -> write_act()
scoreboard receives aligned semantic streams[CHECK] anti-swap tip
use connection logs:
"connect ref_model.ap -> sb.exp_in"
"connect dut_mon.ap -> sb.act_in"
channel swap bugs can look like total functional failureWire semantic sources explicitly to matching imp endpoints.
Log connections to catch swapped channels early.
Separate expected vs actual ingestion paths for cleaner diagnostics.
Troubleshooting analysis_imp_decl usage
Most failures are naming/contract mismatches: missing macro declaration, typo in write method name, or wrong imp type usage in declarations.
[CHECK] common compile/runtime issues
issue: method write_exp not found
cause: macro suffix _exp declared but write_exp missing/misspelled
issue: incompatible connection type
cause: monitor txn type differs from imp type parameter
issue: no transactions in one queue
cause: missing connect or wrong stream endpointfunction void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
`uvm_info("SB_CONN",
$sformatf("exp_in=%s act_in=%s", exp_in.get_name(), act_in.get_name()),
UVM_LOW)
endfunctionKey takeaways
analysis_imp_decl is the standard pattern for multiple analysis write channels in one component.
Suffix naming defines distinct methods and should reflect stream intent.
Dual-stream scoreboards become clearer and safer with explicit write_exp/write_act paths.
Most issues are naming/type/connect mismatches and are easy to catch with small logs.
Common pitfalls
Declaring suffixes but forgetting to implement matching write_<suffix>() methods.
Using vague suffixes that hide stream meaning in logs and reviews.
Swapping expected and actual stream connections in connect_phase.
Mixing transaction types across streams without adapters/converters.