c6103b37de
MMCM (Gap 7): - Add adc_clk_mmcm.v: MMCME2_ADV wrapper (VCO=800MHz, CLKOUT0=400MHz) - Modify ad9484_interface_400m.v: replace BUFG with MMCM path, gate reset on mmcm_locked - Add adc_clk_mmcm.xdc: CDC false paths for clk_mmcm_out0 <-> clk_100m XDC Fix (Build 19 WNS=-0.011 root cause): - Remove conflicting create_generated_clock -name clk_400m_mmcm - Replace all clk_400m_mmcm references with Vivado auto-generated clk_mmcm_out0 - CDC false paths now correctly apply to actual timing paths CIC CREG Pipeline (Build 18 critical path fix): - Explicit DSP48E1 for comb[0] with CREG=1/AREG=1/BREG=1/PREG=1 - Absorbs integrator_sampled_comb fabric FDRE into DSP48 C-port register - Eliminates 0.643ns fabric->DSP routing delay (Build 18 tightest path) - +1 cycle comb latency via data_valid_comb_0_out pipeline - Move shared register declarations above ifndef SIMULATION (iverilog fix) - Update golden data for +1 cycle CIC pipeline shift Build scripts: build19_mmcm.tcl, build20_mmcm_creg.tcl Regression: 18/18 FPGA pass, 20/20 MCU pass Build 20 launched on remote Vivado (pending results)
225 lines
7.7 KiB
Verilog
225 lines
7.7 KiB
Verilog
`timescale 1ns / 1ps
|
|
// ============================================================================
|
|
// adc_clk_mmcm.v — MMCME2 Jitter-Cleaning Wrapper for AD9484 400 MHz DCO
|
|
//
|
|
// PURPOSE:
|
|
// Replaces the direct BUFG on the ADC data clock output (adc_dco) with an
|
|
// MMCME2_ADV configured for 1:1 frequency (400 MHz in → 400 MHz out) with
|
|
// jitter attenuation via the PLL feedback loop.
|
|
//
|
|
// CURRENT ARCHITECTURE (ad9484_interface_400m.v):
|
|
// adc_dco_p/n → IBUFDS → BUFIO (drives IDDR only, near-zero delay)
|
|
// → BUFG (drives all fabric 400 MHz logic)
|
|
//
|
|
// NEW ARCHITECTURE (this module replaces the BUFG path):
|
|
// adc_dco_p/n → IBUFDS → BUFIO (unchanged — drives IDDR only)
|
|
// → MMCME2 CLKIN1 → CLKOUT0 → BUFG (fabric 400 MHz)
|
|
//
|
|
// BENEFITS:
|
|
// 1. Jitter attenuation: MMCM PLL loop filters input jitter from ~50 ps
|
|
// to ~20-30 ps output jitter, reducing clock uncertainty by ~20 ps.
|
|
// 2. Phase control: CLKOUT0_PHASE can fine-tune phase offset if needed.
|
|
// 3. Locked indicator: mmcm_locked output enables proper reset sequencing.
|
|
// 4. Expected WNS improvement: +20-40 ps on the 400 MHz CIC critical path.
|
|
//
|
|
// MMCM CONFIGURATION (Artix-7 XC7A200T-2):
|
|
// CLKIN1 = 400 MHz (from IBUFDS output)
|
|
// DIVCLK_DIVIDE = 1
|
|
// CLKFBOUT_MULT_F = 2.0 → VCO = 400 * 2 = 800 MHz (range: 600-1200 MHz)
|
|
// CLKOUT0_DIVIDE_F = 2.0 → CLKOUT0 = 800 / 2 = 400 MHz
|
|
// CLKFBOUT → BUFG → CLKFBIN (internal feedback for best jitter performance)
|
|
//
|
|
// INTEGRATION:
|
|
// This module is a DROP-IN replacement for the BUFG in ad9484_interface_400m.v.
|
|
// See adc_clk_mmcm_integration.md for step-by-step instructions.
|
|
//
|
|
// SIMULATION:
|
|
// Under `ifdef SIMULATION, this module passes the clock through a simple
|
|
// BUFG (no MMCM primitive), matching the current behavior for iverilog.
|
|
//
|
|
// TARGET: XC7A200T-2FBG484I (Artix-7, speed grade -2, industrial temp)
|
|
// ============================================================================
|
|
|
|
module adc_clk_mmcm (
|
|
// Input: single-ended clock from IBUFDS output
|
|
input wire clk_in, // 400 MHz from IBUFDS (adc_dco after IBUFDS)
|
|
|
|
// System reset (active-low, from 100 MHz domain)
|
|
input wire reset_n,
|
|
|
|
// Outputs
|
|
output wire clk_400m_out, // Jitter-cleaned 400 MHz on BUFG (fabric logic)
|
|
output wire mmcm_locked // 1 = MMCM PLL is locked and clock is stable
|
|
);
|
|
|
|
`ifdef SIMULATION
|
|
// ============================================================================
|
|
// SIMULATION PATH — simple passthrough (no Xilinx primitives)
|
|
// ============================================================================
|
|
// iverilog and other simulators don't have MMCME2_ADV. Pass clock through
|
|
// with a locked signal that asserts after a brief delay matching real MMCM
|
|
// lock time (~10 us at 400 MHz = ~4000 cycles).
|
|
|
|
reg locked_sim;
|
|
reg [12:0] lock_counter;
|
|
|
|
initial begin
|
|
locked_sim = 1'b0;
|
|
lock_counter = 13'd0;
|
|
end
|
|
|
|
always @(posedge clk_in or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
locked_sim <= 1'b0;
|
|
lock_counter <= 13'd0;
|
|
end else begin
|
|
if (lock_counter < 13'd4096) begin
|
|
lock_counter <= lock_counter + 1;
|
|
end else begin
|
|
locked_sim <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
`ifdef SIMULATION_HAS_BUFG
|
|
// If the simulator supports BUFG (e.g., Vivado xsim)
|
|
BUFG bufg_sim (
|
|
.I(clk_in),
|
|
.O(clk_400m_out)
|
|
);
|
|
`else
|
|
// Pure behavioral — iverilog
|
|
assign clk_400m_out = clk_in;
|
|
`endif
|
|
|
|
assign mmcm_locked = locked_sim;
|
|
|
|
`else
|
|
// ============================================================================
|
|
// SYNTHESIS PATH — MMCME2_ADV with jitter-cleaning feedback loop
|
|
// ============================================================================
|
|
|
|
wire clk_mmcm_out0; // MMCM CLKOUT0 (unbuffered)
|
|
wire clk_mmcm_fb_out; // MMCM CLKFBOUT (unbuffered)
|
|
wire clk_mmcm_fb_bufg; // CLKFBOUT after BUFG (feedback)
|
|
wire mmcm_locked_int;
|
|
|
|
// ---- MMCME2_ADV Instance ----
|
|
// Configuration for 400 MHz 1:1 with jitter cleaning:
|
|
// VCO = CLKIN1 * CLKFBOUT_MULT_F / DIVCLK_DIVIDE = 400 * 2.0 / 1 = 800 MHz
|
|
// CLKOUT0 = VCO / CLKOUT0_DIVIDE_F = 800 / 2.0 = 400 MHz
|
|
// Bandwidth = "HIGH" for maximum jitter attenuation
|
|
MMCME2_ADV #(
|
|
// Input clock
|
|
.CLKIN1_PERIOD (2.500), // 400 MHz = 2.500 ns period
|
|
.CLKIN2_PERIOD (0.000), // Unused
|
|
.REF_JITTER1 (0.020), // 20 ps reference jitter (conservative)
|
|
.REF_JITTER2 (0.000), // Unused
|
|
|
|
// VCO configuration
|
|
.DIVCLK_DIVIDE (1), // Input divider = 1 (no division)
|
|
.CLKFBOUT_MULT_F (2.0), // Feedback multiplier → VCO = 800 MHz
|
|
.CLKFBOUT_PHASE (0.0), // No feedback phase shift
|
|
|
|
// Output 0: 400 MHz fabric clock
|
|
.CLKOUT0_DIVIDE_F (2.0), // 800 / 2.0 = 400 MHz
|
|
.CLKOUT0_PHASE (0.0), // Phase-aligned with input
|
|
.CLKOUT0_DUTY_CYCLE (0.5), // 50% duty cycle
|
|
|
|
// Unused outputs — disabled
|
|
.CLKOUT1_DIVIDE (1),
|
|
.CLKOUT1_PHASE (0.0),
|
|
.CLKOUT1_DUTY_CYCLE (0.5),
|
|
.CLKOUT2_DIVIDE (1),
|
|
.CLKOUT2_PHASE (0.0),
|
|
.CLKOUT2_DUTY_CYCLE (0.5),
|
|
.CLKOUT3_DIVIDE (1),
|
|
.CLKOUT3_PHASE (0.0),
|
|
.CLKOUT3_DUTY_CYCLE (0.5),
|
|
.CLKOUT4_DIVIDE (1),
|
|
.CLKOUT4_PHASE (0.0),
|
|
.CLKOUT4_DUTY_CYCLE (0.5),
|
|
.CLKOUT5_DIVIDE (1),
|
|
.CLKOUT5_PHASE (0.0),
|
|
.CLKOUT5_DUTY_CYCLE (0.5),
|
|
.CLKOUT6_DIVIDE (1),
|
|
.CLKOUT6_PHASE (0.0),
|
|
.CLKOUT6_DUTY_CYCLE (0.5),
|
|
|
|
// PLL filter bandwidth — HIGH for maximum jitter attenuation
|
|
.BANDWIDTH ("HIGH"),
|
|
|
|
// Compensation mode — BUFG on feedback path
|
|
.COMPENSATION ("BUF_IN"),
|
|
|
|
// Startup wait for configuration clock
|
|
.STARTUP_WAIT ("FALSE")
|
|
) mmcm_adc_400m (
|
|
// Clock inputs
|
|
.CLKIN1 (clk_in), // 400 MHz from IBUFDS
|
|
.CLKIN2 (1'b0), // Unused second input
|
|
.CLKINSEL (1'b1), // Select CLKIN1
|
|
|
|
// Feedback
|
|
.CLKFBOUT (clk_mmcm_fb_out), // Feedback output (unbuffered)
|
|
.CLKFBIN (clk_mmcm_fb_bufg), // Feedback input (from BUFG)
|
|
|
|
// Clock outputs
|
|
.CLKOUT0 (clk_mmcm_out0), // 400 MHz output (unbuffered)
|
|
.CLKOUT0B (), // Unused inverted
|
|
.CLKOUT1 (),
|
|
.CLKOUT1B (),
|
|
.CLKOUT2 (),
|
|
.CLKOUT2B (),
|
|
.CLKOUT3 (),
|
|
.CLKOUT3B (),
|
|
.CLKOUT4 (),
|
|
.CLKOUT5 (),
|
|
.CLKOUT6 (),
|
|
.CLKFBOUTB (), // Unused inverted feedback
|
|
|
|
// Control
|
|
.RST (~reset_n), // Active-high reset
|
|
.PWRDWN (1'b0), // Never power down
|
|
|
|
// Status
|
|
.LOCKED (mmcm_locked_int),
|
|
|
|
// Dynamic reconfiguration (unused — tie off)
|
|
.DADDR (7'd0),
|
|
.DCLK (1'b0),
|
|
.DEN (1'b0),
|
|
.DI (16'd0),
|
|
.DWE (1'b0),
|
|
.DO (),
|
|
.DRDY (),
|
|
|
|
// Phase shift (unused — tie off)
|
|
.PSCLK (1'b0),
|
|
.PSEN (1'b0),
|
|
.PSINCDEC (1'b0),
|
|
.PSDONE ()
|
|
);
|
|
|
|
// ---- Feedback BUFG ----
|
|
// Routes CLKFBOUT through a BUFG back to CLKFBIN.
|
|
// This is the standard "internal feedback" topology for best jitter performance.
|
|
// Vivado's clock network insertion delay is compensated by the MMCM feedback loop.
|
|
BUFG bufg_feedback (
|
|
.I(clk_mmcm_fb_out),
|
|
.O(clk_mmcm_fb_bufg)
|
|
);
|
|
|
|
// ---- Output BUFG ----
|
|
// Routes the jitter-cleaned 400 MHz CLKOUT0 onto a global clock network.
|
|
BUFG bufg_clk400m (
|
|
.I(clk_mmcm_out0),
|
|
.O(clk_400m_out)
|
|
);
|
|
|
|
assign mmcm_locked = mmcm_locked_int;
|
|
|
|
`endif
|
|
|
|
endmodule
|