← Design Verification Interviews

Interview Question · Code Reading

Lock vs Grab on a Busy Sequencer

L1 JuniorL2 Mid-Leveluvmsequencesarbitration

The Question

The interviewer asks

Two sequences share a sequencer when an urgent one arrives. Predict the order the driver sees with lock() and with grab().

systemverilog
// Sequences A and B each send 4 items on one sequencer, from time 0.
// At 25 ns an urgent sequence U starts and sends 2 items:
task body();                                // in urgent_seq
  if (use_grab) grab(); else lock();
  super.body();                             // send U0, U1
  if (use_grab) ungrab(); else unlock();
endtask
// The driver takes 10 ns per item. In what order does the driver see items?
Reveal the Model Answer

By the usual description, a grab takes the very next slot, while a lock waits behind B1, which was already queued. In both cases A1, already with the driver, finishes first. On uvm-core 2020.3.1 the run shows the same order for both: A0 B0 A1 U0 U1 B1. This library grants any lock request at the next arbitration unless another lock blocks it. Here, a grab differs only by going ahead of locks that are already waiting.

Where Lock and Grab Put a RequestThree sequences ask for a lock at different times. Arrows show lock requests entering the queue, and a grant putting a sequence on lock_list.
Where lock and grab put a requestThree sequences ask for a lock at different times. Arrows show lock requests entering the queue, and a grant putting a sequence on `lock_list`. sequence Sequence A lock() at 15 ns sequence Sequence B lock() at 0 ns sequence Sequence C lock() or grab() at25 ns queue Lock requests in the arbitrationqueue component lock_list who holds a lock requestrequestrequestgrant
  1. B locks0 ns (illustrative)

    B's is the only lock request and nothing blocks it, so it is granted at once. B sends B0 to B3.

    Follow the calls, values and waits
    1. data B: Sequence B → Lock requests
    2. data B: Lock requests → lock_list
  2. A asks15 ns (illustrative)

    lock() appends A's request to the back of the queue. B's lock blocks it, so A waits.

    Follow the calls, values and waits
    1. data A: Sequence A → Lock requests
    2. Waiting: Sequence A — blocked by B
  3. C asks with lock()25 ns (illustrative)

    C's request goes behind A's. The queue holds A, then C.

    Follow the calls, values and waits
    1. data C: Sequence C → Lock requests
    2. Waiting: Sequence C — blocked by B
  4. B releases40 ns (illustrative)

    B unlocks after B3. The first unblocked lock request in the queue is A's, so A goes next and C after it.

    Follow the calls, values and waits
    1. data A: Lock requests → lock_list
    2. Waiting: Sequence C — blocked by A
    3. Released: Sequence A
    4. Released: Sequence C
  5. Had C called grab()25 ns (illustrative)

    grab() puts C's request at the front, ahead of A's. C still waits for B, then goes first: B, C, A.

    Follow the calls, values and waits
    1. data C, to the front: Sequence C → Lock requests
    2. data C: Lock requests → lock_list
    3. Waiting: Sequence A — blocked by C
    4. Released: Sequence A
    5. Released: Sequence C
Read the diagram as text
  • Sequence A: lock() at 15 ns
  • Sequence B: lock() at 0 ns
  • Sequence C: lock() or grab() at 25 ns
  • Lock requests: in the arbitration queue
  • lock_list: who holds a lock
  • Sequence A → Lock requests: request
  • Sequence B → Lock requests: request
  • Sequence C → Lock requests: request
  • Lock requests → lock_list: grant

Only other lock requests see the difference between lock and grab.

The same traffic on two sequencers: the urgent sequence uses lock() on one and grab() on the other.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: lock: A0 B0 A1 U0 U1 B1 A2 B2 A3 B3 grab: A0 B0 A1 U0 U1 B1 A2 B2 A3 B3

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer says both give one sequence the driver alone until it unlocks. It adds that grab jumps the queue, while lock waits its turn.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

