UVM predictor takes too long to update from monitor bus

HI all,

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?

Thanks,
Chris.

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?

In reply to cuonghl:

Without looking at the code, it is tough to determine where the delay is coming from. Just a few thoughts:

  1. Is the monitor,
  • capturing the write transactions right on time as per the protocol?
  • waiting for some event or condition before writing into analysis port (that is connected to adapter)?
  1. Is the adapter adding any delays instead of just converting the bus2reg transaction?
  2. Is the predictor taking time to update the register map, even if it gets a REG WRITE transaction from adapter?

In reply to cuonghl:

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.

Hi guys,

Thank you for your response.

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.

Hi guys,

Maybe there is a race condition here. I found:

  • First loop: Read predictor updated value 8101 to m_desired of register at 10185.0ns
  • Second loop: Set function of write (line 159) updated value 8100 to m_desired of register at 10185.0ns

These proecess updated m_desired with diffrent value at same time? Is that a race condition?

Thanks.

In reply to cuonghl:

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

In reply to cuonghl:

- 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.

In reply to warnerrs:

You are doing a get and a read. The read makes another get and a morror. I’d replace your read by a morror. Maybe there is your problem.

@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.

In reply to Robin Hotchkiss:

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?

In reply to warnerrs:

Not sure forcing driver/monitor order is a good choice. It’s not fixing the cause.

I agree working on a fix on the uvm_reg would probably best. Thanks for your input.