UVM Heartbeat & Deadlock Detection

Don't let your simulation hang forever. Use uvm_heartbeat to detect deadlocks and identify which component forgot to drop its objection.

The Problem: Silent Hangs

One of the most frustrating issues in UVM verification is the "Silent Hang." The simulation doesn't crash, no error is reported, but the clock keeps ticking until the global timeout (e.g., $finish at 1ms) kills it.

Why does this happen?

Usually, a component (like a Driver or Scoreboard) raised an objection but never dropped it. Because UVM waits for all objections to drop before ending the phase, the simulation runs forever.

What is uvm_heartbeat?

uvm_heartbeat is a built-in UVM utility that acts like a watchdog timer. It monitors specific components to ensure they are "alive" (sending signals, processing packets, etc.).

  • Monitors Events: Checks if specific events (objections, triggers) happen within a window.
  • Timeouts: If no event occurs for a specified duration, it issues a UVM_FATAL error.
  • Context: Tells you exactly which component failed to respond.

How to Implement (Step-by-Step)

1. Define the Heartbeat Object

In your test or env class, declare a heartbeat handle.

class my_test extends uvm_test;
    uvm_heartbeat hb;
    uvm_component observed_comps[$]; // List of components to watch
    // ...
endclass

2. Configure and Start (in run_phase)

You typically set this up during the run_phase or start_of_simulation_phase.

task run_phase(uvm_phase phase);
    hb = new("my_heartbeat", this, UVM_OBJECTION);
    // Add components to monitor (e.g., the entire agent)
    hb.add(env.agent); 
    // Set the "Heartbeat" mode
    // UVM_SC_ONE: Trigger if ANY component triggers the event
    // UVM_SC_ALL: Trigger only if ALL components trigger
    hb.set_mode(UVM_SC_ONE); 
    // Define the "Pulse": How often we expect activity?
    // If no objection is raised/dropped for 1000ns, declare Deadlock.
    hb.set_heartbeat(phase.get_objection(), 1000ns);
    // Start monitoring!
    // It will now listen for objection activity on the 'phase' objection.
endtask

Advanced: Custom Heartbeat Events

By default, people use Objections as the heartbeat trigger. But you can use ANY uvm_event. This is useful if you want to ensure a monitor is seeing packets, even if objections aren't changing.

// 1. Create a custom event
uvm_event packet_seen_ev = new("packet_seen");
// 2. In Monitor: Trigger it when packet arrives
task run_phase(uvm_phase phase);
    forever begin
        @(vif.cb);
        if(vif.valid) 
            packet_seen_ev.trigger(); // <--- PULSE!
    end
endtask
// 3. In Test: Use this event for heartbeat
hb = new("monitor_watchdog", this, UVM_ANY);
hb.set_heartbeat(packet_seen_ev, 5000ns); // Error if no packet for 5000ns
Pro Tip: Debugging
If you hit a heartbeat fatal error, simply add +UVM_OBJECTION_TRACE to your command line. Combined with the heartbeat error loop, this makes finding the stuck component trivial.