Part I · Today's module · Chapter 7 of 18

DC balance

AC coupling makes the baseline wander with the data; one inversion flag per frame balances all four outputs at once.

Baseline wander, with and without frame-inversion codingillustrative
Baseline wander animation (loads when scrolled into view)
The TIA’s offset-cancel loop is a first-order high-pass (corner 100,000 Hz illustrative), so each output’s decision baseline follows the low-passed deviation of the data from mid-level. The encoder inverts a whole 256 symbols illustrative frame whenever that pulls the running sum of all four deviations back towards zero.
bk[n]=a bk[n−1]+(1−a) dk[n]\hl{filter}{b_k[n]} = a\,b_k[n-1] + (1-a)\,d_k[n]
a=e−1/τa = e^{-1/\tau}
d=(X0−2, ReX1, ReX2, ImX3)d = (X_0 - 2,\ \mathrm{Re}X_1,\ \mathrm{Re}X_2,\ \mathrm{Im}X_3)
DFT(1−x)=4δ−X⇒d↦−d\mathrm{DFT}(\mathbf 1 - x) = 4\delta - X \Rightarrow d \mapsto -d
fj=[ R⋅Sj>0 ], R←R+(−1)fjSj\hl{flag}{f_j} = [\,R\cdot S_j > 0\,],\ R \leftarrow R + (-1)^{f_j} S_j

What this stage does

The receivers are AC-coupled: a slow loop in each TIA cancels its DC offset. That is useful, because it removes output 1’s carrier pedestal and shrinks the TIA’s required linear range from 7.555 steps to 4 steps. But the loop cannot tell the pedestal from the data’s own running mean. When the data is biased, or holds one word for a long time, the decision baseline drifts with it, and a drift of half a step closes the eye.

Frame-inversion coding fixes this with one bit per frame. Sending a frame as 1 − x instead of x flips the sign of every output’s deviation at once, because the DFT of the all-ones word is 4δ4\delta. The encoder keeps a running sum of the four deviations as a vector and inverts each frame whose sum points the same way; the FPGA undoes it after decoding with X=4δ−X′X = 4\delta - X'.

Why one flag balances all four outputstextbook
Inversion animation (loads when scrolled into view)
Each of the 16 input words (solid) and its inverse (faint): every output's deviation from its mid-level changes sign together.

How it’s modelled

The simulator models the offset-cancel loop as a single first-order high-pass at the TIA input with time constant τ=baud/(2πfc)\tau = \text{baud}/(2\pi f_c), which at the default corner is 22,282 symbols. The encoder balances the running digital sum (not the filter state); the scalar variant balances output 1 alone for comparison. Workloads of 1,048,576 symbols are random bits, biased bits with P(1) = 0.8 illustrative, and runs of one word held for 32,768 symbols illustrative. Flag errors are not modelled. The animation above runs the same model in your browser on its own random stream.

Before any of this the simulator also asks how often a word clips at all (outputs 1 and 3 have five levels, PAM4 four): random bits clip 0.1249 of words; 16-bit two’s-complement small integers clip 0.1037, but sent operand by operand they bunch into runs of up to 30 words.

Clipping duty cycle by workload (operand-major)illustrative
00.020.040.060.080.10.12random bitsresidues mod 65537signed ints σ=3.2, 16-bitsigned ints σ=3.2, 32-bitfraction of wordsrandom bits, none: 0.1249random bits, plane inversion: 0.1235random bits, word inversion: 0residues mod 65537, none: 0.1168residues mod 65537, plane inversion: 0.1175residues mod 65537, word inversion: 0signed ints σ=3.2, 16-bit, none: 0.1037signed ints σ=3.2, 16-bit, plane inversion: 0.1037signed ints σ=3.2, 16-bit, word inversion: 0signed ints σ=3.2, 32-bit, none: 0.09743signed ints σ=3.2, 32-bit, plane inversion: 0.09769signed ints σ=3.2, 32-bit, word inversion: 0
Fraction of words that clip, by workload and inversion scheme. Per-word inversion removes clipping but needs a flag per word.
Data table
Clipping duty cycle by workload (operand-major)
workloadnoneplane inversionword inversion
random bits0.12490.12350
residues mod 655370.11680.11750
signed ints σ=3.2, 16-bit0.10370.10370
signed ints σ=3.2, 32-bit0.097430.097690
Longest clipping runillustrative
0102030405060random bitsresidues mod 65537signed ints σ=3.2, 16-bitsigned ints σ=3.2, 32-bitwordsrandom bits, plane-major: 5random bits, operand-major: 5residues mod 65537, plane-major: 6residues mod 65537, operand-major: 5signed ints σ=3.2, 16-bit, plane-major: 5signed ints σ=3.2, 16-bit, operand-major: 30signed ints σ=3.2, 32-bit, plane-major: 5signed ints σ=3.2, 32-bit, operand-major: 62
Longest run of clipping words: plane-major order breaks runs up, operand-major order of small integers does not.
Data table
Longest clipping run
workloadplane-majoroperand-major
random bits55
residues mod 6553765
signed ints σ=3.2, 16-bit530
signed ints σ=3.2, 32-bit562

