Hey,
My simulation runs super-slow when I toggle interface lines with UVM based methods versus using Xilinx verilog IP to toggle the lines.
I attached the code I think is relevant below (some code was removed for easier reading).
Following the profiling process (in the two photos attached), it seems like “.start” method in uvm_sequence_base.svh, which I call a lot of times, causes that degrade (very high usage of stack).
I use Questa 2020.4 as my simulator.
Thanks.
typedef uvm_sequence #(uvm_sequence_item) uvm_virtual_sequence;
class VirtualSequence extends uvm_virtual_sequence;
`uvm_object_utils(VirtualSequence)
typedef AxiStreamMasterBaseSequence#(AXI_STREAM_DATA_WIDTH_IN) AxiStreamMasterBaseSeq;
typedef AxiStreamSlaveBaseSequence#(AXI_STREAM_DATA_WIDTH_OUT) AxiStreamSlaveBaseSeq;
typedef axi_lite_item #(AXI_LITE_ADDR_WIDTH,AXI_LITE_DATA_WIDTH) axi_lite_item_t;
operation_t axiLiteOp;
MemoryConfig memConfig;
GenericHandlers genericHandlers;
rand frameBufferOps frameBufferOp;
rand int camIDPressure;
int camID;
int lineCount;
virtual AxiStreamIf#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterVif;
AxiStreamSlaveSanitySequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamSlaveSanitySeq;
AxiStreamSlaveNormalSequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamSlaveNormalSeq;
AxiStreamMasterSanitySequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterSanitySeq;
AxiStreamMasterValidReadyRandomSequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterValidReadyRandomSeq;
AxiStreamMasterDropValidSequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterDropValidSeq;
AxiStreamMasterResetSequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterResetSeq;
AxiStreamMasterFullRandomSequence#(AXI_STREAM_DATA_WIDTH_OUT) axiStreamMasterFullRandomSeq;
AxiLiteGenericSequence#(AXI_LITE_ADDR_WIDTH, AXI_LITE_DATA_WIDTH) axiLiteWrSequence;
AxiLiteGenericSequence#(AXI_LITE_ADDR_WIDTH, AXI_LITE_DATA_WIDTH) axiLiteRdSequence;
FrameBufferEnvConfig frameBufferEnvCfg;
uvm_sequencer#(axi_lite_item_t) axiLiteSeqrTPG;
uvm_sequencer#(axi_lite_item_t) axiLiteSeqrDUT;
uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) axiStreamMasterSeqr;
uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) axiStreamSlaveSeqr;
uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) resetSeqr;
uvm_event MemoryConfigUpdated;
uvm_event resetEvent;
bit[7:0] backgroundPattern;
extern function new(string name="VirtualSequence");
extern function void createSequenceObjects();
extern function void initAndUpdateReset();
extern function logic[AXI_STREAM_DATA_WIDTH_OUT-1:0] getNextData();
extern task sanity();
extern task setTuser(AxiStreamMasterBaseSeq axiMasterSeq, AxiStreamSlaveBaseSeq axiSlaveSeq);
extern task resetDUT();
extern task triggerReset();
extern task fullRandom(sofE isSOF = SOF_ENABLE);
extern task transmitPixels(AxiStreamMasterBaseSeq axiMasterSeq1, AxiStreamMasterBaseSeq axiMasterSeq2, AxiStreamSlaveBaseSeq axiSlaveSeq1, AxiStreamSlaveBaseSeq axiSlaveSeq2);
extern task startAxiLiteSequence(uvm_sequencer#(axi_lite_item_t) axiSeqr, AxiLiteGenericSequence#(AXI_LITE_ADDR_WIDTH, AXI_LITE_DATA_WIDTH) axiSeq, logic[31:0] address, logic[31:0] data, operation_t op);
extern task startSlaveSequence(uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) slaveSeqr, AxiStreamSlaveBaseSeq slaveSeq, logic tready = 1'bz);
extern task startMasterSequence(uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) masterSeqr, AxiStreamMasterBaseSeq masterSeq, logic tuser = 1'bz, logic tvalid = 1'bz, logic tlast = 1'bz, logic[AXI_STREAM_DATA_WIDTH_OUT-1:0] tdata = {AXI_STREAM_DATA_WIDTH_OUT{1'bz}}, logic reset = 1'bz, bit takeNewData = 1'b1);
extern virtual task body();
endclass : VirtualSequence
function VirtualSequence::new(string name="VirtualSequence");
super.new(name);
endfunction
function void VirtualSequence::createSequenceObjects();
axiLiteWrSequence = AxiLiteGenericSequence#(AXI_LITE_ADDR_WIDTH, AXI_LITE_DATA_WIDTH)::type_id::create("axiLiteWrSequence");
axiStreamSlaveSanitySeq = AxiStreamSlaveSanitySequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamSlaveSanitySeq");
axiStreamSlaveNormalSeq = AxiStreamSlaveNormalSequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamSlaveNormalSeq");
axiStreamMasterDropValidSeq = AxiStreamMasterDropValidSequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamMasterDropValidSeq");
axiStreamMasterSanitySeq = AxiStreamMasterSanitySequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamMasterSanitySeq");
axiStreamMasterValidReadyRandomSeq = AxiStreamMasterValidReadyRandomSequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamMasterValidReadyRandomSeq");
axiStreamMasterResetSeq = AxiStreamMasterResetSequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamMasterResetSeq");
axiStreamMasterFullRandomSeq = AxiStreamMasterFullRandomSequence#(AXI_STREAM_DATA_WIDTH_OUT)::type_id::create("axiStreamMasterFullRandomSeq");
endfunction
function logic[AXI_STREAM_DATA_WIDTH_OUT-1:0] VirtualSequence::getNextData();
return $urandom_range(0,INPUT_MAX_VAL);
endfunction
task VirtualSequence::startSlaveSequence(uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) slaveSeqr, AxiStreamSlaveBaseSeq slaveSeq, logic tready = 1'bz);
if (tready !== 1'bz)
slaveSeq.tready = tready;
slaveSeq.start(slaveSeqr);
endtask
task VirtualSequence::startMasterSequence(uvm_sequencer#(AxiStreamSequenceItem#(AXI_STREAM_DATA_WIDTH_OUT)) masterSeqr, AxiStreamMasterBaseSeq masterSeq, logic tuser = 1'bz, logic tvalid = 1'bz, logic tlast = 1'bz, logic[AXI_STREAM_DATA_WIDTH_OUT-1:0] tdata = {AXI_STREAM_DATA_WIDTH_OUT{1'bz}}, logic reset = 1'bz, bit takeNewData = 1'b1);
if (reset !== 1'bz)
masterSeq.reset = reset;
if (tuser !== 1'bz)
masterSeq.tuser = tuser;
if (tvalid !== 1'bz)
masterSeq.tvalid = tvalid;
if (tlast !== 1'bz)
masterSeq.tlast = tlast;
masterSeq.start(masterSeqr);
endtask
task VirtualSequence::transmitPixels(AxiStreamMasterBaseSeq axiMasterSeq1, AxiStreamMasterBaseSeq axiMasterSeq2, AxiStreamSlaveBaseSeq axiSlaveSeq1, AxiStreamSlaveBaseSeq axiSlaveSeq2);
while(!axiStreamMasterVif.tlast) begin
fork
begin
startMasterSequence(axiStreamMasterSeqr, axiMasterSeq1, 0);
end
begin
startSlaveSequence(axiStreamSlaveSeqr, axiSlaveSeq1);
end
join
if (frameBufferEnvCfg.axiStreamMasterCfg[camID].disableEOL && axiStreamMasterVif.pixelCounterOut % IMG_WIDTH == 0 && axiStreamMasterVif.pixelCounterOut != 0) begin
lineCount++;
if (lineCount == IMG_HEIGHT) begin
return;
end
end
end
lineCount++;
while (axiStreamMasterVif.tlast) begin
fork
begin
startMasterSequence(axiStreamMasterSeqr, axiMasterSeq2, 0);
end
begin
if (lineCount >= IMG_HEIGHT-1)
startSlaveSequence(axiStreamSlaveSeqr, axiStreamSlaveSanitySeq);
else
startSlaveSequence(axiStreamSlaveSeqr, axiSlaveSeq2);
end
join
end
endtask : transmitPixels
task VirtualSequence::setTuser(AxiStreamMasterBaseSeq axiMasterSeq, AxiStreamSlaveBaseSeq axiSlaveSeq);
fork
begin
startSlaveSequence(axiStreamSlaveSeqr, axiSlaveSeq);
end
begin
startMasterSequence(axiStreamMasterSeqr, axiMasterSeq, 1, 1);
end
join
endtask
function void VirtualSequence::initAndUpdateReset();
lineCount = 0;
genericHandlers.finishedRandomizingMemConfig = 0;
endfunction : initAndUpdateReset
task VirtualSequence::sanity();
int lineCount;
// Raise tready
startSlaveSequence(axiStreamSlaveSeqr, axiStreamSlaveSanitySeq);
axiStreamMasterVif.clockingBlock.tdata <= getNextData();
//Set tuser, tvalid
startMasterSequence(axiStreamMasterSeqr, axiStreamMasterSanitySeq, 1, 1);
// transmit IMG_HEIGHT lines with IMG_WIDTH pixels per line
repeat(IMG_HEIGHT) begin
transmitPixels(axiStreamMasterSanitySeq, axiStreamMasterSanitySeq, axiStreamSlaveSanitySeq, axiStreamSlaveSanitySeq);
end
//drop valid at end of frame
startMasterSequence(axiStreamMasterSeqr, axiStreamMasterDropValidSeq, 1'b0, 1'b0, 1'b0,{ParametersPkg::AXI_STREAM_DATA_WIDTH_OUT{1'bz}}, 1'bz, 1'b0);
lineCount = 0;
endtask : sanity
task VirtualSequence::fullRandom(sofE isSOF = SOF_ENABLE);
int resetLine;
bit isReset;
// randomize if there is reset in this frame
void'(std::randomize(isReset) with {isReset dist{0:= 8, 1:=2};});
//randomize the line in which reset will be activated if isReset=1
void'(std::randomize(resetLine) with {resetLine inside{[1:IMG_HEIGHT-1]};});
axiStreamMasterVif.clockingBlock.tdata <= getNextData();
//decide whether to activate SOF or not
if (isSOF != SOF_DISABLE) begin
setTuser(axiStreamMasterValidReadyRandomSeq, axiStreamSlaveNormalSeq);
end
// transmit IMG_HEIGHT lines with IMG_WIDTH pixels per line
repeat (IMG_HEIGHT) begin
transmitPixels(axiStreamMasterValidReadyRandomSeq, axiStreamMasterValidReadyRandomSeq, axiStreamSlaveNormalSeq, axiStreamSlaveNormalSeq);
if (isReset && camID == 0) begin
if (lineCount == resetLine) begin
resetDUT();
break;
end
end
end
startMasterSequence(axiStreamMasterSeqr, axiStreamMasterDropValidSeq, 1'b0, 1'b0, 1'b0,{AXI_STREAM_DATA_WIDTH_OUT{1'bz}}, 1'bz, 1'b0);
lineCount = 0;
endtask
task VirtualSequence::triggerReset();
fork
forever @(posedge axiStreamMasterVif.clk) begin
if (!axiStreamMasterVif.reset)
resetEvent.trigger();
end
join_none
endtask : triggerReset
task VirtualSequence::resetDUT();
startMasterSequence(resetSeqr, axiStreamMasterResetSeq, , 0, 0);
`uvm_info(get_type_name(), "resetting DUT...", UVM_LOW)
// re-transmit frame, openImageFile() will open the same file for re-transmission
axiStreamMasterSeqr.stop_sequences();
axiStreamSlaveSeqr.stop_sequences();
endtask : resetDUT
task VirtualSequence::body();
int randomDelayBetweenFrames;
if(!uvm_config_db #(virtual AxiStreamIf#(AXI_STREAM_DATA_WIDTH_OUT))::get(.cntxt(null), .inst_name(""), .field_name($sformatf("axiStreamMasterVif_%0d", camID)), .value(axiStreamMasterVif))) begin
`uvm_fatal(get_name(), "NO VIF::interface not found")
end
resetEvent = uvm_event_pool::get_global("resetEvent");
createSequenceObjects();
triggerReset();
configDUT();
wait(axiStreamMasterVif.reset);
case (frameBufferOp)
USE_TPG : begin
`uvm_info(get_type_name(), $sformatf("Running operation %0s..", frameBufferOp.name()), UVM_LOW)
configAndStartTestPatternGenerator();
end
RANDOM_AXI_STREAM : begin
`uvm_info(get_type_name(), $sformatf("Running operation %0s..", frameBufferOp.name()), UVM_LOW)
repeat(13) begin
fullRandom();
void'(std::randomize(randomDelayBetweenFrames) with {randomDelayBetweenFrames inside {[10:100]};});
repeat(randomDelayBetweenFrames) @(posedge axiStreamMasterVif.clk);
end
end
endcase
endtask : body
class AxiStreamMasterBaseSequence#(parameter DATA_WIDTH) extends uvm_sequence #(AxiStreamSequenceItem#(DATA_WIDTH));
`uvm_object_param_utils(AxiStreamMasterBaseSequence#(DATA_WIDTH))
//Environment Config
AxiStreamSequenceItem#(DATA_WIDTH) req;
bit tuser;
bit tvalid;
bit tready;
bit tlast;
bit reset;
bit imageFromFile;
logic[DATA_WIDTH-1:0] tdata;
function new(string name="AxiStreamMasterBaseSequence");
super.new(name);
req = AxiStreamSequenceItem#(DATA_WIDTH)::type_id::create("req");
endfunction
task body();
endtask
endclass
class AxiStreamMasterBaseSequence#(parameter DATA_WIDTH) extends uvm_sequence #(AxiStreamSequenceItem#(DATA_WIDTH));
`uvm_object_param_utils(AxiStreamMasterBaseSequence#(DATA_WIDTH))
//Environment Config
AxiStreamSequenceItem#(DATA_WIDTH) req;
bit tuser;
bit tvalid;
bit tready;
bit tlast;
bit reset;
bit imageFromFile;
logic[DATA_WIDTH-1:0] tdata;
function new(string name="AxiStreamMasterBaseSequence");
super.new(name);
req = AxiStreamSequenceItem#(DATA_WIDTH)::type_id::create("req");
endfunction
task body();
endtask
endclass
class AxiStreamMasterFullRandomSequence#(parameter DATA_WIDTH) extends AxiStreamMasterBaseSequence#(DATA_WIDTH);
`uvm_object_param_utils(AxiStreamMasterFullRandomSequence#(DATA_WIDTH))
function new(string name="AxiStreamMasterFullRandomSequence");
super.new(name);
endfunction
task body();
super.body();
start_item(req);
//`uvm_info(get_type_name(), $psprintf("body started"), UVM_DEBUG)
if (imageFromFile) begin
req.tdata.rand_mode(0);
req.tdata = this.tdata;
end
void'(std::randomize(reset) with {reset dist {0:=1, 1:=10000};});
if(!req.randomize() with {
req.tvalid dist {1:=99, 0:=1};
req.tvalidNumOfCyclesDelay inside {[0:2]};
req.tuser == local::tuser;
req.tlast == local::tlast;
req.reset == local::reset;
(req.reset == 0) -> req.resetNumOfCyclesDelay inside {[50:100]};
}) `uvm_fatal(get_type_name(), "inline randomization error")
finish_item(req);
//`uvm_info(get_type_name(), $psprintf("body finished"), UVM_DEBUG)
endtask
endclass
class AxiStreamSlaveNormalSequence#(parameter DATA_WIDTH) extends AxiStreamSlaveBaseSequence#(DATA_WIDTH);
`uvm_object_param_utils(AxiStreamSlaveNormalSequence#(DATA_WIDTH))
bit treadySwitch;
function new(string name="AxiStreamSlaveNormalSequence");
super.new(name);
endfunction
task body();
super.body();
start_item(req);
//`uvm_info(get_type_name(), $psprintf("body started"), UVM_DEBUG)
if (treadySwitch) begin
req.tready = 1;
end
else begin
// Only tready is interesting here
if(!(req.randomize() with {
req.treadyNumOfCyclesDelay inside {[0:2]};
req.tready dist {1:=95, 0:=5};
})) `uvm_fatal(get_type_name(), "inline randomization error")
end
treadySwitch = (req.tready == 0) ? 1 : 0;
finish_item(req);
//`uvm_info(get_type_name(), $psprintf("body finished"), UVM_DEBUG)
endtask