In a testbench, semaphores are used to manage access to shared resources such as a bus that can have multiple requestors but only one driver at a time.


7.5.1 Semaphore Operations

A semaphore supports three main operations:

Operation Description
new(n) Creates a semaphore with n keys
get(n) Acquires n keys (blocks if not enough keys)
put(n) Returns n keys back to the pool
try_get(n) Tries to acquire n keys without blocking — returns 1 on success, 0 on failure

🧩 Semaphores Controlling Access to a Hardware Resource

program automatic test;
    semaphore sem; // Declare a semaphore

    // Create semaphore and spawn threads
    initial begin
        sem = new(1); // Allocate semaphore with 1 key

        fork
            sequencer;  // Thread 1
            sequencer;  // Thread 2
        join
    end

    // Each sequencer repeatedly performs transactions
    task sequencer;
        repeat ($urandom % 10) // Random wait (0–9 cycles)
            @bus.cb;
        sendTrans; // Execute the transaction
    endtask

    // Task to perform transaction with controlled access
    task sendTrans;
        sem.get(1);        // Acquire the key (enter critical section)
        @bus.cb;           // Drive signals onto the bus
        bus.cb.addr <= t.addr;
        ...
        sem.put(1);        // Release the key when done
    endtask
endprogram

🧮 Behavior Summary


Semaphores with Multiple Keys

When using multiple keys, there are two important caveats:

  1. ⚠️ Over-releasing keys
    You can accidentally put more keys back than you took out, leading to “extra” resources (e.g., two keys but only one car!).

  2. ⚠️ Blocking due to FIFO ordering
    Suppose:

    • 1 key remains.
    • Thread A requests 2 keysblocks.
    • Thread B requests 1 keyalso blocks (because A is ahead in the FIFO queue).

    Even though there are enough keys for B, it must wait behind A.