/ 9_Firmware / 9_2_FPGA / ad9484_interface_400m.v
ad9484_interface_400m.v
  1  module ad9484_interface_400m (
  2      // ADC Physical Interface (LVDS)
  3      input wire [7:0] adc_d_p,        // ADC Data P
  4      input wire [7:0] adc_d_n,        // ADC Data N
  5      input wire adc_dco_p,            // Data Clock Output P (400MHz)
  6      input wire adc_dco_n,            // Data Clock Output N (400MHz)
  7      
  8      // System Interface
  9      input wire sys_clk,              // 100MHz system clock (for control only)
 10      input wire reset_n,
 11      
 12      // Output at 400MHz domain
 13      output wire [7:0] adc_data_400m, // ADC data at 400MHz
 14      output wire adc_data_valid_400m, // Valid at 400MHz
 15      output wire adc_dco_bufg         // Buffered 400MHz DCO clock for downstream use
 16  );
 17  
 18  // LVDS to single-ended conversion
 19  wire [7:0] adc_data;
 20  wire adc_dco;
 21  
 22  // IBUFDS for each data bit
 23  // NOTE: IOSTANDARD and DIFF_TERM are set via XDC constraints, not RTL
 24  // parameters, to support multiple FPGA targets with different bank voltages:
 25  //   - XC7A200T (FBG484): Bank 14 VCCO = 2.5V → LVDS_25
 26  //   - XC7A50T  (FTG256): Bank 14 VCCO = 3.3V → LVDS_33
 27  genvar i;
 28  generate
 29      for (i = 0; i < 8; i = i + 1) begin : data_buffers
 30          IBUFDS #(
 31              .DIFF_TERM("FALSE"),    // Overridden by XDC DIFF_TERM property
 32              .IOSTANDARD("DEFAULT")  // Overridden by XDC IOSTANDARD property
 33          ) ibufds_data (
 34              .O(adc_data[i]),
 35              .I(adc_d_p[i]),
 36              .IB(adc_d_n[i])
 37          );
 38      end
 39  endgenerate
 40  
 41  // IBUFDS for DCO
 42  IBUFDS #(
 43      .DIFF_TERM("FALSE"),    // Overridden by XDC DIFF_TERM property
 44      .IOSTANDARD("DEFAULT")  // Overridden by XDC IOSTANDARD property
 45  ) ibufds_dco (
 46      .O(adc_dco),
 47      .I(adc_dco_p),
 48      .IB(adc_dco_n)
 49  );
 50  
 51  // ============================================================================
 52  // Clock buffering strategy for source-synchronous ADC interface:
 53  //
 54  // BUFIO: Near-zero insertion delay, can only drive IOB primitives (IDDR).
 55  //        Used for IDDR clocking to match the data path delay through IBUFDS.
 56  //        This eliminates the hold violation caused by BUFG insertion delay.
 57  //
 58  // BUFG:  Global clock buffer for fabric logic (downstream processing).
 59  //        Has ~4 ns insertion delay but that's fine for fabric-to-fabric paths.
 60  // ============================================================================
 61  wire adc_dco_bufio;   // Near-zero delay — drives IDDR only
 62  wire adc_dco_buffered; // BUFG output — drives fabric logic
 63  
 64  BUFIO bufio_dco (
 65      .I(adc_dco),
 66      .O(adc_dco_bufio)
 67  );
 68  
 69  // MMCME2 jitter-cleaning wrapper replaces the direct BUFG.
 70  // The PLL feedback loop attenuates input jitter from ~50 ps to ~20-30 ps,
 71  // reducing clock uncertainty and improving WNS on the 400 MHz CIC path.
 72  wire mmcm_locked;
 73  
 74  adc_clk_mmcm mmcm_inst (
 75      .clk_in       (adc_dco),          // 400 MHz from IBUFDS output
 76      .reset_n      (reset_n),
 77      .clk_400m_out (adc_dco_buffered), // Jitter-cleaned 400 MHz on BUFG
 78      .mmcm_locked  (mmcm_locked)
 79  );
 80  assign adc_dco_bufg = adc_dco_buffered;
 81  
 82  // IDDR for capturing DDR data
 83  wire [7:0] adc_data_rise;  // Data on rising edge (BUFIO domain)
 84  wire [7:0] adc_data_fall;  // Data on falling edge (BUFIO domain)
 85  
 86  genvar j;
 87  generate
 88      for (j = 0; j < 8; j = j + 1) begin : iddr_gen
 89          IDDR #(
 90              .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),
 91              .INIT_Q1(1'b0),
 92              .INIT_Q2(1'b0),
 93              .SRTYPE("SYNC")
 94          ) iddr_inst (
 95              .Q1(adc_data_rise[j]),   // Rising edge data
 96              .Q2(adc_data_fall[j]),   // Falling edge data
 97              .C(adc_dco_bufio),       // BUFIO clock (near-zero insertion delay)
 98              .CE(1'b1),
 99              .D(adc_data[j]),
100              .R(1'b0),
101              .S(1'b0)
102          );
103      end
104  endgenerate
105  
106  // ============================================================================
107  // Re-register IDDR outputs into BUFG domain
108  // IDDR with SAME_EDGE_PIPELINED produces outputs stable for a full clock cycle.
109  // BUFIO and BUFG are derived from the same source (adc_dco), so they are
110  // frequency-matched. This single register stage transfers from IOB (BUFIO)
111  // to fabric (BUFG) with guaranteed timing.
112  // ============================================================================
113  reg [7:0] adc_data_rise_bufg;
114  reg [7:0] adc_data_fall_bufg;
115  
116  always @(posedge adc_dco_buffered) begin
117      adc_data_rise_bufg <= adc_data_rise;
118      adc_data_fall_bufg <= adc_data_fall;
119  end
120  
121  // Combine rising and falling edge data to get 400MSPS stream
122  reg [7:0] adc_data_400m_reg;
123  reg adc_data_valid_400m_reg;
124  reg dco_phase;
125  
126  // ── Reset synchronizer ────────────────────────────────────────
127  // reset_n comes from the 100 MHz sys_clk domain.  Assertion (going low)
128  // is asynchronous and safe — the FFs enter reset instantly.  De-assertion
129  // (going high) must be synchronised to adc_dco_buffered to avoid
130  // metastability.  This is the classic "async assert, sync de-assert" pattern.
131  //
132  // mmcm_locked gates de-assertion: the 400 MHz domain stays in reset until
133  // the MMCM PLL has locked and the jitter-cleaned clock is stable.
134  (* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync_400m;
135  wire reset_n_400m;
136  wire reset_n_gated = reset_n & mmcm_locked;
137  
138  always @(posedge adc_dco_buffered or negedge reset_n_gated) begin
139      if (!reset_n_gated)
140          reset_sync_400m <= 2'b00;           // async assert (or MMCM not locked)
141      else
142          reset_sync_400m <= {reset_sync_400m[0], 1'b1};  // sync de-assert
143  end
144  assign reset_n_400m = reset_sync_400m[1];
145  
146  always @(posedge adc_dco_buffered or negedge reset_n_400m) begin
147      if (!reset_n_400m) begin
148          adc_data_400m_reg <= 8'b0;
149          adc_data_valid_400m_reg <= 1'b0;
150          dco_phase <= 1'b0;
151      end else begin
152          dco_phase <= ~dco_phase;
153          
154          if (dco_phase) begin
155              // Output falling edge data (completes the 400MSPS stream)
156              adc_data_400m_reg <= adc_data_fall_bufg;
157          end else begin
158              // Output rising edge data
159              adc_data_400m_reg <= adc_data_rise_bufg;
160          end
161          
162          adc_data_valid_400m_reg <= 1'b1; // Always valid when ADC is running
163      end
164  end
165  
166  assign adc_data_400m = adc_data_400m_reg;
167  assign adc_data_valid_400m = adc_data_valid_400m_reg;
168  
169  endmodule