What IfProve ItBreak ItCompare

  1. Lock vs Grab on a Busy Sequencer
  2. CompareSequencer Arbitration ModesL2 Mid-Level

    Compare the sequencer arbitration modes. When does a sequence's priority matter?

    Reveal the Model Answer

    The default, UVM_SEQ_ARB_FIFO, grants requests in arrival order and ignores priority. UVM_SEQ_ARB_WEIGHTED picks randomly with priority as the weight. The strict modes always serve the highest priority first, by arrival or at random. UVM_SEQ_ARB_RANDOM ignores priority, and UVM_SEQ_ARB_USER calls your own function. Set the mode with set_arbitration().

    One Queue, Two Rules for ChoosingThree sequences with priorities 100, 200 and 300, run once under FIFO and once under STRICT_FIFO. Arrows show requests entering the arbitration queue and granted items leaving it.
    One queue, two rules for choosingThree sequences with priorities 100, 200 and 300, run once under FIFO and once under STRICT_FIFO. Arrows show requests entering the arbitration queue and granted items leaving it. sequence Sequence A priority 100 sequence Sequence B priority 200 sequence Sequence C priority 300 queue Arbitration queue oldest request first component Driver one item per clock start_itemstart_itemstart_itemgranted item
    1. Three requests0 ns (illustrative)

      The three sequences start in the same time step. Their requests enter the queue in the order A, B, C.

      Follow the calls, values and waits
      1. data A0: Sequence A → Arbitration queue
      2. data B0: Sequence B → Arbitration queue
      3. data C0: Sequence C → Arbitration queue
      4. Waiting: Sequence A — waiting for a grant
      5. Waiting: Sequence B — waiting for a grant
      6. Waiting: Sequence C — waiting for a grant
    2. FIFO: the oldest wins

      The driver asks for an item. FIFO grants the oldest request, A0, without reading any priority.

      Follow the calls, values and waits
      1. call get_next_item: Driver → Arbitration queue
      2. data A0: Arbitration queue → Driver
      3. Released: Sequence A
    3. A asks again, at the back

      When A0 is done, A's next request joins the queue behind B0 and C0. B0 and C0 win next, so the three take turns: A0 B0 C0 A1 B1 C1 A2 B2 C2.

      Follow the calls, values and waits
      1. data A1: Sequence A → Arbitration queue
      2. data B0: Arbitration queue → Driver
      3. data C0: Arbitration queue → Driver
      4. Released: Sequence B
      5. Released: Sequence C
    4. STRICT_FIFO: the same queue

      The third scenario starts the same way. STRICT_FIFO counts only the highest-priority requests, so C0 wins although it is the newest.

      Follow the calls, values and waits
      1. data A0: Sequence A → Arbitration queue
      2. data B0: Sequence B → Arbitration queue
      3. data C0: Sequence C → Arbitration queue
      4. data C0: Arbitration queue → Driver
      5. Waiting: Sequence A — waiting for a grant
      6. Waiting: Sequence B — waiting for a grant
      7. Released: Sequence A
      8. Released: Sequence B
      9. Released: Sequence C
    5. C keeps winning

      C's next request is in the queue before each arbitration, so C1 and C2 follow. Then B, then A: C0 C1 C2 B0 B1 B2 A0 A1 A2.

      Follow the calls, values and waits
      1. data C1: Sequence C → Arbitration queue
      2. data C1: Arbitration queue → Driver
      3. data C2: Sequence C → Arbitration queue
      4. data C2: Arbitration queue → Driver
    Read the diagram as text
    • Sequence A: priority 100
    • Sequence B: priority 200
    • Sequence C: priority 300
    • Arbitration queue: oldest request first
    • Driver: one item per clock
    • Sequence A → Arbitration queue: start_item
    • Sequence B → Arbitration queue: start_item
    • Sequence C → Arbitration queue: start_item
    • Arbitration queue → Driver: granted item

    The queue is the same in both runs. Only the rule that picks from it changed.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Priority always matters, even in the default mode."

    FIFO, the default, sends items in request order whatever their priority.

    Source Verification Academy, UVM Cookbook, Sequences/Arbitration (PDF p275)

  3. What IfStrict Priority and a Starving SequenceL2 Mid-Level

    What if a high-priority sequence keeps asking under UVM_SEQ_ARB_STRICT_FIFO?

    Reveal the Model Answer

    Lower priorities wait until it stops asking, so they starve. That is fine for a short interrupt burst, but not for long background traffic. UVM_SEQ_ARB_WEIGHTED shares the driver in proportion to priority, with no bound on any one wait. If a low-priority sequence has a deadline, check it with a timeout.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Priorities work in the default mode."

    FIFO, the default, never reads priorities. Set a strict or weighted mode with set_arbitration() first.

    Source Lesson U5.1 key points

  4. What IfAn Interrupt Service SequenceL2 Mid-Level

    What if the urgent sequence services an interrupt? How do you structure it?

    Reveal the Model Answer

    Run it in the background for the whole test. It waits for the interrupt, then grabs the bus sequencer and reads the status. It services the cause, clears the interrupt and ungrabs. Normal traffic pauses only for that window. Use grab when the interrupt must jump ahead of queued traffic, and lock when waiting a turn is acceptable.

    Self-Check

    Tick each key point you covered in your answer.

    Wrong Altitude

    "Poll the interrupt line from the main sequence between items."

    It ties the main scenario to interrupt timing, and the response latency depends on how long each item takes.

    Source Accellera, UVM 1.1 User's Guide, §6.5.1 Implementing Complex Scenarios: interrupt sequences (PDF p149)

  5. Prove ItSolve U16.4.1: The Interrupt That Clears ItselfSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem U16.4.1

  6. Break ItTwo Locks That DeadlockedL3 Senior

    How can two virtual sequences that each lock two sequencers hang the test?

    Reveal the Model Answer

    Sequence one locks the AXI sequencer, then asks for the UART one. Sequence two has already locked UART and now asks for AXI. Each lock() blocks until it is granted, so both wait forever. Always take locks in one fixed order across the bench, or take one lock at a time.

    Two Processes, Two Keys, Opposite OrdersThe same cycle with plain semaphores: two processes take two keys in opposite orders. A dashed badge is a get(1) call; a solid badge is a key handed over or returned.
    Two processes, two keys, opposite ordersThe same cycle with plain semaphores: two processes take two keys in opposite orders. A dashed badge is a `get(1)` call; a solid badge is a key handed over or returned. resource bus semaphore, one key process P1 bus, then mem process P2 mem, then bus resource mem semaphore, one key get / putget / putget / putget / put
    1. Each takes its first keyt=0 (illustrative)

      P1 takes the bus key and P2 takes the mem key. Both succeed, because each semaphore had its key free.

      Follow the calls, values and waits
      1. data bus key: bus → P1
      2. data mem key: mem → P2
    2. Each asks for the other keyt=1 (illustrative)

      P1 calls mem.get(1) and P2 calls bus.get(1). Each key is held by the other process, so both block.

      Follow the calls, values and waits
      1. call get(1): P1 → mem
      2. call get(1): P2 → bus
      3. Waiting: P1 — waiting for mem
      4. Waiting: P2 — waiting for bus
    3. Deadlockt=1 and later (illustrative)

      Neither process can reach its put(), so neither key ever returns. If nothing else is scheduled, the simulation just ends here.

    4. One order: both start with bust=0 (illustrative)

      Now both processes take bus first. One of them, here P1, gets it. P2 blocks at bus while it holds nothing.

      Follow the calls, values and waits
      1. data bus key: bus → P1
      2. call get(1): P2 → bus
      3. Waiting: P2 — waiting for bus
      4. Released: P1
      5. Released: P2
    5. P1 finishes, then P2 runst=1 and later (illustrative)

      mem is free, so P1 takes it, finishes its work and returns both keys. P2 then gets bus, and mem after it.

      Follow the calls, values and waits
      1. data mem key: mem → P1
      2. return mem key: P1 → mem
      3. return bus key: P1 → bus
      4. data bus key: bus → P2
      5. Released: P2
    Read the diagram as text
    • bus: semaphore, one key
    • P1: bus, then mem
    • P2: mem, then bus
    • mem: semaphore, one key
    • P1 → bus: get / put
    • P1 → mem: get / put
    • P2 → bus: get / put
    • P2 → mem: get / put

    A deadlock needs a cycle of waits. Two sequences that lock two sequencers in opposite orders form the same cycle. One order for everyone breaks it.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "UVM detects the deadlock and reports it."

    Nothing detects it. lock() simply blocks, and the test runs until the timeout.

    Source Verification Academy, UVM Cookbook, Sequences/LockGrab: lock is blocking until granted (PDF p284)

  7. Prove ItSolve U5.1.1: Catch the Sequencers That Grant Out of TurnSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem U5.1.1

What People Get Wrong

Common wrong answers and why they fail.

Misconception

"grab() interrupts the item the driver is already driving."

Nothing takes back an item the driver already holds. In the run, A1 finishes before U0 starts.

Source Measured: the run on this question (A1 completes before U0), uvm-core 2020.3.1 on Verilator 5.052

Tool as Truth

"A lock always waits until every earlier request has been served."

That is how the Cookbook describes it. uvm-core 2020.3.1 grants an unblocked lock at the next arbitration, so B1 waited here.

Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequencer_base.svh, grant_queued_locks: find_first_index of any unblocked SEQ_TYPE_LOCK request

Missed Corner

Calls lock() and forgets unlock() on an error path.

The sequencer stays locked, and every other sequence waits forever.

Source Verification Academy, UVM Cookbook, Sequences/LockGrab: unlock must be called (PDF p284)

Half-Truth

"Just give the urgent sequence a higher priority instead."

Under the default FIFO arbitration, priority is ignored. It needs a strict or weighted mode, and it still is not exclusive.

Source Verification Academy, UVM Cookbook, Sequences/Arbitration: SEQ_ARB_FIFO ignores priority (PDF p275)

Learn It, Then Prove It