Tutorials

Learn More

The UVM Heartbeat acts as a watchdog timer that provides a flexible way for the environment to ensure that their descendants are alive. The uvm_heartbeat class is derived from uvm_object and it is associated with a specific objection object.

UVM Heartbeat Usage

The UVM heartbeat can detect simulation hang or lock-up conditions at an early stage rather than the expiry of the global simulation timeout. Thus, it can save the simulation time and terminate it at an early state. The lock-up may happen due to FSM issues, race around conditions between two communicating modules waiting for the response for transmitted packet or packet is not being transmitted, etc.

UVM Heartbeat Working

The uvm_heartbeat monitor activity for an objection object. The UVM Heartbeat detects testbench activity for registered components based on raises or drops objections on that objection object during the heartbeat monitor window. If the heartbeat monitor does not see any activity in that window, FATAL is issued and simulation is terminated.

Note: The UVM 1.1d has an objection object of type uvm_callbacks_objection which is derived from uvm_objection class and UVM 1.2 has an objection object of uvm_objection class.

uvm_heartbeat class hierarchy

uvm_heartbeat hierarchy

uvm_heartbeat Methods

Method

Definition

Description

new

function new (string name,  uvm_component cntxt, uvm_objection objection = null

)

The uvm_heartbeat constructor creates a new heartbeat instance associated with cntxt.

name: instance name for uvm_heartbeat.

cntxt: Specifies context where heartbeat objections will be monitored. The ‘this’ keyword is usually passed.

objection: Represents the objections that are being monitored by heartbeat. It is optional. It can be null but it must be set before activation of the heartbeat monitor.

set_mode 

function uvm_heartbeat_modes set_mode (uvm_heartbeat_modes mode = UVM_NO_HB_MODE)

Sets or retrieves the heartbeat mode.

Modes:
1. UVM_ALL_ACTIVE  – all components are active
2. UVM_ONE_ACTIVE – Only one component is active
3. UVM_ANY_ACTIVE – any component is active

set_heartbeat

function void set_heartbeat (uvm_event e, ref uvm_component comps[$])

Assigns a list of objects to watch and set up the heartbeat event and monitoring starts immediately.
1. If event e is null, then monitoring has to be started by calling the start() method explicitly.
2. If a user wants to change event e, then monitoring has to be stopped using stop() call and start() call can start monitoring with a new event trigger.

add

function void add (uvm_component comp)

Adds a single component to the list to be monitored.

remove

function void remove (uvm_component comp)

Removes a single component from the list to be monitored.

start

function void start (
uvm_event e = null)

Starts the heartbeat monitor.
1. If e is null, it uses a previously set event.

2. If no event was set previously, it issues a warning.

3. If the event triggered is different from the current event for a running monitor, it issues an error.

stop

function void stop ()

Stops the heartbeat monitor and can be started using a start() method call.


Note: The uvm_event e must be triggered periodically and it sets up a heartbeat window. If the heartbeat monitor fails to see any activity within that period, HBFAIL fatal message is generated. Generally, event e can be triggered in the infinite loop as a forever ongoing process.

UVM Heartbeat Examples

Success case

In the below example, component comp is always active as an objection is raised after every 40 time-units and heartbeat event is triggered after every 50 time-units. 

Loop delays:for

  1. comp: 40 time-units
  2. hb_e: 50 time-units

Hence, whenever a heartbeat event is triggered, it sees activity on component comp. This completes simulation without any FATAL termination

class component extends uvm_component;
  uvm_objection obj;
  `uvm_component_utils(component)
  
  function new (string name = "component", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
  
  task run_phase(uvm_phase phase);
    int i;
    forever begin
      #40;
      obj.raise_objection(this);
      `uvm_info(get_name(), $sformatf("raised objection for i = %0d", i), UVM_LOW)
      i++;
    end
  endtask
endclass

class base_test extends uvm_test;
  uvm_objection obj;
  component comp;
  uvm_component hb_comp[$];
  uvm_heartbeat hb;
  uvm_event hb_e;
  
  `uvm_component_utils(base_test)
  
  function new (string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    obj = new("obj");
    comp = component::type_id::create("comp", this);
    hb = new("hb", this, obj);
    hb_e = new("hb_e");
    
    comp.obj = this.obj;
  endfunction
  
  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    
    hb.set_mode(UVM_ANY_ACTIVE);
    hb.set_heartbeat(hb_e,hb_comp);
    hb.add(comp);
    hb.start(hb_e);
    
    repeat(5) begin
      #50 `uvm_info(get_type_name(), $sformatf("triggering hb_e"), UVM_LOW)
      hb_e.trigger();
    end
    phase.drop_objection(this);
  endtask
endclass

module heartbeat_example();
  initial begin
    run_test("base_test");
  end
endmodule

Output:

