Skip to content
VLSI Mentor

C Part II · Module 25 · Debugging DPI-C and Simulator Integration

Losing X and Z Information

An integer access to a four-state signal is not a conversion, it is a discard — and the result is a legal value the signal never held. Measured: a signal holding 0xF0 with one bit set to X reads back as 0xF0, identical to the clean case; the same signal with a different bit set to Z reads back as 0xD0. One of those changes the number and one does not, neither carries any indication, and the detection in both cases is a single comparison against a word the integer access has already thrown away.

Expert22 min readCPart IIVerificationDPI-C

Part II · Module 25 · Chapter 25.9 · Losing X and Z Information

1. A Discard, Not a Conversion

Chapter 20.5 established the mechanism: a four-state value crosses as two words, and reading only the value word makes Z indistinguishable from 0 and X from 1.

This chapter is the debugging half — finding out that it already happened, in a model that is running and producing numbers nobody has questioned.

The framing that makes the rest obvious: an integer access is not a conversion. A conversion maps one representation onto another and the result is a faithful image of the input. This discards half the information and returns a number that is a legal value the signal never held.

2. The Same Signal, Read Two Ways

Measured, one 32-bit signal read as four-state and as an integer in the same call.

Clean, holding 0xF0:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  aval=0x000000F0 bval=0x00000000 -> clean ; read as int: 0x000000F0

Both agree. Nothing to see.

One bit set to X — bit 2, which was a 0:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  aval=0x000000F4 bval=0x00000004 -> HAS X or Z ; read as int: 0x000000F0

The integer reading is 0xF0 — identical to the clean case. An X arrived, was read as a 0, and the number is bit-for-bit what a perfectly healthy signal produces.

One bit set to Z — bit 5, which was a 1:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  aval=0x000000D0 bval=0x00000020 -> HAS X or Z ; read as int: 0x000000D0

0xD0. The signal held 0xF0, one bit stopped being driven, and the model received a value that was never present anywhere — not the old value, not a flagged value, just a different ordinary number.

3. Two Shapes, Neither of Them Visible

Those two cases are the chapter, and the pair is worse than either alone.

An X on a bit that was 0 leaves the integer value unchanged. There is no symptom of any kind, and a comparison against an expected value passes.

A Z on a bit that was 1 changes the integer value. There is a symptom — a wrong number — but nothing about it says why, and 0xD0 is as plausible as 0xF0.

So flattening is not a uniform rule you could allow for downstream. It is a silent edit that sometimes alters the result and sometimes does not, depending on what the bit happened to be before, and in neither case is there an indication.

The same 32-bit value read two ways, three times over, to show what an integer access does to four-state information. A clean value of 0xF0 gives an aval of 0xF0 and a bval of zero, and reads back through an integer access as 0xF0, which is correct. The same value with bit 2 set to X gives an aval of 0xF4 and a bval of 4, and the integer access reads 0xF0 — the same number as the clean case, so an X has been read as a zero and the result is indistinguishable from data. The same value with bit 5 set to Z gives an aval of 0xD0 and a bval of 0x20, and the integer access reads 0xD0, which is a value the signal never held: bit 5 was a one before it became Z, and it has come back as a zero. The detection is one comparison, because the bval word is non-zero exactly when some bit is X or Z, and the three measured cases give zero, four and 0x20 respectively.What the signalholdsthe same 32-bit valueeach timeRead as 4-statemeasured: aval and bvalRead as an integermeasured: what ordinarycode getsIs anything lost?A clean value0xF0, every bit a 0 or a1aval 0xF0, bval 0measured0xF0correctnothe two readings agreeOne bit set to Xbit 2, which was a 0aval 0xF4, bval 4measured: the bval namesthe bit0xF0measured: identical tothe clean caseyes, and invisiblyan X read as a 0,indistinguishable fromdataOne bit set to Zbit 5, which was a 1aval 0xD0, bval0x20measured0xD0measured: a value thesignal never heldyes, and the numberchangeda 1 became a 0 with noindicationThe detectionone comparison, beforeusing the valuebval is non-zeroexactly when some bit isX or Z — measured: 0, 4and 0x20the integer accesscannot tell youit has already thrown thesecond word awayso ask for the4-state formand check bval firstAn integer access is not a conversion, it is a discard — and the result is a legal value thesignal never heldmeasured: 0xF0 with bit 5 at Z came back as 0xD0, which no amount of looking at the number could reveal12
Figure 1 — the same signal read two ways, three times. The first row is the control and the two readings agree. The second row is the dangerous one: a bit set to X produces an integer reading identical to the clean case, so nothing downstream can tell the difference. The third row changes the number — a bit that was a one came back as a zero — which looks like an ordinary wrong value rather than like missing information. The fourth row is the detection, and it is a single comparison: the second word is non-zero exactly when some bit is unknown, measured here as zero, four and 0x20. The integer access cannot perform that check, because it has already discarded the word the check needs.

4. The Detection, and the Audit

Detection is one comparison. The second word is non-zero exactly when some bit is unknown. Measured across the three cases: 0, 4, 0x20.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (v.bval != 0) { report("input contains X or Z"); return; }

Two things to notice about where that check has to go.

It must happen before the value is used, not after a result looks wrong — because §3 measured that half the time the result does not look wrong.

