Below is my sample virtual sequence from which I am driving two other sequences. Trying to lock a sequence on a particular seqr from p_seqr. But it is not working as expected. Can you please guide me in this?
//sample code
typedef base_seq uvm_sequence#(my_sequence_item);
class my_seq extends base_seq;
`uvm_declare_p_sequencer(virtual_seqr); //virtual sequencer, which has so many sequencer pointers and config objects
function new ();
...
virtual task body();
SEQA seqa;
SEQB seqb;
fork
begin
@(p_sequencer.vif.interrupt); //not exactly this way, but waiting for some uvm_event
sqa = SEQA::type_id_create ("seqa");
assert (sqa.randomize());
p_sequencer.seqr.lock( sqa ); // Lock is not working - start menthod not getting called
sqa.start(p_sequencer.seqr);
p_sequencer.seqr.unlock( sqa );
end
begin
sqb = SEQB::type_id_create ("seqb");
assert (sqb.randomize());
sqb.start(p_sequencer.seqr);
end
join
endtask
endclass
Can you explain more about what you mean by ‘not working as expected’? What does happen vs what do you expect to happen?
From the documentation: A lock request will be arbitrated the same as any other request. A lock is granted after all earlier requests are completed and no other locks or grabs are blocking this sequence.
Thanks for the reply. sqa.start(p_sequencer.seqr); is not executing at all. only seqb is completed and the test is getting killed. If I remove the lock on the sequencer, both sequences are interleaving.
In all the examples, I referred to, the lock is always called from the body of a sequence which is generating seq_items. Is it not possible to call lock from a virtual sequence as in my case?
Looking at the UVM Cookbook regarding locks, you want to pass the sequence doing the locking instead of the sub-sequence.
Try this:
virtual task body();
SEQA seqa;
SEQB seqb;
fork
begin
@(p_sequencer.vif.interrupt); //not exactly this way, but waiting for some uvm_event
sqa = SEQA::type_id_create ("seqa");
assert (sqa.randomize());
p_sequencer.seqr.lock( this ); // Lock the sub-sequencer by 'this' sequence
sqa.start(p_sequencer.seqr);
p_sequencer.seqr.unlock( this ); // Unlock the sub-sequencer
end
begin
sqb = SEQB::type_id_create ("seqb");
assert (sqb.randomize());
sqb.start(p_sequencer.seqr);
end
join
endtask
Without a running representative testcase, it is difficult to isolate the issue. The lock() call waits until the sequence is arbitrated, so it’s possible that other sequences are being run which competes with this sequence.
Have you tried using grab()/ungrab()? This will preempt the arbitration and take immediate control of the sequencer.
In reply to cgales:
I tried with
p_sequencer.seqr.lock( this ); // Lock the sub-sequencer by ‘this’ sequence
but still, it is not working. Could you please help?
The second argument of the call to start is important because it identifies the calling sequence, whichis the virtual sequence that currently has the lock, as the parent of child sequence; Without thisargument, the child sequence would be blocked from running until the virtual sequence had called unlock, leading to immediate deadlock.
It is challenging to pinpoint the problem without a functioning representative testcase. It’s conceivable that other sequences are being performed that compete with this sequence since the lock() function waits until the sequence is arbitrated.
In reply to Agnes0:
This is not correct. The lock sequence does not belong to the arbitration process. It has precedence over all other sequences.
See here a running example
According to the documentation for uvm_sequence_base for lock()
A lock request will be arbitrated the same as any other request. A lock is granted after all earlier requests are completed and no other locks or grabs are blocking this sequence.
As opposed to grab()
A grab request is put in front of the arbitration queue. It will be arbitrated before any other requests. A grab is granted when no other grabs or locks are blocking this sequence.
My understanding of the lock is that the call to lock suspends any sequences already running on the sequencer and gives the virtual sequence that called lock exclusive control over the sequencer until unlock has ben called.
Have a look at uvm_sequencer_base.svh source code. Both lock() and unlock() call m_lock_req(), just with a different flag. Here is the key code that adds the requested sequence to the queue.
task m_lock_req(..., bit lock);
...
if (lock == 1) begin
// Locks are arbitrated just like all other requests
arb_sequence_q.push_back(new_req);
end else begin
// Grabs are not arbitrated - they go to the front
arb_sequence_q.push_front(new_req);
end
...
endtask
Thanks Chris.
I visited the UVM Standard.
It says:
“A lock request is arbitrated the same as any other request. A lock is granted after all previously arbitrated requests are completed and no other locks or grabs are blocking this sequnnce.”
Nothing is suspended.
We may be in total agreement. To me, suspend means that a process or thread stops running because of an outside action, like how on a CPU, a hardware interrupt suspends the current process. This is not what is happening with sequences. A sequence calls start_item() and fully expects to be blocked if the driver is still sending another item, or the sequencer is locked or grabbed.
If sequence A calls lock() after sequences B and C call start_item(), the current items for B and C won’t be blocked (or suspended). When B and C make their next calls to start_item(), those will be blocked until A unlocks.
I am assuming FIFO arbitration. I have not tried this with other arbitration algorithms.