DDR · Module 28
Signal-Integrity Problems
RTL cannot measure an eye. The answerable question is which digital observations make a physical hypothesis the cheapest remaining one — and what that domain then requires.
Chapter 28.5 kept deferring one hypothesis. Pattern dependence, frequency sensitivity, and a lane-local failure surviving every digital exclusion all pointed the same way — and each time the chapter said not yet.
This chapter is where that hypothesis is taken seriously, and it starts by refusing the obvious framing. The question is not what is the channel doing. Digital logic cannot answer that, and neither can any RTL in this module.
The question is:
WHAT DIGITAL OBSERVATION JUSTIFIES MOVING A HYPOTHESIS INTO A DOMAIN WHERE RTL CANNOT FOLLOW?
That is answerable, it is cheap, and getting it wrong is expensive in both directions. Escalate too early and a team spends a week with a scope on a problem that was a unit-conversion error. Escalate too late and a team spends a month excluding digital causes that were already excluded.
1. The Escalation Criterion
State it precisely, because the rest of the chapter is its justification.
A physical hypothesis is justified when every cheaper hypothesis has been excluded by experiment, and the surviving digital evidence has a structure the digital causes do not explain.
Two clauses, and both are required. Exclusion alone is not enough — you can exclude everything and still have no reason to think the channel is involved. Structure alone is not enough either, because every structure in 28.5 has at least one digital explanation.
| Clause | Why it is necessary |
|---|---|
| Cheaper causes excluded by experiment | otherwise you are escalating on a hunch, and the digital cause is still there |
| Surviving structure not explained by them | otherwise the structure is the digital cause, and the channel is a coincidence |
The exclusions, gathered from the whole module:
| Cause | Excluded by | Owner |
|---|---|---|
| Training committed a bad setting | bitmap structure, window width, a finer sweep | 28.1 |
| A timing contract is violated | an empty violation snapshot over a long run | 28.2 |
| Parameter conversion or rounding | re-deriving the applied counts by hand | 28.2 §11 |
| A refresh obligation was missed | zero deadline misses | 28.3 |
| Calibration never started, converged, or committed | the four lifecycle fields | 28.4 |
| A committed value is stale | the provenance tag comparison | 28.4 §5 |
| Correction is hiding the fault structure | correction disabled for a debug run | 28.5 §12 |
Seven exclusions, all digital, all cheaper than a physical measurement. §11's gate counts them, and it will not report escalation as justified until they are recorded.
2. Three Experiments Mistaken for Proof
The three results that get reported as it is signal integrity, and what each actually supports.
Lower frequency helps
Supports: that something scales with the clock period.
| Also consistent with | Digital? | Cheaper to test? |
|---|---|---|
| A unit-conversion error — 28.2 §11 | yes | yes |
| The wrong frequency bin's settings | yes | yes |
A max(nCK, ns) dual-term crossover — 28.2 §12 | yes | yes |
| A calibration valid only at the trained frequency — 28.4 §5 | yes | yes |
| Fewer accesses per second, so less exposure | yes | yes |
| Channel margin | no | no |
Five digital explanations, all cheaper. And the fifth is a measurement artefact rather than a mechanism: lower frequency usually means fewer accesses per unit time, so an error rate per second falls even when the rate per access is unchanged. Report per access or the experiment is uninterpretable.
One lane fails
Supports: that the fault follows that lane's path.
CURRICULUM-DERIVED from 19.1 and 28.1 §8, that path contains: the lane's delay setting, its capture logic, its share of the classifier, its package trace, its board trace, and its device pin. Four of the six are digital.
| Experiment | If the cause is digital | If it is the physical path |
|---|---|---|
| Remap the lane — 28.5 §9 | the failure follows the logical lane | the failure stays with the physical pin |
| Swap the device | the failure stays | the failure may move |
| Swap the board | the failure stays | the failure may move |
The remap experiment is the decisive one and it is digital. If the failure follows the logical lane through a remap, the cause is in logic that the remap moved — and no channel measurement was needed to establish that.
ODT sensitivity
Supports: that termination configuration affects the outcome.
Chapter 22.1 owns the three selectable values, the dynamic switch, and the timing that makes the switch land where the data is — and that last clause is the reason ODT sensitivity is ambiguous.
| Hypothesis | Mechanism | Digital? |
|---|---|---|
| The termination value is wrong for this channel | electrical | no |
| The switch timing is wrong, so termination is absent when data arrives | digital — a timing parameter | yes |
| The wrong ranks are being told to terminate — 6.7 | digital — a configuration field | yes |
Two of three are digital, and both are cheaper than a measurement. So a failure that changes with ODT settings is not evidence of a channel problem until the switch timing and the rank selection have been checked — and both are register reads.
3. Digital Observables That Genuinely Bear on the Channel
Not everything is ambiguous. Four digital observations carry real information about channel behaviour, and three of them come from mechanisms Module 22 and 25.4 already own.
Pattern structure. Chapter 22.3 establishes that the three degradation mechanisms interact through the data pattern. So a failure rate that varies with the number of transitions in the pattern — rather than with a specific value — is a digital measurement of a pattern dependence the mechanisms predict. §4 makes this a counter rather than an impression.
The aggressor/victim separation. Chapter 22.2 owns a precise and unusually testable fact: a reflection arrives late, and therefore corrupts a different bit than the one that caused it. That is a digital prediction — the corrupted position is displaced from the transition that produced it — and §5 shows how to measure the displacement.
CRC reports. Chapter 22.4 owns write CRC including its detection guarantee and its error report, and 25.4 owns CRC in both directions and ALERT_n's semantics. A CRC error is a real, reported, digital event about a transfer that was corrupted in flight — which is closer to a channel observation than anything else available without instrumentation.
Data bus inversion behaviour. Chapter 22.3 owns DBI, its encoding and its decision policy. If a failure rate changes when DBI is enabled or disabled, the transition count of what is actually on the wires changed — which is a pattern-structure experiment with a single configuration bit.
| Observable | What it measures | What it still does not give you |
|---|---|---|
| Errors versus transition count | a pattern dependence the mechanisms predict | which mechanism, or any magnitude |
| Aggressor/victim displacement | that corruption is displaced from its cause | the reflection's amplitude or timing |
| CRC error rate | in-flight corruption, reported by hardware | where on the channel, or why |
| DBI on/off delta | sensitivity to wire-level transition density | any electrical quantity |
All four are digital, all four are cheap, and none of them is a measurement of the channel. They make a physical hypothesis more likely and they do not make it established — which is the distinction §11's gate encodes.
4. Measuring Pattern Dependence Properly
The most valuable experiment in this chapter, and it is usually done badly.
Done badly: run a random pattern, run a walking-ones pattern, note that random is worse, conclude pattern dependence.
The problem is that those two patterns differ in several ways at once — transition count, transition density per lane, the number of simultaneously switching lanes, and the specific values. A difference between them localises nothing.
Done properly: vary transition count while holding everything else you can hold, and measure errors per access at each level.
errors per 10^9 accesses versus transitions per beat, ILLUSTRATIVE
transitions/beat errors/10^9 what the shape says
---------------- ----------- -------------------
0 0
2 0
4 1
8 11
16 47
32 198
monotone and steeply rising: consistent with a pattern-dependent
mechanism, which 22.3 says all three degradation mechanisms are.
contrast with a DIGITAL cause that also looks pattern-dependent:
transitions/beat errors/10^9
---------------- -----------
0 0
2 43 <-- one specific VALUE in this set
4 0
8 0
32 0
non-monotone, and concentrated at one level -- which is a value
effect, not a transition effect. 28.5 §7 makes the same point: a
channel responds to transitions, it does not single out a value.Monotonicity in transition count is the signature, and it is what separates a pattern-dependent mechanism from a pattern-specific bug. A non-monotone profile with a spike is a decode, mask, or alignment problem wearing pattern dependence as a disguise.
And the profile must be errors per access. Different patterns may be generated at different rates; a profile in errors per second measures the generator as much as the channel.
5. The Aggressor/Victim Displacement
Chapter 22.2's fact, turned into a measurement.
The prediction: the corrupted bit position is displaced from the transition that caused it, because the disturbance arrives late.
So the measurement is a joint distribution: for each error, the displacement between the corrupted position and the nearest large transition. A histogram over that displacement is a digital object.
| Displacement histogram | Supports |
|---|---|
| Peaked at zero | the corruption coincides with its own transition — not a late-arriving mechanism |
| Peaked at a consistent non-zero displacement | a late-arriving disturbance, which is what 22.2 predicts |
| Flat | no relationship between transitions and corruption |
| Peaked at one displacement, and that displacement changes with frequency | consistent with a fixed time delay expressed in a changing number of beats |
Row four is the strongest digital evidence in this chapter, and it is worth being precise about why. A fixed physical delay occupies a fixed time. Expressed in beats, that time is more beats at higher frequency. So a displacement that grows with frequency in proportion to the frequency behaves like a fixed time delay — and no digital mechanism has that property, because digital delays are counted in cycles and stay constant in cycles.
That is an inference from a digital measurement to a physical character, not a measurement of a physical quantity. It says something here behaves like a fixed time rather than a fixed number of cycles. It does not say how long, how large, or where — and those are exactly the questions that require the instrumentation §13 describes.
6. A Displaced Error, Cycle by Cycle
The aggressor and the victim in one burst. ILLUSTRATIVE data and displacement.
The corrupted beat is not the beat that caused it
10 cyclesThe displacement of two beats is the entire finding, and it is a digital number. No voltage, no time, no amplitude — a difference between two beat indices.
What makes it evidence rather than a coincidence is repetition. One displaced error is noise; a histogram peaked at displacement two over thousands of errors is a structure, and §5 row four's frequency test is what turns that structure into an argument about character.
And what it is not: a statement that a reflection occurred. Chapter 22.2 predicts displaced corruption from a reflection, and a prediction being satisfied is not the same as its mechanism being established — an equaliser with wrong taps (22.4) produces the same digital signature, and that is a register you can read.
7. The Escalation Boundary
Three things the structure makes explicit.
Only one edge crosses into the physical domain, and it comes from Justified. There is no path from evidence directly to the physical domain — every route passes through the gate, which is the architectural form of §1's two-clause criterion.
The exclusions are inputs, not conclusions. They arrive as recorded facts from four other chapters, and §11's gate cannot derive them. An investigation that skipped an exclusion produces a gate that says not yet, which is the correct answer.
And Not yet justified routes back rather than terminating. That is the common outcome and it is not a failure: most escalation candidates are digital causes that have not been excluded yet.
8. The Gate
// ---------------------------------------------------------------------
// si_escalation_gate -- SIMULATION-ONLY.
//
// WHAT IT DOES: §1's two-clause criterion, executable. Clause one is
// the seven digital exclusions; clause two is a surviving structure
// that no digital cause explains.
//
// WHY IT CANNOT RETURN A PHYSICAL VERDICT: nothing digital measures a
// channel. 22.3 owns why a measured pass/fail grid is not an eye
// diagram, and a gate that returned "signal integrity" would be a
// fabrication wearing the clothes of evidence. The method that would
// return one is a REFUSAL, so a caller cannot ask.
//
// WHY THE EXCLUSIONS ARE INPUTS: they are facts about which
// EXPERIMENTS were run, in four other chapters. No register can
// derive them, and a gate that defaulted them to true would let an
// investigation skip the cheap work and escalate on a hunch.
//
// WHAT IT DOES NOT MODEL: anything electrical. No impedance, no
// amplitude, no time, no eye, no jitter, no crosstalk magnitude.
//
// WHAT IT WOULD MISS: a physical problem coexisting with an
// unexcluded digital one -- common, which is why the output is
// "cheapest remaining hypothesis" and not "cause".
// ---------------------------------------------------------------------
typedef enum {
EX_TRAINING, // 28.1: bitmap structure, window width, finer sweep
EX_TIMING, // 28.2: empty violation snapshot over a long run
EX_CONVERSION, // 28.2 §11: applied counts re-derived by hand
EX_REFRESH, // 28.3: zero deadline misses
EX_CAL_LIFECYCLE, // 28.4: started, converged, committed, readback
EX_CAL_STALENESS, // 28.4 §5: provenance tag comparison
EX_ECC_MASKING // 28.5 §12: correction disabled for a debug run
} si_exclusion_e;
class si_evidence;
// clause two: surviving structure
bit pattern_profile_monotone; // §4 -- monotone in TRANSITION count
int pattern_levels_measured; // how many transition levels were run
int displacement_peak; // §5 -- beats, -1 if no peak
bit displacement_scales_with_freq; // §5 row four
bit errors_per_access_normalised; // §2 -- was the rate normalised?
int crc_errors; // 22.4, 25.4
int alert_events; // 25.4
bit equaliser_config_checked; // §5's callout -- a digital cause
bit odt_switch_timing_checked; // §2 -- a digital cause
bit odt_rank_selection_checked; // §2, 6.7 -- a digital cause
bit lane_remap_experiment_done; // §2 -- the decisive digital test
bit failure_followed_logical_lane; // result of that experiment
endclass
class si_escalation_gate;
protected bit m_excluded[si_exclusion_e];
protected string m_method[si_exclusion_e];
// An exclusion requires the METHOD that established it, for the same
// reason 27.6 §10's capability register does: an experiment nobody
// can describe was not run.
function bit record_exclusion(si_exclusion_e e, string method);
if (method == "") begin
$error("si_gate: exclusion of %s refused -- no method given", e.name());
return 0;
end
m_excluded[e] = 1'b1;
m_method[e] = method;
return 1;
endfunction
function int exclusions_recorded();
int n = 0;
foreach (m_excluded[e]) if (m_excluded[e]) n++;
return n;
endfunction
function void missing_exclusions(ref string out[$]);
out.delete();
for (si_exclusion_e e = e.first(); ; e = e.next()) begin
if (!m_excluded.exists(e) || !m_excluded[e]) out.push_back(e.name());
if (e == e.last()) break;
end
endfunction
// Clause two. A structure "unexplained by digital causes" is not a
// judgement -- it is a conjunction of specific digital checks having
// been run and come back clean.
function bit structure_unexplained(si_evidence v);
// §2: the lane remap is the decisive digital test. If the failure
// followed the LOGICAL lane, the cause is in logic the remap
// moved, and no physical hypothesis is warranted at all.
if (v.lane_remap_experiment_done && v.failure_followed_logical_lane)
return 0;
// §2: ODT sensitivity has two digital explanations that must be
// checked before it counts as channel evidence.
if (!v.odt_switch_timing_checked) return 0;
if (!v.odt_rank_selection_checked) return 0;
// §5's callout: an equaliser with wrong taps produces the same
// displacement signature.
if (!v.equaliser_config_checked) return 0;
// §2, §4: an un-normalised rate makes every frequency and pattern
// result uninterpretable.
if (!v.errors_per_access_normalised) return 0;
// §4: a pattern profile is evidence only if it is monotone in
// transition count AND enough levels were measured to see a shape.
if (v.pattern_profile_monotone && v.pattern_levels_measured >= 4)
return 1;
// §5 row four: a displacement that scales with frequency behaves
// like a fixed TIME rather than a fixed cycle count, and no
// digital mechanism has that property.
if (v.displacement_peak > 0 && v.displacement_scales_with_freq)
return 1;
// Hardware-reported in-flight corruption, with the digital causes
// above already checked.
if (v.crc_errors > 0 || v.alert_events > 0) return 1;
return 0;
endfunction
// §1: BOTH clauses. Neither alone.
function bit escalation_justified(si_evidence v);
return (exclusions_recorded() == 7) && structure_unexplained(v);
endfunction
// The honest output: a routing decision plus the reason.
function string recommendation(si_evidence v);
string miss[$];
missing_exclusions(miss);
if (miss.size() != 0)
return $sformatf("not yet: %0d digital exclusion(s) unrecorded, starting with %s",
miss.size(), miss[0]);
if (v.lane_remap_experiment_done && v.failure_followed_logical_lane)
return "not physical: the failure followed the logical lane through a remap, so the cause is in logic the remap moved";
if (!v.errors_per_access_normalised)
return "not yet: normalise the error rate per ACCESS before interpreting any frequency or pattern result";
if (!v.equaliser_config_checked)
return "not yet: an equaliser with wrong taps produces the same displacement signature (22.4) -- read its configuration";
if (!v.odt_switch_timing_checked || !v.odt_rank_selection_checked)
return "not yet: ODT sensitivity has two digital explanations (switch timing, rank selection) -- check both";
if (!structure_unexplained(v))
return "not yet: no surviving structure that a digital cause fails to explain";
return "justified: a physical hypothesis is now the cheapest remaining one -- see §13 for what that domain requires";
endfunction
// §1 and 22.3's boundary, enforced in code rather than in a comment.
function string physical_verdict(si_evidence v);
return "unavailable: no digital observation measures an eye, jitter, crosstalk, impedance or settling. 22.3 owns why a pass/fail grid is not an eye diagram; this gate routes a hypothesis and does not diagnose one";
endfunction
// §4: the profile shape, computed so "monotone" is a fact rather
// than an impression. Returns -1 when too few levels were measured
// for a shape to exist -- not 0, which would read as "not monotone".
function int monotone_run(int errors_per_level[$]);
int run = 0;
if (errors_per_level.size() < 3) return -1;
for (int i = 1; i < errors_per_level.size(); i++)
if (errors_per_level[i] >= errors_per_level[i-1]) run++;
return run;
endfunction
endclassphysical_verdict() is a refusal, and it is the most important method in the chapter. A gate that could be asked for a channel diagnosis would be asked for one. Replacing the answer with the reason it is unavailable is the only version that survives a reviewer who wants a verdict.
structure_unexplained() returns false when the lane remap showed the failure following the logical lane. That is not a missing exclusion — it is positive evidence against a physical cause, and the gate treats it as disqualifying rather than merely absent.
And monotone_run() returns −1 for fewer than three levels. Two points cannot have a shape, and returning 0 would read as not monotone — the same three-valued discipline this curriculum has needed in 27.3 §6, 27.4 §2, 28.1 §18, 28.2 §17 and 28.3 §13.
9. The Pattern and Displacement Accumulator
// ---------------------------------------------------------------------
// pattern_displacement_counter -- CLASSIFICATION: synthesisable, BINDABLE.
//
// WHAT IT DOES: counts TRANSITIONS per beat and the DISPLACEMENT in
// beats between an error and the most recent high-transition beat.
// Both are digital counts.
//
// WHY TRANSITIONS AND NOT PATTERNS (§4): 22.3 establishes the three
// degradation mechanisms interact through the data pattern, and the
// aspect they interact through is transitions. A profile over
// transition COUNT is monotone for a pattern-dependent mechanism and
// spiky for a value-specific bug -- which is the discrimination.
//
// WHY DISPLACEMENT (§5): 22.2 owns the fact that a reflection arrives
// late and corrupts a DIFFERENT bit than the one that caused it. The
// displacement is that prediction made countable.
//
// WHAT IT CANNOT TELL YOU: amplitude, time, or mechanism. This is a
// distribution over beat indices. 22.3 owns why a pass/fail grid is
// not an eye diagram, and this is not even a grid.
//
// SYNTHESIS: an XOR-popcount, two small histograms, a shift of beat
// indices. No DUT handle, no electrical model of any kind.
// ---------------------------------------------------------------------
module pattern_displacement_counter #(
parameter int DATA_W = 64,
parameter int TRANS_HI = 24, // "high transition" threshold, ILLUSTRATIVE
parameter int DISP_MAX = 8, // displacement bins, in beats
parameter int LEVELS = 8 // transition-count buckets -- §4
)(
input logic clk,
input logic rst_n,
input logic beat_valid,
input logic [DATA_W-1:0] beat_data,
input logic err_valid, // from the scoreboard
input logic clear,
output logic [$clog2(DATA_W+1)-1:0] transitions,
output logic aggressor,
output logic victim,
output logic [15:0] trans_hist [LEVELS],
output logic [15:0] disp_hist [DISP_MAX],
output logic [31:0] beats_at_level [LEVELS],
output logic [31:0] errors_at_level[LEVELS],
output logic [31:0] total_beats,
output logic [31:0] total_errors,
output logic disp_overflow
);
initial begin
if (DATA_W < 2) $fatal(1, "pattern_displacement_counter: DATA_W must be >= 2");
if (DISP_MAX < 2) $fatal(1, "pattern_displacement_counter: DISP_MAX must be >= 2");
if (LEVELS < 2) $fatal(1, "pattern_displacement_counter: LEVELS must be >= 2");
if (TRANS_HI < 1 || TRANS_HI > DATA_W)
$fatal(1, "pattern_displacement_counter: TRANS_HI must be in 1..DATA_W");
end
logic [DATA_W-1:0] prev_data;
logic have_prev;
// Beats since the most recent aggressor. Saturating: a wrapped value
// would place a displacement in the wrong bin, which inverts the
// histogram's shape -- the one thing it exists to show.
logic [$clog2(DISP_MAX+1)-1:0] since_aggr;
logic aggr_seen;
// Bucket a transition count into one of LEVELS buckets.
function automatic int level_of(int t);
int L = (t * LEVELS) / (DATA_W + 1);
return (L >= LEVELS) ? LEVELS - 1 : L;
endfunction
always_comb begin
transitions = have_prev ? $countones(beat_data ^ prev_data)
: {$clog2(DATA_W+1){1'b0}};
aggressor = beat_valid && have_prev && (transitions >= TRANS_HI);
victim = err_valid;
end
always_ff @(posedge clk) begin
if (!rst_n || clear) begin
prev_data <= '0;
have_prev <= 1'b0;
since_aggr <= '0;
aggr_seen <= 1'b0;
total_beats <= '0;
total_errors <= '0;
disp_overflow <= 1'b0;
for (int i = 0; i < LEVELS; i++) begin
trans_hist[i] <= '0;
beats_at_level[i] <= '0;
errors_at_level[i] <= '0;
end
for (int i = 0; i < DISP_MAX; i++) disp_hist[i] <= '0;
end else begin
if (beat_valid) begin
// have_prev gates the transition count: the FIRST beat has no
// predecessor, and counting against a reset value of zero
// would report a spurious aggressor whenever the first beat
// happened to be dense.
if (have_prev) begin
automatic int L = level_of(int'(transitions));
if (trans_hist[L] != 16'hFFFF) trans_hist[L] <= trans_hist[L] + 1'b1;
if (beats_at_level[L] != 32'hFFFF_FFFF) beats_at_level[L] <= beats_at_level[L] + 1'b1;
end
prev_data <= beat_data;
have_prev <= 1'b1;
if (total_beats != 32'hFFFF_FFFF) total_beats <= total_beats + 1'b1;
// Displacement bookkeeping. An aggressor restarts the count;
// otherwise it ages, saturating at DISP_MAX.
if (aggressor) begin
since_aggr <= '0;
aggr_seen <= 1'b1;
end else if (aggr_seen && since_aggr != DISP_MAX[$clog2(DISP_MAX+1)-1:0]) begin
since_aggr <= since_aggr + 1'b1;
end
end
if (err_valid) begin
automatic int L = level_of(int'(transitions));
if (errors_at_level[L] != 32'hFFFF_FFFF)
errors_at_level[L] <= errors_at_level[L] + 1'b1;
if (total_errors != 32'hFFFF_FFFF) total_errors <= total_errors + 1'b1;
// §5: bin the displacement -- but only if an aggressor has
// been seen. Without one there is no displacement to measure,
// and binning at zero would manufacture a peak at zero, which
// §5's table reads as "coincides with its own transition".
if (aggr_seen) begin
if (since_aggr < DISP_MAX[$clog2(DISP_MAX+1)-1:0]) begin
if (disp_hist[since_aggr] != 16'hFFFF)
disp_hist[since_aggr] <= disp_hist[since_aggr] + 1'b1;
end else begin
// Beyond the window: recorded as an overflow rather than
// folded into the last bin, which would create a false peak.
disp_overflow <= 1'b1;
end
end
end
end
end
endmoduleaggr_seen gates the displacement binning, and omitting it would manufacture the chapter's most misleading result. Without it, every error before the first aggressor bins at displacement zero — and §5's table reads a peak at zero as the corruption coincides with its own transition, which is the signature that argues against a late-arriving mechanism. The gate would then reach the opposite conclusion from the truth.
Displacements beyond the window set disp_overflow rather than folding into the last bin. Folding creates a peak at DISP_MAX − 1 that is an artefact of the window size, and a peak is exactly what §5 interprets.
And errors_at_level alongside beats_at_level is what makes §4's profile per-access. Errors per level divided by beats per level is a rate; errors per level alone measures the pattern generator's distribution, which is the mistake §4 warns about.
10. What the Physical Domain Requires
The gate says justified. What happens next is worth stating, because it is the part RTL cannot do and therefore the part this module must hand off honestly.
What the digital investigation delivers to that domain:
| Deliverable | Why it matters there |
|---|---|
| A minimal reproducer — 28.5 §15 | measurement time is expensive; a narrow reproducer makes it feasible |
| The error rate per access, with the duration it was measured over | so a change can be shown to have done something — 28.5 §3 |
| The lane, beat and address structure | it says where to probe |
| The pattern profile | it says what to drive while probing |
| The displacement peak and whether it scaled with frequency | it says what character of disturbance to look for |
| The seven exclusions with their methods | so the physical work is not repeating digital work |
That list is the chapter's real product. Not a verdict — a well-characterised digital experiment plus a named unanswered physical question.
And what the physical domain requires that no register provides: instrumentation that measures voltage against time on the actual channel, a model of the channel, and the ability to compare a measurement against a specification. None of those is a simulation of a reflection, and this chapter builds none of them for that reason.
11. What the Assertions Prove
// Bound to §9's counter. Every property carries `disable iff`, and
// every antecedent is covered below -- 27.2 measured this curriculum
// at 78.53% implications, so a silent pass is otherwise
// indistinguishable from an unbound block.
//
// NOTE ON SCOPE: every property here constrains COUNTING. None of them
// is an electrical statement, and none could be.
module pattern_displacement_sva #(
parameter int DATA_W = 64, parameter int DISP_MAX = 8,
parameter int LEVELS = 8, parameter int TRANS_HI = 24
)(
input logic clk, rst_n, clear,
input logic beat_valid, err_valid, aggressor, victim, disp_overflow,
input logic [$clog2(DATA_W+1)-1:0] transitions,
input logic [31:0] total_beats, total_errors,
input logic [15:0] disp_hist [DISP_MAX],
input logic [31:0] beats_at_level [LEVELS], errors_at_level [LEVELS]
);
// ---- P1. FORBIDDEN. An aggressor is never flagged below the
// threshold. §9: the threshold defines the measurement.
property p_aggressor_threshold;
@(posedge clk) disable iff (!rst_n)
aggressor |-> (transitions >= TRANS_HI);
endproperty
assert property (p_aggressor_threshold)
else $error("counter: aggressor flagged with %0d transitions", transitions);
// ---- P2. FORBIDDEN. No aggressor on the very first beat. §9: the
// first beat has no predecessor, and counting against a reset value
// would report a spurious aggressor on a dense first beat.
property p_no_aggressor_without_predecessor;
@(posedge clk) disable iff (!rst_n)
(beat_valid && total_beats == 32'd0) |-> !aggressor;
endproperty
assert property (p_no_aggressor_without_predecessor)
else $error("counter: aggressor on the first beat");
// ---- P3. victim tracks the error strobe exactly. The displacement
// measurement is between these two events and nothing else.
property p_victim_is_error;
@(posedge clk) disable iff (!rst_n)
victim == err_valid;
endproperty
assert property (p_victim_is_error)
else $error("counter: victim does not track err_valid");
// ---- P4. INVARIANT. total_beats advances only on a valid beat.
property p_beats_only_on_valid;
@(posedge clk) disable iff (!rst_n)
(!beat_valid) |=> (total_beats == $past(total_beats));
endproperty
assert property (p_beats_only_on_valid)
else $error("counter: total_beats advanced with no beat");
// ---- P5. INVARIANT. total_errors advances only on an error.
property p_errors_only_on_error;
@(posedge clk) disable iff (!rst_n)
(!err_valid) |=> (total_errors == $past(total_errors));
endproperty
assert property (p_errors_only_on_error)
else $error("counter: total_errors advanced with no error");
// ---- P6. FORBIDDEN. Errors at a level never exceed beats at that
// level. §9: errors per beat is the rate, and a rate above one means
// the two counters are driven from different events.
property p_rate_is_a_rate;
@(posedge clk) disable iff (!rst_n)
(errors_at_level[0] <= beats_at_level[0]);
endproperty
assert property (p_rate_is_a_rate)
else $error("counter: more errors than beats at level 0");
// ---- P7. INVARIANT. The displacement histogram is monotone
// non-decreasing per bin -- bins only ever fill.
property p_disp_hist_monotone;
@(posedge clk) disable iff (!rst_n)
(!clear) |=> (disp_hist[0] >= $past(disp_hist[0]));
endproperty
assert property (p_disp_hist_monotone)
else $error("counter: a displacement bin decreased");
// ---- P8. FORBIDDEN. An out-of-window displacement sets the
// overflow flag rather than folding into the last bin. §9: folding
// creates a peak that is an artefact of the window size.
property p_overflow_is_sticky;
@(posedge clk) disable iff (!rst_n)
(disp_overflow && !clear) |=> disp_overflow;
endproperty
assert property (p_overflow_is_sticky)
else $error("counter: disp_overflow cleared without a clear");
// ---- P9. FORBIDDEN. The transition count never exceeds the bus
// width. A count above DATA_W means the XOR or the popcount is wrong.
property p_transitions_bounded;
@(posedge clk) disable iff (!rst_n)
(transitions <= DATA_W);
endproperty
assert property (p_transitions_bounded)
else $error("counter: %0d transitions on a %0d-bit bus", transitions, DATA_W);
// ---- P10. INVARIANT. Level counters sum to no more than the total.
property p_levels_bounded_by_total;
@(posedge clk) disable iff (!rst_n)
(beats_at_level[0] <= total_beats);
endproperty
assert property (p_levels_bounded_by_total)
else $error("counter: level-0 beats exceed total beats");
// ---- P11. Clear releases everything, including the overflow flag.
property p_clear_releases;
@(posedge clk) disable iff (!rst_n)
clear |=> (total_beats == 32'd0 && total_errors == 32'd0 && !disp_overflow);
endproperty
assert property (p_clear_releases)
else $error("counter: clear did not release the accumulators");
// ---- P12. FORBIDDEN. An aggressor and a zero transition count
// never coincide. This is P1's degenerate case and it is the one a
// reset-value bug produces.
property p_no_aggressor_at_zero;
@(posedge clk) disable iff (!rst_n)
(transitions == 0) |-> !aggressor;
endproperty
assert property (p_no_aggressor_at_zero)
else $error("counter: aggressor flagged at zero transitions");
// ---- 27.2 §7: publish every antecedent.
cover property (@(posedge clk) disable iff (!rst_n) beat_valid);
cover property (@(posedge clk) disable iff (!rst_n) err_valid);
cover property (@(posedge clk) disable iff (!rst_n) aggressor);
cover property (@(posedge clk) disable iff (!rst_n) victim);
cover property (@(posedge clk) disable iff (!rst_n) aggressor && victim);
cover property (@(posedge clk) disable iff (!rst_n) clear);
cover property (@(posedge clk) disable iff (!rst_n) disp_overflow);
cover property (@(posedge clk) disable iff (!rst_n) transitions == 0);
cover property (@(posedge clk) disable iff (!rst_n) transitions >= TRANS_HI);
cover property (@(posedge clk) disable iff (!rst_n) transitions == DATA_W);
cover property (@(posedge clk) disable iff (!rst_n) disp_hist[0] != 16'd0);
cover property (@(posedge clk) disable iff (!rst_n) disp_hist[2] != 16'd0);
cover property (@(posedge clk) disable iff (!rst_n) errors_at_level[0] != 32'd0);
cover property (@(posedge clk) disable iff (!rst_n) total_errors > 32'd1000);
endmodule12. DV — Testing the Counters
// SIMULATION-ONLY. Independent reference. It stores the beat stream in
// a QUEUE and computes transitions and displacements by RESCANNING,
// rather than accumulating incrementally as §9 does. A different
// algorithm, so agreement is evidence -- 27.4 §15's argument.
//
// WHAT IT DOES NOT MODEL: anything electrical. Like §9, it counts bit
// changes and beat-index differences and nothing else.
class pattern_reference #(parameter int DATA_W = 64, parameter int TRANS_HI = 24);
typedef struct { longint data; bit err; } beat_t;
beat_t log[$];
function void beat(longint d, bit e);
beat_t b; b.data = d; b.err = e;
log.push_back(b);
endfunction
function int transitions_at(int i);
if (i <= 0 || i >= log.size()) return -1; // no predecessor
return $countones(log[i].data ^ log[i-1].data);
endfunction
function bit is_aggressor(int i);
int t = transitions_at(i);
return (t >= 0) && (t >= TRANS_HI);
endfunction
// §5: the displacement is measured backwards from an error to the
// most recent aggressor. Returns -1 when no aggressor precedes it,
// which §9's aggr_seen gate exists to reproduce -- and which a
// naive implementation reports as 0, manufacturing a peak at zero.
function int displacement_of(int i);
if (i < 0 || i >= log.size() || !log[i].err) return -1;
for (int k = i; k >= 0; k--) if (is_aggressor(k)) return i - k;
return -1;
endfunction
function int errors_with_no_aggressor();
int n = 0;
foreach (log[i]) if (log[i].err && displacement_of(i) < 0) n++;
return n;
endfunction
function int disp_count(int d);
int n = 0;
foreach (log[i]) if (log[i].err && displacement_of(i) == d) n++;
return n;
endfunction
// §4: the per-access rate at a transition level. Returns -1.0 when
// no beats landed at that level, because zero errors over zero
// beats is not a rate.
function real rate_at_level(int lo, int hi);
int beats = 0, errs = 0;
for (int i = 1; i < log.size(); i++) begin
int tr = transitions_at(i);
if (tr >= lo && tr <= hi) begin
beats++;
if (log[i].err) errs++;
end
end
if (beats == 0) return -1.0;
return real'(errs) / real'(beats);
endfunction
// §8: monotonicity as a fact. Needs three levels to have a shape.
function bit profile_monotone(real r[$]);
if (r.size() < 3) return 1'b0;
for (int i = 1; i < r.size(); i++) if (r[i] < r[i-1]) return 1'b0;
return 1'b1;
endfunction
endclass| Check | What it establishes |
|---|---|
| Replay §6's burst through both; compare every field | Incremental counter and rescanning reference agree |
| 40,000 random beat streams through both | Two algorithms agree on transitions, aggressors and displacements |
| First beat after reset | Reference returns −1 transitions; counter reports no aggressor — P2 |
| All-zero beat stream | Zero transitions everywhere; no aggressor — P12 |
| Beat stream alternating all-zeros and all-ones | Transitions equal DATA_W; aggressor every beat |
| Errors before any aggressor | Reference errors_with_no_aggressor() > 0; counter bins none |
Remove aggr_seen from §9 and rerun that case | Counter bins them all at 0 — the manufactured peak |
| Error at displacement 2, repeated | disp_hist[2] and disp_count(2) agree |
Error at displacement DISP_MAX | disp_overflow set; no bin incremented — P8 |
| Level with zero beats | rate_at_level() returns −1.0, not 0.0 |
| Two transition levels measured | profile_monotone() false; gate's monotone_run() returns −1 |
| Record six of seven exclusions | escalation_justified() false; recommendation() names the seventh |
| Record an exclusion with an empty method | Refused, count unchanged |
| Lane remap followed the logical lane | escalation_justified() false and disqualifying |
Call physical_verdict() | Returns a refusal, never a verdict |
| Run with the data lanes disconnected | All 12 properties pass; all 14 covers empty |
Two reports are worth publishing, and the second is the specific way this chapter's work goes wrong:
TWO PASSING SI INVESTIGATIONS
(A) the counter was never connected
all 12 properties PASS
total_beats 0
total_errors 0
aggressor never asserted
disp_hist[*] all zero
------------------------------------------------
cover beat_valid 0 hits
... all 14 covers 0 hits
seven of the twelve properties are implications and never
armed. The five INVARIANTS -- transition bound, level bounds,
rate bound, histogram monotonicity, aggressor-at-zero -- pass
on a block at reset.
a report reading "no pattern dependence observed" is produced
by this run AND by a genuinely pattern-independent failure.
total_beats is the field that separates them.
(B) a confident escalation that was digital all along
stimulus : 2,900 errors; a displacement histogram peaked
sharply at 2; monotone pattern profile over 6
transition levels; errors normalised per access
disp_hist[2] 2,731
disp_hist[others] 169 combined
profile monotone TRUE, 6 levels
exclusions recorded 7 of 7
------------------------------------------------
escalation_justified() FALSE
recommendation() "not yet: an equaliser with wrong
taps produces the same
displacement signature (22.4) --
read its configuration"
every clause-one exclusion was recorded and the structure is
textbook: displaced, pattern-dependent, repeatable. A team
reading only the histogram escalates here, and books a
measurement session.
the gate refuses, because equaliser_config_checked is false.
22.4 owns equalisation as the predict-and-subtract family, and
an equaliser whose taps are wrong ADDS a displaced
disturbance -- producing this exact signature from a register
value.
diagnosis : (A) is the vacuity case, and its trap is that an
empty histogram reads as a negative RESULT rather than an
absent measurement. (B) is §5's callout doing its job: the
structure was real, the mechanism was digital, and the gate's
refusal saved the measurement session.
the fix : (A) read total_beats before any histogram. (B) nothing
-- the gate already did it. Which is the argument for encoding
§1's criterion as a data structure rather than as advice.Report (B) is the strongest case in this chapter for building the gate at all. The structure was genuine, repeatable and textbook, and the correct action was still to read a register — which is exactly the decision a team under pressure does not make when the histogram is in front of them.
13. Corner Cases
| Case | Behaviour | Why |
|---|---|---|
DATA_W, DISP_MAX or LEVELS below 2 | Elaboration fails | a histogram or count needs at least two values |
TRANS_HI above DATA_W | Elaboration fails | the threshold would be unreachable |
| First beat after reset | No aggressor | P2 — no predecessor to compare against |
| Transition count zero | No aggressor | P12 — the reset-value bug's signature |
| Error before any aggressor | Not binned | §9 — binning would manufacture a peak at zero |
| Displacement beyond the window | disp_overflow set, not folded | §9 — folding creates an artefact peak |
| Errors per level exceed beats per level | P6 fires | the counters are driven from different events |
| Two transition levels measured | monotone_run() returns −1 | §8 — two points have no shape |
| Six of seven exclusions recorded | escalation_justified() false | §1 — both clauses required |
| All seven recorded, no surviving structure | False | §1 clause two |
| Remap showed the failure followed the logical lane | False, and disqualifying | §8 — positive evidence against physical |
| Exclusion recorded with no method | Refused | §8 — an experiment nobody can describe was not run |
physical_verdict() called | Returns a refusal | §10 — nothing digital measures a channel |
| CRC errors non-zero, digital causes checked | Structure counts as unexplained | §3 — hardware-reported in-flight corruption |
| Error rate not normalised per access | recommendation() says not yet | §2, §4 — un-normalised rates are uninterpretable |
Rows eleven and twelve are the two that stop an escalation for good reasons. One is evidence against the hypothesis; the other is an investigation that did not happen.
14. Misconceptions
“It fails at high frequency, so it is signal integrity.” §2. Five digital explanations come first, all cheaper, and one of them is that fewer accesses happen per second at lower frequency.
“One lane fails, so that lane's channel is bad.” §2. Four of the six things in a lane's path are digital, and the remap experiment settles it without a scope.
“Changing ODT changes the failure, so it is termination.” §2. Two of three hypotheses are digital — the switch timing and which ranks were told to terminate — and both are register reads.
“Random data fails and walking-ones passes, so it is pattern-dependent.” §4. Those two patterns differ in several ways at once. Vary transition count and measure a profile.
“A pattern-dependent failure is a channel problem.” §4. Monotone in transition count is consistent with one; a spike at one transition level is a value effect, which is logic.
“The displacement histogram measures the reflection.” §5's callout. It is a distribution over beat indices. It gives no amplitude, no time, and does not identify a mechanism.
“A displaced error proves a reflection.” §5. An equaliser with wrong taps produces the same digital signature, and that is a register you can read.
“A pass bitmap shows the eye is closing.” 28.1 §1, and 22.3 owns the underlying statement that a pass/fail grid is not an eye diagram.
“The assertions prove the channel is fine.” §11's callout. Every property constrains a counter. The design has no voltage for a property to constrain.
“We excluded the digital causes, so it is physical.” §1. Exclusion is one clause of two. Without surviving structure that the digital causes fail to explain, there is no positive reason to escalate.
“We can simulate the channel in RTL to check.” The opening callout. Such a model would be a fabrication wearing the clothes of evidence, and this chapter deliberately contains none.
15. Interview Reasoning
When is a signal-integrity hypothesis justified? When every cheaper digital cause has been excluded by experiment and the surviving evidence has a structure those causes do not explain. Either clause alone is insufficient.
A failure goes away at lower frequency. What do you conclude? That something scales with the period. Five digital explanations come before the channel, and one is simply that fewer accesses occur per second — so re-measure per access first.
One byte lane fails. What is the decisive experiment? Remap the lane. If the failure follows the logical lane the cause is in logic the remap moved; if it stays with the physical pin, the physical path is implicated. No measurement needed.
Changing ODT settings changes the failure. Is that signal integrity? Not yet. The switch timing and the rank selection are both digital and both register reads, and either can make termination absent when the data arrives.
How do you measure pattern dependence properly? Vary the transition count across several levels and measure errors per access at each. A monotone rise is consistent with a pattern-dependent mechanism; a spike at one level is a value-specific bug.
What is the aggressor/victim displacement and why does it matter? The gap in beats between a high-transition beat and a corrupted one. A consistent non-zero peak is consistent with a late-arriving disturbance, which is what a reflection predicts.
What makes that evidence stronger? If the displacement grows with frequency in proportion, it behaves like a fixed time rather than a fixed number of cycles — and no digital mechanism has that property.
Does that measure the reflection? No. It gives no amplitude, no time and no mechanism, and an equaliser with wrong taps produces the same signature.
What do you hand to the physical investigation? A minimal reproducer, the error rate per access with its measurement duration, the lane, beat and address structure, the pattern profile, the displacement peak and its frequency behaviour, and the seven exclusions with the method used for each.
Why not model the channel in RTL to test the hypothesis? Because the model would not be evidence. RTL has no voltage and no time; a simulated reflection would only demonstrate the assumptions used to write it.
16. Exercises
-
§1 requires both clauses. Construct the case where all seven exclusions are recorded, real channel degradation is present, and the gate correctly says not yet — then say what is missing.
-
§2 gives five digital explanations for frequency sensitivity. Design the smallest set of runs that excludes all five, and state the total cost relative to one measurement session.
-
§4 contrasts a monotone profile with a spike. Construct the digital bug that produces a monotone profile, and say what additional observation would distinguish it.
-
§5 row four argues that a frequency-scaling displacement behaves like a fixed time. Derive the displacement in beats for a fixed delay at two frequencies, and state the assumption your derivation makes.
-
§8's gate refuses an exclusion without a method. Write the three weakest "methods" you have seen accepted in practice, and say what each fails to establish.
-
§9 gates displacement binning on
aggr_seen. Remove that gate and derive the resulting histogram for a run whose first hundred errors precede any aggressor, then say which of §5's rows the gate would report. -
§11 argues no property here can be electrical. Construct the strongest electrical-sounding property you can over this design's signals, and identify precisely what it actually constrains.
-
A colleague proposes adding an RTL channel model with a configurable reflection coefficient so the team can "reproduce the SI failure in simulation". Write the objection, and describe what such a model would legitimately be useful for.
17. Where This Goes
Signal integrity is the hypothesis this module defers longest and escalates to most carelessly. The criterion has two clauses — every cheaper digital cause excluded by experiment, and a surviving structure those causes do not explain — and the three experiments most often cited as proof each have digital explanations that are cheaper to test. What genuinely bears on the channel is digital too: a monotone pattern profile in transition count, a displacement between aggressor and victim that scales with frequency, and hardware's own CRC and alert reports.
Four results carry forward. The lane remap is decisive and digital — if the failure follows the logical lane, no physical hypothesis is warranted. Monotone in transition count, measured per access, is what separates a pattern-dependent mechanism from a value-specific bug. A displacement that scales with frequency behaves like a fixed time, which is the strongest digital evidence available here and still not a measurement. And the product of a digital investigation is a characterised experiment plus a named unanswered question, not a verdict.
Two things stay open, and neither is a gap. No property in this chapter is electrical, because the design has no electrical signal for a property to constrain — §11 states that as structural. And the gate is not assertable: its inputs are facts about which experiments a human actually ran, which no register can establish.
Chapter 28.7 closes the module by asking what would have prevented most of it. Every chapter here began with a system already running and a failure already present, and the investigations were expensive largely because many variables had changed at once before anyone looked. Bring-up is the discipline that stops that: stage the system so that each layer is established and believed before the next depends on it, change one controlled variable at a time, and capture evidence before recovering — so that when a failure does appear, the list of things that could have caused it is short by construction rather than by investigation.
Continue learning
Related tutorials
- Related topic
DDR2
A clock cycle has only two edges, so DDR2 raised bandwidth by deepening prefetch to 4n — making prefetch depth the frequency ratio between interface and core. Raising the rate on a multi-load bus then forced termination onto the die.
- Related topic
ODT — On-Die Termination Control
ODT is a control input selecting whether a device presents termination. Which device should terminate depends on which way data is flowing — and the one rule that never varies is that a driver must not terminate its own output.
- Related topic
Reflections
A reflection is energy that had nowhere to go. It returns after a round trip, so it corrupts a later bit than the one that caused it — and that single fact explains why a training sweep finds a narrow region.
- Related topic
Signal Integrity
Three mechanisms eat the same margin budget and interact through the data pattern. Data bus inversion is the first digital mitigation here — and it changes what the channel carries rather than improving the channel.
Standards & specifications
- Governing standard
- JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)
Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the DDR curriculum.