And it cannot be done from the integer access. By the time you have an int, the word the check needs has been discarded. The model has to ask for the four-state form, which is a decision made when the code is written.

Which gives the audit for an existing model, and it is one line:

Does the model read the second word anywhere?

If not, every result it has ever produced was computed with X and Z silently flattened. That is not a hypothetical: Chapter 20.5 §7 measured why the absence of reports is not evidence of absence — a model that cannot report an unknown accumulates evidence that unknowns never arrive, because it never says so.

5. The Decision

Chapter 20.5 §5 established three strategies and they are the policy here, restated with the debugging consequence attached.

Check and refuse. Lossless. The model reports "I was given unknowns", which is true and usually a finding. For a reference model this is the right default — a model that computes on X is producing fiction, and Chapter 24.1's obligations make fiction that agrees with the design the worst available outcome.

Force to a defined value, deliberately. Lossy and predictable: the same input always gives the same output and the rule is written in the code. Appropriate when the design may legitimately present unknowns and the model must continue. Say which way you forced them, and ideally count them so the rate is known.

Ignore the second word. What happens if you do nothing, and the only one of the three that is never a decision. It produces §2's measurements.

The debugging-specific addition: whichever you choose, make it visible in the run. A model that refuses should say so; a model that forces should count. Chapter 23.6 measured that a count is what distinguishes a check that is working from one that stopped running — and an unknown-handling policy with no output is indistinguishable from no policy at all.

6. The Misconception: We Would Notice

"If an X reached the model the numbers would be obviously wrong. We would see it."

Measured false in both directions.

An X on a 0 bit produced an identical number — 0xF0 against 0xF0. Not nearly identical: the same value, bit for bit, as a perfectly healthy signal. A scoreboard comparing against an expected value passes.

A Z on a 1 bit produced 0xD0 — a different number, and an entirely plausible one. Nothing about 0xD0 suggests missing information rather than ordinary disagreement, so it is triaged as a data bug and the search starts somewhere else.

And the deeper reason noticing is not available: the unknown-ness is not encoded in the number. It lived in a separate word, and the integer access threw that word away before your code ran. There is no analysis of the result — no range check, no parity, no plausibility test — that can recover information which is not in the value.

The accurate position:

You cannot detect flattening by examining the flattened value, because the evidence is in the word that was discarded. The check has to happen at the point of access, on the four-state form, and it is one comparison.

7. What You Should Be Able to Say Now

  1. An integer access is a discard, not a conversion — the result is a legal value the signal never held.
  2. Measured, clean: aval=0xF0 bval=0 → 0xF0. Both readings agree.
  3. Measured, an X on a 0 bit: aval=0xF4 bval=4 → 0xF0 — identical to the clean case.
  4. Measured, a Z on a 1 bit: aval=0xD0 bval=0x20 → 0xD0 — the number changed, to one that was never there.
  5. Two shapes, neither visible: sometimes the value is unchanged, sometimes it is altered, and never is there an indication.
  6. Detection is one comparison — the second word is non-zero exactly when some bit is unknown: measured 0, 4, 0x20.
  7. It must happen before the value is used, because half the time the result looks fine.
  8. It cannot be done from an integer access — the word has already gone.
  9. The audit: does the model read the second word anywhere? If not, every result it ever produced was flattened.
  10. Three strategies (20.5 §5): refuse · force deliberately · ignore — and the third is never a decision.
  11. Make the policy visible — refuse out loud, or count; a policy with no output is indistinguishable from none.
  12. The symptom is test-dependent and looks like flakiness — a new test that reads uninitialised state is a classic trigger.

8. Exercises

  1. Read the same signal as four-state and as an integer, with one bit set to X. Do the two readings disagree?
  2. Repeat with the X on a bit that was a 1 instead of a 0. Does the integer value change now?
  3. Set a bit to Z that was previously 1. What value arrives, and was it ever present on the signal?
  4. Write the one-comparison detection and apply it to all three cases. What does the second word read each time?
  5. Audit a model you have: does it read the second word anywhere? What follows from your answer?
  6. For a block you know, list three tests in which an unknown could legitimately reach the model.
  7. A model and a design agree on the whole regression and disagree on one new test. What question does §5's callout suggest asking first?
  8. §6 says you cannot detect flattening from the flattened value. Explain why, in terms of where the information was.

9. Summary

  • An integer access discards — the result is a legal value the signal never held.
  • Measured: clean 0xF0 → 0xF0 · an X on a 0 → 0xF0, identical · a Z on a 1 → 0xD0.
  • Two shapes, neither visible: sometimes unchanged, sometimes altered, never indicated.
  • Detection is one comparison — second word non-zero: measured 0, 4, 0x20.
  • Before use, and from the four-state form — an integer access has already discarded the evidence.
  • The audit: does the model read the second word anywhere?
  • Three strategies: refuse · force deliberately · ignore (never a decision).
  • Make the policy visible — refuse out loud, or count.
  • Test-dependent, and looks like flakiness — a new test reading uninitialised state is the classic trigger.
  • You cannot detect it from the value, because the evidence was in the word that was thrown away.

Flattening is a fault in what the model was given. The last failure class is about something else entirely: code that receives exactly the right data and runs at a moment, or a frequency, that its author never intended.

Where this fits

Part of the C Programming curriculum.