β: This English translation is in beta — the Traditional-Chinese original is the authoritative version.
DJ and the Dual-Dirac Model: The Industry-Standard Tool for TJ@BER
Prerequisites: serdes_clocking_connection (first appearance of RJ/DJ/TJ, eye and BER), lab_12 (RJ-only bathtub), lab_08 ( integrated from phase noise) | Next: exercises, lab_13
serdes_clocking_connection already gave the shorthand "TJ DJ RJ". This page builds that formula rigorously from scratch: what RJ is, what DJ is, which integral comes from, how the bathtub curve is derived from the CDF (cumulative distribution function) of jitter, and the dual-Dirac model the industry actually uses (SerDes specs from Fibre Channel MJSQ onward) — including its most misunderstood and most important honesty note: the model parameter is inherently less than or equal to the actual peak-to-peak DJ, and this "under-report" is deliberate — it is precisely what makes the TJ extrapolation accurate.
Physical intuition (conclusion first): jitter has two fundamentally different components. RJ (random jitter) is the time-domain incarnation of oscillator phase noise — Gaussian, unbounded; the longer you wait (the tighter the BER target), the larger it "grows", so it must be booked as times a multiplier that increases with the BER target. DJ (deterministic jitter) is driven by deterministic physical mechanisms (inter-symbol interference, duty-cycle distortion, supply ripple); its amplitude has a physical upper bound, so it is booked peak-to-peak and does not grow with BER. The dual-Dirac model compresses "bounded DJ of arbitrary shape" into two Diracs and keeps RJ as a Gaussian, in exchange for a straight line that can be extrapolated to . Measuring a BER of requires waiting for bits — even at 10 Gb/s that is 100 s just to "see one error on average", and statistical confidence takes hours; extrapolation is not laziness, it is an engineering necessity.
Step 1: RJ — unbounded Gaussian, all the way from phase noise (this site's chain)
RJ is the endpoint of the chain built over this site's first six chapters; a step-by-step recap (each step has its own page):
- Device white noise → 1/f² phase noise: [P1] Eq.(21), p.185 gives ( dimensionless, in C, in A²/Hz). See white_noise_to_phase_noise.
- Phase-noise integration → rms jitter: (unit: s; canonical example C: GHz, dBc/Hz, 1/f², integrate 1→100 MHz → fs). See lab_08.
- Time-domain view — random walk: [P2] Eq.(8), p.792 gives the accumulated phase jitter ( in , from [P2] Eq.(11)/(12), p.793 ; note the expression contains no — converting to the time version requires dividing by once more).
- Why Gaussian: every period the oscillator absorbs a large number of mutually independent tiny noise kicks; the total phase error is a sum of independent increments → central limit theorem → Gaussian. lab_11 verified directly by Monte Carlo that the histogram is Gaussian and .
RJ's key property: no upper bound. The Gaussian tail is never zero — there is no "guaranteed never to cross" margin; you can only ask "how small is the crossing probability". This is why RJ must be booked as paired with a target BER (the function of Step 3).
Step 2: DJ — bounded, driven by deterministic mechanisms
DJ is the half that ISF theory "cannot see" (it does not come from the oscillator's random noise), yet it is often the largest component of the TJ measured in a SerDes. Three main sources, each with clear physics and a clear upper bound:
| DJ type | Physical mechanism | PDF shape | Why bounded |
|---|---|---|---|
| ISI (inter-symbol interference) | Finite channel bandwidth with memory: edge position depends on the preceding bit pattern | Multiple discrete spikes (one per pattern) | Channel impulse response has finite length |
| DCD (duty-cycle distortion) | Rise/fall asymmetry, threshold offset: rising edges systematically early, falling edges late | Exactly two Diracs | The asymmetry is a fixed amount |
| PJ/SJ (periodic/sinusoidal jitter; supply spurs, crosstalk) | Supply ripple modulates the VCO via supply pushing (see varactor_tuning_supply_pushing); coupling from adjacent clocks | arcsine (double-horn) distribution | Sinusoid amplitude is fixed |
Note that DCD's PDF is inherently two Diracs — the dual-Dirac model is exact for it; the model's name and shape come precisely from this "worst-case shape".
PDF of sinusoidal DJ (the arcsine distribution), derived step by step. This is the DJ used in lab_31 and the standard model for supply spurs. Let the edge's time offset be , with the amplitude (unit: s); the spur is asynchronous with the data, so the sampled phase is uniform on , (unit: 1/rad). Change of variables: within one period each corresponds to two branches, each contributing , and :
- Units: ✓ (the PDF integrated over is dimensionless).
- Normalization check: ✓.
- Physics: the sinusoid dwells longest near its turning points → the probability density diverges at the two ends (an integrable "double horn"). Both horns are clearly visible in the histogram of lab_31 panel (a).
- Bounded: (peak-to-peak, unit: s). Applicability: the spur is asynchronous with the data (uniform phase); if the spur is locked to the data (synchronous), the PDF degenerates into discrete spikes — still bounded.
Step 3: The function — Gaussian tail integral, and where comes from
RJ's bookkeeping tool is the Gaussian tail probability. For a standard normal (dimensionless):
Step-by-step reduction to erfc (this is what connects it to scipy and this site's serdes_utils.Q). Substitute , ;
the lower limit becomes :
The last step used the definition . For a general Gaussian (mean , standard deviation , both in s), substitute once more with to get — the argument of is always "how many from the mean", dimensionless ✓.
Deep-tail asymptotic form (one integration by parts):
Use it to estimate by hand: exponent , , divide by → ✓. This is the "BER ↔ " correspondence used throughout this site's jitter chapters (consistent with the table in 06 exercises):
| Target BER | RJ peak-to-peak | |
|---|---|---|
| (written 7.03 on this site) | ||
| (written 7.94 on this site) |
One-line Python verification (using this site's real API; inverts the tail integral above via erfcinv):
import numpy as np
from scipy.special import erfcinv
from simulations.common.serdes_utils import Q
q = np.sqrt(2) * erfcinv(2 * 1e-12) # inverse of Q (inverting the tail integral)
print("Qinv(1e-12) =", round(float(q), 3)) # -> 7.034
print("Q(7.034) =", float(Q(7.034))) # -> 1.0e-12
Step 4: The dual-Dirac model — definition and PDF
The model does two things (external literature, not among the five source PDFs; methodology references at the bottom of the page):
- The TJ PDF is the convolution of the DJ PDF with the Gaussian RJ (RJ and DJ statistically independent): , where (unit: 1/s).
- Compress the bounded of arbitrary shape into two equal-weight Diracs, with spacing denoted :
The convolution uses the Dirac sampling property , giving the model PDF in one step:
— two Gaussians with the same , each carrying half the probability. In the symmetric case (this page and lab_31), .
The tail function, integrated step by step. Define ( is the CDF). Apply Step 3's result term by term to the model PDF, :
Only one term survives in the deep tail. The ratio of the two terms (asymptotic form, symmetric case):
With lab_31's numbers ( ps, ps, region of interest ps), this ratio is — completely negligible. So the deep tail is just a single Gaussian with weight ½:
The Q-scale straight line — the principle behind extracting . Invert the expression above:
— on the "Q-scale" (vertical axis , horizontal axis ), the deep tail is a straight line: slope , horizontal-axis intercept . Instruments (a BERT scan or an oscilloscope's TIE histogram) perform exactly this straight-line fit to the measured tail. Note the factor 2 inside : it books the fact that "each Dirac carries only half the probability" — the first factor-of-2 to keep an eye on in this page (Step 7 has two more).
Step 5: The bathtub curve — derived step by step from the jitter CDF
Assume NRZ data with UI (unit interval) (unit: s). Take the eye center as the time origin and let the sampling-instant offset be ; the left data edge sits nominally at , the right edge at , each carrying jitter (identically distributed, tail , CDF ).
- Error event one (left edge late): the left edge actually lands at ; if it arrives later than the sampling instant, i.e. , the sampler reads the previous bit. Probability .
- Error event two (right edge early): the right edge actually lands at ; if it arrives earlier than the sampling instant, i.e. , probability , which for a symmetric distribution .
- Only a transition can produce an error: for random data the probability that adjacent bits differ is (transition density ). Both events are rare, so the probability of the union the sum (the intersection is second-order small):
Consistency check: for pure RJ, and the expression reduces to
— exactly the formula of lab_12 and serdes_utils.ber_bathtub
✓. The bathtub's two "walls" are just the left and right tails of the jitter CDF, replotted with the horizontal axis
changed from "jitter magnitude" to "sampling position". DJ's effect is immediately visible: it shifts the entire tail by → both walls move inward;
RJ sets the walls' slope (on a log-BER axis, the slope is set by ).
- Dimension check: the argument of is , its output a probability (dimensionless); BER dimensionless ✓.
- Applicability: rare events (); edge jitter stationary and data-independent (strictly speaking, ISI is data-correlated — see the failure conditions in Step 8).
Step 6: The TJ(BER) extrapolation formula — derivation and factor audit
Derivation. Goal: at a specified BER, how much eye width does jitter "eat"? Define the right-wall position as the point where the dominant Gaussian of the right deep tail drops to the target BER (industry convention books the per-Gaussian tail BER directly; audit below):
Symmetrically, the left wall . Total jitter is defined as the total width eaten by the two walls:
At BER , . Horizontal eye opening .
- Dimension check: ✓.
- Physical meaning: the DJ part does not change with BER (bounded, eaten once); the RJ part grows slowly as while the BER tightens ( only moves from to — the logarithmic growth of the Gaussian tail).
Factor-of-2 audit (site convention: spell out every 2). The formula above uses , which implicitly books "the tail of each Gaussian BER". Strictly following the bookkeeping of Steps 4 and 5 there are two more ½'s: the Dirac weight ½ (per-side tail ) and the transition density (the bathtub multiplies by ½ again). With everything counted, the wall position is set by :
Convention Wall condition Multiplier (BER ) lab_31 resulting TJ per-Gaussian (industry formula) ps per-side tail ps bathtub (, this site's lab_12 convention) ps The three differ by /side — ps total in this example, about 2% of TJ; the industry formula is conservative. In practice this is not a problem, because are fitted from the tail and substituted back for extrapolation under one and the same convention — with a consistent convention the errors nearly cancel; but when comparing DJ/RJ reports from different instruments, always first ask which convention each one uses. (lab_31 prints all three numbers.)
Step 7: The honesty note — , and the under-report is deliberate
This is the most commonly misunderstood point about dual-Dirac: is a model parameter, not the actual peak-to-peak DJ. lab_31's numbers: the true sinusoidal DJ has ps, while the fit gives ps.
Why it is necessarily smaller (derivation). The total jitter tail is the average of the Gaussian tail over the DJ distribution (obtained by swapping the order of integration in the convolution once):
Because and is strictly decreasing, the integrand satisfies pointwise . Split the integral into the right half (, total mass ½) and the left half (, whose deep-tail contribution is smaller by another factor of ):
Equality holds only when the entire right-half mass is concentrated at (i.e. a two-point distribution like DCD). Flip to the Q-scale: — the true tail curve always lies above the line with "Diracs placed at the true extreme " (blue line vs gray dashed line in lab_31 panel (b)). A straight-line fit to the true curve therefore necessarily has intercept , i.e.:
Why the under-report actually makes TJ accurate. For the extrapolation to be accurate, what is needed is that "over the few decades around the target BER, the line hugs the true tail height" — and the fit is anchored exactly that way (lab_31: the dual-Dirac extrapolated eye opening is ps vs ps for the exact composite — a ps difference). Conversely, forcing the Diracs onto the true extremes (using ps as ) over-reports the DJ term alone by ps: plugging into the formula (true ps) gives ps, pessimistic by ps compared with the exact bathtub's ps — margin thrown away for nothing. Intuition: the DJ distribution carries only finite probability mass near its extremes (the sinusoid's double horns are still integrable singularities), so the deep tail is effectively a "discounted Gaussian" and the equivalent center naturally pulls inward.
The costs (also stated honestly):
- depends on the fit depth. lab_31 sweeps three fit windows: ps, ps, ps — the deeper the window, the closer to (but never exceeding) . A DJ/RJ decomposition report should state its fit window; the spec documents (the MJSQ family) provide explicit methodology for this.
- DJ "leaks" into : the fit gives ps, slightly larger than the true RJ's ps — the residual curvature of the DJ tail is absorbed by the straight line into its slope. So do not take the instrument-reported RJ directly as the integral of the oscillator's phase noise when cross-checking (a few % discrepancy is normal); to cross-check, isolate DJ with a clean clock pattern.
Step 8: lab_31 numerical verification
Full script: simulations/lab_31_dual_dirac.py (dependencies: Q from simulations/common/serdes_utils.py,
savefig from simulations/common/plot_utils.py; running python scripts/run_all_sims.py
re-runs it along with everything else). Synthesize RJ ( ps Gaussian) sinusoidal DJ ( ps → ps)
at ps (10 Gb/s, same as lab_12). The tail is computed semi-analytically ("averaging over the sinusoid's phase") down to a depth of
(no Monte-Carlo noise); the fit follows Step 4's Q-scale straight line.
| Parameter | Variable | Value | Unit | Notes |
|---|---|---|---|---|
| RJ rms | sigma_rj | s | Gaussian, unbounded (exaggerated for teaching; the canonical example-C clock is 447.9 fs) | |
| DJ amplitude | a_dj | s | Sinusoidal (supply-spur type), ps | |
| Unit interval | ui | s | 10 Gb/s NRZ | |
| Target BER | ber_target | — | Common SerDes spec | |
| Fit window | t_deep, t_shallow | — (tail probability) | Q-scale line-fit region | |
| MC sample count | n_mc | — | Used for the histogram only |
Run output (excerpt; # -> marks verifiable numbers):
extracted DJ_dd = 3.16 ps # -> 3.16 (< DJ_pp = 4.0)
extracted sigma = 1.03 ps # -> 1.03 (true RJ rms 1.0)
TJ@1e-12 (formula) = 17.65 ps # -> 17.65
TJ@1e-12 (bathtub) = 17.23 ps # -> 17.23
eye opening @1e-12: composite 82.77 ps vs dual-Dirac 82.76 ps

How to read this figure:
- (a) PDF: the blue (true) curve has arcsine double horns smeared round by the Gaussian; the red dashed curve (dual-Dirac) visibly does not fit in the mid-section — the model never claims to fit the PDF; it is responsible only for the deep tail. The red dash-dot lines (Dirac positions ps) sit inside the gray dotted lines (true extremes ps): this is .
- (b) Q-scale: in the deep-tail region the blue curve is dead straight → Gaussian dominated; the red dashed line is the fitted line (slope , intercept ); the gray dotted line is the pessimistic prediction with "Diracs forced at ", lying below the true curve (a larger tail probability at the same ).
- (c) bathtub: blue (exact) and red dashed (dual-Dirac extrapolation) nearly coincide at (0.01 ps difference in opening) — the model doing its proper job; the green dotted line (RJ-only, no DJ) opens far wider: DJ shifts the walls, RJ sets the slope.
Interactive: fit one yourself. The widget below lets you directly control the RJ σ and the sinusoidal DJ amplitude (synthesized population, seeded PRNG, ), and switch between two BER-decade fit windows (shallow: ; deep: ) to watch , , their difference , and the extrapolated move in real time as the fit depth changes — this is the quantitative version of Step 7's "the deeper the fit, the closer gets to (but never exceeds) ", and it holds for whatever you pick yourself.
simulations/lab_31_dual_dirac.py), since BER windows this deep cannot be resolved honestly from any finite sample. Q(x) via the Numerical Recipes erfc approximation; Q⁻¹ via Acklam's rational approximation to the inverse normal CDF. Pedagogical toy model — real links superpose ISI+DCD+PJ and ISI is data-correlated (this page's Step 8 / Applicability table).One-liner verification (uses lab_31's real functions; a run takes a few seconds):
from simulations.lab_31_dual_dirac import fit_dual_dirac, q_inv
dj_dd, sigma_fit, _ = fit_dual_dirac(a_dj=2e-12, sigma_rj=1e-12)
print("DJ_dd =", round(dj_dd * 1e12, 2), "ps") # -> 3.16
print("sigma =", round(sigma_fit * 1e12, 2), "ps") # -> 1.03
tj = dj_dd + 2 * q_inv(1e-12) * sigma_fit
print("TJ@1e-12 =", round(float(tj) * 1e12, 2), "ps") # -> 17.65
This is a pedagogical model (not transistor-level): DJ is a single sinusoid only, and RJ is the equivalent Gaussian after integrating white noise; a real link's DJ is a superposition of ISI+DCD+PJ, and ISI is correlated with the data pattern.
Worked examples
Example 1 (TJ budget: canonical clock + given DJ) 10 Gb/s ( ps). Clock RJ from canonical example C: fs ( GHz, dBc/Hz, 1/f², integrated 1→100 MHz). The link measures ps. Find the TJ and eye opening at BER .
Step-by-step substitution (with units):
The RJ term ps matches Step 4 of serdes_clocking_connection, "448 fs → RJ eats ps" ✓. Dimension check: ✓.
sigma_t = 447.9e-15; dj_dd = 3e-12; ui = 100e-12
tj = dj_dd + 14.07 * sigma_t
print("TJ@1e-12 =", round(tj * 1e12, 2), "ps") # -> 9.3
print("eye opening =", round((ui - tj) * 1e12, 1), "ps") # -> 90.7
Example 2 (working an RJ spec backward into a phase-noise spec) Same link; the system allocates a TJ budget of ps () to the clock path, and the link DJ takes ps. Question: how much clock RJ is allowed at most? And converted back into an spec (assuming example C's 1/f² shape and 1→100 MHz integration bandwidth), what does that become?
Step-by-step substitution (with units):
With the 1/f² shape fixed, (proportional to amplitude), and example C's anchor is dBc/Hz fs, so the allowed relaxation is:
Dimension check: ✓; the dB conversion acts on a dimensionless ratio ✓. Design message: once DJ eats two-thirds of the budget, the RJ spec immediately drops to sub-ps — this is why SerDes teams track DJ (layout/supply/equalization) and RJ (oscillator phase noise — ISF's territory) separately.
import numpy as np
ui, tj_budget, dj_dd = 100e-12, 30e-12, 20e-12
sigma_max = (tj_budget - dj_dd) / 14.07
print("sigma_max =", round(sigma_max * 1e15, 1), "fs") # -> 710.7
dL = 20 * np.log10(sigma_max / 447.9e-15)
print("L(1MHz) max =", round(-100 + dL, 1), "dBc/Hz") # -> -96.0
Applicability and failure conditions
| Assumption | Holds when | Fails when |
|---|---|---|
| RJ Gaussian and stationary | Thermal noise dominates; free-running or measured in-loop | Deep tail contaminated by spurs, or non-stationary (temperature drift) → Q-scale no longer a straight line |
| DJ bounded and independent of RJ | Supply spurs, DCD, fixed-channel ISI | DJ amplitude drifts slowly (supply load transients) → the decomposition drifts over time |
| Deep tail dominated by a single Gaussian | Fit window deep enough () | Fit window too shallow: residual DJ curvature → over-estimated, under-estimated even more |
| Edge jitter independent of data | Clock jitter, asynchronous spurs | ISI is pattern-correlated: a strict treatment bins by pattern (conditioning the CDF) and then recombines |
| Rare events add (union sum) | With the eye nearly closed (BER approaching 0.5), higher-order terms are no longer negligible | |
| Consistent convention | Comparisons within one instrument / one convention | Comparing DJ/RJ numbers across instruments: per-Gaussian vs conventions differ by /side (Step 6 audit table) |
Relation to ISF / this site's chain, and design knobs
- ISF owns RJ: every knob for lives in the earlier chapters — lower (waveform symmetry, symmetry), raise (tank_swing), lower , use the loop high-pass to cut the lower integration limit (serdes_clocking_connection Step 6).
- ISF cannot see DJ: DJ's knobs are power integrity (LDO/decoupling, against PJ), equalization (CTLE/DFE, against ISI), and duty-cycle correction (against DCD). For how spurs are distinguished from random phase noise in the spectrum, see measurement_and_spurs.
- Making the shorthand precise: the serdes page's is engineering shorthand (conservative); the strict version replaces with the fitted , and Step 7 of this page quantifies the difference between the two (here 4.0 vs 3.16 ps → TJ over-reported by 0.84 ps).
Key takeaways
- RJ: Gaussian, unbounded, from phase noise ([P1] Eq.(21) → integration → ; [P2] Eq.(8) random walk); booked as × . DJ: bounded (ISI/DCD/PJ); booked peak-to-peak.
- comes from the Gaussian tail integral; , .
- dual-Dirac: ; deep tail → Q-scale straight-line fit yields .
- The bathtub is just the two tails of the jitter CDF replotted.
- ; the conventions (per-Gaussian / per-side / ) differ by /side — align conventions before comparing instrument numbers.
- , and the under-report is deliberate: the fit is anchored to the true deep tail → accurate extrapolation (lab_31: 0.01 ps difference in opening); forcing in is pessimistic instead and wastes margin. lab_31: ps → ps, ps, TJ@ ps.
Methodology references (external literature, not among the five source PDFs): dual-Dirac is the industry-standard method; see INCITS T11.2, Fibre Channel — Methodologies for Jitter and Signal Quality Specification (MJSQ), Technical Report Rev 14.0, June 2005; and R. Stephens, "Jitter Analysis: The Dual-Dirac Model, RJ/DJ, and Q-Scale," Agilent Technologies Whitepaper, Dec. 2004. The jitter clauses of modern SerDes specs (PCIe, the OIF-CEI family) all inherit this model. The theory of phase noise / RJ itself comes from [P1]/[P2].
Further reading
- The full SerDes clocking chain and the choice of integration bandwidth: serdes_clocking_connection
- RJ-only bathtub and eye diagram: lab_12_serdes_eye_ber
- integrated from : lab_08_jitter_integration
- Why RJ is Gaussian (Monte-Carlo): lab_11_monte_carlo_jitter
- Separating spurs from random phase noise in measurement: measurement_and_spurs
- End-to-end capstone (LC → phase noise → jitter → BER): capstone_lc_end_to_end