Add high resolution mode for ecppll Uses the PLL secondary output as the clock output to give higher frequency resolution Signed-off-by: Michael Nolan <mtnolan2640@gmail.com>
diff --git a/libtrellis/tools/ecppll.cpp b/libtrellis/tools/ecppll.cpp index a8fee97..4cf0ad8 100644 --- a/libtrellis/tools/ecppll.cpp +++ b/libtrellis/tools/ecppll.cpp
@@ -45,12 +45,14 @@ int refclk_div; int feedback_div; int output_div; + int secondary_div; float fout; float fvco; }; pll_params calc_pll_params(float input, float output); +pll_params calc_pll_params_highres(float input, float output); void write_pll_config(pll_params params, const char* name, ofstream& file); int main(int argc, char** argv){ @@ -58,9 +60,10 @@ po::options_description options("Allowed options"); options.add_options()("help,h", "show help"); - options.add_options()("input,i", po::value<float>()->required(), "Input frequency in MHz"); - options.add_options()("output,o", po::value<float>()->required(), "Output frequency in MHz"); + options.add_options()("input,i", po::value<float>(), "Input frequency in MHz"); + options.add_options()("output,o", po::value<float>(), "Output frequency in MHz"); options.add_options()("file,f", po::value<string>(), "Output to file"); + options.add_options()("highres", "Use secondary PLL output for higher frequency resolution"); po::variables_map vm; po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).run(); @@ -74,6 +77,11 @@ cerr << "Copyright (C) 2018 David Shah <david@symbioticeda.com>" << endl; cerr << endl; cerr << options << endl; + return 1; + } + if(vm.count("input") != 1 || vm.count("output") != 1){ + cerr << "Error: missing input or output frequency!\n"; + return 1; } float inputf = vm["input"].as<float>(); float outputf = vm["output"].as<float>(); @@ -85,12 +93,21 @@ cerr << "Output frequency " << outputf << "MHz not in range (" << OUTPUT_MIN << "MHz, " << OUTPUT_MAX << "MHz)\n"; return 1; } - pll_params params = calc_pll_params(inputf, outputf); + pll_params params; + if(vm.count("highres")){ + params = calc_pll_params_highres(inputf, outputf); + } + else{ + params = calc_pll_params(inputf, outputf); + } cout << "Pll parameters:" << endl; cout << "Refclk divisor: " << params.refclk_div << endl; cout << "Feedback divisor: " << params.feedback_div << endl; cout << "Output divisor: " << params.output_div << endl; + if(params.secondary_div != 0){ + cout << "Secondary divisor: " << params.secondary_div << endl; + } cout << "VCO frequency: " << params.fvco << endl; cout << "Output frequency: " << params.fout << endl; if(vm.count("file")){ @@ -139,8 +156,45 @@ return params; } +pll_params calc_pll_params_highres(float input, float output){ + float error = std::numeric_limits<float>::max(); + pll_params params = {0}; + for(int input_div=1;input_div <= 128; input_div++){ + + float fpfd = input / (float)input_div; + if(fpfd < PFD_MIN || fpfd > PFD_MAX) + continue; + for(int feedback_div=1;feedback_div <= 80; feedback_div++){ + for(int output_div=1;output_div <= 128; output_div++){ + float fvco = fpfd * (float)feedback_div * (float) output_div; + + if(fvco < VCO_MIN || fvco > VCO_MAX) + continue; + for(int secondary_div = 1; secondary_div <= 128; secondary_div++){ + float fout = fvco / (float) secondary_div; + if(fabsf(fout - output) < error || + (fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){ + error = fabsf(fout-output); + params.refclk_div = input_div; + params.feedback_div = feedback_div; + params.output_div = output_div; + params.secondary_div = secondary_div; + params.fout = fout; + params.fvco = fvco; + + } + } + + } + } + } + return params; +} + void write_pll_config(pll_params params, const char* name, ofstream& file){ file << "module " << name << "(input clki, output clko);\n"; + file << "wire clkfb;\n"; + file << "wire clkos;\n"; file << "(* ICP_CURRENT=\"12\" *) (* LPF_RESISTOR=\"8\" *) (* MFG_ENABLE_FILTEROPAMP=\"1\" *) (* MFG_GMCREF_SEL=\"2\" *)\n"; file << "EHXPLLL #(\n"; file << " .PLLRST_ENA(\"DISABLED\"),\n"; @@ -152,13 +206,18 @@ file << " .OUTDIVIDER_MUXA(\"DIVA\"),\n"; file << " .CLKOP_ENABLE(\"ENABLED\"),\n"; file << " .CLKOP_DIV(" << params.output_div << "),\n"; + if(params.secondary_div != 0){ + file << " .CLKOS_ENABLE(\"ENABLED\"),\n"; + file << " .CLKOS_DIV(" << params.secondary_div << "),\n"; + } file << " .CLKFB_DIV(" << params.feedback_div << "),\n"; file << " .CLKI_DIV(" << params.refclk_div <<"),\n"; file << " .FEEDBK_PATH(\"CLKOP\")\n"; file << " ) pll_i (\n"; file << " .CLKI(clki),\n"; - file << " .CLKFB(clko),\n"; - file << " .CLKOP(clko),\n"; + file << " .CLKFB(clkfb),\n"; + file << " .CLKOP(clkfb),\n"; + file << " .CLKOS(clkos),\n"; file << " .RST(1'b0),\n"; file << " .STDBY(1'b0),\n"; file << " .PHASESEL0(1'b0),\n"; @@ -168,6 +227,12 @@ file << " .PLLWAKESYNC(1'b0),\n"; file << " .ENCLKOP(1'b0),\n"; file << " );\n"; + if(params.secondary_div == 0){ + file << "assign clko = clkfb;\n"; + } + else { + file << "assign clko = clkos;\n"; + } file << "endmodule\n"; }