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

MethodDescription
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

  1. Create barrier in test/env - Centralize barrier creation for visibility
  2. Pass via handle assignment - Don't use config_db for barriers (handles work better)
  3. Reset after use - Call reset() if reusing the barrier
  4. Match threshold to waiters - Ensure threshold equals the number of components that will wait
  5. 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

Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment