UVM Heartbeat
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 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: |
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. |
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 ( |
Starts the heartbeat monitor. 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
- comp: 40 time-units
- 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
- comp: 40 time-units
- 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
UVM Tutorials