A SystemVerilog Semaphore is a tool used to manage access to shared resources, ensuring that processes can interact in a controlled and synchronized manner. Think of a semaphore as a bucket that holds a set number of keys. Processes must first take a key from this bucket before they can proceed with their tasks. If no keys are available, other processes must wait until a key becomes free. Semaphores are ideal for mutual exclusion, resource access control, and basic synchronization.

What is a Semaphore in SystemVerilog?

In SystemVerilog, a semaphore is a built-in class used to control access to shared resources. It allows processes to obtain keys, execute their tasks, and return the keys when done. This system ensures that only a fixed number of processes can use a resource at any given time.

How to Declare and Use a Semaphore

To declare a semaphore in SystemVerilog, you simply use the following syntax:

semaphore semaphoreName;

Just like any other class object, the semaphore has several methods that allow you to control the number of keys it contains. These methods include the ability to allocate keys, retrieve keys, and return keys.

Semaphore Methods

Here are the key methods associated with semaphores in SystemVerilog:

MethodDescription
function new(int keyCount = 0);Allocates a specified number of keys to the semaphore bucket.
function void put(int keyCount = 1);Returns a certain number of keys back to the semaphore bucket.
task get(int keyCount = 1);Attempts to obtain a specified number of keys from the semaphore.
function int try_get(int keyCount = 1);Tries to get the specified number of keys. Returns 0 if unavailable.

Example Code: Managing Shared Resources with Semaphores

Below is a simple example where two people (Person A and Person B) try to access a shared room using a semaphore.

module tb_top;
    semaphore roomKey;  // Declare a semaphore named roomKey

    initial begin
        roomKey = new(1);  // Initialize the semaphore with 1 key (only 1 person can access the room at a time)
        
        fork
            personA();  // Person A tries to access the room
            personB();  // Person B tries to access the room
            #25 personA();  // After some time, Person A tries again
        join_none
    end

    // Task to simulate a person trying to get a room
    task getRoom(bit [1:0] id);
        $display("[%0t] Trying to get a room for id[%0d] ...", $time, id);
        roomKey.get(1);  // Person tries to get a key
        $display("[%0t] Room Key retrieved for id[%0d]", $time, id);
    endtask

    // Task to simulate a person leaving the room and returning the key
    task putRoom(bit [1:0] id);
        $display("[%0t] Leaving room id[%0d] ...", $time, id);
        roomKey.put(1);  // Person returns the key
        $display("[%0t] Room Key put back for id[%0d]", $time, id);
    endtask

    // Person A's task
    task personA();
        getRoom(1);   // Person A tries to get the room
        #20 putRoom(1);  // After 20 time units, Person A leaves and puts the key back
    endtask

    // Person B's task
    task personB();
        #5 getRoom(2);   // After 5 time units, Person B tries to get the room
        #10 putRoom(2);  // Person B leaves and returns the key after 10 time units
    endtask
endmodule

Simulation Log for the Example

The simulation output will look like this:

TimeEvent
0Trying to get a room for id[1] …
0Room Key retrieved for id[1]
5Trying to get a room for id[2] …
20Leaving room id[1] …
20Room Key put back for id[1]
20Room Key retrieved for id[2]
25Trying to get a room for id[1] …
30Leaving room id[2] …
30Room Key put back for id[2]
30Room Key retrieved for id[1]
50Leaving room id[1] …
50Room Key put back for id[1]

How Does This Example Work?

  • Semaphore Initialization: The semaphore is initialized with 1 key, meaning only one person can access the room at a time.
  • Task Execution: Two tasks (personA and personB) are running in parallel. Person A tries to access the room, followed by Person B.
  • Access Control: When Person A leaves, the key is returned to the semaphore bucket, allowing Person B to enter the room.

Conclusion: Why Use SystemVerilog Semaphores?

SystemVerilog semaphores provide a simple and effective way to manage access to shared resources, ensuring that only a limited number of processes can access the resource at the same time. They are crucial in scenarios requiring mutual exclusion and basic synchronization.

By using semaphores, you can prevent race conditions, ensure smooth execution of concurrent tasks, and improve the overall efficiency and stability of your simulations.

Scroll to Top