
Semaphore
A semaphore in SystemVerilog is a synchronization mechanism used to control access to shared resources among multiple processes.
It is particularly useful in testbenches to prevent race conditions when multiple threads try to access the same resource.
A semaphore consists of a fixed number of keys (or tokens), which can be acquired and released by processes. If a process wants to use a resource, it must acquire a key; once it is done, it releases the key, allowing other processes to access the resource.
Real-Life Analogy
Consider a public restroom with three stalls. If three people are using the stalls, others must wait until a stall is free. This scenario can be modeled using a semaphore with three keys:
- Each person (process) acquires one key before using a stall.
- If no keys are available, they must wait.
- Once done, they release the key, making a stall available for the next person.
Semaphore Methods
SystemVerilog provides a built-in semaphore class with the following key methods:
new()
– Creates a semaphore with a defined number of keys.get(n)
– Acquiresn
keys from the semaphore before accessing a shared resource.put(n)
– Releasesn
keys back to the semaphore after resource usage.try_get(n)
– Attempts to acquiren
keys without blocking the process if keys are unavailable.
Important things to consider before writing semaphore code in System Verilog:
- Understand the Need for Synchronization
Identify the shared resources in your testbench that multiple processes will access.
Determine if resource contention could cause race conditions.
- Decide the Number of Keys
Set the number of semaphore keys based on the number of available resources.
Example: If you have three memory blocks that can be accessed concurrently, initialize the semaphore with three keys.
- Proper Use of get() and put() Methods
Use get(n) to acquire n keys before accessing a shared resource.
Use put(n) to release n keys after completing the resource usage.
Ensure that every get(n) has a corresponding put(n) to avoid deadlocks.
- Avoid Deadlocks
Deadlocks occur when multiple processes are waiting indefinitely for keys that are never released.
Ensure that resources are always released after use.
- Use try_get() for Non-blocking Access
If a process should proceed even if it fails to acquire a key, use try_get(n) instead of get(n).
Example: If try_get(1) fails, the process can take an alternative action instead of waiting.
- Consider the Impact of fork-join
When using fork-join, be aware of concurrent processes trying to access the semaphore.
Use fork-join_none or fork-join_any if required to avoid unnecessary blocking.
- Debugging with num()
Use the num() method to check the current number of available keys for debugging purposes.
Example: $display(“Available keys: %0d”, sem.num());
Example:- Simple Semaphore Example
module restroom_semaphore;
semaphore restroom; // Declare semaphore
initial begin
restroom = new(3); // Initialize semaphore with 3 keys (3 stalls available)
end
task use_restroom(int person_id);
restroom.get(1); // Acquire one stall
$display("Person %0d entered the washroom at time %0t", person_id, $time);
#5; // Simulate washroom usage
$display("Person %0d exited the washroom at time %0t", person_id, $time);
restroom.put(1); // Release the stall
endtask
initial begin
fork
use_restroom(1);
use_restroom(2);
use_restroom(3);
use_restroom(4);
use_restroom(5);
join
end
endmodule
Output:
xcelium> run
Person 1 entered the washroom at time 0
Person 2 entered the washroom at time 0
Person 3 entered the washroom at time 0
Person 5 exited the washroom at time 5
Person 5 exited the washroom at time 5
Person 5 exited the washroom at time 5
Person 5 entered the washroom at time 5
Person 5 entered the washroom at time 5
Person 5 exited the washroom at time 10
Person 5 exited the washroom at time 10
xmsim: *W,RNQUIE: Simulation is complete.
xcelium> exit
Explanation of the code:
The given SystemVerilog code models a scenario where multiple people attempt to use a restroom with limited stalls, using a semaphore to manage access.
The module is named restroom_semaphore
. It encapsulates the entire functionality of the semaphore-based restroom simulation.
A semaphore named restroom
is declared.
Inside the initial
block, the semaphore is instantiated with 3 keys, representing 3 restroom stalls.
This means that at any given time, only 3 people (processes) can use the restroom.
The use_restroom
task represents a person using a restroom stall.
The person_id
argument helps differentiate between different individuals.
The process follows these steps:
- Acquire a stall using
restroom.get(1)
. If no stalls (keys) are available, the person waits. - Print a message indicating that the person has entered the washroom.
- Wait for 5 time units (
#5
) to simulate using the restroom. - Print a message indicating that the person has exited.
- Release the stall using
restroom.put(1)
, making it available for others.
- The
initial
block starts executing at simulation time0
. - The
fork-join
block launches five parallel processes, representing five different people trying to use the restroom. - Since the semaphore has only 3 keys, at most 3 people can enter the restroom at a time.
- The remaining 2 people must wait until someone exits and releases a stall.
Execution Flow
- At time
0
, the first three people acquire a key and enter the restroom. - They stay inside for 5 time units (
#5
). - Once they exit, they release their keys.
- The remaining two people can now acquire a key and enter the restroom.
- This continues until all five processes are completed.
Key Takeaways
- Concurrency Control: The semaphore ensures that no more than 3 people use the restroom at the same time.
- Blocking Mechanism: If a process tries to acquire a key but none are available, it waits until one is released.
- Efficient Resource Management: Using
get()
andput()
, the restroom stalls are efficiently managed without conflicts.
Using a Class to Modify Semaphore Keys Dynamically
In real-world scenarios, the number of restroom users may increase. Below is an updated version using a System Verilog class to allow dynamic key allocation.
class RestroomControl;
semaphore restroom;
function new(int num_stalls);
restroom = new(num_stalls); // Initialize semaphore with dynamic keys
endfunction
task use_restroom(int person_id);
restroom.get(1); // Acquire one stall
$display("Person %0d entered the restroom at time %0t", person_id, $time);
#5; // Simulate washroom usage
$display("Person %0d exited the restroom at time %0t", person_id, $time);
restroom.put(1); // Release the stall
endtask
endclass
module restroom_semaphore_class;
RestroomControl rc;
initial begin
rc = new(3); // Initially, only 3 stalls
#10;
rc = new(5); // Later, increase to 5 stalls
end
initial begin
fork
rc.use_restroom(1);
rc.use_restroom(2);
rc.use_restroom(3);
rc.use_restroom(4);
rc.use_restroom(5);
rc.use_restroom(6);
rc.use_restroom(7);
rc.use_restroom(8);
join
end
endmodule
Output:-
xcelium> run
Person 1 entered the restroom at time 0
Person 2 entered the restroom at time 0
Person 3 entered the restroom at time 0
Person 1 exited the restroom at time 5
Person 2 exited the restroom at time 5
Person 3 exited the restroom at time 5
Person 4 entered the restroom at time 5
Person 5 entered the restroom at time 5
Person 6 entered the restroom at time 5
Person 4 exited the restroom at time 10
Person 5 exited the restroom at time 10
Person 6 exited the restroom at time 10
Person 7 entered the restroom at time 10
Person 8 entered the restroom at time 10
Person 7 exited the restroom at time 15
Person 8 exited the restroom at time 15
xmsim: *W,RNQUIE: Simulation is complete.
xcelium> exit
Explanation:
Explanation of the Code
- The
RestroomControl
class encapsulates the semaphore functionality. - The
new(int num_stalls)
constructor allows dynamic allocation of stalls. - The
use_
restroom task ensures synchronized access using semaphore methods. - The
restroom_semaphore_class
module initializes the class with 3 stalls and later increases it to 5 as demand grows. - Eight people try to use the restroom, demonstrating controlled access as per the updated semaphore keys.
Conclusion
Semaphores in SystemVerilog are crucial for synchronizing multiple processes and preventing resource conflicts. The use of a class-based approach provides flexibility, allowing the semaphore size to be adjusted dynamically as the number of users increases.