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.

diagram
[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)
systemverilog
`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
diagram
[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 review
  • Use 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.

systemverilog
`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
diagram
[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
diagram
[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 environment
  • Implement 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.

systemverilog
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
diagram
[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
diagram
[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 failure
  • Wire 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.

diagram
[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 endpoint
systemverilog
function 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)
endfunction

Key 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.