Difficulty: Medium · Puzzle 3 of 6 · Topic: Sequences and Sequencers
UVM 1.2. Sequence A already holds the sequencer by lock() (granted). While A runs, B calls lock() and then C calls grab() on the same sequencer. A then calls unlock(). Which sequence gets exclusive use next, and which after that?
// A (running, lock already granted)
lock(m_sequencer);
...
unlock(m_sequencer);
// B, arriving later
lock(m_sequencer);
...
unlock(m_sequencer);
// C, arriving after B
grab(m_sequencer);
...
ungrab(m_sequencer);A grab is a lock that is allowed to jump the queue of waiting requests, but not to take the sequencer away from the current owner.
1) lock requests wait their turn in the arbitration queue: B is queued at the back.
2) A grab request is inserted at the front of that queue, ahead of waiting lock requests such as B.
3) Neither B nor C can take the sequencer away from A, which owns it until it unlocks.
4) After A's unlock, C (grab) is granted before B (lock); after C's ungrab, B is granted. Order: C, then B.Answer: C gets the sequencer first (grab jumps ahead of the waiting lock), then B.
Checked by running the code in Verilator 5; the printed output supports the answer above. Hand-written model of the documented UVM 1.2 behaviour in plain SystemVerilog (Verilator), not the UVM library: an arbitration queue where lock does push_back and grab does push_front, as documented for uvm_sequencer_base.
grab does not pre-empt a lock that has already been granted: it only wins against requests still waiting; and a sequence that holds a lock blocks everyone else's items, including ones its own children would need if it forgets to unlock.
What do you do when a sequence must always run exclusively, e.g. a reset sequence, even if another lock is waiting? Model answer: have it grab the sequencer (so it goes ahead of all waiting requests) and ungrab in the same body on every exit path, preferably a single exit, or use a sequencer-wide mechanism such as an arbitration mode with priorities; never rely on lock for urgency.