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:
aval=0x000000F0 bval=0x00000000 -> clean ; read as int: 0x000000F0Both agree. Nothing to see.
One bit set to X — bit 2, which was a 0:
aval=0x000000F4 bval=0x00000004 -> HAS X or Z ; read as int: 0x000000F0The 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:
aval=0x000000D0 bval=0x00000020 -> HAS X or Z ; read as int: 0x000000D00xD0. 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.
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.
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
Xreached 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
- An integer access is a discard, not a conversion — the result is a legal value the signal never held.
- Measured, clean:
aval=0xF0 bval=0 → 0xF0. Both readings agree. - Measured, an
Xon a0bit:aval=0xF4 bval=4 → 0xF0— identical to the clean case. - Measured, a
Zon a1bit:aval=0xD0 bval=0x20 → 0xD0— the number changed, to one that was never there. - Two shapes, neither visible: sometimes the value is unchanged, sometimes it is altered, and never is there an indication.
- Detection is one comparison — the second word is non-zero exactly when some bit is unknown: measured
0,4,0x20. - It must happen before the value is used, because half the time the result looks fine.
- It cannot be done from an integer access — the word has already gone.
- The audit: does the model read the second word anywhere? If not, every result it ever produced was flattened.
- Three strategies (20.5 §5): refuse · force deliberately · ignore — and the third is never a decision.
- Make the policy visible — refuse out loud, or count; a policy with no output is indistinguishable from none.
- The symptom is test-dependent and looks like flakiness — a new test that reads uninitialised state is a classic trigger.
8. Exercises
- Read the same signal as four-state and as an integer, with one bit set to
X. Do the two readings disagree? - Repeat with the
Xon a bit that was a1instead of a0. Does the integer value change now? - Set a bit to
Zthat was previously1. What value arrives, and was it ever present on the signal? - Write the one-comparison detection and apply it to all three cases. What does the second word read each time?
- Audit a model you have: does it read the second word anywhere? What follows from your answer?
- For a block you know, list three tests in which an unknown could legitimately reach the model.
- 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?
- §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· anXon a0→0xF0, identical · aZon a1→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.
