DSP to ASIC
FFE Implementation
M. A. Kossel et al., "8.3 An 8b DAC-Based SST TX Using Metal Gate Resistors with 1.4pJ/b Efficiency at 112Gb/s PAM-4 and 8-Tap FFE in 7nm CMOS," 2021 IEEE International Solid-State Circuits Conference (ISSCC), San Francisco, CA, USA, 2021, pp. 130-132 [https://sci-hub.ru/10.1109/ISSCC42613.2021.9365784]
TODO π
DFE Implementation
Synopsys Italia, Tech Talk: Introduction to DSP-based SerDes [https://youtu.be/puEP0DlVZGI]
Chen, Kuan-Chang (2022) Energy-Efficient Receiver Design for High-Speed Interconnects. Dissertation (Ph.D.), California Institute of Technology. [https://thesis.library.caltech.edu/14318/9/chen_kuan-chang_2022_thesis_final.pdf]

Parallel implementation


Loop-Unrolling DFE


Corresponding to the three distinct voltage thresholds in the PAM4 systems, it would need 12 slicers, 3 multiplexers, and one thermometer-to-binary decoder in each deserialized data path, even if only one tap of the DFE is unrolled
Look-Ahead Multiplexing DFE

The look-ahead multiplexing technique brings the key benefit that the timing constraint can be significantly relaxed, as the iteration bound is doubled at the expense of extra hardware

time-division data conversion
wide_to_narrow

1 | module wide_to_narrow |
narrow_to_wide

1 | module narrow_to_wide |
MASH 1-1-1 Implementation
MASH-1-1-1 Noise-Shaping Truncator (DLF β DAC) [https://github.com/raytroop/dsmwk]
Digital block between a 20-bit digital loop filter (DLF) output and an 8-bit DAC input. The 12 truncated LSBs are pushed through a MASH-1-1-1 delta-sigma modulator so the truncation error is 3rd-order noise-shaped instead of lost:
1 | dac(z) = dlf(z)/2^12 - (1 - z^-1)^3 Β· q(z) (Γ z^-1 latency) |
where e3 is the stage-3 accumulator residue in raw
20-bit-LSB counts (0β¦4095, quantizer step 2ΒΉΒ²) and q is the
same error referred to the 8-bit output grid β the unit-step
quantization noise of the effective 1-LSBβ quantizer.
dac(z) = dlfMSB8 (z)/212 + dlfLSB12 (z)/212+ q(z) Β· (1 - z-1)3 = dlf(z)/212 + q(z) Β· (1 - z-1)3
1 | dlf_in[19:12] ββββββββββββββββββββββββββββββββββ |
flowchart LR
IN["dlf_in<br/>20 bits"] -->|"upper 8 bits"| XMSB["x_msb<br/>coarse value"]
IN -->|"lower 12 bits"| A1["Stage 1<br/>acc1 + x_frac"]
R1[("acc1<br/>register")] --> A1
A1 -->|"residue r1"| R1
A1 -->|"r1"| A2["Stage 2<br/>acc2 + r1"]
A1 -->|"carry c1"| NC["Noise cancellation<br/>c1 + c2 β c2_d1<br/>+ c3 β 2c3_d1 + c3_d2"]
R2[("acc2<br/>register")] --> A2
A2 -->|"residue r2"| R2
A2 -->|"r2"| A3["Stage 3<br/>acc3 + r2"]
A2 -->|"carry c2"| NC
A2 -->|"c2"| C2D1[("c2_d1<br/>zβ»ΒΉ")]
C2D1 -->|"βc2_d1"| NC
R3[("acc3<br/>register")] --> A3
A3 -->|"residue r3"| R3
A3 -->|"carry c3"| NC
A3 -->|"c3"| C3D1[("c3_d1<br/>zβ»ΒΉ")]
C3D1 -->|"β2c3_d1"| NC
C3D1 --> C3D2[("c3_d2<br/>zβ»Β²")]
C3D2 -->|"+c3_d2"| NC
NC -->|"signed correction y<br/>β3β¦+4"| ADD["x_msb + y"]
XMSB --> ADD
ADD --> SAT["Clamp to 0β¦255"]
SAT --> OUTREG[("Output register")]
OUTREG --> OUT["dac_out<br/>8 bits"]
| Branch | Expression | Range |
|---|---|---|
| stage 1 | c1 |
{0, 1} |
| stage 2 | (1βzβ»ΒΉ)Β·c2 = c2 β c2_d1 |
{β1, 0, +1} |
| stage 3 | (1βzβ»ΒΉ)Β²Β·c3 = c3 β 2Β·c3_d1 + c3_d2 |
[β2, +2] |
| total | y |
[β3, +4] |
1 | wire signed [5:0] y = |
Each carry is a bare bit {0,1}. {5'b0, c1}
zero-extends it to 6 bits (value unchanged), and
$signed(...) marks it for signed arithmetic. The term
{4'b0, c3_d1, 1'b0} is c3_d1 with a zero
appended β a shift-left-by-1, i.e. 2Β·c3_d1 (value 0 or 2) β
so the multiply costs nothing.
The reachable values of y and their actual bit
patterns:
| value | y[5:0] |
value | y[5:0] |
|
|---|---|---|---|---|
| +4 | 000100 |
0 | 000000 |
|
| +3 | 000011 |
β1 | 111111 |
|
| +2 | 000010 |
β2 | 111110 |
|
| +1 | 000001 |
β3 | 111101 |
Worked example:
c1=0, c2=0, c2_d1=1, c3=0, c3_d1=1, c3_d2=0 β
y = 0 + (0β1) + (0β2+0) = β3 β 111101.
clamp [-3, 259] to [0, 255]
For msb β [3, 251], x_msb + y never leaves
[0,255], the mux always takes the pass-through leg, and
dac_sat β‘ dac_sum bit-for-bit.
1 | //------------------------------------------------------------------------- |
In a locked PLL the DLF integrator sits mid-range and the clamp never fires β cost: zero. It engages only during acquisition/slew, where noise is irrelevant and its job is exactly right: drive the DAC monotonically to the rail without wrapping
overall ENOB calculation
1 | SNR = P_sig / (P_q,in + P_dsm) (powers add β sources are independent) |
with everything in the same units (LSBβΒ² is convenient) and β critically β both noise terms integrated in-band only (0 β¦ f_bw, i.e. what survives the analog filter):
| Term | Expression (LSBβΒ², fs = f_clk) |
|---|---|
| Signal (full-scale sine convention) | P_sig = FSΒ²/8 = 256Β²/8 = 8192 |
| DSM shaped noise, in-band | P_dsm = (1/12)Β·(ΟβΆ/7)Β·OSRβ»β· |
| Input quantization noise, in-band | P_q,in = (2β»ΒΉΒ²)Β²/12 Β· ΞΊ |
Sanity check at OSR = 50 (static input, ΞΊ=1):
P_q,in = 4.97e-9, P_dsm = 1.47e-11 β SNR =
122.1 dB β ENOB = 19.98 β the DSM costs only 0.13 dB
against the ideal 20.00. At OSR = 10: DSM dominates, ENOB = 16.1
RTL module
Wei Yet, MakerCode RTL Challenge [https://github.com/Weiyet/MakerCode_RTLChallenge]
decimation_filter
Filter Equation
1 | filtered[n] = (1*x[n] + 3*x[n-1] + 3*x[n-2] + 1*x[n-3]) / 8 |
1 | iverilog -g2012 -o sim.vvp solution.sv tb.sv |
1 | module decimation_filter #( |
Line 34 uses non-blocking assignment:
1 | x1 <= data_in; // line 34: scheduled, NOT applied yet |
Non-blocking assignments don't take effect until the end of the current time step (after all the blocking statements in the block have run). So at line 37:
x1still holds its previous value β i.e. the sample from the lastdata_valid_incycle, which isx[n-1].data_inis the current sample,x[n].
They are different values. That's deliberate and necessary for the FIR math to be correct:
1 | filtered = 1*x[n] + 3*x[n-1] + 3*x[n-2] + 1*x[n-3] |
| Symbol | Value at line 37 | Filter tap |
|---|---|---|
data_in |
x[n] (current) | coeff 1 |
x1 |
x[n-1] | coeff 3 |
x2 |
x[n-2] | coeff 3 |
x3 |
x[n-3] | coeff 1 |

gray-code
1 | function automatic logic [3:0] bin2gray(input logic [3:0] bin); |
1 | function automatic logic [3:0] bin2gray(input logic [3:0] bin); |
Sign Extension In Verilog
[https://www.ece.ucdavis.edu/~bbaas/281/notes/Handout.sign.extension.pdf]



reference
K. K. Mishra, Advanced Chip Design: Practical Examples in Verilog. CreateSpace Independent Publishing Platform, 2013
Wei Yet, MakerCode RTL Challenge [https://github.com/Weiyet/MakerCode_RTLChallenge]
Bevan Baas, EEC281 VLSI Digital Signal Processing, [https://www.ece.ucdavis.edu/~bbaas/281/]
Mark Horowitz. EE371: Advanced VLSI Circuit Design Spring 2006-2007 [https://web.stanford.edu/class/archive/ee/ee371/ee371.1066/]
Tinoosh Mohsenin. CMPE 691: Digital Signal Processing Hardware Implementation [https://userpages.cs.umbc.edu/tinoosh/cmpe691/]
Keshab K. Parhi [http://www.ece.umn.edu/users/parhi/]
θ¬η§η. 2019 η©ι«ι»θ·―θ¨θ¨ε°θ« [link]