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:
- Declare and initialize it with a specific number of keys (available resources).
- Processes attempt to acquire keys before accessing the shared resource.
- 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:
new(): Initializes the semaphore with a specified number of keys.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.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.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:
- Semaphore Initialization:
bus_semais created with 2 keys, meaning only two processes can use the bus at a time. - Requesting Access: Each process calls
bus_user, which tries to acquire 1 key frombus_sema. - 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.