An Overview
In VLSI design, “verification” is a crucial step to verify the correctness and behaviour of the logic design codes. One of the common techniques is functional verification, which is used to ensure that the design meets its specification by simulating at the register transfer level (RTL). However, for any complex ASIC/SOC designs, one cannot merely rely on functional simulation, as this technique cannot catch all the design bugs. There are other techniques which must be used along with functional verification based on the chip requirements like formal verification (assertions-based verification), low power verification, gate level simulations (GLS), etc.
The functional verification phase accounts for around 70% of the time used in any complicated ASIC/FPGA designs, especially as advanced process nodes make design and verification more challenging. For SOC/ASIC DV cycle, there is one more verification phase i.e., gate level simulations (GLS). They are preferable and most of the companies use these to gain confidence on the netlists drop and DFT insertion. However, the AI era has created extreme time-to-market pressure for chipmakers. Therefore, nowadays companies rely on small subsets of test lists (bring up/sanity test suites only) for post-synthesis gate level simulations (GLS).
Usually, the GLS needs to be run for each netlist drop. One of the most critical and foremost phases is the final netlist drop GLS which usually occurs at the end of a bulk verification cycle i.e., just before the final tape out. Unfortunately, the GLS are approximately ten times slower than the RTL simulations and these simulations require high memory space on a server and have exceptionally long runtimes.
When bugs are found at the GLS phase, it is costly to implement the fix and one of the trickiest challenges is to debug Xs. So, the intriguing question would be, what if we can catch potential bugs and Xs in earlier phase of verification rather than waiting for netlists drop? Yes, this is possible with the X–propagation mode of simulation tool. These XPROP sims are not as slow as the GLS and the Xs also get debugged easily. X-propagation simulation is a digital design verification technique used in Electronic Design Automation (EDA) to trace “unknown” logical states through a digital circuit, making it essential for identifying potential issues early in the verification process
In this article, we shall discuss what the X-propagation simulation is and the exact difference between X-optimism, pessimism, and propagation terms, and following which we explore why they do matter, how Xs can give wrong results in normal RTL simulations, their benefits, and how they can be debugged easily.
What is the X-propagation, X-optimism, and X-pessimism in simulations?
Verilog HDL models and System Verilog define four different logic values: 0, 1, Z, and X. The 0 and 1 logic values represent false and true, respectively. The Z represents a tri-state or an undriven port, and the X represents an unknown or indeterminate value.
System Verilog/Verilog LRM adheres that whenever conditional expressions have X (unknown) values, they will be evaluated as false. This is the default simulation mode since simulation tools are compliant with the LRM.
Assume that the RTL module has the piece of code given below:
if(input == 1’b0) output < = 8’hFF;else output < = 8’h33;
always @(posedge i_clk) begin
if(status_en) status_out < = output;
else status_out < = ‘h0;
end
X-optimism: With the default RTL simulation mode, if the “input” port is equal to 1’bx, the if condition will be evaluated as false, and the “output” port will be assigned 8’h33. Here, the X does not propagate from input to output and gets squashed. This phenomenon is called X-optimism.
X-propagation: When this piece of code is synthesized into a gate-level netlist, the “output” port will be assigned 8’hxx; the X will propagate from input to output. This phenomenon is called X-propagation.
X-pessimism: Since the “output” port is assigned with 8’hxx value, the “status_out” port also gets assigned with ‘hxx value even though the “status_en” port does not have an X value. The reason being that the “status_out” port gets pessimized when a logic block’s expression does not have any Xs. However, its output gets ruined when Xs are spread to it from other logic blocks. This phenomenon is called X-pessimism.
Why it matters?
X and Z logic values are abstraction modelling semantics in Verilog. Whereas, in real silicon, X and Z do not even exist.
The X logic value is an especially useful abstraction in simulation; it can be created intentionally or unintentionally. For example, if same reg/net is being driven with multiple drivers, then that register will have an unintentional X value, while some designers intentionally use the X value for catching illegal FSM states.
Most common sources of Xs are uninitialized registers and flops, which are not tied with hardware reset. These types of registers are usually present in pipelines or data path stages.
Xs always get optimized as per the SV LRM by RTL simulations and they show up during the GLS. So, the test that passes with the RTL will not necessarily pass with the gate level netlist, or in the lab. Xs can mask real RTL bugs by hiding functional issues and trigger false failures. Hence, it is desirable to get rid of them by using the XPROP to save time and effort before the GLS.
The following are common examples of how simulation-silicon mismatches are created if they are not debugged properly before taping out. Let us understand them.
Example 1: If-else statements:
The always block given below represents a simple 2×1 multiplexer Verilog model and here is the truth table of this given Verilog model. The first three columns show the inputs of this Verilog model. The last two columns show outputs, and they show the distinct difference between an RTL simulation and the actual hardware.
As seen in the truth table below, when both data inputs “strb_sync_latched” and “strb_idb_latched” are same. Then select signal “synced_cmd” value is not important, the output “strb_latched” is equal to data inputs value. However, when both data inputs are different and a conditional signal has the unknown value (X), then the output signal cannot be determined with actual hardware. In real silicon this case depends on a conditional value (so the output will vary between 0 to 1) whereas in RTL simulation it determines a logical value to the output as an else statement will be executed.

