Hi Moderators,
In my AXI4 Tb there exists a sequence ‘seq’ which is created once and whenever there is a call to write or read API, seq_h.start is called
Within it’s body() task ::
use_response_handler(1);
req = user_seq_item::create("req",this);
start_item(req);
// req is randomized
finish_item(req);
endtask
virtual function void response_handler( uvm_sequence_item response);
// Some logic here
endfunction
Note that during each call to seq.start it doesn’t wait for response
The issue with above code is even when the response is sent by Driver (via the sequencer) the response_handler is never called
On debugging the UVM Base Class Library I notice
function void uvm_sequencer_param_base::put_response (RSP t);
....
sequence_ptr = m_find_sequence(t.get_sequence_id());
if (sequence_ptr != null) begin
// If the response_handler is enabled for this sequence, then call the response handler
if (sequence_ptr.get_use_response_handler() == 1) begin
sequence_ptr.response_handler(t);
return;
end
sequence_ptr.put_response(t);
end
Response handler is called only if m_find_sequence returns non-null
On further debugging I observe the implementation of task ‘start’ is as follows
// Within uvm_sequence_base::start
........................
m_sequence_state = UVM_BODY;
#0;
body();
m_sequence_state = UVM_ENDED;
#0;
if (parent_sequence != null) begin
parent_sequence.post_do(this);
end
if (call_pre_post == 1) begin
m_sequence_state = UVM_POST_BODY;
#0;
post_body();
end
m_sequence_state = UVM_POST_START;
#0;
post_start();
// Drop the objection if enabled
if (get_automatic_phase_objection()) begin
m_safe_drop_starting_phase("automatic phase objection");
end
m_sequence_state = UVM_FINISHED;
#0;
end
join
if (m_sequencer != null) begin
m_sequencer.end_tr(this);
end
// Clean up any sequencer queues after exiting; if we
// were forcibly stoped, this step has already taken place
if (m_sequence_state != UVM_STOPPED) begin
if (m_sequencer != null)
m_sequencer.m_sequence_exiting(this);
end
#0; // allow stopped and finish waiters to resume
if ((m_parent_sequence != null) && (m_parent_sequence.children_array.exists(this))) begin
m_parent_sequence.children_array.delete(this);
end
old_automatic_phase_objection = get_automatic_phase_objection();
m_init_phase_daps(1);
set_automatic_phase_objection(old_automatic_phase_objection);
endtask
The call to function ‘m_sequence_exiting’ internally calls function ‘m_unregister_sequence’ which unregisters the sequence from the Sequencer.
Since the goal is to invoke the reponse_handler when the response is returned by the driver ( even after task ‘start’ ends ), one solution that I am thinking of is to override the virtual start task in my sequence.
In the overridden task ‘start’ I am thinking of ending the task once fork join completes
// Within the overriden task 'start'
..........
m_sequence_state = UVM_FINISHED;
#0;
end
join
// Doesn't call the following APIs ::
// m_sequencer.end_tr(this) ,
// m_sequencer.m_sequence_exiting(this); etc ...
endtask:start
[1] Would this be a correct approach ?
[2] Any alternate suggestions are welcome