blob: f730449791047f01d4983a145f1965f8c802c1ee [file]
/* I was unable to find good information on how the PLL dividers work
in the ECP5 PLL Design and Usage Guide, so I ran several frequencies
through Lattice's clarity designer, and was surprised with what I
found:
| Input | Output | refclk | feedback | output | fvco |
| 12 | 48 | 1 | 4 | 12 | 576 |
| 12 | 60 | 1 | 5 | 10 | 600 |
| 20 | 30 | 2 | 3 | 20 | 600 |
| 45 | 30 | 3 | 2 | 20 | 600 |
| 100 | 400 | 1 | 4 | 1 | 400 |
| 200 | 400 | 1 | 2 | 2 | 800 |
| 50 | 400 | 1 | 8 | 2 | 800 |
| 70 | 40 | 7 | 4 | 15 | 600 |
| 12 | 36 | 1 | 3 | 18 | 648 |
| 12 | 96 | 1 | 8 | 6 | 576 |
| 90 | 40 | 9 | 4 | 15 | 600 |
| 90 | 50 | 9 | 5 | 13 | 650 |
| 43 | 86 | 1 | 2 | 7 | 602 |
it appears that
f_pfd = f_in/refclk
f_vco = f_pfd * feedback * output
f_out = f_vco / output
*/
#define INPUT_MIN 8.0f
#define INPUT_MAX 400.0f
#define OUTPUT_MIN 10.0f
#define OUTPUT_MAX 400.0f
#define PFD_MIN 3.125f
#define PFD_MAX 400.0f
#define VCO_MIN 400.0f
#define VCO_MAX 800.0f
#include <iostream>
#include <limits>
#include <boost/program_options.hpp>
using namespace std;
struct pll_params{
int refclk_div;
int feedback_div;
int output_div;
float fout;
float fvco;
};
pll_params calc_pll_params(float input, float output);
int main(int argc, char** argv){
namespace po = boost::program_options;
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");
po::variables_map vm;
po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).run();
po::store(parsed, vm);
po::notify(vm);
if(vm.count("help")){
cerr << "Project Trellis - Open Source Tools for ECP5 FPGAs" << endl;
cerr << "ecpunpack: ECP5 bitstream to text config converter" << endl;
cerr << endl;
cerr << "Copyright (C) 2018 David Shah <david@symbioticeda.com>" << endl;
cerr << endl;
cerr << options << endl;
}
float inputf = vm["input"].as<float>();
float outputf = vm["output"].as<float>();
if(inputf < INPUT_MIN || inputf > INPUT_MAX){
fprintf(stderr, "Input frequency %.3fMHz not in range (%.3fMHz, %.3fMHz)\n",
inputf, INPUT_MIN, INPUT_MAX);
return 1;
}
if(outputf < OUTPUT_MIN || outputf > OUTPUT_MAX){
fprintf(stderr, "Output frequency %.3fMHz not in range (%.3fMHz, %.3fMHz)\n",
outputf, OUTPUT_MIN, OUTPUT_MAX);
return 1;
}
pll_params params = calc_pll_params(inputf, outputf);
fprintf(stdout, "Pll parameters:\n");
fprintf(stdout, "Refclk divisor: %d\n", params.refclk_div);
fprintf(stdout, "Feedback divisor: %d\n", params.feedback_div);
fprintf(stdout, "Output divisor: %d\n", params.output_div);
fprintf(stdout, "VCO frequency: %f\n", params.fvco);
fprintf(stdout, "Output frequency: %f\n", params.fout);
}
pll_params calc_pll_params(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;
float fout = fvco / (float) output_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.fout = fout;
params.fvco = fvco;
}
}
}
}
return params;
}