How to Verify a DUT Output Signal

How to Verify a DUT Output Signal

Posted by

Let’s assume that we need to implement a check for a DUT output signal like a FIFO full interrupt with the following behavior: it becomes 1 when a FIFO is full, it becomes 0 only when the interrupt is cleared (e.g. sticky interrupt).

Seams pretty simple, right?
In my experience with junior verification engineers I notice that most of the time they tend to implement just a part of this check. For this reason I came up with this simple rule:

Check that if it changed it had a reason, check that if it should change it will change.

Let’s see how this rule is applied to our FIFO full interrupt.



The rule translates into four separate checks:

#1 Expected value changed from 0 to 1

In this case the FIFO full interrupt was 0 and our model of the FIFO just became full. This means that the FIFO full interrupt output signal must become 1.
The most straight forward implementation is to start a thread which waits for some clock cycles to see if the output signal goes from 0 to 1.
A SystemVerilog implementation might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class cfs_scoreboard extends uvm_component;
   ...
   //this function is called when there is a PUSH action in the FIFO
   function void push_to_fifo(bit[31:0] data);
      fifo.push_back(data);
      if((fifo.size() > 128) && (reg_block.irq.fifo_full.get_mirrored_value() == 0)) begin
         //set the register sticky interrupt to 1
         reg_block.irq.fifo_full.predict(1);

         //start a background thread as we are in a function
         fork
            begin
               //maximum number of cycles to wait for the interrupt to become 1
               int unsigned tolerance = 5;

               for(int cycle_count = 0; cycle_count < tolerance; cycle_count++) begin
                  if(vif.full_irq == 1) begin
                     break;
                  end
                  @(posedge vif.clock);
               end

               if(vif.full_irq != 1) begin
                  `uvm_fatal("DUT_ERROR", "FIFO full interrupt did not became 1")
               end
            end
         join_none
      end
   endfunction
endclass

The algorithm above is the first half of the rule “check that if it should change it will change“.

#2 Expected value goes from 1 to 0

In this case the bit fifo_full from the interrupt register is cleared so the output interrupt must become 0.
As in the previous case, we must also start a background thread which waits for the output signal to change:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class cfs_scoreboard extends uvm_component;
   ...
   //this function is called when there is a CLEAR request for the FIFO full interrupt
   function void clear_irq_fifo_full();
      if((reg_block.irq.fifo_full.get_mirrored_value() == 1) begin
         //clear the register value
         reg_block.irq.fifo_full.predict(0);

         //start a background thread as we are in a function
         fork
            begin
               //maximum number of cycles to wait for the interrupt to become 0
               int unsigned tolerance = 5;

               for(int cycle_count = 0; cycle_count < tolerance; cycle_count++) begin
                  if(vif.full_irq == 0) begin
                     break;
                  end
                  @(posedge vif.clock);
               end

               if(vif.full_irq != 0) begin
                  `uvm_fatal("DUT_ERROR", "FIFO full interrupt was not cleared")
               end
            end
         join_none
      end
   endfunction
endclass

The algorithm above is the second half of the rule “check that if it should change it will change“.

#3 FIFO full interrupt output signal goes from 0 to 1

In this case we detected that the output signal changed from 0 to 1. Because the verification environment is faster than the DUT we can check immediately if the new value (e.g. 1) is in correspondence with the verification environment:

1
2
3
4
5
6
7
8
9
class cfs_scoreboard extends uvm_component;
   ...
   //this function is called when the output signal changed to 1
   function void irq_fifo_full_rising_edge();
      if((reg_block.irq.fifo_full.get_mirrored_value() != 1) begin
         `uvm_fatal("DUT_ERROR", "FIFO full output signal became 1 for not reason")
      end
   endfunction
endclass

The algorithm above is the first half of the rule “check that if it changed it had a reason“.

#4 FIFO full interrupt output signal goes from 1 to 0

In this case we detected that the output signal changed from 1 to 0. As in the previous case we can check this new value right away:

1
2
3
4
5
6
7
8
9
class cfs_scoreboard extends uvm_component;
   ...
   //this function is called when the output signal changed to 0
   function void irq_fifo_full_falling_edge();
      if((reg_block.irq.fifo_full.get_mirrored_value() != 0) begin
         `uvm_fatal("DUT_ERROR", "FIFO full output signal became 0 for not reason")
      end
   endfunction
endclass

The algorithm above is the second half of the rule “check that if it changed it had a reason“.

Coming back to junior verification engineers I saw that the tendency is to implement only cases #3 and #4 and usually they miss to implement the checks presented in case #1 and #2.
With this four checks all the possible scenarios are covered and there is no way for a bug to be missed.
A visual representation of these four checks is this one:
Checks for output signals
The same algorithm can be applied to any other output signal valid in every clock cycle. But regardless of how you implement the checks always make sure that your code will obey this rule:

Check that if it changed it had a reason, check that if it should change it will change.

Hope this helps!



Cristian Slav

5 Comments

  • hdlcohen@gmail.com' ben says:

    Wouldn’t SVA assertions be easier to write and read?
    “When a FIFO is full, it becomes 0 only when the interrupt is cleared (e.g. sticky interrupt).”
    ap_fifo_full_intrpt: assert property(@(posedge clk) $rose(fifo_full) |-> intrpt[->1] ##1 !fifo_full); ;
    Ben Cohen
    http://www.systemverilog.us/
    * SystemVerilog Assertions Handbook 4th Edition, 2016 ISBN 978-1518681448

  • Hi Ben,
    From my point of view, as long as you respect the rule “Check that if it changed it had a reason, check that if it should change it will change.” you can implement the checks in any way you want.

    In this particular example I do not like to use the assertions because I am obligated to do at least one of the following:

    1. I have to rely on some internal information from the RTL (e.g. fifo_full) which might be buggy
    2. If I do not want look inside the RTL for fifo_full I have to implement in the interface some logic for computing the expected value of fifo_full. This means more or less to copy some logic from scoreboard.
    3. If I do not want look inside the RTL for fifo_full I have to bring this information from the scoreboard. Making the interface dependent on the scoreboard is a bad practice because I will not be able to reuse only the interface (e.g. at system level)

    Personally I use assertions a lot for making sure that the protocol rules are respected (e.g. APB, AHB etc)

    Cristi

  • tudor.timi@verificationgentleman.com' Tudor Timi says:

    What I’ve seen more often is only rules 1 and 2 getting implemented and 3 and 4 being overlooked. The whole requirement could be expressed as “cause” == “effect”, but people tend to interpret it only as “cause” -> “effect”.

  • santoshssantosh@gmail.com' Santosh says:

    Don’t we need one more check to make sure the width of the pulse is as expected?
    We are only checking posedge and negedge.
    What if the signal was supposed to be high only for 1 cycle but it was asserted for 1 cycle sometimes and 2 cycle someother time?
    I believe we will need an assertion for this.

    • Hi Santosh,

      It depends on the requirements. I was referring to scenarios in which the actual moment of the edge can vary with some tolerance.
      If you have a DUT where you have to check the exact clock cycles then most likely you would have to model the signal cycle accurate and then you can simply check it every clock cycle but this is very tricky – any change in the DUT will result in a change in the environment.

Leave a Reply

Your email address will not be published.