Semaphores in System Verilog

A semaphore in SystemVerilog is a synchronization object that manages a certain number of “keys.” These keys represent access to a shared resource. When a process needs to access the resource, it must first obtain one or more keys from the semaphore. If the required number of keys is available, the process is granted access; if not, the process waits until keys are released by other processes.

Semaphores help in controlling the access of multiple processes to a shared resource, ensuring orderly and safe access to avoid race conditions and conflicts.

Basic Semaphore Usage

To use a semaphore in SystemVerilog:

  1. Declare and initialize it with a specific number of keys (available resources).
  2. Processes attempt to acquire keys before accessing the shared resource.
  3. After accessing the resource, processes release the keys back to the semaphore.

Example: Creating a Semaphore

semaphore sema = new(3);  // Creates a semaphore with 3 keys

In this example, sema is initialized with 3 keys, which means up to 3 processes can simultaneously access the resource controlled by sema. If a process requests more keys than are available, it will have to wait until other processes release the necessary keys.

Methods of Semaphore

SystemVerilog provides several methods for managing semaphores:

  1. new(): Initializes the semaphore with a specified number of keys.
  2. get(): Requests a specific number of keys from the semaphore. If enough keys are available, the process proceeds; if not, the process waits until the required keys are available.
  3. try_get(): Attempts to obtain keys from the semaphore without waiting. If the required number of keys is available, the process proceeds; if not, the process moves on without waiting.
  4. put(): Releases keys back to the semaphore, making them available for other processes.

Each of these methods serves a different purpose and allows for flexible control over resource access in a verification environment.

Semaphore Methods in Detail

new(): Semaphore Initialization

The new() method initializes a semaphore with a specific number of keys. This is usually done when the semaphore is created.

semaphore sema = new(5);  // Creates a semaphore with 5 keys

In this example, the semaphore sema starts with 5 keys, meaning up to 5 processes can access the resource simultaneously. This method does not return a value.

get(): Requesting Keys from the Semaphore

The get() method is used by a process to request keys from the semaphore. If the requested number of keys is available, the process acquires them and proceeds. If not, the process waits until enough keys are available.

  • Syntax
sema.get(N);  // Request N keys

Example:

task use_resource();
  sema.get(2);  // Request 2 keys
  $display("Resource accessed by %0t", $time);
  // Perform operations with the resource
  sema.put(2);  // Release 2 keys
endtask

In this example, the process requests 2 keys from sema. If the keys are available, it proceeds to access the resource; otherwise, it waits until 2 keys are available. After using the resource, it releases the 2 keys using put().

try_get(): Non-blocking Key Request

The try_get() method is a non-blocking version of get(). It attempts to acquire the specified number of keys, but if they are not immediately available, it returns without waiting.

  • Syntax
if (sema.try_get(N)) begin
  // Proceed if N keys are available
end else begin
  // Take alternative action if keys are not available
end

Example:

task attempt_resource_access();
  if (sema.try_get(1)) begin
    $display("Access granted at %0t", $time);
    // Perform operations with the resource
    sema.put(1);  // Release 1 key
  end else begin
    $display("Access denied at %0t", $time);
    // Take alternative action
  end
endtask

In this example, the process attempts to get 1 key without waiting. If successful, it accesses the resource; otherwise, it takes an alternative action, such as logging an “access denied” message.

put(): Releasing Keys to the Semaphore

The put() method releases keys back to the semaphore, increasing the number of available keys. This method is used after a process has completed its use of the shared resource.

  • Syntax
sema.put(N);  // Release N keys

Example:

task release_resource();
  // Perform operations with the resource
  sema.put(1);  // Releases 1 key back to the semaphore
endtask

Here, the process releases 1 key back to sema, allowing other waiting processes to access the resource if they require it.

Practical Example: Using Semaphore to Control Access to a Shared Resource

Let’s consider a scenario where multiple processes need access to a shared bus. Only two processes can access the bus at a time, so we initialize a semaphore with 2 keys and use it to control access to the bus.

module test;
  semaphore bus_sema = new(2);  // Initialize with 2 keys

  task bus_user(string user_name);
    $display("%s requests access at time %0t", user_name, $time);
    bus_sema.get(1);  // Request 1 key to access the bus
    $display("%s has access to the bus at time %0t", user_name, $time);
    
    #10;  // Simulate bus usage for 10 time units
    
    $display("%s releases the bus at time %0t", user_name, $time);
    bus_sema.put(1);  // Release the key
  endtask

  initial begin
    fork
      bus_user("Process A");
      bus_user("Process B");
      bus_user("Process C");
    join
  end
endmodule

Explanation:

  1. Semaphore Initialization: bus_sema is created with 2 keys, meaning only two processes can use the bus at a time.
  2. Requesting Access: Each process calls bus_user, which tries to acquire 1 key from bus_sema.
  3. Releasing Access: After using the bus for 10 time units, each process releases its key, allowing other processes to access the bus.

Output:

Process A requests access at time 0
Process A has access to the bus at time 0
Process B requests access at time 0
Process B has access to the bus at time 0
Process C requests access at time 0
Process C releases the bus at time 10
Process C releases the bus at time 10
Process C has access to the bus at time 10
Process C releases the bus at time 20

This example ensures that no more than two processes can access the bus at the same time.