Thursday 21 April 2011

VHDL Test Bench: What is Self Checking??


What is meant by a self checking?

Firstly, a self checking test case is one that gets some status from the DUT and tests that it is correct. The status could be that which is read form a DUT status register. You read the status register and test that it has the correct contents. Or it could be that some BFM implementing a bus protocol, with protocol a checker and internal status register, reading it could indicate that everything is good. When a status is checked there is the possibility that it is wrong. This should cause the simulation to output a message and terminate the run. The message should be as descriptive as possible as you want to find the source of the problem quickly. A test case is self checking in that it tests for some condition and if not correct outputs an indication and terminates the run.

I always implement READ commands in the test bench. Every READ command puts the value read into a common variable. This enables various VERIFY commands to test for conditions after every read. For example, if I had a READ_CPU and a READ_BFM command, and they both put the read value in a variable called v_temp_read, then a VERIFY (word) and a VERIFY_BIT command could look to the same place to get the data to be tested. (some tips about command creation) The VERIFY command is the self checking element of a test case.

When a VERIFY or testing type command checks a value and it is wrong, the output message should have enough detail to enable the problem to be located quickly. I use the assertion facility of VHDL to output useful messages. The file name and line number in that file are part of the instruction and the current value is always available. Specifically the file_name string variable and the file_line integer variable. These variables can be used in the assert statement so the user will know where the error originated. (NOTE: While testing this code it was found that the file_name variable contains nothing. I found this bug in the release version and it will be fixed in a new release, coming very soon.) Below is an example of how I create a VERIFY command. We assume a read took place before a verify is done, and the value is in the v_temp_read variable.

-----------------------------------------------------------------------------
elsif (instruction(1 to len) = "VERIFY") then
v_temp_vec1 := std_logic_vector(conv_unsigned(par1, 32));
assert (v_temp_vec1 = v_temp_read)
report LF & "ERROR: Compare Value was not as expected!!" &
LF & "Got " & (to_hstring(v_temp_read)) & LF &
LF & "Expected " & (to_hstring(v_temp_vec1)) & LF &
"Found on line " & (integer'image(file_line)) & " in file " & file_name
severity failure;

In the above example, if par1 does not match v_temp_read, the assertion will fire. The nice error message will print out stating there was a miss-compare, tell you the received value and the expected value, the file name and line number in the script that caused the error. The “to_hstring” function is available in VHDL 2008. The only other item needed before a user can copy/paste the above, fully functional VERIFY command, is to add the v_read_data std_logic_vector variable to the read_file process. The above command and a few others have been added to the code snips file here: (All code snips are now part of the Opencores distribution)

There is a benefit to having the VERIFY command separate from the READ command in larger verification environments. The scripting system can easily be made to create a READ_VERIFY command, where you both read and verify in the same command. The disadvantage to this that if you have more than one read type command, like DUT reads and BFM reads, you will have to create a READ_VERIFY type command for each read type. And if you wanted to create VERIFY_BIT commands, again one of those for each ready type you want to test. Everything that is read should be tested. If the read and verify are separate commands then one VERIFY command can service all READ type commands.

Another form of self checking relates to the Interrupts and Waiting Post. An unexpected interrupt causes the system to put out a message and terminate the simulation. The self checking part is the process that watches the interrupt output pin, and if not expected causes an assertion. This relates to scripts in that, a script has to inform the interrupt watcher that there will be an expected interrupt, do not terminate the simulation when it comes.

The last form of self checking comes from BFM's. Specifically checker type BFM's. These objects usually monitor some complicated function like a communications protocol, a bus monitor, a ram access checker and so on. The function of these objects is to indicate to the verification system when something goes wrong. For instance, a bus monitor could check the contents of a bus on each rising edge of a clock, and if there are x's on any line indicate an error. Or the monitor could be checking actual timing and or sequences. The topic of monitoring BFM's will be further elaborated on in future posts.

I looked over some home projects and found that I never used a verify type instruction, looking at wave forms seems to have fit my needs. My designs at home are very small and I was just playing around. Projects at work are 1000 times bigger and there are >100 test cases. When I run a regression, when a test case fails, I need to know what and where the failure happened. Good checking and good messages are a must for good verification results.

Sckoarn

No comments:

Post a Comment