Catching x-propagation issues at RTL saves time and reduces uncertainty in gate-level verification
Simulation usually involves simplifications that make the process more practical or convenient. In logic design, the models used in RTL simulation have been simplified to make it easier to write a simulator to run them.
For example, RTL models and simulators support Xs, which represent unknown or uninitialized Boolean states. Using these enables one simulation run to test all permutations of 1, 0 and the uncertainty conditions X and Z that may happen due to a reset, power on or other event that introduces uncertainty.
Without support for this X state, designers would have to run 2n simulations to explore the condition of n registers. The disadvantage of supporting X states is that they can propagate through a simulation, creating false error states that can only be resolved during the costly and time-consuming debug process.
There’s a further issue: X states are handled differently at RTL and gate level.
Because of their semantics, RTL simulations can be overly optimistic, wrongly turning X states into 0s or 1s. Gate-level simulation, on the other hand, can be overly pessimistic, propagating X states unnecessarily.
Why it matters
Why is this? Let’s look at a fragment of RTL code implemented in both VHDL and Verilog, with a possible implementation.
Figure 1 Differing interpretations at RTL and gate level of unknown states can create verification issues (Source: Synopsys)
- In the RTL simulation, we check for c to be true, but since c is unknown the RTL semantics make the expression false. The RTL will execute the Else clause, assigning b to q, which gives an output of 0.
- In the gate-level version, because c is unknown, the actual output is also going to be unknown – an X.
- The real hardware output could be 0 or 1, depending on the value of c.
In this case, RTL optimism means we evaluate the expression as false, converting the X to a false value and getting an incorrect answer. This result is also a mismatch with both the gate-level and real hardware behaviors. In a second state, c is X again but a and b are 1:
- The RTL will still take the Else branch, and assign the value of 1 because c is X and X is treated as false.
- At the gate level, we still get an X value because of the reconvergent fanout of c, caused by implementing the mux as two And gates and an Or gate.
- The actual hardware output will always be 1, because a and b are the same and c has become a ‘don’t care’.
In this case, the gate-level simulation model is more pessimistic than the real hardware: an X at c is propagated to q unnecessarily. The example shows that there is inconsistency between RTL and gate-level simulations, depending on the input values, making debug more difficult. The X optimism of RTL simulation can also block Xs and so conceal low-power and reset errors.
Dealing with X optimism
There are various ways to deal with X optimism:
- Run more gate-level simulations, although this can increase debug effort, due to the extra Xs that gate-level simulation can throw up.
- Use coding guidelines to ensure designers avoid code constructs that create X optimistic behavior. Unfortunately, avoiding If and Case statements limits the expressive power of HDLs.
- Use formal tools, though they may be limited to the block level and take a lot of configuration.
- Write a tool to add pessimistic behavior to RTL code to remove the optimistic behavior. This makes debug more difficult because the transformed code doesn’t look like the original, and the transformation may have introduced errors.
- Do multiple simulation runs with randomly initialized registers, although this won’t offer good coverage in a realistic amount of time.
- Reset all your state elements, retaining all the registers, avoiding the problem of having uninitialized registers. This isn’t possible for all designs because it takes routing resources to run reset lines. The approach doesn’t completely avoid the problem, either, since you could, for example, read from an uninitialized memory location.
The Synopsys solution
Our solution, embodied in VCS X-Prop Add-on, is to make the simulator consider an X as both a 1 and a 0 simultaneously, substituting the two values for the X during evaluation and then merging the result of the two evaluations. This reduces the mismatch between RTL and gate-level simulation, propagating uncertainty through both conditional constructs and edge operators.
Problematic constructs for X optimism
Both Verilog and VHDL have constructs that are evaluated over-optimistically when they encounter an X value.
In IF….ELSE statements, if the value being tested is unknown, a classic simulator considers the result as false and executes the else branch. This is incorrect, optimistic behavior, converting an unknown state into a valid 0 or 1.
In CASE statements, if the value being tested is unknown, a classical simulator will execute the default branch, if one exists, or not execute any branch that causes the variables to retain state. Neither behavior is how real hardware would work.
In Verilog, if a clock goes from X to 1, this is considered a clock edge, when in reality there may or may not have been an edge. Both Verilog and VHDL will optimistically use the ‘edge’ to clock values in, whether or not real hardware would actually experience a real clock edge.
Using VCS X-Prop for combinational statements
Consider this code fragment:
if (c) begin a = 0; b = 3; end else begin a = d; b = e;
in which one branch of the conditional gives one set of assignments and the other, two other assignments. If c is X, a classical Verilog simulator performs the Else assignments so that a gets the value d, and b the value e. In reality, because c is unknown, we need to consider the value of both branches being executed, by using X-Prop to evaluate the conditional for the values of 0 and 1, and then merging the two results:
When ‘c’ is ‘x’ -> a <= merge(0, d); b <= merge(3, e);
In a conventional simulator, an X value will be converted to a False value, which causes an expression to be evaluated with incorrect results. With T-merge, an expression yields an X output when all the input values are different – just like the ternary operator would. This removes the optimism from RTL models. Now consider a mux defined in RTL and implemented in three ways: with three NAND gates, three NOR gates, or as a mux.
Figure 2 Merge functions can help provide a more realistic view of the impact of unknown states (Source: Synopsys)
For the NAND gate implementation, the conditional is always X. The T-merge function will give a real logical value if a and b are the same, but if a and b are different it will give an X value.
The table also shows the result of an X-merge, the other (very pessimistic) merge mode, which will unconditionally set X for the output.
The table’s Y column shows what the value of Y would be for the particular gate-level implementation.
T-merge for the second implementation, using three NOR gates, only gives a value of 1 when a and b are 1.
For the mux implementation, T-merge gives values of 0 and 1 if a and b are the same, otherwise it gives the value X.
In summary, T-merge is very close to the actual behavior of the hardware, while X-merge will be more pessimistic than the gate-level simulation.
The extra pessimism built into the gate-level model is apparent when comparing the results of a T-merge and of Y.
Synopsys usually recommends using T-merge, which gives the correct behavior minus any additional pessimism that would be introduced in a gate-level simulation.
X-merge is supported for those who want to explore the worst-case scenario for any implementation.
X-Prop as applied to sequential blocks
Figure 3 Unknown states can also have an impact on sequential logic (Source: Synopsys)
Consider a latch coded as an If statement. If a clock signal is X, it is not known whether the logic will latch in the next value or hold the current value. X-Prop handles this by evaluating both cases and merging the results for both d and q.
Flip-flops are edge sensitive. They experience two types of edge: a clean rising edge in which the signal goes from 0 to 1; and, in Verilog, an ambiguous rising edge in which a signal goes from 0 to X. In this second case, standard Verilog will (optimistically) clock in the value of d to q.
Under X-Prop semantics, the simulator looks at the current value of the flop, the d, and the q and merges them. If they are different, the simulator assigns an X to the flop, but if they are the same then q holds its value. This is a more accurate way of evaluating the expression than classical Verilog evaluation.
CASE statements are similar to a series of nested IF THEN ELSE statements, and may include a default branch.
If c, the condition being evaluated, can be 0, 1 or X, then substituting both a 0 and a 1 for X means that c could match several branches of the statement. X-Prop handles this by merging the multiple values of the variable being set by the statement.
X-Prop acts much as it does with If statements, again removing unnecessary optimism in the RTL model.
Debugging with X-Prop
X-Prop is built into VCS and can be invoked with a switch that applies the T-merge strategy to the whole design.
Since X-Prop is intended to stop the simulator handling Xs in an overly optimistic way when simulating real hardware, there may be parts of your code to which the strategy should not be applied. There is an X-Prop configuration file in which one can specify the design hierarchies that should contain the more accurate X-Prop behavior. Typically a user would enable X-Prop behavior on the synthesizable part of their hierarchy.
X-Prop doesn’t do away with all X propagation issues. Once X-Prop is enabled, Xs can propagate through If and CASE statements, rather than optimistically being turned in o real logic values.
This means that flops that were optimistically clocking data in may stop working; for example, a gated cell that was optimistically reset using the classical semantics is no long reset under the Xprop semantics.
Checkers or assertions in the design should be used to catch unintended behaviors, such as improper resets, one-hot signals, Xs on control signals etc., which X-Prop reveals when more realistically evaluating designs.
Designers will still need to use an HDL debugger, such as Verdi (which supports the X-Prop semantics), to find the root cause of such bugs.
X propagation issues are becoming more common as aggressive power-management schemes make it more likely that parts of a design will be powered off when unused, creating opportunities for them to be repowered in uncertain states. The simplifications used to make simulation practical have also led to inconsistencies – RTL simulation turns unknown states into valid logical values, gate-level simulation is overly pessimistic about X states; while synthesis handles them in a third way.
Changing simulators so that they evaluate what happens if an X is a logic 0 or 1 and then merge the results address these issues. It reduces the optimism of RTL simulation, thus exposing errors earlier in the design flow when it is simpler and less costly to address them.
Bruce S. Greene is a Principal Engineer at Synopsys. His primary focus is to help customers solve complex problems to get their silicon working quickly and accurately. He has worked in many areas of verification including UVM/VMM, VCS, assertions, and static technologies. Bruce enjoys being a Lecturer at Santa Clara University in his free time. He holds a MS in Electrical Engineering from the University of Illinois at Urbana-Champaign, and a Ph.D. in Electrical Engineering from Santa Clara University.