ADS1675 24位ADC的FPGA采集设计

ADS1675 24位ADC的FPGA采集设计(CMOS与LVDS双版本)

一、芯片简介

ADS1675 是 TI 推出的 24 位 Delta-Sigma 模数转换器,最高采样率 4MSPS,TQFP-64 封装。主要参数:动态范围 103dB@4MSPS,INL ±3ppm,功耗 575mW。模拟 5V/数字 3V 供电,参考电压 VREF=3.0V。

内部集成 Delta-Sigma 调制器 + 双路可编程数字滤波器 + PLL 时钟倍频器,支持 CMOS 和 LVDS 两种数字接口。

二、三种工作模式

ADS1675 通过 FPATH 和 LL_CONFIG 引脚组合,提供三种滤波器模式(f_CLK=32MHz,DRATE=011 时 1MSPS):

模式一:LL Fast-Response(推荐)

配置:FPATH=1, LL_CONFIG=1。滤波器以指数衰减权重累积历史数据,1MSPS 下 tDRDY=1µs,阶跃响应约 4µs 恢复 99.9%。采样率最高 4MSPS(需 LVDS)。适合绝大多数数据采集场景,兼顾高精度与快速响应。

模式二:LL Single-Cycle

配置:FPATH=1, LL_CONFIG=0。每个转换周期完全独立,无滤波器历史记忆。最高 277.78kSPS,每笔数据 100% 有效。适合对确定性要求极高的静态测量。

模式三:Wide-Bandwidth

配置:FPATH=0。55 阶线性相位 FIR 滤波器,通带纹波 <0.00002dB,阻带衰减 86dB。阶跃恢复需 55 个周期(55µs@1MSPS)。适合 AC 频谱分析等频域精度要求高的场景,不适合信号快速变化的场合。

三、CMOS 与 LVDS 接口对比

项目 CMOS LVDS
信号线 3 根单端 3 对差分(6 根)
SCLK 频率 f_CLK = 32MHz 3×f_CLK = 96MHz(PLL 倍频)
最高数据率 1MSPS 4MSPS(HS 模式)
FPGA 时钟要求 ≥100MHz ≥300MHz(需 >3× SCLK)
FPGA 原语 IBUFDS × 3
时序参考 手册 Figure 3 手册 Figure 1
DRDY 脉宽 ~1 T_SCLK(~31ns) 2~4 T_SCLK(~20-40ns)
tLSCLKDR 2.2~4.4ns 2.0~3.0ns
tLDOPD 1.9~2.8ns 1.5~2.5ns

CMOS 版本 FPGA 100MHz,SCLK 32MHz,频率比 3.1:1,满足过采样要求。

LVDS 版本 PLL 将 32MHz 主时钟 3 倍频至 96MHz SCLK,FPGA 需 ≥300MHz 才能可靠采样(>3× 奈奎斯特)。HS 模式(2M/4M)强制使用 LVDS,分辨率降为 23-bit。

四、数据编码

二进制补码,MSB 先行输出:+VREF(3.0V) → 7FFFFFh,0V → 000000h,-VREF(-3.0V) → 800000h。超出量程时输出钳位并触发报警。LL/WB 模式 24-bit,HS 模式 23-bit。

五、FPGA 采集设计(CMOS 版本)

5.1 整体架构

100MHz FPGA 过采样 32MHz SCLK 和 DRDY。三段式状态机:

  • S_IDLE:等待首个 DRDY,忽略所有 SCLK,不产生任何 FIFO 写入
  • S_WAIT_MSB:DRDY 已出现,等待 MSB SCLK 边沿
  • S_CAPTURE:移位采集 24-bit 数据,完成后回到 S_WAIT_MSB

5.2 MSB 双路径检测(核心设计)

手册 Figure 3 的关键细节:MSB(D23) 在 DRDY 拉高的那个 SCLK 上升沿呈现,与 DRDY 是同一个 SCLK 边沿,不是下一个 ------ 这是最常见的 RTL 设计错误,会导致数据整体左偏 1 bit。

