From 83a2430d190e172693231d845c84895227f04c74 Mon Sep 17 00:00:00 2001 From: Joris van Zwieten Date: Thu, 30 Jul 2026 15:38:22 +0200 Subject: [PATCH] himbaechel/xilinx: fix MMCM loop filter and power register programming The MMCM fasm writer put the 10-bit BANDWIDTH loop-filter word into FILTREG1_RESERVED[11:0] and wrote a hardcoded 0x3d4 into TABLE[9:0]. prjxray's TABLE[9:0] is the loop-filter table word; with the filter word misplaced the MMCM analog loop is misconfigured and never asserts LOCKED on hardware (clock outputs stay dead). Swap them: TABLE[9:0] gets filter_lookup[CLKFBOUT_MULT-1], FILTREG1_RESERVED[11:0] gets the constant 0x8, matching Vivado bitstreams for the same configurations. Also program POWER_REG from the actual configuration instead of 0xffff: 0x0100 for integer-only counters, 0x9900 when CLKOUT0 or CLKFBOUT uses fractional divide, again matching Vivado (verified on integer and CLKOUT0_DIVIDE_F=6.5 fractional testcases by diffing against Vivado bitstreams). Hardware-validated on xc7a100t (QMTech Wukong): MMCM-derived clocks (integer and fractional configurations) lock and run; the same designs never asserted LOCKED before this change. Co-Authored-By: Claude Fable 5 --- himbaechel/uarch/xilinx/fasm.cc | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/himbaechel/uarch/xilinx/fasm.cc b/himbaechel/uarch/xilinx/fasm.cc index dc70320c..e054b32a 100644 --- a/himbaechel/uarch/xilinx/fasm.cc +++ b/himbaechel/uarch/xilinx/fasm.cc @@ -1691,14 +1691,23 @@ struct FasmBackend filter_lookup = Xc7MMCM::filter_lookup_high; else filter_lookup = Xc7MMCM::filter_lookup_optimized; - write_int_vector("FILTREG1_RESERVED[11:0]", filter_lookup[clkfbout_mult - 1], 12); + // prjxray's TABLE[9:0] IS the 10-bit loop-filter word; putting the + // filter word in FILTREG1_RESERVED with a hardcoded TABLE=0x3d4 + // (as before) yields an MMCM that never locks on hardware. + // Values verified against Vivado bitstreams (integer and + // CLKOUT0_DIVIDE_F=6.5 fractional testcases) and hardware-validated + // on a QMTech Wukong xc7a100t. + write_int_vector("FILTREG1_RESERVED[11:0]", 0x8, 12); - // 0x9900 enables fractional counters - // only int counters would be 0x1 << 8 - // 0xffff enables everything, I suppose, this is what is used in xap888 - write_int_vector("POWER_REG_POWER_REG_POWER_REG[15:0]", 0xffff, 16); + // POWER_REG: 0x1 << 8 for integer-only counters, 0x9900 when + // CLKOUT0 or CLKFBOUT use fractional divide. + float clkout0_divide_f = float_or_default(ci, "CLKOUT0_DIVIDE_F", 1.0); + float clkfbout_mult_f = float_or_default(ci, "CLKFBOUT_MULT_F", 5.000); + bool fractional = (clkout0_divide_f != (float)(int)clkout0_divide_f) || + (clkfbout_mult_f != (float)(int)clkfbout_mult_f); + write_int_vector("POWER_REG_POWER_REG_POWER_REG[15:0]", fractional ? 0x9900 : 0x0100, 16); write_bit("LOCKREG3_RESERVED[0]"); - write_int_vector("TABLE[9:0]", 0x3d4, 10); + write_int_vector("TABLE[9:0]", filter_lookup[clkfbout_mult - 1], 10); pop(2); } void write_dsp_cell(CellInfo *ci)