Some of the stimulus I provide my DUT with requires some (light) ordering. My basic solution was this:
class tb_sequence extends uvm_sequence#(tb_transaction);
`uvm_object_utils(tb_sequence)
function new(string name="");
super.new(name);
endfunction : new
task body();
tb_transaction tx;
tb_transaction tmp_tx;
repeat(1000) begin
// Generate next transaction
assert(tx.randomize());
// Next transaction requires a special mode, set it first before sending the transaction
if ( tx.cmd == SPECIAL_MODE_CMD ) begin
// Generate special mode change transaction
start_item(tmp_tx);
assert(tmp_tx.randomize() with { cmd == SET_SPECIAL_MODE; });
finish_item(tmp_tx);
end
// Send special mode transaction
start_item(tx);
finish_item(tx);
end
endtask : body
endclass : tb_sequence
Is this a reasonable approach, or is there something better? I am working on my first full UVM testbench as a lone verification engineer, so I’m not sure about a lot of best practices or common pitfalls to avoid.
repeat(1000) begin
// Generate next transaction
assert(tx.randomize());
// Next transaction requires a special mode, set it first before sending the transaction
if ( tx.cmd == SPECIAL_MODE_CMD ) begin
// Generate special mode change transaction
start_item(tmp_tx);
assert(tmp_tx.randomize() with { cmd == SET_SPECIAL_MODE; });
finish_item(tmp_tx);
end
// Send special mode transaction
start_item(tx);
finish_item(tx);
end
Do you really start with a completely unconstrained seq_item and then you want to genearte another one where the data member cmd = SET_SPECIAL_MODE:
Even when you want to do this you can remove the if construct.
The idea is that DUT has no automatic response, it just responds to a sequence of commands. So when a special command is sent, I don’t want the DUT and the TB getting out of sync, and I force that mode automatically.
My “correct” (non-error test) order of commands is:
SET_SPECIAL_MODE
SPECIAL_MODE_CMD
…
n. EXIT_SPECIAL_MODE
Since these are both discrete sequence items, I was using the sequence to enforce this order; it this good practice?
Another option is to set the default constraints in the transaction to the distribution {SET_SPECIAL_MODE := 10; SPECIAL_MODE_CMD := 1; EXIT_SPECIAL_MODE := 1}, use the monitor to detect if the mode was entered, and the sequencer can then adjust the distribution to {SET_SPECIAL_MODE := 1; SPECIAL_MODE_CMD := 10; EXIT_SPECIAL_MODE := 10}. This can be communicated using ports, analysis ports, uvm_config, or virtual sequences. I don’t like using the virtual sequences because this interface is wholly independent, so there shouldn’t be any synchronization or timing problems.
I could also use multiple sequences with different transaction distributions and use the sequencer to properly decide which to use based on the mode from the monitor as well.