/ 9_Firmware / 9_2_FPGA / adc_clk_mmcm.v
adc_clk_mmcm.v
  1  `timescale 1ns / 1ps
  2  // ============================================================================
  3  // adc_clk_mmcm.v — MMCME2 Jitter-Cleaning Wrapper for AD9484 400 MHz DCO
  4  //
  5  // PURPOSE:
  6  //   Replaces the direct BUFG on the ADC data clock output (adc_dco) with an
  7  //   MMCME2_ADV configured for 1:1 frequency (400 MHz in → 400 MHz out) with
  8  //   jitter attenuation via the PLL feedback loop.
  9  //
 10  // CURRENT ARCHITECTURE (ad9484_interface_400m.v):
 11  //   adc_dco_p/n → IBUFDS → BUFIO (drives IDDR only, near-zero delay)
 12  //                         → BUFG  (drives all fabric 400 MHz logic)
 13  //
 14  // NEW ARCHITECTURE (this module replaces the BUFG path):
 15  //   adc_dco_p/n → IBUFDS → BUFIO (unchanged — drives IDDR only)
 16  //                         → MMCME2 CLKIN1 → CLKOUT0 → BUFG (fabric 400 MHz)
 17  //
 18  // BENEFITS:
 19  //   1. Jitter attenuation: MMCM PLL loop filters input jitter from ~50 ps
 20  //      to ~20-30 ps output jitter, reducing clock uncertainty by ~20 ps.
 21  //   2. Phase control: CLKOUT0_PHASE can fine-tune phase offset if needed.
 22  //   3. Locked indicator: mmcm_locked output enables proper reset sequencing.
 23  //   4. Expected WNS improvement: +20-40 ps on the 400 MHz CIC critical path.
 24  //
 25  // MMCM CONFIGURATION (Artix-7 XC7A200T-2):
 26  //   CLKIN1 = 400 MHz (from IBUFDS output)
 27  //   DIVCLK_DIVIDE = 1
 28  //   CLKFBOUT_MULT_F = 2.0   → VCO = 400 * 2 = 800 MHz (range: 600-1200 MHz)
 29  //   CLKOUT0_DIVIDE_F = 2.0  → CLKOUT0 = 800 / 2 = 400 MHz
 30  //   CLKFBOUT → BUFG → CLKFBIN (internal feedback for best jitter performance)
 31  //
 32  // INTEGRATION:
 33  //   This module is a DROP-IN replacement for the BUFG in ad9484_interface_400m.v.
 34  //   See adc_clk_mmcm_integration.md for step-by-step instructions.
 35  //
 36  // SIMULATION:
 37  //   Under `ifdef SIMULATION, this module passes the clock through a simple
 38  //   BUFG (no MMCM primitive), matching the current behavior for iverilog.
 39  //
 40  // TARGET: XC7A200T-2FBG484I (Artix-7, speed grade -2, industrial temp)
 41  // ============================================================================
 42  
 43  module adc_clk_mmcm (
 44      // Input: single-ended clock from IBUFDS output
 45      input  wire clk_in,         // 400 MHz from IBUFDS (adc_dco after IBUFDS)
 46  
 47      // System reset (active-low, from 100 MHz domain)
 48      input  wire reset_n,
 49  
 50      // Outputs
 51      output wire clk_400m_out,   // Jitter-cleaned 400 MHz on BUFG (fabric logic)
 52      output wire mmcm_locked     // 1 = MMCM PLL is locked and clock is stable
 53  );
 54  
 55  `ifdef SIMULATION
 56  // ============================================================================
 57  // SIMULATION PATH — simple passthrough (no Xilinx primitives)
 58  // ============================================================================
 59  // iverilog and other simulators don't have MMCME2_ADV. Pass clock through
 60  // with a locked signal that asserts after a brief delay matching real MMCM
 61  // lock time (~10 us at 400 MHz = ~4000 cycles).
 62  
 63  reg locked_sim;
 64  reg [12:0] lock_counter;
 65  
 66  initial begin
 67      locked_sim = 1'b0;
 68      lock_counter = 13'd0;
 69  end
 70  
 71  always @(posedge clk_in or negedge reset_n) begin
 72      if (!reset_n) begin
 73          locked_sim <= 1'b0;
 74          lock_counter <= 13'd0;
 75      end else begin
 76          if (lock_counter < 13'd4096) begin
 77              lock_counter <= lock_counter + 1;
 78          end else begin
 79              locked_sim <= 1'b1;
 80          end
 81      end
 82  end
 83  
 84  `ifdef SIMULATION_HAS_BUFG
 85  // If the simulator supports BUFG (e.g., Vivado xsim)
 86  BUFG bufg_sim (
 87      .I(clk_in),
 88      .O(clk_400m_out)
 89  );
 90  `else
 91  // Pure behavioral — iverilog
 92  assign clk_400m_out = clk_in;
 93  `endif
 94  
 95  assign mmcm_locked = locked_sim;
 96  
 97  `else
 98  // ============================================================================
 99  // SYNTHESIS PATH — MMCME2_ADV with jitter-cleaning feedback loop
100  // ============================================================================
101  
102  wire clk_mmcm_out0;    // MMCM CLKOUT0 (unbuffered)
103  wire clk_mmcm_fb_out;  // MMCM CLKFBOUT (unbuffered)
104  wire clk_mmcm_fb_bufg; // CLKFBOUT after BUFG (feedback)
105  wire mmcm_locked_int;
106  
107  // ---- MMCME2_ADV Instance ----
108  // Configuration for 400 MHz 1:1 with jitter cleaning:
109  //   VCO = CLKIN1 * CLKFBOUT_MULT_F / DIVCLK_DIVIDE = 400 * 2.0 / 1 = 800 MHz
110  //   CLKOUT0 = VCO / CLKOUT0_DIVIDE_F = 800 / 2.0 = 400 MHz
111  //   Bandwidth = "HIGH" for maximum jitter attenuation
112  MMCME2_ADV #(
113      // Input clock
114      .CLKIN1_PERIOD      (2.500),    // 400 MHz = 2.500 ns period
115      .CLKIN2_PERIOD      (0.000),    // Unused
116      .REF_JITTER1        (0.020),    // 20 ps reference jitter (conservative)
117      .REF_JITTER2        (0.000),    // Unused
118  
119      // VCO configuration
120      .DIVCLK_DIVIDE      (1),        // Input divider = 1 (no division)
121      .CLKFBOUT_MULT_F    (2.0),      // Feedback multiplier → VCO = 800 MHz
122      .CLKFBOUT_PHASE     (0.0),      // No feedback phase shift
123  
124      // Output 0: 400 MHz fabric clock
125      .CLKOUT0_DIVIDE_F   (2.0),      // 800 / 2.0 = 400 MHz
126      .CLKOUT0_PHASE      (0.0),      // Phase-aligned with input
127      .CLKOUT0_DUTY_CYCLE (0.5),      // 50% duty cycle
128  
129      // Unused outputs — disabled
130      .CLKOUT1_DIVIDE     (1),
131      .CLKOUT1_PHASE      (0.0),
132      .CLKOUT1_DUTY_CYCLE (0.5),
133      .CLKOUT2_DIVIDE     (1),
134      .CLKOUT2_PHASE      (0.0),
135      .CLKOUT2_DUTY_CYCLE (0.5),
136      .CLKOUT3_DIVIDE     (1),
137      .CLKOUT3_PHASE      (0.0),
138      .CLKOUT3_DUTY_CYCLE (0.5),
139      .CLKOUT4_DIVIDE     (1),
140      .CLKOUT4_PHASE      (0.0),
141      .CLKOUT4_DUTY_CYCLE (0.5),
142      .CLKOUT5_DIVIDE     (1),
143      .CLKOUT5_PHASE      (0.0),
144      .CLKOUT5_DUTY_CYCLE (0.5),
145      .CLKOUT6_DIVIDE     (1),
146      .CLKOUT6_PHASE      (0.0),
147      .CLKOUT6_DUTY_CYCLE (0.5),
148  
149      // PLL filter bandwidth — HIGH for maximum jitter attenuation
150      .BANDWIDTH          ("HIGH"),
151  
152      // Compensation mode — BUFG on feedback path
153      .COMPENSATION       ("BUF_IN"),
154  
155      // Startup wait for configuration clock
156      .STARTUP_WAIT       ("FALSE")
157  ) mmcm_adc_400m (
158      // Clock inputs
159      .CLKIN1             (clk_in),           // 400 MHz from IBUFDS
160      .CLKIN2             (1'b0),             // Unused second input
161      .CLKINSEL           (1'b1),             // Select CLKIN1
162  
163      // Feedback
164      .CLKFBOUT           (clk_mmcm_fb_out),  // Feedback output (unbuffered)
165      .CLKFBIN            (clk_mmcm_fb_bufg), // Feedback input (from BUFG)
166  
167      // Clock outputs
168      .CLKOUT0            (clk_mmcm_out0),    // 400 MHz output (unbuffered)
169      .CLKOUT0B           (),                 // Unused inverted
170      .CLKOUT1            (),
171      .CLKOUT1B           (),
172      .CLKOUT2            (),
173      .CLKOUT2B           (),
174      .CLKOUT3            (),
175      .CLKOUT3B           (),
176      .CLKOUT4            (),
177      .CLKOUT5            (),
178      .CLKOUT6            (),
179      .CLKFBOUTB          (),                 // Unused inverted feedback
180  
181      // Control
182      .RST                (~reset_n),         // Active-high reset
183      .PWRDWN             (1'b0),             // Never power down
184  
185      // Status
186      .LOCKED             (mmcm_locked_int),
187  
188      // Dynamic reconfiguration (unused — tie off)
189      .DADDR              (7'd0),
190      .DCLK               (1'b0),
191      .DEN                (1'b0),
192      .DI                 (16'd0),
193      .DWE                (1'b0),
194      .DO                 (),
195      .DRDY               (),
196  
197      // Phase shift (unused — tie off)
198      .PSCLK              (1'b0),
199      .PSEN               (1'b0),
200      .PSINCDEC           (1'b0),
201      .PSDONE             ()
202  );
203  
204  // ---- Feedback BUFG ----
205  // Routes CLKFBOUT through a BUFG back to CLKFBIN.
206  // This is the standard "internal feedback" topology for best jitter performance.
207  // Vivado's clock network insertion delay is compensated by the MMCM feedback loop.
208  BUFG bufg_feedback (
209      .I(clk_mmcm_fb_out),
210      .O(clk_mmcm_fb_bufg)
211  );
212  
213  // ---- Output BUFG ----
214  // Routes the jitter-cleaned 400 MHz CLKOUT0 onto a global clock network.
215  BUFG bufg_clk400m (
216      .I(clk_mmcm_out0),
217      .O(clk_400m_out)
218  );
219  
220  assign mmcm_locked = mmcm_locked_int;
221  
222  `endif
223  
224  endmodule