Better Way to Create Testbench: For Error Cases

Hi,

I would like to know your opinion or experiences in creating a testbench, particularly in handling error cases.
Normal cases are easy to create and easy to handle by the testbench. Complications come when error cases are driven.
The scoreboard or test needs to be intelligent in order to decode a transaction with or without errors.

In my experience of creating a testcase, I have handled the error cases in 2 ways:
1.) I put a cmd_type in my transaction classs, which consist of the following: WRITE, READ, ERRONEOUS_WRITE, ERRONEOUS_READ.
I’m driving it on the interface so the monitor can see it. On this way, the monitor does not need to be intelligent
in order to decode if the transaction is erroneous or not. This is very helpful in a coverage component. I can easily
pass the transaction on the specific covergroup by just giving the cmd_type.

ADVANTAGE: Easy to create error cases and easy to decode error cases by the monitor, scoreboard, and coverage component.
DISADVANTAGE: The driver needs to have multiple ways of driving for each command type.

2.) The transaction class doesn’t have the ERRONEOUS_WRITE or ERRONEOUS_READ in the cmd_type. The driver will drive any patterns,
and then the monitor samples them and passed to the scoreboard. The scoreboard will analyze the transaction if it’s an
transaction with errors or without.

ADVANTAGE: The driver is simple. It will only get the transaction item and then drives it whatever the transaction tells it to do.
DISADVANTAGE: Hard to decode the error cases. The monitor, scoreboard, and coverage components need to be intelligent to properly decode
              an erroneous transaction.

So, what do you think guys? In your experience, what’s the best way to do it?

Regards,
Reuben

In reply to Reuben:
Hi Reuben,

I have used the callback method to inject an error. I have injected an error in SV testbench, so I have used a polymorphism to inject an error, but u can use the uvm_callback.

In my case, I have an abstract class “error_injector”, which has two virtual methods, pre_drive() and post_drive(). These methods are called from a driver class, before and after a drive() method. By default, these methods do nothing, but these methods can be overridden by its (error_injector’s) extended class.

So, using the callback method, we can,

  1. Inject an error
  2. provide a delay in the transaction
  3. drop the transaction

In reply to bdreku:

Hi bdreku,

That uvm_callback looks interesting. I’ll do a research on that.
Thanks for your advice.

Regards,
Reuben

I would recommend against using uvm_callback as they can add unnecessary complexity to your environment and create significant confusion. It also goes against the TLM nature of the UVM.

Embedding the error conditions in your transaction (as you mentioned in case 1 of your original post) would be the recommended way to go. This gives you the flexibility to control error generation from the test level by enabling or disabling the creation of error transactions.

Yes, you will need to allow for error recognition in your driver and monitor, but this is good practice in general.

Also, there should be no communication between the driver and monitor, or the sequencer and monitor. The monitor should be able to independently determine if a transaction is correct or incorrect. You can choose to either discard erroneous transactions, or send them out the analysis_port where your scoreboard can account for them in your coverage model.