由于 DRDY 在 SCLK↑ 后 2.2~4.4ns 才拉高,100MHz(10ns 周期)采样存在相位不确定性,采用双路径检测保证可靠:

  • 主检测(实时命中):SCLK↑ 的同时 DRDY 已经为高 → 直接确认 MSB 边沿
  • 回退检测 (延迟确认):上一拍记录潜在 MSB 边沿(SCLK↑ 但 DRDY 尚未确认),本拍 DRDY 确认后回退确认

5.3 采集与同步

  • 移位寄存器:MSB 先行左移 r_shift_reg <= {r_shift_reg[22:0], i_ads_dout}
  • FIFO 对齐:r_fifo_wr_pre 在第 24 个 SCLK↑ 产生,延迟 1 拍与 r_data_out 锁存时刻对齐
  • 同步器:DRDY 3 级、SCLK 2 级,消除亚稳态
c 复制代码
`timescale 1 ns / 1 ns
//////////////////////////////////////////////////////////////////////////////////
// Company:       
// Engineer:        felix
//
// Create Date:   
// Design Name:    
// Module Name:     ads1675_capture
// Project Name:   
// Target Devices:  Xilinx ZYNQ7030
// Tool Versions:   
// Description:
//   ADS1675 24-bit 串行数据采集模块。
//   模式:  LL Fast-Response, DRATE=011 (1MSPS)
//   接口:  CMOS + 内部 SCLK (SCLK_SEL=0)
//   控制:  START=高 (连续转换), CS=低
//
//   手册 Figure 3 关键时序 (内部 SCLK 模式):
//   - SCLK     = 连续自由运行, f_SCLK = f_CLK = 32MHz, 周期 31.25ns
//   - DRDY     = 数据就绪脉冲, 在 SCLK↑ 后 tLSCLKDR 拉高
//   - tLSCLKDR = SCLK↑ → DRDY↑ = 2.2~4.4ns
//   - tLDOPD   = SCLK↑ → DOUT 有效 = 1.9~2.8ns
//   - DOUT     = MSB 先行, 24-bit 二进制补码
//   - MSB(D23) = 在 DRDY 拉高的那个 SCLK 边沿呈现 (与 DRDY 同一 SCLK↑!)
//
// Dependencies:  无
//
// Revision:
//   date       version    author       description
//   2026-06-22 V0.1       felix       
//
//////////////////////////////////////////////////////////////////////////////////

module ads1675_capture
#(
    parameter P_DATA_WIDTH = 24                                    // ADC 数据位宽
)
(
    input  logic                    i_clk                        ,// FPGA 100MHz 时钟
    input  logic                    i_rstn                       ,// 异步复位 (低有效)
    input  logic                    i_ads_drdy                   ,// ADS1675 数据就绪脉冲
    input  logic                    i_ads_sclk                   ,// ADS1675 串行时钟 (连续 32MHz)
    input  logic                    i_ads_dout                   ,// ADS1675 串行数据
    output logic                    o_fifo_wr                    ,// FIFO 写脉冲 (单周期)
    output logic [P_DATA_WIDTH-1:0] o_fifo_data                  // FIFO 并行数据 (24-bit)
);

    localparam L_CNT_W = $clog2(P_DATA_WIDTH)                     ;// 位计数器位宽 (5bit)
    localparam L_CNT_M = P_DATA_WIDTH                              ;// 位计数器最大值 (24)

    typedef enum logic [1:0] {
        S_IDLE      = 2'b00                                      ,// 空闲态: 等待首个 DRDY
        S_WAIT_MSB  = 2'b01                                      ,// 等待MSB: DRDY已见, 等MSB SCLK边沿
        S_CAPTURE   = 2'b10                                       // 采集态: 移位采集24bit数据
    } state_t                                                      ;// 状态机类型
    state_t r_cu_state                                             ;// 现态寄存器
    state_t r_nx_state                                             ;// 次态寄存器 (三段式用)

    logic r_drdy_r0                                                ;// DRDY 同步0 (1级采样)
    logic r_drdy_r1                                                ;// DRDY 同步1
    logic r_drdy_r2                                                ;// DRDY 同步2
    logic r_sclk_r0                                                ;// SCLK 同步0 (1级采样)
    logic r_sclk_r1                                                ;// SCLK 同步1
    logic r_sclk_pos_d1                                            ;// w_sclk_pos 延迟1拍
    logic r_potential_msb                                          ;// 潜在MSB边沿标记 (SCLK↑但DRDY未确认)
    logic r_saved_dout                                             ;// 潜在MSB边沿时保存的DOUT值
    logic [P_DATA_WIDTH-1:0] r_shift_reg                           ;// 移位寄存器 (MSB先行左移)
    logic [L_CNT_W-1:0]      r_bit_cnt                             ;// 位计数器 (0=空闲, 1~24=采集)
    logic [P_DATA_WIDTH-1:0] r_data_out                            ;// 并行数据输出
    logic                    r_fifo_wr_pre                         ;// FIFO写预脉冲 (SCLK边沿产生)
    logic                    r_fifo_wr                             ;// FIFO写使能 (延迟1拍对齐data_out)
    logic w_drdy_pos                                               ;// DRDY 上升沿 (0→1)
    logic w_sclk_pos                                               ;// SCLK 上升沿 (0→1)
    logic w_msb_primary                                            ;// MSB主检测 (SCLK↑ 且 DRDY=1 同拍)
    logic w_msb_fallback                                           ;// MSB回退检测 (潜在边沿+DRDY后确认)
    logic w_msb_detected                                           ;// MSB边沿已确认 (主+回退)

    assign w_drdy_pos = r_drdy_r1 && !r_drdy_r2                    ;// DRDY上升沿: r1=1且r2=0
    assign w_sclk_pos = r_sclk_r0 && !r_sclk_r1                    ;// SCLK上升沿: r0=1且r1=0
    assign w_msb_primary  = (r_cu_state == S_WAIT_MSB)
                            && w_sclk_pos && r_drdy_r0             ;// 主检测: SCLK↑同拍DRDY=1
    assign w_msb_fallback = (r_cu_state == S_WAIT_MSB)
                            && r_potential_msb && r_drdy_r0        ;// 回退检测: 潜在边沿+DRDY确认
    assign w_msb_detected = w_msb_primary || w_msb_fallback        ;// MSB确认: 主检测或回退检测
    assign o_fifo_wr   = r_fifo_wr                                 ;// FIFO写使能输出
    assign o_fifo_data = r_data_out                                ;// FIFO并行数据输出

    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_cu_state <= S_IDLE                                    ;// 复位→空闲态
        else
            r_cu_state <= r_nx_state                                ;// 次态→现态
    end

    always_comb begin
        case (r_cu_state)
            S_IDLE: begin
                    r_nx_state = S_WAIT_MSB                          ;// MSB边沿确认→进入采集态
            end
            S_WAIT_MSB: begin
                if (w_msb_detected)
                    r_nx_state = S_CAPTURE                          ;// MSB边沿确认→进入采集态
                else
                    r_nx_state = r_cu_state                         ;// 保持等待态
            end
            S_CAPTURE: begin
                if (w_sclk_pos && r_bit_cnt == L_CNT_W'(L_CNT_M - 1))
                    r_nx_state = S_WAIT_MSB                         ;// 采集完24bit→等待下次DRDY
                else
                    r_nx_state = r_cu_state                         ;// 保持采集态
            end
            default: r_nx_state = S_IDLE                            ;// 安全回退→空闲态
        endcase
    end

    //DRDY 同步链 (3级打拍, 同类信号同块) 
    always_ff @(posedge i_clk) begin
        r_drdy_r0 <= i_ads_drdy                                     ;// 第0级: 捕获IO
        r_drdy_r1 <= r_drdy_r0                                      ;// 第1级: 同步
        r_drdy_r2 <= r_drdy_r1                                      ;// 第2级: 同步
    end

    //SCLK 同步链 (2级打拍) 
    always_ff @(posedge i_clk) begin
        r_sclk_r0 <= i_ads_sclk                                     ;// 第0级: 捕获IO
        r_sclk_r1 <= r_sclk_r0                                      ;// 第1级: 同步
    end

    //SCLK 上升沿延迟1拍 (MSB回退检测用) 
    always_ff @(posedge i_clk) begin
        r_sclk_pos_d1 <= w_sclk_pos                                 ;// 延迟1拍
    end

    //潜在MSB边沿记录 (S_WAIT_MSB态, SCLK↑但DRDY尚未确认) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn) begin
            r_potential_msb <= 1'b0                                 ;// 复位清除
            r_saved_dout    <= 1'b0                                 ;// 复位清除
        end else if (r_cu_state == S_WAIT_MSB && w_sclk_pos && !r_drdy_r0) begin
            r_potential_msb <= 1'b1                                 ;// 标记潜在MSB边沿
            r_saved_dout    <= i_ads_dout                           ;// 保存当前DOUT (可能为MSB)
        end else if (r_drdy_r0) begin
            r_potential_msb <= 1'b0                                 ;// DRDY确认或超时→清除标记
            r_saved_dout    <= r_saved_dout                         ;
        end else if (w_sclk_pos) begin
            r_potential_msb <= 1'b0                                 ;// 新SCLK边沿→上次非MSB, 清除
            r_saved_dout    <= r_saved_dout                         ;
        end else begin
            r_potential_msb <= r_potential_msb                      ;
            r_saved_dout    <= r_saved_dout                         ;
        end
    end

    //位计数器
    //   S_IDLE/S_WAIT_MSB: 保持0
    //   MSB确认瞬间: 初始化为1 (MSB=第1个bit)
    //   S_CAPTURE + SCLK↑: 递增计数
    //   计到L_CNT_M(24)后: 自动回到S_WAIT_MSB, 计数器被下一状态清零
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_bit_cnt <= '0                                         ;
        else if (r_cu_state == S_WAIT_MSB && w_msb_detected)
            r_bit_cnt <= L_CNT_W'(1)                                ;// MSB确认→bit_cnt=1
        else if (r_cu_state == S_CAPTURE && w_sclk_pos)
            r_bit_cnt <= r_bit_cnt + L_CNT_W'(1)                    ;// 采集态+SCLK↑→计数递增
        else if (r_cu_state != S_CAPTURE)
            r_bit_cnt <= '0                                         ;// 非采集态→清零
        else
            r_bit_cnt <= r_bit_cnt                                  ;
    end

    //移位寄存器 (MSB先行, 左移) 
    //   MSB确认瞬间: 装入MSB (主检测用当前DOUT, 回退检测用保存的DOUT)
    //   S_CAPTURE + SCLK↑: 左移1位, LSB装入当前DOUT
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_shift_reg <= '0                                       ;
        else if (r_cu_state == S_WAIT_MSB && w_msb_primary)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], i_ads_dout} ;// 主检测: 当前DOUT即MSB
        else if (r_cu_state == S_WAIT_MSB && w_msb_fallback)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], r_saved_dout} ;// 回退检测: 用保存的DOUT
        else if (r_cu_state == S_CAPTURE && w_sclk_pos)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], i_ads_dout} ;// 采集态+SCLK↑: 左移装入
        else
            r_shift_reg <= r_shift_reg                              ;
    end

    //数据锁存 (bit_cnt==24时锁存完整的24bit并行数据) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_data_out <= '0                                        ;
        else if (r_bit_cnt == L_CNT_W'(L_CNT_M))
            r_data_out <= r_shift_reg                               ;// bit_cnt=24→锁存移位寄存器
        else
            r_data_out <= r_data_out                                ;
    end

    // FIFO写预脉冲 (bit_cnt==23 且 SCLK↑ 时产生, 即第24个bit移入时刻) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_fifo_wr_pre <= 1'b0                                   ;
        else if (r_cu_state == S_CAPTURE && w_sclk_pos
                 && r_bit_cnt == L_CNT_W'(L_CNT_M - 1))
            r_fifo_wr_pre <= 1'b1                                   ;// 第24bit移入→预写脉冲
        else
            r_fifo_wr_pre <= 1'b0                                   ;// 单周期脉冲, 其余时刻清零
    end

    //FIFO写使能 (延迟r_fifo_wr_pre 1拍, 对齐r_data_out锁存时刻) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_fifo_wr <= 1'b0                                       ;
        else
            r_fifo_wr <= r_fifo_wr_pre                              ;// 延迟1拍: 与data_out同时有效
    end

endmodule

六、FPGA 采集设计(LVDS 版本)

6.1 与 CMOS 版本的差异

LVDS 版本核心采集逻辑(状态机、MSB 检测、移位寄存、FIFO 对齐)完全复用 CMOS 版本代码,仅物理层不同:

  1. FPGA 时钟提升至 300MHz(96MHz SCLK 需 >3× 采样率)
  2. 输入管脚改为差分对(DRDY_P/N、SCLK_P/N、DOUT_P/N)
  3. 例化 3 个 IBUFDS 原语,DIFF_TERM="TRUE" 使能内部 100Ω 终端
  4. IBUFDS 输出的单端信号接入相同的同步器和状态机

6.2 IBUFDS 例化

复制代码
IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_drdy (.I(drdy_p), .IB(drdy_n), .O(w_drdy));
IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_sclk (.I(sclk_p), .IB(sclk_n), .O(w_sclk));
IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_dout (.I(dout_p), .IB(dout_n), .O(w_dout));

FPGA 管脚约束需设置 IOSTANDARD = LVDS_25。

6.3 时序(手册 Figure 1)

LVDS 模式下 PLL 将 CLK 倍频至 96MHz。tLSCLKDR=2.03.0ns,tLDOPD=1.52.5ns,DRDY 脉宽 2~4 个 SCLK 周期。数据帧 24×10.417ns≈250ns,远小于 1MSPS 的 1024ns 周期,裕量充足。

c 复制代码
`timescale 1 ns / 1 ns
//////////////////////////////////////////////////////////////////////////////////
// Company:     
// Engineer:      felix
//
// Create Date:   
// Design Name:     
// Module Name:     ads1675_capture_lvds
// Project Name:  
// Target Devices:  Xilinx ZYNQ7030
// Tool Versions:  
// Description:
//   ADS1675 24-bit 串行数据采集模块 (LVDS 接口版本)。
//   模式:  LL Fast-Response, DRATE=011 (1MSPS)
//   接口:  LVDS 差分对 (SCLK/DOUT/DRDY) + 内部 SCLK (SCLK_SEL=0)
//   控制:  START=高 (连续转换), CS=低
//
//   手册 Figure 1 关键时序 (LVDS 模式, 内部 SCLK):
//   - SCLK     = 差分, 连续自由运行, f_SCLK = 3×f_CLK = 96MHz, 周期 10.417ns
//   - DRDY     = 差分, 数据就绪脉冲, 脉宽 2~4 T_SCLK ≈ 20~40ns
//   - tLSCLKDR = SCLK↑ → DRDY↑ = 2.0~3.0ns (本设计取 2.5ns)
//   - tLDOPD   = SCLK↑ → DOUT 有效 = 1.5~2.5ns (本设计取 2.0ns)
//   - MSB(D23) = 在 DRDY 拉高的那个 SCLK 边沿呈现 (与 DRDY 同一 SCLK↑)
//   - 数据帧 = 24 SCLK 周期 ≈ 250ns, DRDY 周期 = 1024ns@1MSPS (裕量充足)
//   - 24-bit 二进制补码, MSB 先行
//
//   设计: FPGA 300MHz 过采样 (96MHz SCLK 需 >3× 采样率确保可靠),
//         IBUFDS 转换差分→单端, 三段式 FSM 采集
//
// Dependencies:  
//
// Revision:
//   date       version    author       description
//       
//
//////////////////////////////////////////////////////////////////////////////////