| synced_cmd | strb_sync_latched | strb_idb_latched | strb_latched (RTL) | strb_latched (Silicon) |
| 1’bx | 0 | 0 | 0 | 0 |
| 1’bx | 0 | 1 | 1 | X (0 or 1) |
| 1’bx | 1 | 0 | 0 | X (0 or 1) |
| 1’bx | 1 | 1 | 1 | 1 |
Example 2: Case with and without default:
The always block given below has the case statement with the default state and in its truth table the first four columns are inputs, and last two columns are outputs of the RTL simulation v/s actual silicon. It is the same as in Example 1 when all data inputs are equal then conditional input signal value is not important, thus outputs have the same value as inputs However, when the data inputs are different, and the conditional signal has an unknown value (X) then the output signal cannot be determined in real silicon whereas in RTL sims the always default case statement will be evaluated.

| i_pkt_sel | header_pkt | middle_pkt | trailer_pkt | pkt_type (RTL) | pkt_type (Silicon) |
| 2’bxx | 0 | 0 | 0 | 0 | 0 |
| 2’bxx | 0 | 0 | 1 | 1 | X (0 or 1) |
| 2’bxx | 0 | 1 | 0 | 0 | X (0 or 1) |
| 2’bxx | 0 | 1 | 1 | 1 | X (0 or 1) |
| 2’bxx | 1 | 0 | 0 | 0 | X (0 or 1) |
| 2’bxx | 1 | 0 | 1 | 1 | X (0 or 1) |
| 2’bxx | 1 | 1 | 0 | 0 | X (0 or 1) |
| 2’bxx | 1 | 1 | 1 | 1 | 1 |
In the design example given below, the case statement does not have a default case. So whenever conditional expressions have X value none of the given cases will be evaluated and will preserve the previous state in RTL sims and this is true even when the data inputs are the same whereas in real silicon, output cannot be determined when the data inputs are different but unlike the RTL sims, when both data inputs are same, the output will determine the logical value same as both the data inputs.

