There are two DUTs I have to verify. and what I need to do is compare the transactions between these two DUTs. ( one is RTL design, named SIM. And the other is based on cpp file, named EMU) For SIM, it’s just like a traditional DUT and for EMU we just use DPI function to instance.
Here is my question. When I try to use queue to record each transaction in EMU and SIM in order to compare in testbench, I met a problem needed your help. (Please notice that the EMU and the SIM are concurrent running)
Firstly I create a emu_queue[] to record each transaction for EMU and create a sim_queue[] to record each transaction for SIM.
And waiting both size of emu_queue and sim_queue are not zero, then pop up the first element in the queues to compare. (because sometimes they won’t be have data in the same point)
For example, a1, a2, a3 for EMU are pushed into emu_queue[] one by one. And then a1 for SIM is pushed into sim_queue[]. So I can start to pop up the first element for both queues to compare. But the data popped in emu_queue[$] is not a1 but a3. It seems to me that the emu_queue is not effected to reserve all transactions happened before this point.
Here is some snippet code below. In write_sim() and write_emu(), I will push_back the tr into the sim_queue and emu_queue separately. And in task compare() we will wait both size is not zero. And then pop_front the first element to compare.
...
my_trans emu_queue[$];
my_trans sim_queue[$];
...
task write_sim(my_trans tr);
sim_queue.push_back(tr);
endtask
task write_emu(my_trans tr);
emu_queue.push_back(tr);
endtask
task compare(uvm_phase phase);
tr = new(“tr”);
tr_emu = new(“tr_emu”);
forever begin
wait(emu_queue.size && sim_queue.size)
tr_emu = emu_queue.pop_front();
tr = sim_queue.pop_front();
tr.print();
tr_emu.print();
if (!tr.compare(tr_emu)) begin
`uvm_fatal(get_type_name(), " transcation is not match")
end
...
end
endtask
Without seeing the code which writes the transactions to the queues, I’m going to guess that you are re-using the same transaction handles. Make sure that you clone the transactions prior to writing to the queue, or create a new transaction after writing to the queue.
The clone or new construction should happen before the write in the monitor. Most likely you should be calling create() just before calling ap.write().
This code pushes 3 handlers (tr) to sim_queue. If you don’t new/create the object before calling ap.write(), then these handlers are pointing to the same object, when you change one of them, the others will be changed also.