UVM Heartbeat - Detecting Hung Components
In complex UVM testbenches, components can become unresponsive due to deadlocks, infinite loops, or protocol violations. Without proper monitoring, these issues may cause simulations to hang indefinitely. The uvm_heartbeat class provides a watchdog mechanism to detect when components stop making progress.
What is uvm_heartbeat?
uvm_heartbeat monitors registered components for activity within specified time windows. If a component fails to signal activity before a heartbeat event triggers, UVM raises a fatal error - catching hangs early rather than waiting for simulation timeouts.
Key Concepts
- Heartbeat Event: A recurring trigger that checks if monitored components are active
- Activity Signal: Components raise/drop objections on a shared objection object to indicate they're alive
- Heartbeat Modes: Control whether all, any, or specific components must show activity
Heartbeat Architecture
The heartbeat mechanism uses three key elements:
- uvm_heartbeat: The monitor that tracks component activity
- uvm_callbacks_objection: Components raise/drop objections to signal they're active
- uvm_event: Triggers the heartbeat check at regular intervals
sequenceDiagram
participant T as Test
participant HB as uvm_heartbeat
participant C as Component
participant O as Objection
T->>HB: add(component)
T->>HB: start(heartbeat_event)
loop Every heartbeat window
C->>O: raise_objection()
Note over C: Do work...
C->>O: drop_objection()
T->>HB: trigger heartbeat_event
HB->>O: Check activity
alt Activity detected
HB-->>T: Continue
else No activity
HB-->>T: UVM_FATAL
end
end
Implementation Example
Let's walk through a complete example demonstrating heartbeat monitoring.
Test Class - Setting Up the Heartbeat
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_component component;
uvm_heartbeat heartbeat;
uvm_event#(uvm_object) heartbeat_e;
uvm_callbacks_objection hb_objection;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
component = my_component::type_id::create("component", this);
// Create objection for heartbeat signaling
hb_objection = new("hb_objection");
heartbeat = new("heartbeat", this, hb_objection);
// Share objection with monitored components
uvm_config_db#(uvm_objection)::set(this, "*", "hb_objection", hb_objection);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
heartbeat_e = new("heartbeat_e");
// Register components to monitor
heartbeat.add(component);
// Set mode: UVM_ANY_ACTIVE, UVM_ALL_ACTIVE, or UVM_ONE_ACTIVE
void'(heartbeat.set_mode(UVM_ANY_ACTIVE));
// Start heartbeat monitoring
heartbeat.start(heartbeat_e);
// Trigger heartbeat checks at regular intervals
repeat(10) begin
#120 heartbeat_e.trigger(this);
`uvm_info("TEST/HB", "Trigger HB Event", UVM_NONE)
end
heartbeat.stop();
heartbeat.remove(component);
phase.drop_objection(this);
endtask
endclass
Component Class - Signaling Activity
Each monitored component must periodically raise and drop the heartbeat objection to indicate it's still processing:
class my_component extends uvm_component;
`uvm_component_utils(my_component)
// Objection to signal heartbeat activity
uvm_objection hb_objection;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void connect_phase(uvm_phase phase);
// Get shared heartbeat objection
if(!uvm_config_db#(uvm_objection)::get(this, "", "hb_objection", hb_objection))
`uvm_fatal("NOHBOBJ", "Cannot find Heartbeat Objection setting")
endfunction
task run_phase(uvm_phase phase);
int count;
// Initial delay before starting work
#50;
forever begin
count++;
// Signal: "I'm alive and working"
hb_objection.raise_objection(this);
`uvm_info("COMP", "Heartbeat Objection Raised", UVM_NONE)
// Simulate doing work
#100;
// Signal: "Done with this cycle"
hb_objection.drop_objection(this);
`uvm_info("COMP", "Heartbeat Objection Dropped", UVM_NONE)
#20;
end
endtask
endclass
Heartbeat Modes
The set_mode() method controls how heartbeat checks are evaluated:
| Mode | Description | Use Case |
|---|---|---|
UVM_ALL_ACTIVE | All registered components must show activity | Strict monitoring - every component must be alive |
UVM_ANY_ACTIVE | At least one component must show activity | Flexible monitoring - system is alive if any component works |
UVM_ONE_ACTIVE | Exactly one component must show activity | Mutex-style exclusive access patterns |
Simulating a Hung Component
To test the heartbeat mechanism, you can inject a bug that causes a component to miss its heartbeat window:
task run_phase(uvm_phase phase);
int count;
#50;
forever begin
count++;
hb_objection.raise_objection(this);
#100;
// Inject hang on iteration 8
`ifdef BUG
if(count == 8) begin
#100; // Extra delay causes heartbeat timeout
`uvm_info("COMP/BUG", "[8] Extra delay for HB to expire..", UVM_NONE)
end
`endif
hb_objection.drop_objection(this);
#20;
end
endtask
When compiled with +define+BUG, the component takes too long on iteration 8, and the heartbeat mechanism catches this:
UVM_FATAL @ 960: uvm_test_top.heartbeat [HBFAIL] Heartbeat failure for component 'uvm_test_top.component'
Best Practices
Choosing Heartbeat Intervals
- Too short: False positives from legitimate processing delays
- Too long: Slow detection of actual hangs
- Recommendation: Set interval to 2-3x your expected maximum processing time
What to Monitor
- Drivers: Should complete transactions within bounded time
- Monitors: Should see activity on interfaces
- Scoreboards: Should receive and process transactions
- Sequences: Long-running sequences should signal progress
Integration Tips
// In base_test for all tests
class base_test extends uvm_test;
uvm_heartbeat hb;
uvm_callbacks_objection hb_obj;
uvm_event#(uvm_object) hb_event;
// Configurable heartbeat window
int heartbeat_window = 1000;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
hb_obj = new("hb_objection");
hb = new("heartbeat", this, hb_obj);
uvm_config_db#(uvm_objection)::set(this, "*", "hb_objection", hb_obj);
endfunction
task run_phase(uvm_phase phase);
hb_event = new("hb_event");
// Add components in extended tests
configure_heartbeat();
hb.start(hb_event);
fork
trigger_heartbeat();
join_none
endtask
virtual function void configure_heartbeat();
// Override in derived tests to add components
endfunction
task trigger_heartbeat();
forever begin
#(heartbeat_window);
hb_event.trigger(this);
end
endtask
endclass
Key Takeaways
uvm_heartbeatdetects hung components by monitoring objection activity- Components signal "alive" status by raising/dropping objections on a shared objection object
- Heartbeat events trigger checks at configurable intervals
- Three modes control how multi-component activity is evaluated
- Essential for catching deadlocks and infinite loops early
- Integrate into base test class for project-wide monitoring
The heartbeat mechanism is invaluable for long-running verification - it catches hangs within seconds rather than waiting for hour-long simulation timeouts.
Part of the UVM Examples series. See also: UVM Barrier for synchronizing parallel components.
Comments (0)
Leave a Comment