UVM_INFO components.sv(18) @ 40: uvm_test_top.comp [comp] raised objection for i = 0
UVM_INFO testbench.sv(40) @ 50: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 80: uvm_test_top.comp [comp] raised objection for i = 1
UVM_INFO testbench.sv(40) @ 100: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 120: uvm_test_top.comp [comp] raised objection for i = 2
UVM_INFO testbench.sv(40) @ 150: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 160: uvm_test_top.comp [comp] raised objection for i = 3
UVM_INFO testbench.sv(40) @ 200: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 200: uvm_test_top.comp [comp] raised objection for i = 4
UVM_INFO components.sv(18) @ 240: uvm_test_top.comp [comp] raised objection for i = 5
UVM_INFO testbench.sv(40) @ 250: uvm_test_top [base_test] triggering hb_e

Fail case

Component comp is active till 120 time-units as an objection is raised after every 40 time-units for  three times and heartbeat event is triggered after every 50 time-units for five times 

Loop delays: for

  1. comp: 40 time-units
  2. hb_e: 50 time-units

The heartbeat event does not see activity on component comp after 120 time-units i.e. there is no activity between two heartbeat events with 150~200 time units. Thus, the heartbeat monitor issued HBFAIL FATAL and terminated the simulation.

class component extends uvm_component;
  uvm_objection obj;
  `uvm_component_utils(component)
  
  function new (string name = "component", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
  
  task run_phase(uvm_phase phase);
    int i;
    repeat(3) begin
      #40;
      obj.raise_objection(this);
      `uvm_info(get_name(), $sformatf("raised objection for i = %0d", i), UVM_LOW)
      i++;
    end
  endtask
endclass

class base_test extends uvm_test;
  uvm_objection obj;
  component comp;
  uvm_component hb_comp[$];
  uvm_heartbeat hb;
  uvm_event hb_e;
  
  `uvm_component_utils(base_test)
  
  function new (string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    obj = new("obj");
    comp = component::type_id::create("comp", this);
    hb = new("hb", this, obj);
    hb_e = new("hb_e");
    
    comp.obj = this.obj;
  endfunction
  
  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    
    hb.set_mode(UVM_ANY_ACTIVE);
    hb.set_heartbeat(hb_e,hb_comp);
    hb.add(comp);
    hb.start(hb_e);
    
    repeat(5) begin
      #50 `uvm_info(get_type_name(), $sformatf("triggering hb_e"), UVM_LOW)
      hb_e.trigger();
    end
    phase.drop_objection(this);
  endtask
endclass

module heartbeat_example();
  initial begin
    run_test("base_test");
  end
endmodule

Output:

UVM_INFO components.sv(18) @ 40: uvm_test_top.comp [comp] raised objection for i = 0
UVM_INFO testbench.sv(40) @ 50: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 80: uvm_test_top.comp [comp] raised objection for i = 1
UVM_INFO testbench.sv(40) @ 100: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(18) @ 120: uvm_test_top.comp [comp] raised objection for i = 2
UVM_INFO testbench.sv(40) @ 150: uvm_test_top [base_test] triggering hb_e
UVM_INFO testbench.sv(40) @ 200: uvm_test_top [base_test] triggering hb_e
UVM_FATAL @ 200: uvm_test_top [HBFAIL] Did not recieve an update of obj on any component since last event trigger at time 150. The list of registered components is:
  uvm_test_top.comp

With multiple components

Multiple component instances are created as 

component_A comp_A[0], comp_A[1], comp_A[2], 

component_B comp_B[0], comp_B[1], comp_B[2], and 

objections are raised with a random delay within the range of 40~50 time-units.

Also, notice that set_mode has UVM_ALL_ACTIVE set as a monitoring mode. so that if any instance comp_A[i] and comp_B[i] is not active FATAL will be issued (where, i = 0, 1, 2)

component_A and component_B code:

class component_A extends uvm_component;
  uvm_objection obj;
  int delay;
  `uvm_component_utils(component_A)
  
  function new (string name = "component_A", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
  
  task run_phase(uvm_phase phase);
    int i;
    forever begin
      #delay;
      obj.raise_objection(this);
      `uvm_info(get_name(), $sformatf("raised objection for i = %0d", i), UVM_LOW)
      i++;
    end
  endtask
endclass

class component_B extends uvm_component;
  uvm_objection obj;
  int delay;
  `uvm_component_utils(component_B)
  
  function new (string name = "component_B", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
  endfunction
  
  task run_phase(uvm_phase phase);
    int i;
    forever begin
      #delay;
      obj.raise_objection(this);
      `uvm_info(get_name(), $sformatf("raised objection for i = %0d", i), UVM_LOW)
      i++;
    end
  endtask
