UVM Barrier - Synchronizing Parallel Components
When building complex UVM testbenches, you often have multiple components running in parallel that need to synchronize at specific points. The uvm_barrier class provides an elegant solution for this synchronization challenge.
What is uvm_barrier?
A uvm_barrier is a synchronization primitive that blocks multiple processes until a specified number of processes have reached the barrier. Once all processes arrive, they are all released simultaneously to continue execution.
Think of it like a meeting point - everyone waits until the entire group arrives, then they all proceed together.
When to Use uvm_barrier
- Multi-agent coordination - Ensure all agents complete initialization before starting transactions
- Phase synchronization - Coordinate multiple components at specific test phases
- Parallel test scenarios - Synchronize stimulus from multiple sources
- Resource handoff - Coordinate when one component finishes and another takes over
Basic Usage
The barrier is created with a threshold value indicating how many processes must call wait_for() before all are released:
// Create barrier for 2 processes
uvm_barrier my_barrier = new("my_barrier", 2);
// In component 1
my_barrier.wait_for(); // Blocks until component 2 also calls wait_for()
// In component 2
my_barrier.wait_for(); // Both components released simultaneously
Complete Example
Let's walk through a complete example with two components that synchronize using a barrier.
Test Class - Creating and Distributing the Barrier
class test extends uvm_test;
`uvm_component_utils(test)
uvm_barrier c_barrier;
my_component_1 comp_1;
my_component_2 comp_2;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
comp_1 = my_component_1::type_id::create("comp_1", this);
comp_2 = my_component_2::type_id::create("comp_2", this);
// Create barrier to sync 2 processes
c_barrier = new("c_barrier", 2);
endfunction
function void start_of_simulation_phase(uvm_phase phase);
// Assign same barrier to different components
comp_1.barrier = c_barrier;
comp_2.barrier = c_barrier;
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
endclass
Component 1 - Fast Component
class my_component_1 extends uvm_component;
`uvm_component_utils(my_component_1)
uvm_barrier barrier;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
`uvm_info("COMP1", "Inside run_phase()", UVM_NONE)
#20; // Fast - only 20 time units
barrier.wait_for();
`uvm_info("COMP1", "Reached barrier synchronization..", UVM_NONE)
// Reset the barrier for reuse
barrier.reset();
barrier.wait_for();
`uvm_info("COMP1", "Reached barrier synchronization.. (1)", UVM_NONE)
endtask
endclass
Component 2 - Slow Component
class my_component_2 extends uvm_component;
`uvm_component_utils(my_component_2)
uvm_barrier barrier;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
`uvm_info("COMP2", "Inside run_phase()", UVM_NONE)
// No delay - reaches barrier immediately
barrier.wait_for();
`uvm_info("COMP2", "Reached barrier synchronization..", UVM_NONE)
// Reset the barrier for reuse
barrier.reset();
#40; // Slow - 40 time units before second sync
barrier.wait_for();
`uvm_info("COMP2", "Reached barrier synchronization.. (1)", UVM_NONE)
endtask
endclass
Expected Output
UVM_INFO @ 0: comp_1 [COMP1] Inside run_phase()
UVM_INFO @ 0: comp_2 [COMP2] Inside run_phase()
UVM_INFO @ 20: comp_2 [COMP2] Reached barrier synchronization..
UVM_INFO @ 20: comp_1 [COMP1] Reached barrier synchronization..
UVM_INFO @ 60: comp_1 [COMP1] Reached barrier synchronization.. (1)
UVM_INFO @ 60: comp_2 [COMP2] Reached barrier synchronization.. (1)
Notice how both components print "Reached barrier synchronization" at exactly the same time (20ns for first sync, 60ns for second), even though they have different delays.
Key Methods
| Method | Description |
|---|---|
new(name, threshold) | Create barrier with specified threshold |
wait_for() | Block until threshold processes arrive |
reset() | Reset barrier for reuse (call from one component) |
set_threshold(value) | Change threshold dynamically |
get_threshold() | Get current threshold value |
get_num_waiters() | Get number of processes currently waiting |
cancel() | Release all waiting processes immediately |
Best Practices
- Create barrier in test/env - Centralize barrier creation for visibility
- Pass via handle assignment - Don't use config_db for barriers (handles work better)
- Reset after use - Call
reset()if reusing the barrier - Match threshold to waiters - Ensure threshold equals the number of components that will wait
- Use meaningful names - Name barriers descriptively (e.g., "init_complete_barrier")
Common Pitfalls
- Threshold mismatch - If threshold is 3 but only 2 components wait, they'll block forever
- Forgetting to reset - Barrier won't work correctly on second use without reset
- Race conditions - Ensure barrier is assigned before run_phase starts
Source Code
Complete working example available on GitHub: UVM Barrier Example
Next: Back to UVM Hub
Comments (0)
Leave a Comment