| i_pkt_sel | header_pkt | data_pkt | pkt_type (RTL) | pkt_type (Silicon) |
| 1’bx | 0 | 0 | pkt_type[t-1] ((t-1) = Previous state of pkt_type) | 0 |
| 1’bx | 0 | 1 | pkt_type[t-1] | X (0 or 1) |
| 1’bx | 1 | 0 | pkt_type[t-1] | X (0 or 1) |
| 1’bx | 1 | 1 | pkt_type[t-1] | 1 |
Since the XPROP sims give us the benefit of catching any potential RTL bugs that are hidden due to the X issue, there is a legitimate question as to why X-optimism removal is not enabled by default? That is because:
- Simulation in XPROP mode is 15-20% slower than the regular one.
- Random regressions add no value for X-optimism hunting; it always produces hard failures. So, there is no need to run regressions repeatedly.
- XPROP sims are required to be run at 4-5 key phases of bulk DV.
- Lastly, user needs to ensure that before final netlist drop (final RTL freeze), there is a sufficient coverage on Xprop sims.
Similarly normal sims XPROP sims can be performed at the IP/block level, chip/cluster level, etc. using the same TB. The user just needs to enable the XPROP feature in the tool using the compile time option.
XPROP issues are the trickiest ones to debug, and debugging those issues requires a much time and many iterations. By following some proactive debug steps, one can prevent future recurrences and reduce the overall debug time. Now let us discuss them.
How to debug X-propagation issues with ease :
(1) Identify the source of X :
- Rerun the failing XPROP test with the same seed and waves and trace back the first source of X. The person who leads the debugging process should coordinate with designers to ensure efficient identification of the X source.
- While trace backing the RTL, focus on the pipelines and the data path flops and check with designers if they are missing the reset.
- Before running the XPROP sims, enable assertions which are added to catch X issues.
- Formal X-Propagation Verification (FXP) can be used here, where formal engines exhaustively inject ‘X’ at suspected sources and check critical destinations, providing a thorough approach to identifying X propagation paths.
- Be aware of X-Pessimism, which refers to over-propagation leading to too many X-values, making it hard to find the true source. Selective application of X-propagation is necessary to avoid this challenge.
(2) Identify TB problems :
- Check that the UVM TB does not insert unnecessary Xs. This situation mostly occurs with the driver, monitor and RTL wrapper module in TB (RTL top instantiation tie-offs i.e., input or in out ports should not drive unnecessary Xs).
- If it requires adding some workarounds in the TB, then add them under ifdef e.g., XPROP_SIM. Such issues would not reoccur or affect the normal RTL sims as well.
- Identify critical driving points in TB and initialize them with a random value either 0 or 1 at the beginning of simulation.
- Check that backdoor configurations of registers/memories are not enabled while running the XPROP sims.
In this example, the “int_db_clk” internal clock is generated with “i_ext_clk” external clock, and the user has used an internal clock domain reset to generate the internal clock. There is no issue with RTL sims. However, when the XPROP gets enabled, all sims get affected due to X-pessimism. The reason being that at 0 time or at the beginning of sim time the snippet logic given below leaves “int_db_clk” uninitialized and in other words before the hardware reset (main chip reset) it has Xs. This causes unintentional XPROP issue.

Therefore, the potential solution for this case is, initialize “int_db_clk” with random values (0 or 1) at 0 simulation time as seen in the snippet below.

(3) Check the initialization and reset routine :
- Check if all the critical memories and flops are initialized or not. In complex SOC/ASIC, users need to program certain memories during the chip initialization process with predefined rules.
- Check if any special FIFOs need to be flushed with software reset registers once the clock is stable. This kind of flush removes an unknown value (X) from FIFO write/read pointers.
(4) Use assertions :
- Add assertions to check the X glitches or unknown values on the interface when there are valid requests or responses, if needed.
- Add assertions on real-time error status ports if needed.
In the example below, using the SVA $isunknown construct, Xs/Zs are monitored on the SPI interface control and a valid port.

In the example below, using the case equality operator Xs/Zs are also monitored on error status port.

How to enable the XPROP mode in the Cadence tool?
The Cadence tool has a compile-time option “-xfile <xfile.config>” to control the XPROP feature where the xfile.config file defines the design hierarchy to enable or disable XPROP feature.
![]()
As seen in the above snippet, the user can configure the xfile.config file where C and D stand for (Compute as Ternary) XPROP mode and default RTL simulation mode, respectively.
In the above example, the XPROP feature is enabled for all modules except the “pll_gen_blk” module. Thus, the user can choose either a different XPROP simulator mode or default RTL simulation mode based on their needs.