endclass
class base_test extends uvm_test;
  uvm_objection obj;
  component_A comp_A[3];
  component_B comp_B[3];
  uvm_component hb_comp[$];
  uvm_heartbeat hb;
  uvm_event hb_e;
  
  `uvm_component_utils(base_test)
  
  function new (string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    obj = new("obj");
    foreach(comp_A[i]) begin
      comp_A[i] = component_A::type_id::create($sformatf("comp_A[%0d]", i), this);
      comp_A[i].obj = this.obj;
      comp_A[i].delay = $urandom_range(50, 40);
    end
    
    foreach(comp_B[i]) begin
      comp_B[i] = component_B::type_id::create($sformatf("comp_B[%0d]", i), this);
      comp_B[i].obj = this.obj;
      comp_B[i].delay = $urandom_range(50, 40);
    end
    
    hb = new("hb", this, obj);
    hb_e = new("hb_e");
  endfunction
  
  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    
    hb.set_mode(UVM_ALL_ACTIVE);
    hb.set_heartbeat(hb_e,hb_comp);
    foreach(comp_A[i]) hb.add(comp_A[i]);
    foreach(comp_B[i]) hb.add(comp_B[i]);
    hb.start(hb_e);
    
    repeat(5) begin
      #50 `uvm_info(get_type_name(), $sformatf("triggering hb_e"), UVM_LOW)
      hb_e.trigger();
    end
    phase.drop_objection(this);
  endtask
endclass

module heartbeat_example();
  initial begin
    run_test("base_test");
  end
endmodule

Output:

UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO components.sv(43) @ 40: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 0
UVM_INFO components.sv(19) @ 41: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 0
UVM_INFO components.sv(43) @ 42: uvm_test_top.comp_B[2] [comp_B[2]] raised objection for i = 0
UVM_INFO components.sv(43) @ 44: uvm_test_top.comp_B[0] [comp_B[0]] raised objection for i = 0
UVM_INFO components.sv(19) @ 49: uvm_test_top.comp_A[2] [comp_A[2]] raised objection for i = 0
UVM_INFO components.sv(19) @ 49: uvm_test_top.comp_A[0] [comp_A[0]] raised objection for i = 0
UVM_INFO testbench.sv(48) @ 50: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(43) @ 80: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 1
UVM_INFO components.sv(19) @ 82: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 1
UVM_INFO components.sv(43) @ 84: uvm_test_top.comp_B[2] [comp_B[2]] raised objection for i = 1
UVM_INFO components.sv(43) @ 88: uvm_test_top.comp_B[0] [comp_B[0]] raised objection for i = 1
UVM_INFO components.sv(19) @ 98: uvm_test_top.comp_A[2] [comp_A[2]] raised objection for i = 1
UVM_INFO components.sv(19) @ 98: uvm_test_top.comp_A[0] [comp_A[0]] raised objection for i = 1
UVM_INFO testbench.sv(48) @ 100: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(43) @ 120: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 2
UVM_INFO components.sv(19) @ 123: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 2
UVM_INFO components.sv(43) @ 126: uvm_test_top.comp_B[2] [comp_B[2]] raised objection for i = 2
UVM_INFO components.sv(43) @ 132: uvm_test_top.comp_B[0] [comp_B[0]] raised objection for i = 2
UVM_INFO components.sv(19) @ 147: uvm_test_top.comp_A[2] [comp_A[2]] raised objection for i = 2
UVM_INFO components.sv(19) @ 147: uvm_test_top.comp_A[0] [comp_A[0]] raised objection for i = 2
UVM_INFO testbench.sv(48) @ 150: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(43) @ 160: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 3
UVM_INFO components.sv(19) @ 164: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 3
UVM_INFO components.sv(43) @ 168: uvm_test_top.comp_B[2] [comp_B[2]] raised objection for i = 3
UVM_INFO components.sv(43) @ 176: uvm_test_top.comp_B[0] [comp_B[0]] raised objection for i = 3
UVM_INFO components.sv(19) @ 196: uvm_test_top.comp_A[2] [comp_A[2]] raised objection for i = 3
UVM_INFO components.sv(19) @ 196: uvm_test_top.comp_A[0] [comp_A[0]] raised objection for i = 3
UVM_INFO testbench.sv(48) @ 200: uvm_test_top [base_test] triggering hb_e
UVM_INFO components.sv(43) @ 200: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 4
UVM_INFO components.sv(19) @ 205: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 4
UVM_INFO components.sv(43) @ 210: uvm_test_top.comp_B[2] [comp_B[2]] raised objection for i = 4
UVM_INFO components.sv(43) @ 220: uvm_test_top.comp_B[0] [comp_B[0]] raised objection for i = 4
UVM_INFO components.sv(43) @ 240: uvm_test_top.comp_B[1] [comp_B[1]] raised objection for i = 5
UVM_INFO components.sv(19) @ 245: uvm_test_top.comp_A[2] [comp_A[2]] raised objection for i = 4
UVM_INFO components.sv(19) @ 245: uvm_test_top.comp_A[0] [comp_A[0]] raised objection for i = 4
UVM_INFO components.sv(19) @ 246: uvm_test_top.comp_A[1] [comp_A[1]] raised objection for i = 5
UVM_INFO testbench.sv(48) @ 250: uvm_test_top [base_test] triggering hb_e