How are registers supposed to deal with resets in UVM 2020?

I’m struggling with writing a reset test. In it, I see the following behaviour:

  • I start writing a register R.
  • The uvm_reg::write task calls XatomicX(1), which gets a token from the semaphore and sets the m_process class variable to be the current process.
  • After calling set, the task recurses into do_write. I haven’t extended the class, so this is just uvm_reg::do_write.
  • The first thing that do_write does is to call XatomicX(1) again, which notices that m_process is the current process and increments m_atomic_cnt (from zero to one) instead of getting blocked on the semaphore.
  • NOW A RESET HAPPENS
  • That’s ok: the testbench notices the reset and calls the reset function on the register.
  • That function does a slightly odd dance with try_get and put to make it so that the semaphore has exactly one key again. It also nulls out m_process.
  • Now my sequence tries to write the register again. The outer call to XatomicX gets the key from the semaphore, then the inner call (from do_write) notices that our process already has the lock. So it increments m_atomic_cnt. Unfortunately, m_atomic_cnt didn’t get cleared as part of the reset, so it’s now equal to 2.
  • When the register write completes, the outer call to XatomicX(0) just decrements m_atomic_cnt down to zero, not returning the key to the semaphore.
  • (… some time passes …)
  • Now an unrelated sequence (and thus a different process) wants to write the register. The call to XatomicX notices that its process doesn’t already own the register, so it calls m_atomic.get(1).
  • Deadlock

This looks rather strange to me: it seems like a pretty obvious race condition. But maybe this is just showing that I’m using the tool wrongly. Can anyone explain how I should be using the library to avoid this happening?

Please share the code of your sequence.
Where you use the set() api of the register field and the write() api.

I can’t easily share the sequence code itself, but there are two ways that registers get written:

  • my_reg_block.reg_name.write(.status(status), .value(123))
  • A subclass of uvm_reg_sequence that contains things like write_reg(some_register, status, 123)

But I’ve just checked. The subclass doesn’t define its own write_reg and the version in uvm_reg_sequence is basically the same as the more direct case.

I don’t use the set API directly (but it is called as the first thing in write).

I realise that diagnosing someone’s code without being able to see it is impossible(!) My general question is the following: “How can calls to uvm_reg::write get interrupted by a reset without messing up the locks?”

(It occurred to me that the problem might be caused by the fact that my sequencer is killing the sequence that is sending the bus transaction when the reset appears. Maybe that’s the root of the problem? What do people expect to happens when there is a reset?)

To be very precise, the bus sequencer calls stop_sequences when it sees a reset. Looking at the code in uvm_reg.svh, I guess it’s deep inside the call to rw.local_map.do_write(rw); and this (because there is a reg_adapter) will presumably be inside do_bus_write and presumably waiting for the rw.parent.finish_item(bus_req); line.

But I can’t see any reason that this should be killed: I would have expected the finish_item call just to finish early.

Does that match your understanding? If so, I think I’m going to need to add some print statements tomorrow…

Reading a bit more carefully, I think calling stop_sequences is the root of the problem. I’ve just read the “On the Fly Reset” paper here and I’m pretty convinced. Some coding for me tomorrow, I think.

My next question was if you are killing the running sequences..
When you receive a “reset-on-the-fly” into your DUT, the reset should be received also in the neighbor DUTs.
So if there was on-going/inflight rd/wr transation on the register bus to some responder/slave verification agent - it should be reseted as well. Like in a real system your DUT would not expect to recive it’s response back, if it received reset in the middle.

Please pay attention that both:
uvm_reg_block
uvm_reg
Has a reset API - which mentions the sempahore you wrote about previously.

You could read about here:

Make sure your UVM RAL model is being reset upon monitored reset event.