module ads1675_capture_lvds
#(
    parameter P_DATA_WIDTH = 24                                     // ADC 数据位宽
)
(
    input  logic                    i_clk                          ,// FPGA 300MHz 时钟
    input  logic                    i_rstn                         ,// 异步复位 (低有效)
    input  logic                    i_ads_drdy_p                   ,// ADS1675 DRDY 差分正端
    input  logic                    i_ads_drdy_n                   ,// ADS1675 DRDY 差分负端
    input  logic                    i_ads_sclk_p                   ,// ADS1675 SCLK 差分正端
    input  logic                    i_ads_sclk_n                   ,// ADS1675 SCLK 差分负端
    input  logic                    i_ads_dout_p                   ,// ADS1675 DOUT 差分正端
    input  logic                    i_ads_dout_n                   ,// ADS1675 DOUT 差分负端
    output logic                    o_fifo_wr                      ,// FIFO 写脉冲 (单周期)
    output logic [P_DATA_WIDTH-1:0] o_fifo_data                     // FIFO 并行数据 (24-bit)
);

    localparam L_CNT_W = $clog2(P_DATA_WIDTH)                      ;// 位计数器位宽 (5bit)
    localparam L_CNT_M = P_DATA_WIDTH                              ;// 位计数器最大值 (24)

    typedef enum logic [1:0] {
        S_IDLE      = 2'b00                                        ,// 空闲态: 等待首个 DRDY
        S_WAIT_MSB  = 2'b01                                        ,// 等待MSB: DRDY已见, 等MSB SCLK边沿
        S_CAPTURE   = 2'b10                                         // 采集态: 移位采集24bit数据
    } state_t                                                      ;// 状态机类型
    state_t r_cu_state                                             ;// 现态寄存器
    state_t r_nx_state                                             ;// 次态寄存器 (三段式用)

    logic r_drdy_r0                                                ;// DRDY 同步0 (1级采样)
    logic r_drdy_r1                                                ;// DRDY 同步1
    logic r_drdy_r2                                                ;// DRDY 同步2
    logic r_sclk_r0                                                ;// SCLK 同步0 (1级采样)
    logic r_sclk_r1                                                ;// SCLK 同步1
    logic r_sclk_pos_d1                                            ;// w_sclk_pos 延迟1拍
    logic r_potential_msb                                          ;// 潜在MSB边沿标记 (SCLK↑但DRDY未确认)
    logic r_saved_dout                                             ;// 潜在MSB边沿时保存的DOUT值
    logic [P_DATA_WIDTH-1:0] r_shift_reg                           ;// 移位寄存器 (MSB先行左移)
    logic [L_CNT_W-1:0]      r_bit_cnt                             ;// 位计数器 (0=空闲, 1~24=采集)
    logic [P_DATA_WIDTH-1:0] r_data_out                            ;// 并行数据输出
    logic                    r_fifo_wr_pre                         ;// FIFO写预脉冲 (SCLK边沿产生)
    logic                    r_fifo_wr                             ;// FIFO写使能 (延迟1拍对齐data_out)
    logic w_drdy                                                   ;// DRDY 单端 (IBUFDS输出)
    logic w_sclk                                                   ;// SCLK 单端 (IBUFDS输出)
    logic w_dout                                                   ;// DOUT 单端 (IBUFDS输出)
    logic w_drdy_pos                                               ;// DRDY 上升沿 (0→1)
    logic w_sclk_pos                                               ;// SCLK 上升沿 (0→1)
    logic w_msb_primary                                            ;// MSB主检测 (SCLK↑ 且 DRDY=1 同拍)
    logic w_msb_fallback                                           ;// MSB回退检测 (潜在边沿+DRDY后确认)
    logic w_msb_detected                                           ;// MSB边沿已确认 (主+回退)

    IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_drdy (
        .I  (i_ads_drdy_p)                                        ,// 差分正端
        .IB (i_ads_drdy_n)                                        ,// 差分负端
        .O  (w_drdy)                                               // 单端输出
    );

    IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_sclk (
        .I  (i_ads_sclk_p)                                        ,// 差分正端
        .IB (i_ads_sclk_n)                                        ,// 差分负端
        .O  (w_sclk)                                               // 单端输出
    );

    IBUFDS #(.DIFF_TERM("TRUE")) u_ibuf_dout (
        .I  (i_ads_dout_p)                                        ,// 差分正端
        .IB (i_ads_dout_n)                                        ,// 差分负端
        .O  (w_dout)                                               // 单端输出
    );

    assign w_drdy_pos = r_drdy_r1 && !r_drdy_r2                    ;// DRDY上升沿: r1=1且r2=0
    assign w_sclk_pos = r_sclk_r0 && !r_sclk_r1                    ;// SCLK上升沿: r0=1且r1=0
    assign w_msb_primary  = (r_cu_state == S_WAIT_MSB)
                            && w_sclk_pos && r_drdy_r0             ;// 主检测: SCLK↑同拍DRDY=1
    assign w_msb_fallback = (r_cu_state == S_WAIT_MSB)
                            && r_potential_msb && r_drdy_r0        ;// 回退检测: 潜在边沿+DRDY确认
    assign w_msb_detected = w_msb_primary || w_msb_fallback        ;// MSB确认: 主检测或回退检测
    assign o_fifo_wr   = r_fifo_wr                                 ;// FIFO写使能输出
    assign o_fifo_data = r_data_out                                ;// FIFO并行数据输出

    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_cu_state <= S_IDLE                                    ;// 复位→空闲态
        else
            r_cu_state <= r_nx_state                                ;// 次态→现态
    end

    always_comb begin
        case (r_cu_state)
            S_IDLE: begin
                    r_nx_state = S_WAIT_MSB ;   
            end
            S_WAIT_MSB: begin
                if (w_msb_detected)
                    r_nx_state = S_CAPTURE                          ;// MSB边沿确认→进入采集态
                else
                    r_nx_state = r_cu_state                         ;// 保持等待态
            end
            S_CAPTURE: begin
                if (w_sclk_pos && r_bit_cnt == L_CNT_W'(L_CNT_M - 1))
                    r_nx_state = S_WAIT_MSB                         ;// 采集完24bit→等待下次DRDY
                else
                    r_nx_state = r_cu_state                         ;// 保持采集态
            end
            default: r_nx_state = S_IDLE                            ;// 安全回退→空闲态
        endcase
    end

    //DRDY 同步链 (3级打拍, 同类信号同块) 
    always_ff @(posedge i_clk) begin
        r_drdy_r0 <= w_drdy                                         ;// 第0级: 捕获IBUFDS输出
        r_drdy_r1 <= r_drdy_r0                                      ;// 第1级: 同步
        r_drdy_r2 <= r_drdy_r1                                      ;// 第2级: 同步
    end

    //SCLK 同步链 (2级打拍) 
    always_ff @(posedge i_clk) begin
        r_sclk_r0 <= w_sclk                                         ;// 第0级: 捕获IBUFDS输出
        r_sclk_r1 <= r_sclk_r0                                      ;// 第1级: 同步
    end

    //SCLK 上升沿延迟1拍 (MSB回退检测用) 
    always_ff @(posedge i_clk) begin
        r_sclk_pos_d1 <= w_sclk_pos                                 ;// 延迟1拍
    end

    //潜在MSB边沿记录 (S_WAIT_MSB态, SCLK↑但DRDY尚未确认) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn) begin
            r_potential_msb <= 1'b0                                 ;// 复位清除
            r_saved_dout    <= 1'b0                                 ;// 复位清除
        end else if (r_cu_state == S_WAIT_MSB && w_sclk_pos && !r_drdy_r0) begin
            r_potential_msb <= 1'b1                                 ;// 标记潜在MSB边沿
            r_saved_dout    <= w_dout                               ;// 保存当前DOUT (可能为MSB)
        end else if (r_drdy_r0) begin
            r_potential_msb <= 1'b0                                 ;// DRDY确认或超时→清除标记
            r_saved_dout    <= r_saved_dout                         ;// 保持
        end else if (w_sclk_pos) begin
            r_potential_msb <= 1'b0                                 ;// 新SCLK边沿→上次非MSB, 清除
            r_saved_dout    <= r_saved_dout                         ;// 保持
        end else begin
            r_potential_msb <= r_potential_msb                      ;// 保持
            r_saved_dout    <= r_saved_dout                         ;// 保持
        end
    end

    //位计数器 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_bit_cnt <= '0                                         ;// 复位清零
        else if (r_cu_state == S_WAIT_MSB && w_msb_detected)
            r_bit_cnt <= L_CNT_W'(1)                                ;// MSB确认→bit_cnt=1
        else if (r_cu_state == S_CAPTURE && w_sclk_pos)
            r_bit_cnt <= r_bit_cnt + L_CNT_W'(1)                    ;// 采集态+SCLK↑→计数递增
        else if (r_cu_state != S_CAPTURE)
            r_bit_cnt <= '0                                         ;// 非采集态→清零
        else
            r_bit_cnt <= r_bit_cnt                                  ;// 保持
    end

    //移位寄存器 (MSB先行, 左移) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_shift_reg <= '0                                       ;// 复位清零
        else if (r_cu_state == S_WAIT_MSB && w_msb_primary)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], w_dout} ;// 主检测: 当前DOUT即MSB
        else if (r_cu_state == S_WAIT_MSB && w_msb_fallback)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], r_saved_dout} ;// 回退检测: 用保存的DOUT
        else if (r_cu_state == S_CAPTURE && w_sclk_pos)
            r_shift_reg <= {r_shift_reg[P_DATA_WIDTH-2:0], w_dout} ;// 采集态+SCLK↑: 左移装入
        else
            r_shift_reg <= r_shift_reg                              ;// 保持
    end

    //数据锁存 (bit_cnt==24时锁存完整的24bit并行数据) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_data_out <= '0                                        ;// 复位清零
        else if (r_bit_cnt == L_CNT_W'(L_CNT_M))
            r_data_out <= r_shift_reg                               ;// bit_cnt=24→锁存移位寄存器
        else
            r_data_out <= r_data_out                                ;// 保持
    end

    //FIFO写预脉冲 (bit_cnt==23 且 SCLK↑ 时产生, 即第24个bit移入时刻) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_fifo_wr_pre <= 1'b0                                   ;// 复位清零
        else if (r_cu_state == S_CAPTURE && w_sclk_pos
                 && r_bit_cnt == L_CNT_W'(L_CNT_M - 1))
            r_fifo_wr_pre <= 1'b1                                   ;// 第24bit移入→预写脉冲
        else
            r_fifo_wr_pre <= 1'b0                                   ;// 单周期脉冲, 其余时刻清零
    end

    //FIFO写使能 (延迟r_fifo_wr_pre 1拍, 对齐r_data_out锁存时刻) 
    always_ff @(posedge i_clk) begin
        if (!i_rstn)
            r_fifo_wr <= 1'b0                                       ;// 复位清零
        else
            r_fifo_wr <= r_fifo_wr_pre                              ;// 延迟1拍: 与data_out同时有效
    end

endmodule

七、关键注意事项

  1. MSB 在同一 SCLK 边沿:手册明确 MSB 与 DRDY 同一 SCLK↑,不是下一个。错位导致数据整体左偏 1 bit。
  2. 首 DRDY 前不写 FIFO:FSM 初始态 S_IDLE 屏蔽所有 SCLK 活动。
  3. 同步器必不可少:DRDY/SCLK 均与 FPGA 异步,需 2~3 级同步。
  4. FIFO 写对齐:r_fifo_wr 必须延迟 1 拍与 data_out 对齐,确保下游采样正确。
  5. LVDS 需高速 FPGA:96MHz SCLK 要求 FPGA ≥300MHz,保证边沿检测可靠。
  6. WB 模式慎用:55 周期滤波器记忆导致输入突变后数据不可靠,快变信号必须用 LL Fast-Response。
  7. 上电等待:START 拉高后需等待 tSETTLE(@1MSPS≈3.6ms)才有有效 DRDY。
  8. 参考电压旁路:VREFP/VREFN 各需 10µF+0.1µF 陶瓷电容紧靠芯片。
  9. 架构推荐:FPGA 硬件盲采 + 上位机后处理。FPGA 不做数据过滤,前 3~5 笔丢弃。
  10. HS 模式注意:2M/4M 采样率强制 LVDS,分辨率降为 23-bit。

可自行仿真。

两版本个人仿真均通过。