1#!/usr/bin/env python3 2 3import argparse 4 5parser = argparse.ArgumentParser(description="PLL parameter calculator") 6parser.add_argument("--input", "-i", default=12, help="Input (reference) frequency. Default 12 MHz", type=float) 7parser.add_argument("--ref-min", default=5, help="Override minimum reference frequency. Default 5 MHz", type=float) 8parser.add_argument("--vco-max", default=1600, help="Override maximum VCO frequency. Default 1600 MHz", type=float) 9parser.add_argument("--vco-min", default=750, help="Override minimum VCO frequency. Default 750 MHz", type=float) 10parser.add_argument("--low-vco", "-l", action="store_true", help="Use a lower VCO frequency when possible. This reduces power consumption, at the cost of increased jitter") 11parser.add_argument("output", help="Output frequency in MHz.", type=float) 12args = parser.parse_args() 13 14# Fixed hardware parameters 15fbdiv_range = range(16, 320 + 1) 16postdiv_range = range(1, 7 + 1) 17ref_min = 5 18refdiv_min = 1 19refdiv_max = 63 20 21refdiv_range = range(refdiv_min, max(refdiv_min, min(refdiv_max, int(args.input / args.ref_min))) + 1) 22 23best = (0, 0, 0, 0, 0) 24best_margin = args.output 25 26for refdiv in refdiv_range: 27 for fbdiv in (fbdiv_range if args.low_vco else reversed(fbdiv_range)): 28 vco = args.input / refdiv * fbdiv 29 if vco < args.vco_min or vco > args.vco_max: 30 continue 31 # pd1 is inner loop so that we prefer higher ratios of pd1:pd2 32 for pd2 in postdiv_range: 33 for pd1 in postdiv_range: 34 out = vco / pd1 / pd2 35 margin = abs(out - args.output) 36 if margin < best_margin: 37 best = (out, fbdiv, pd1, pd2, refdiv) 38 best_margin = margin 39 40print("Requested: {} MHz".format(args.output)) 41print("Achieved: {} MHz".format(best[0])) 42print("REFDIV: {}".format(best[4])) 43print("FBDIV: {} (VCO = {} MHz)".format(best[1], args.input / best[4] * best[1])) 44print("PD1: {}".format(best[2])) 45print("PD2: {}".format(best[3])) 46