Hi,
Suppose a UVMF generated top environment with 2 instances of the same interface (SPI for example), one configured as initiator, the other one as a responder. From top level sequence I initiate a read command using the register model method read(). The responder sees the read frame and needs to construct a response frame. For that the responder needs to know the value of the requested register in register model. Therefore the responder sequence (generated by UVMF) must have handle to the register model and use register model method get() to respond correctly. Since bench sequence base class has handles to both top environment and interface responder sequence, it would be the logical spot to assign the handle from one to the other.
Is this approach correct? If not, what is the common practice to access register model handle from responder sequence?
In reply to SamuelPetras:
Your approach is generally correct. Passing a handle to the register model from the initiator sequence to the responder sequence is a reasonable way to allow the responder sequence to access the register model and respond correctly based on the requested register value.
Here’s a brief overview of the approach:
Initiator Sequence Initiates Read Command: The initiator sequence, running on the initiator interface, initiates a read command using the register model.
Responder Sequence Responds Based on Register Model: When the responder sees the read frame, it needs to construct a response frame. To do this accurately, it requires knowledge of the requested register value from the register model.
Passing Register Model Handle: Since the responder sequence might not have direct access to the register model, it can receive a handle to the register model from the initiator sequence or a higher-level component.
This approach ensures that the responder sequence can access the register model and use its methods, such as read(), to get the value of the requested register and construct an appropriate response.
Here’s a simplified illustration of how this might look in your UVM environment:
class top_env extends uvm_env;
// Assuming you have an interface instance named spi_if
spi_if initiator_if;
spi_if responder_if;
your_register_model reg_model;
function new(string name, uvm_component parent);
super.new(name, parent);
initiator_if = new("initiator_if", this);
responder_if = new("responder_if", this);
reg_model = new("reg_model", this);
endfunction
virtual task run_phase(uvm_phase phase);
// Create a sequence and pass the register model handle
your_sequence seq;
seq = your_sequence::type_id::create("seq");
seq.initiator_if = initiator_if;
seq.responder_if = responder_if;
seq.reg_model = reg_model;
seq.start(m_sequencer);
seq.wait_for_sequence();
endtask
endclass
class your_sequence extends uvm_sequence;
spi_if initiator_if;
spi_if responder_if;
your_register_model reg_model;
task body();
// Initiator sequence initiates a read command
// ...
// Pass the register model handle to the responder sequence
responder_seq rsp_seq;
rsp_seq = responder_seq::type_id::create("rsp_seq");
rsp_seq.initiator_if = initiator_if;
rsp_seq.responder_if = responder_if;
rsp_seq.reg_model = reg_model;
rsp_seq.start(m_sequencer);
rsp_seq.wait_for_sequence();
endtask
endclass
class responder_seq extends uvm_sequence;
spi_if initiator_if;
spi_if responder_if;
your_register_model reg_model;
task body();
// Access the register model to get the requested register value
// Construct a response based on the register value
// ...
endtask
endclass
This structure allows you to maintain a clean separation of concerns and provide the necessary information to the responder sequence without violating encapsulation principles.