There are three practical ways to “implement ADC on an [FPGA](https://www.ampheo.com/c/fpgas-field-programmable-gate-array),” depending on what you really need: ![FPGA-kit-with-the-ADC-DAC-data-conversion-board-emphasizing-the-dip-switches-and-the](https://hackmd.io/_uploads/Bykjpfxsxl.png) **1) Use an external ADC and interface it (most common)** You don’t synthesize the analog converter in LUTs—you capture its digital outputs. **Pick an interface by speed:** * Low speed (≤1–2 MSPS): SPI/I²C SAR ADC (e.g., [MCP3008](https://www.onzuu.com/search/MCP3008), [ADS7042](https://www.onzuu.com/search/ADS7042)). * Mid speed (10–125 MSPS): Parallel CMOS/LVDS data + data clock (source-synchronous). * High speed (100 MSPS–Gsps): JESD204B/C over high-speed serial transceivers. **FPGA tasks:** Clean clocking (PLL/MMCM), source-synchronous capture (IDDR/ISERDES), lane alignment, FIFO, optional digital filtering and calibration. **Minimal SPI SAR example (MCP3008-style, 10-bit):** ``` module spi_adc_mcp3008 ( input wire clk, // e.g., 50 MHz input wire start, // one-shot start conversion input wire [2:0] ch, // channel 0..7 output reg busy, output reg [9:0] data, // SPI pins output reg cs_n, sclk, mosi, input wire miso ); localparam IDLE=0, CMD=1, READ=2, DONE=3; reg [1:0] state; reg [5:0] bitcnt; reg [15:0] shifter; always @(posedge clk) begin case (state) IDLE: begin sclk<=0; cs_n<=1; busy<=0; if (start) begin // Start bit=1, SGL/DIFF=1, D2..D0=channel shifter <= {5'b11000 | ch, 11'd0}; // command frame bitcnt <= 0; cs_n <= 0; busy <= 1; state <= CMD; end end CMD: begin // SPI mode 0 sclk <= ~sclk; if (!sclk) mosi <= shifter[15]; else begin shifter <= {shifter[14:0],1'b0}; bitcnt <= bitcnt + 1; if (bitcnt==15) begin bitcnt<=0; state<=READ; end end end READ: begin sclk <= ~sclk; if (sclk) begin shifter <= {shifter[14:0], miso}; bitcnt <= bitcnt + 1; if (bitcnt==11) begin // 1 null + 10 bits data <= shifter[9:0]; state <= DONE; end end end DONE: begin cs_n<=1; busy<=0; state<=IDLE; end endcase end endmodule ``` **Parallel/LVDS capture sketch:** * Use the ADC’s output data clock (DCO) as input to an IDDR/ISERDES slice to sample data lines. * Phase-shift DCO with MMCM for optimal setup/hold. * Constrain with XDC: input delays, clock groups, and false paths across clock domains. ``` # Example XDC snippets create_clock -name dco_clk -period 5.000 [get_ports ADC_DCO] ;# 200 MHz set_input_delay -clock dco_clk 1.0 [get_ports {ADC_D[*]}] set_false_path -from [get_clocks dco_clk] -to [get_clocks sys_clk] ``` **JESD204B/C:** * Use FPGA GT/GTH transceivers + vendor JESD IP (link layer, transport). * Tasks: CGS/ILAS bring-up, lane deskew, deterministic latency, LMFC alignment, subclass selection. **2) Use the FPGA’s built-in ADC hard block (if available)** Some devices have on-chip ADCs (e.g., [Xilinx 7-series](https://www.vemeko.com/product/#xilinx) XADC, [Intel MAX 10](https://www.vemeko.com/max-10-cplds/) ADC, some Lattice parts). * Great for board monitoring (voltages, temperature) or low-speed sensors. * Instantiate vendor IP core; read samples via AXI/APB or simple registers; add a small moving-average/IIR filter. **3) Build a discrete ADC around the FPGA (hybrid)** You can implement the digital side of certain ADC architectures and use a few external analog parts: **Sigma-Delta (1st/2nd-order):** * External: 1-bit comparator + simple RC (integrator). * FPGA: generate 1-bit DAC (PWM/DS), read comparator, run CIC/decimation to produce N-bit samples. * Pros: excellent resolution for audio/slow sensors; minimal analog; Cons: latency, needs good clocking. **SAR-style loop:** * External: comparator + [resistor](https://www.onzuu.com/category/resistors) ladder/DAC; * FPGA: binary search over DAC codes, read comparator to converge on N-bit result. * Good for learning/medium speeds; not trivial to make precision-grade. **Tiny 1-bit ΣΔ decimator (CIC) example (conceptual):** ``` // Sum 1-bit stream (signed) and decimate by R module cic_decimator #(parameter R=256, W=24)( input wire clk, // modulator clock input wire sd_bit, // 1-bit stream: 1->+1, 0->-1 output reg valid, output reg signed [W-1:0] y ); reg signed [W-1:0] acc; reg [$clog2(R)-1:0] cnt; wire signed [1:0] s = sd_bit ? 2'sd1 : -2'sd1; always @(posedge clk) begin acc <= acc + s; cnt <= cnt + 1; if (cnt==R-1) begin y <= acc; valid <= 1'b1; acc <= '0; cnt <= '0; end else valid <= 1'b0; end endmodule ``` **System design checklist** * Clocking & Jitter: ADC SNR is clock-jitter sensitive. Use low-jitter clocks (XO + PLL). * IO Standards: Match ADC IO (LVDS, CMOS 1.8/2.5/3.3 V), set proper termination. * Timing Closure: For source-synchronous, use IDELAY/ISERDES and constrain correctly. * Throughput buffering: CDC FIFOs from ADC domain → system/AXI domain. * Digital filtering: Moving average/IIR for slow SAR; FIR/CIC/halfband for ΣΔ or decimation chains. * Calibration: Offset/gain/phase correction; background calibration for multichannel. * Board layout: Short, impedance-controlled pairs for LVDS; isolate analog ground; solid power decoupling near ADC. **Quick “which path should I take?” guide** * You need real performance (audio–RF, reliable specs): Use a commercial ADC and capture it in [FPGA](https://www.ampheoelec.de/c/fpgas-field-programmable-gate-array). * You need quick board telemetry or very low speed: Use the FPGA’s internal ADC (if present). * You want to learn/experiment, audio-band: Try a sigma-delta with comparator + RC and do the [DSP](https://www.ampheo.com/c/dsp-digital-signal-processors) in FPGA.