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