What we found

Uncoded, random data is harmless (0.0184 steps of wander), but biased data drifts by 1.213 steps and run-heavy data by 1.937 steps, far past the ½-step margin. Vector frame inversion holds biased data to 0.008412 steps with 64-symbol frames and run-heavy data to 0.02622 steps with 256-symbol frames and 0.1029 steps with 1024-symbol frames. Balancing output 1 alone is not enough: on runs the other outputs still wander by 1.541 steps.

Worst baseline wander against code-frame length (default corner)illustrative
101001,00010⁴10⁻³0.010.1110code frame (symbols)worst wander (steps)half-step margin
Worst wander (max over outputs) against code-frame length at the default corner. Dashed: uncoded, or balancing output 1 only.
Data table
Worst baseline wander vs frame length (1e5 Hz)
code frame (symbols)random, nonerandom, vectorbiased 0.8 ones, nonebiased 0.8 ones, vectorruns of 32768 symbols, noneruns of 32768 symbols, vectorruns of 32768 symbols, scalar
640.01840.0018931.2130.0084121.9370.0065861.541
1280.01840.0028341.2130.01051.9370.013151.541
2560.01840.0034711.2130.018061.9370.026221.541
5120.01840.0047711.2130.036261.9370.05211.542
1,0240.01840.0062241.2130.074261.9370.10291.544
2,0480.01840.0081221.2130.14931.9370.20051.547
4,0960.01840.012191.2130.2891.9370.38111.553
Worst wander vs high-pass cornerillustrative
10⁴10⁵10⁶10⁷10⁻⁴10⁻³0.010.1110high-pass corner (Hz)worst wander (steps)half-step margin
Worst wander against the high-pass corner, uncoded (dashed) and vector-coded with 256-symbol frames. A faster loop wanders more.
Data table
Worst wander vs high-pass corner
high-pass corner (Hz)random, uncodedrandom, vectorbiased 0.8 ones, uncodedbiased 0.8 ones, vectorruns of 32768 symbols, uncodedruns of 32768 symbols, vector
10,0000.0035550.00034771.190.0017760.76530.002423
30,0000.0083030.0010491.2060.0050981.3510.007313
100,0000.01840.0034711.2130.018061.9370.02622
300,0000.031450.0099511.2240.0571520.08401
1 × 10⁶0.059950.030881.2470.192420.2682
3 × 10⁶0.11470.080691.2810.515720.704
1 × 10⁷0.21270.1961.3461.08821.53
The costs

The fix is not free. Each frame needs a flag (0.3906 % of the symbols at 256-symbol frames) and the encoder must see a whole frame before deciding, adding 18.29 ns of latency (73.14 ns with 1024-symbol frames). The largest swept frame that keeps the worst case under a tenth of a step is 512 symbols. A dark calibration pause offsets output 1 by 2 steps and needs 5.871 µs to settle unless the idle fill is itself balanced; and calibrating with one pattern held still errs by 0.3358 steps, against 0.005736 steps when each pattern alternates with its inverse.

What would need measured data

The high-pass corner and the single-pole loop are illustrative; a real TIA’s offset-cancel loop may have more poles or a different corner, and its response to a dark pause is what would set the calibration procedure. One would also need the flag channel’s error rate (one flag error corrupts a whole frame) and the clipping statistics of the operands a real workload sends.