What is the right methodology to write a register check?

Hi,
For the sake of the question, imagine the following scenario.
I have a register that changes and should be checked after a sequence of milestones happens. For example,

  1. A value should be written using AHB on Add1 address
  2. Some value should be written on Add3 using AXI
  3. B value should be written on AHB on Add2 address
  • CHECK after this sequence of events that the register address Add3 has the value X.

The check could be more complex because, in between of these events, other AHB, AXI access from other instances can write.

In our team there are two first ideas to implement this check but maybe you can offer me other better methodologies.
First idea: This allows re-usability according to
https://www.doulos.com/knowhow/sysverilog/uvm/easier_uvm_guidelines/detail/
which says that all checkers should be implemented not “in the sequence that generates the stimulus” but in a component that is dependent only on the monitored bus.
The idea is to collect or flag all different monitored TLM transactions that match the sequence above (collected or flagged as class members of the scoreboard) from AHB, AXI. Then, a checker is created that is called each time that the 3rd step happens. The checker will verify if previous steps are also correctly done and then it will check the register value at Add3.
The disadvantage of this methodology is the extra effort to code the checker.

On the other hand, the second idea, is easier to implement but from the methodology point of view will not be re-usable (if you replace your sequence with another DUT you dont have the checker)
You can see that somehow on the following article of Mentor

The second solution would be to just perform the steps 1,2,3 in your sequence and the do a read of the register value from the sequence and check that has the correct value X.
In the article of Mentor it is suggested that sequences can also be used as “checkers” and they explicitly write and read a value from the sequence not to control only the stimulus but to perform comparisons and checks of register values.
What is the correct approach?.
In the verification academy i haven’t seen a clear answer to that yet. If there is a clear methodology, then could you point me to that?.
Is there any other better solution?.

Lastly, it would be good a methodology not only for register checkers, because the question applies to general checkers. In any case, if there is a solution for this using the API of the uvm register model (e.g. quirky register?) then i would appreciate it.

Looking forward your answer
Best regards,
Jonathan

In reply to Jonathan_Alvarez:

Hi Jonathan, you are addressing an interesting topic. The term checker is quite unspecific, because it doesn’t say anything what and how to check. This is the reason I don’t like this term.
Instead you should use scoreboard, assertion checker etc.
If I understand your question right your checks are definitely not scoreboard related. Scoreboard means you are dealing with a reference model.
Your check is sequence related. Right? In this case it is a good style to implement your check in a sequence. And I believe your sequence can be re-used.

In reply to chr_sue:

Hi Chr_sue,
Thanks for your answer.
Yes in this question the scoreboard doesn’t compute anything as it would be expected from a reference model, in this case, the reference model can be seen as “a behavior that is expected”, that is to say, once a specific sequence is received, then a value must be set on that register address.
If i replace that sequence by a master DUT device which generates those AXI and AHB transactions, then how can i re-use my sequence to do the check?
Thanks again for your comments and i agree that the methodology and terminology for checkers is somehow unspecific and should be enforced more clearly.
Best regards,
Jonathan

In reply to Jonathan_Alvarez:

I don’t see all the details of your problem.
But after writing to a register you have to perform a read on the register of interest:
You can do this with a command like this:
data_reg.mirror(status, UVM_CHECK, .parent(this));
Or you are writing the data in the register and in an associative array. Afterwards, when you are reading back from a certain address you can compare this value with the entry in the associative array.

In reply to chr_sue:

Hi,

The thing is that in my suggested example i don’t write in the register from my sequence. It is the Device under test which internally update the status of the register to an specific a defined/constant value X (it could also be a calculated value) after the sequence of event from AHB and AXI bus are performed.

In any case, the question is about if you check the status of the register from the sequence or from a scoreboard (a general component that only looks on the bus but not from the stimulus generator sequence).
If i understand you well, then you suggest to perform the check and read the register value direct from the sequence, using the UVM register model API. (i.e. option two but using the register model API)

Best regards,
Jonathan

In reply to Jonathan_Alvarez:

Yes, option 2 might be a solution. But I’m not sure if there is a fixed distance between the commands you have to execute. I guess it is not. Then you could bind a module to your registers which is observing any changes on register values.
I believe there is no simple answer, without knowing all the details.
But your key question was if the compare should happen in a testbench component or in a sequence.
For me this answer is clear. It is the sequence.

In reply to chr_sue:

Register value checking should be done passively to ensure vertical reuse. Checking in a sequence or test that you read what you expect is an active/directed style checking that will not work without active control. The way I’ve often seen it, at least for block level environments, is that the test should read all the registers at the end of the test or even during the run-time of the test. This will cause expected/observed checking. Configuration registers are rather straightforward since the adapter sniffs the writes and updates it’s mirrored/expected value. Status registers, however, are more challenging. To passively predict status registers can require a more complicated prediction model to set the expected value when the prediction model determines that the status bit should be set. Also, status registers can be racey and sometimes it’s hard to set the expected value at the right time and avoid false negative mismatches. Sometimes people use white boxing to overcome racey mismatches of the expected values of volatile status registers, but white-boxing is never ideal.

In reply to jeremy.ralph:

Thanks Jeremy. I think the same. I think the right way would be to put the checkers in the scoreboard which is a passive component. The scoreboard or predictor could be connected to a TLM channel from the register predictor (and register adapter) as described here Registers | Verification Academy.

virtual function void write_reg_update(uvm_reg_item register_item);

With this “write_reg_update” TLM port write function and its “register_item” the scoreboard can do direct predictions and checks. The scoreboard can take the register predictions that have to do with sequence of actions. For example, if there is 1 writes here, and 2 there, then this register should have this value. For easier predictions i think quirky registers flow is more recommended. See below.

Regarding the status registers that are volatile, i also tend to do white boxing using backdoors interfaces with the SV command “bind”. The interfaces are imported from the config database in the connect phase of the scoreboard and then are monitored to any change to update the register model. It is not desired but i think it is OK when you cannot predict from outside.

Regarding quirky registers that have complicated dependencies with other registers but are not depending on sequence of events. In those cases, i tend to think that the scoreboard should be leaved to predictions that have more to do with other TLM protocol inputs or other blocks (and sequence of TLM events).
That means, i try to put the specific register predictions in an extension class of the specific register class. I use the UVM SV register autogenerator too for the register model. That is provided by Questa (RUVM). Then, i modify the extension from "uvm_reg "to my “custom” reg classes and in those custom classes i create custom versions of the predict function. The predict function is called on each write/read call to a register which is detected by the reg predictor component. There, in that predict function, i can access and predict or check other registers using the parent_reg_block variable of each register. I find that way more encapsulated because the prediction of each register stays on the specific register class and not in a general scoreboard component.