The physical world doesn't operate in 1s and 0s. Variables like temperature, mechanical strain, or chemical composition generate continuous voltage and current signals. On the other side of the divide, a Field Programmable Gate Array (FPGA) is a rigid, discrete fabric of lookup tables and flip-flops. It cannot inherently comprehend a 1.74V signal; it only recognizes strict logic HIGH or logic LOW thresholds.

The Analog-to-Digital Converter (ADC) is the essential bridge that translates continuous analog phenomena into the discrete, binary words that an FPGA's data paths can process. But crossing this bridge requires a deep understanding of both analog physics and digital architecture.


The Physics of Sampling: The Nyquist Theorem

Before a signal even reaches the ADC, you must address the fundamental laws of signal processing. Digitizing an analog signal is not just a matter of capturing data; it is a matter of capturing enough data to reconstruct reality accurately.

This is governed by the Nyquist-Shannon sampling theorem, which states that the sampling rate (f_s) must be at least twice the highest frequency component (f_max) present in the analog signal to perfectly reconstruct it:

f_s ≥ 2f_max

If you fail to respect this mathematical boundary, you introduce aliasing. If a sensor picks up high-frequency noise that exceeds the Nyquist frequency f_s/2, that noise doesn't just disappear during sampling it "folds back" into the lower baseband frequencies. Once an aliased frequency is digitized, the digital data is permanently corrupted. The FPGA cannot distinguish between the true signal and the folded noise.

The Hardware Solution: This is why an analog anti-aliasing front-end is strictly necessary. An active or passive low-pass RC filter must sit before the ADC to aggressively attenuate any frequencies above f_max before the ADC takes its first sample. The ADC can only accurately digitize what has been properly conditioned.

The Power of Determinism: Why Use an FPGA?

A common question in mixed-signal design is: Why not just use a microcontroller with a built-in ADC?

The answer lies in ruthless hardware-level timing. Microcontrollers rely on software loops and interrupts to trigger ADC reads. Software execution is inherently non-deterministic; an interrupt might be delayed by a few clock cycles if the CPU is handling another task, introducing jitter.

If a system requires deterministic streaming—such as sampling an industrial sensor precisely at 1 MSPS without a nanosecond of deviation—software-driven polling will fail. FPGAs allow you to dedicate a specific, custom piece of RTL logic exclusively to driving the ADC. This guarantees cycle-accurate timing that never drifts, regardless of what the rest of the chip is doing.


ADC Module
ADC Module

Architecting the RTL Pipeline: The SPI Master

Handling the digitized data inside the FPGA requires a deliberate architectural approach. Most precision ADCs communicate via the Serial Peripheral Interface (SPI).

Writing a custom SPI ADC master in Verilog involves managing the serial clock (sclk), the chip select line (cs_n), and precisely shifting in the data bits (sdata) according to the strict timing diagrams of the ADC's datasheet. This module acts as the rigid conductor, ensuring the data is pulled exactly when it is ready.

Here is a simplified Verilog state machine demonstrating how an FPGA controls an ADC to shift in a 16-bit word:

module spi_adc_master (
    input wire clk,          // System clock
    input wire rst_n,        // Active-low reset
    input wire start,        // Trigger to start ADC conversion
    input wire sdata,        // Serial data coming from the ADC (MISO)

    output reg cs_n,         // Active-low chip select
    output reg sclk,         // SPI clock generated by the FPGA
    output reg [15:0] data,  // 16-bit parallel output data
    output reg data_valid    // Flag indicating data is ready
);

    // State Machine Encoding
    localparam IDLE      = 2'b00;
    localparam CS_LOW    = 2'b01;
    localparam SHIFT_IN  = 2'b10;
    localparam DONE      = 2'b11;

    reg [1:0] state, next_state;
    reg [4:0] bit_count;
    reg [15:0] shift_reg;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= IDLE;
            bit_count <= 0;
            cs_n <= 1'b1;
            sclk <= 1'b0;
            data_valid <= 1'b0;
            shift_reg <= 16'd0;
        end else begin
            // Default assignments
            sclk <= ~sclk; // Simple sclk generation (clk / 2)
            data_valid <= 1'b0;

            case (state)
                IDLE: begin
                    cs_n <= 1'b1;
                    if (start) begin
                        state <= CS_LOW;
                        sclk <= 1'b0;
                    end
                end

                CS_LOW: begin
                    cs_n <= 1'b0; // Pull CS low to initiate ADC
                    state <= SHIFT_IN;
                    bit_count <= 16;
                end

                SHIFT_IN: begin
                    // Sample data on the rising edge of sclk
                    if (sclk == 1'b1) begin
                        shift_reg <= {shift_reg[14:0], sdata};
                        bit_count <= bit_count - 1;
                        if (bit_count == 1) state <= DONE;
                    end
                end

                DONE: begin
                    cs_n <= 1'b1;
                    data <= shift_reg;
                    data_valid <= 1'b1;
                    state <= IDLE;
                end
            endcase
        end
    end
endmodule

Note: In a production environment, you would add clock dividers to ensure the sclk meets the specific maximum frequency rating of your chosen ADC.


Crossing Domains and Streaming the Output

Once the ADC outputs an N-bit word and pulses data_valid, that data needs a secure destination. A robust FPGA architecture pushes this incoming data directly into a synchronous FIFO buffer.

The FIFO is critical. It acts as an elastic shock absorber, absorbing the rigid, unrelenting timing of the ADC's sampling rate on the write side, and allowing slower downstream processing modules—like a UART transmitter or a digital filter to read the data out on the read side at their own pace without dropping samples.


Conclusion

An FPGA is rarely an isolated digital island. To interact meaningfully with the physical world, it relies on the ADC as a hardware gatekeeper. By combining careful analog front-end design, a deep respect for the mathematics of sampling, and disciplined RTL architecture, you can build systems that capture the continuous world with absolute digital precision.

Thank you for taking the time to read this. I hope this architectural breakdown resonated with you and offered something meaningful to reflect on for your own hardware designs. If you enjoyed this piece and want to explore more of my technical writings, you can find them by simply scrolling down below, or by heading back to the home page.