Semaphores in SystemVerilog

Semaphores control access to shared resources. Think of them as a limited number of "keys" - only processes with keys can proceed.

What Is a Semaphore?

Imagine a parking lot with 3 spaces. The semaphore is the parking attendant who gives out 3 keys. When all keys are taken, new cars must wait until someone returns a key.

Basic Semaphore
semaphore sem;

initial begin
    sem = new(3);  // Create semaphore with 3 keys
    
    fork
        use_resource("Process A");
        use_resource("Process B");
        use_resource("Process C");
        use_resource("Process D");  // Must wait!
    join
end

task use_resource(string name);
    sem.get(1);  // Get 1 key (blocks if none available)
    $display("[%0t] %s got key, using resource", $time, name);
    #10;  // Use resource for 10 time units
    $display("[%0t] %s returning key", $time, name);
    sem.put(1);  // Return 1 key
endtask

Semaphore Methods

new(keyCount)

Creates semaphore with specified number of keys:

Creating Semaphores
semaphore mutex = new(1);  // Binary semaphore (mutex)
semaphore pool = new(5);   // Allow 5 concurrent users

get(count) - Blocking

Gets specified keys. Blocks if not enough available:

get() Method
sem.get(1);   // Get 1 key, wait if needed
sem.get(2);   // Get 2 keys at once

try_get(count) - Non-blocking

Tries to get keys. Returns 1 if successful, 0 if not:

try_get() Method
if (sem.try_get(1)) begin
    // Success - got the key
    use_resource();
    sem.put(1);
end else begin
    // Failed - no key available
    $display("Resource busy, try later");
end

put(count)

Returns keys to the semaphore:

put() Method
sem.put(1);   // Return 1 key
sem.put(3);   // Return 3 keys

Mutex (Binary Semaphore)

A semaphore with just 1 key is called a mutex (mutual exclusion). Only one process can hold it at a time.

Mutex for Exclusive Access
class SharedResource;
    semaphore mutex;
    int shared_counter;
    
    function new();
        mutex = new(1);  // Only 1 key = mutex
        shared_counter = 0;
    endfunction
    
    task increment();
        mutex.get(1);  // Lock
        
        // Critical section - only one process at a time
        shared_counter++;
        $display("Counter = %0d", shared_counter);
        
        mutex.put(1);  // Unlock
    endtask
endclass

Practical Example: Bus Arbitration

Bus Access Control
class BusController;
    semaphore bus_access;
    
    function new();
        bus_access = new(1);  // Only 1 master at a time
    endfunction
    
    task request_bus(string master_name);
        $display("[%0t] %s requesting bus", $time, master_name);
        bus_access.get(1);
        $display("[%0t] %s got bus access", $time, master_name);
    endtask
    
    task release_bus(string master_name);
        $display("[%0t] %s releasing bus", $time, master_name);
        bus_access.put(1);
    endtask
endclass

// Usage
BusController bus = new();

initial begin
    fork
        begin
            bus.request_bus("Master0");
            #20;  // Use bus
            bus.release_bus("Master0");
        end
        begin
            #5;  // Start a bit later
            bus.request_bus("Master1");  // Will wait!
            #15;
            bus.release_bus("Master1");
        end
    join
end

Quick Summary

  • semaphore - Controls access to shared resources
  • new(n) - Create with n keys
  • get(n) - Get n keys (blocks)
  • try_get(n) - Try to get (non-blocking)
  • put(n) - Return n keys
  • Mutex - Semaphore with 1 key for exclusive access