I got issue with UVM predictor with bus monitor. Here is scenario:
I write a value using uvm_reg::write method, the write operation is completed but it’s takes to long to receive transaction from monitor bus to predictor (following figure)
My question, in uvm reg sequence, is there any way to know when uvm predictor successfully recevies transaction from bus monitor?
In reply to cuonghl:
What do you mean with ‘takes too long’? Have in mind your monitor is observing the pinlevel interafce, i.e. your protocol might take a certain time.
Can you specify in more details does the delay come from the pinlevel interface or on the TL interface?
The UVM testbench is un-timed and nothing should be considered to take ‘too long’. As Chris mentioned, the register reads/writes generate sequence items. These sequence items will take time to process through the sequencer (as they will be arbitrated with the other sequence items), and the bus protocol will take time. It is only when the transaction is complete per the monitor that the register predictor will see the read/write.
However, these delays shouldn’t affect the behavior of the testbench. If you are waiting on a specific register value, you should be polling that register, just like the software will.
I actually got this issue from the following code (uvm_bit_bash_seq, default sequce of UVM-1.1d):
152 repeat (2) begin
153 val = rg.get();
154 v = val;
155 exp = val;
156 val[k] = ~val[k];
157 bit_val = val[k];
158
159 rg.write(status, val, UVM_FRONTDOOR, map, this);
160 if (status != UVM_IS_OK) begin
161 `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when writing to register \"%s\" through map \"%s\".",
162 status.name(), rg.get_full_name(), map.get_full_name()));
163 end
164
165 exp = rg.get() & ~dc_mask;
166 rg.read(status, val, UVM_FRONTDOOR, map, this);
167 if (status != UVM_IS_OK) begin
168 `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Status was %s when reading register \"%s\" through map \"%s\".",
169 status.name(), rg.get_full_name(), map.get_full_name()));
170 end
171
172 val &= ~dc_mask;
173 if (val !== exp) begin
174 `uvm_error("uvm_reg_bit_bash_seq", $sformatf("Writing a %b in bit #%0d of register \"%s\" with initial value 'h%h yielded 'h%h instead of 'h%h",
175 bit_val, k, rg.get_full_name(), v, val, exp));
176 end
177 end
At repeat (2) loop: - First loop: read function (line 166) is finished but predictor haven’t received trans from monitor bus because of delay - Sencond loop: write function (line 159) set new desired value of register, but this value is overriden by predictor of read funtion from the first loop. Therefore, I got error because get() function doesn’t return correct value.
I think this is a bug of UVM, read function should wait predictor complete.
I’ve seen the same race condition. I’ve been searching to see if our model is integrated incorrectly, as the built-in bit-bash test seems to assume the prediction will complete before the model is queried with the get call.
This assumption is correct if the auto_predict is used, but using an external prediction, the operation of the read/write is executed in a separate thread to the prediction. And I can’t see how the order of execution can be ensured.
The race I see is when a register is being bit bashed, and the predict from rg.read from the previous loop is scheduled while the rg.write of the current loop is in progress. The write occurs correctly, but when the write returns the predict has updated the desired value to the previous read value. The write starts and the read predict occur in the same cycle. I’ve also run the same code on a slightly different code base, and the thread order occurs such that the predict thread executes after the rg.read / rg.write call completes.
Has anyone else had this problem, and uncovered if this is a bug in the code or something wrong in the model setup?
To solve the problem I observed, where the separate threads of the read/write and external predict could execute in orders that cause the bit bash to fail, I created a callback which I attached to a field within each register. The callback synchronized between the post_write / post_read and the post_predict. To attach the callback, I created a basic function that looped through the registers and attached the callback to one field in each register.
This may not work for all forms of register behaviour, but it seem to solve the observed problem by making the register model behave in a similar way that the model operates when auto_predict is used. Please if anybody knows of a more appropriate solution, then please reply to the thread.
//----------------------------------------------------------------------------
// pre_read
//------------------------------------------------------------------
virtual task pre_read (uvm_reg_item rw);
if (rw.path == UVM_FRONTDOOR)
begin
m_waiting_on_ext_predict.put();
end
endtask : pre_read
//----------------------------------------------------------------------------
// pre_write
//------------------------------------------------------------------
virtual task pre_write (uvm_reg_item rw);
if (rw.path == UVM_FRONTDOOR)
begin
m_waiting_on_ext_predict.put();
end
endtask : pre_write
//----------------------------------------------------------------------------
// post_read
//------------------------------------------------------------------
virtual task post_read (uvm_reg_item rw);
if (rw.path == UVM_FRONTDOOR)
begin
m_ext_predict_done.get();
end
endtask : post_read
//----------------------------------------------------------------------------
// post_write
//------------------------------------------------------------------
virtual task post_write (uvm_reg_item rw);
if (rw.path == UVM_FRONTDOOR)
begin
m_ext_predict_done.get();
end
endtask : post_write
//----------------------------------------------------------------------------
// post_predict
//------------------------------------------------------------------
virtual function void post_predict (input uvm_reg_field fld,
input uvm_reg_data_t previous,
inout uvm_reg_data_t value,
input uvm_predict_e kind,
input uvm_path_e path,
input uvm_reg_map map);
if (m_waiting_on_ext_predict.try_get())
begin
m_ext_predict_done.put();
end
endfunction : post_predict
- First loop: read function (line 166) is finished but predictor haven’t received trans from monitor bus because of delay - Sencond loop: write function (line 159) set new desired value of register, but this value is overriden by predictor of read funtion from the first loop. Therefore, I got error because get() function doesn’t return correct value.
Do you see any warnings like? “Trying to predict value of register ‘FOO’ while it is being accessed”
Does the subsequent rg.write() complete before the monitor reports the prior read?
If the read predict happens before the following write(), you should of course have no problems.
If the read predict happens during the following write(), you should see the above warning, but still it should work.
If the read predict happens after the following write() returns, I’d expect to see what you’re describing. Which would seem like a bug in your monitor.
@warnerrs : No warnings are seen from the model when this occurs. To clarify: The predict of the previous read is performed after the next call to write starts, but before the time progresses - so before the write starts on the bus, but after some of the non-time consuming code of the write task has been executed. This is why I think it is a thread execution order issue. Without some form of synchronization similar to the form I suggested above, or by changing the UVM codebase, I can’t see how anything at the user-level could influence the execution order of the threads in the UVM package code.
@chr_sue : The code provided by cuonghl above is part of the UVM package. This is part of the provided test for bit bashing the registers. I currently believe that the code in the UVM package is not robust enough to work for all scenarios when auto_predict is disabled.
Rather than the synchronizing solution I described above, I am now looking at using the factory to swap out the UVM package sequence with an extended the bit-bash sequence that alters the code so that doesn’t care about the thread execution order.
No warnings are seen from the model when this occurs. The predict of the previous read is performed after the next call to write starts, but before the time progresses - so before the write starts on the bus, but after some of the non-time consuming code of the write task has been executed.
Hmmm. I see now the code for that warning doesn’t apply here. It protects multiple read(), write(), mirror(), and update() calls from simultaneously driving the bus, but it doesn’t prevent predict() from coming in like this. As you said, I don’t see anything to prevent such a race. It seems to me predict() should test m_atomic.try_get(1). If it doesn’t get a key, it should warn that the register is already in use and simply return, throwing the prediction away.
There’s really no way to completely prevent a late prediction race like this, but predict() could certain do a better job of protecting you. Can you change how your driver or monitor are coded to change the event order? Can you modify your uvm library to add a test for m_atomic?