diff options
author | Claire Wolf <clifford@clifford.at> | 2020-03-30 13:55:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-30 13:55:23 +0200 |
commit | d1cee1d4ae545fdca995cd656ef52d8923aa77e9 (patch) | |
tree | eec0f8fe9ba4bd89862a85079f1a9903b77c5216 | |
parent | d38852b48d17f2a0eba1ed68e681a84899920271 (diff) | |
parent | c1d49fe58b8538aa2228d5de35ba939906e111b9 (diff) | |
download | icestorm-d1cee1d4ae545fdca995cd656ef52d8923aa77e9.tar.gz icestorm-d1cee1d4ae545fdca995cd656ef52d8923aa77e9.tar.bz2 icestorm-d1cee1d4ae545fdca995cd656ef52d8923aa77e9.zip |
Merge pull request #245 from SmallRoomLabs/iteratePLL
icepll: Add iteration over list of frequencies for best solution
-rw-r--r-- | icepll/icepll.cc | 207 |
1 files changed, 153 insertions, 54 deletions
diff --git a/icepll/icepll.cc b/icepll/icepll.cc index f9ebecf..a2db72f 100644 --- a/icepll/icepll.cc +++ b/icepll/icepll.cc @@ -51,6 +51,14 @@ void help(const char *cmd) printf(" -S\n"); printf(" Disable SIMPLE feedback path mode\n"); printf("\n"); + printf(" -b\n"); + printf(" Find best input frequency for desired PLL Output frequency\n"); + printf(" using the normally stocked oscillators at Mouser\n"); + printf("\n"); + printf(" -B <filename>\n"); + printf(" Find best input frequency for desired PLL Output frequency\n"); + printf(" using frequencies read from <filename>\n"); + printf("\n"); printf(" -f <filename>\n"); printf(" Save PLL configuration as Verilog to file\n"); printf(" If <filename> is - then the Verilog is written to stdout.\n"); @@ -67,6 +75,119 @@ void help(const char *cmd) exit(1); } +bool analyze( + bool simple_feedback, double f_pllin, double f_pllout, + double *best_fout, int *best_divr, int *best_divf, int *best_divq + ) +{ + bool found_something = false; + *best_fout = 0; + *best_divr = 0; + *best_divf = 0; + *best_divq = 0; + + int divf_max = simple_feedback ? 127 : 63; + // The documentation in the iCE40 PLL Usage Guide incorrectly lists the + // maximum value of DIVF as 63, when it is only limited to 63 when using + // feedback modes other that SIMPLE. + + if (f_pllin < 10 || f_pllin > 133) { + fprintf(stderr, "Error: PLL input frequency %.3f MHz is outside range 10 MHz - 133 MHz!\n", f_pllin); + exit(1); + } + + if (f_pllout < 16 || f_pllout > 275) { + fprintf(stderr, "Error: PLL output frequency %.3f MHz is outside range 16 MHz - 275 MHz!\n", f_pllout); + exit(1); + } + + for (int divr = 0; divr <= 15; divr++) + { + double f_pfd = f_pllin / (divr + 1); + if (f_pfd < 10 || f_pfd > 133) continue; + + for (int divf = 0; divf <= divf_max; divf++) + { + if (simple_feedback) + { + double f_vco = f_pfd * (divf + 1); + if (f_vco < 533 || f_vco > 1066) continue; + + for (int divq = 1; divq <= 6; divq++) + { + double fout = f_vco * exp2(-divq); + + if (fabs(fout - f_pllout) < fabs(*best_fout - f_pllout) || !found_something) { + *best_fout = fout; + *best_divr = divr; + *best_divf = divf; + *best_divq = divq; + found_something = true; + } + } + } + else + { + for (int divq = 1; divq <= 6; divq++) + { + double f_vco = f_pfd * (divf + 1) * exp2(divq); + if (f_vco < 533 || f_vco > 1066) continue; + + double fout = f_vco * exp2(-divq); + + if (fabs(fout - f_pllout) < fabs(*best_fout - f_pllout) || !found_something) { + *best_fout = fout; + *best_divr = divr; + *best_divf = divf; + *best_divq = divq; + found_something = true; + } + } + } + } + } + + return found_something; +} + + // Table of frequencies to test in "best" mode defaults to ABRACOM Crystal + // oscillators "Normally stocked" at Mouser + double freq_table[100] = + { + 10, 11.0592, 11.2896, 11.7846, 12, 12.288, 12.352, 12.5, 13, 13.5, 13.6, 14.31818, 14.7456, 15, 16, 16.384, 17.2032, 18.432, 19.2, 19.44, 19.6608, + 20, 24, 24.576, 25, 26, 27, 27.12, 28.63636, 28.9, 29.4912, + 30, 32, 32.768, 33, 33.206, 33.333, 35.328, 36, 37.03, 37.4, 38.4, 38.88, + 40, 40.95, 40.97, 44, 44.736, 48, + 50, 54, 57.692, + 60, 64, 65, 66, 66.666, 68, + 70, 72, 75, 76.8, + 80, 80.92, + 92.16, 96, 98.304, + 100, 104, 106.25, 108, + 114.285, + 120, 122.88, 125, + 0 + }; + +void readfreqfile(const char *filename) { + FILE *f; + f = fopen(filename, "r"); + if (f == NULL) { + fprintf(stderr, "Error: Can't open file %s!\n",filename); + exit(1); + } + + // Clear and overwrite the default values in the table + memset(freq_table, 0, sizeof(freq_table)); + int i = 0; + double freq=0; + while((i < sizeof(freq_table)/sizeof(double)) && (fscanf(f, "%lf", &freq) > 0)) + { + freq_table[i++] = freq; + } + fclose(f); +} + int main(int argc, char **argv) { #ifdef __EMSCRIPTEN__ @@ -88,10 +209,12 @@ int main(int argc, char **argv) bool file_stdout = false; const char* module_name = NULL; bool save_as_module = false; + bool best_mode = false; + const char* freqfile = NULL; bool quiet = false; int opt; - while ((opt = getopt(argc, argv, "i:o:Smf:n:q")) != -1) + while ((opt = getopt(argc, argv, "i:o:Smf:n:bB:q")) != -1) { switch (opt) { @@ -113,6 +236,13 @@ int main(int argc, char **argv) case 'n': module_name = optarg; break; + case 'b': + best_mode = true; + break; + case 'B': + best_mode = true; + freqfile = optarg; + break; case 'q': quiet = true; break; @@ -137,68 +267,37 @@ int main(int argc, char **argv) quiet = true; } + if (freqfile) { + readfreqfile(freqfile); + } + bool found_something = false; double best_fout = 0; int best_divr = 0; int best_divf = 0; int best_divq = 0; - // The documentation in the iCE40 PLL Usage Guide incorrectly lists the - // maximum value of DIVF as 63, when it is only limited to 63 when using - // feedback modes other that SIMPLE. - int divf_max = simple_feedback ? 127 : 63; - - if (f_pllin < 10 || f_pllin > 133) { - fprintf(stderr, "Error: PLL input frequency %.3f MHz is outside range 10 MHz - 133 MHz!\n", f_pllin); - exit(1); - } - - if (f_pllout < 16 || f_pllout > 275) { - fprintf(stderr, "Error: PLL output frequency %.3f MHz is outside range 16 MHz - 275 MHz!\n", f_pllout); - exit(1); - } - - for (int divr = 0; divr <= 15; divr++) - { - double f_pfd = f_pllin / (divr + 1); - if (f_pfd < 10 || f_pfd > 133) continue; - - for (int divf = 0; divf <= divf_max; divf++) + if (!best_mode) { + // Use only specified input frequency + found_something = analyze(simple_feedback, f_pllin, f_pllout, &best_fout, &best_divr, &best_divf, &best_divq); + } else { + // Iterate over all standard crystal frequencies and select the best + for (int i = 0; freq_table[i]>0.0 ; i++) { - if (simple_feedback) - { - double f_vco = f_pfd * (divf + 1); - if (f_vco < 533 || f_vco > 1066) continue; - - for (int divq = 1; divq <= 6; divq++) - { - double fout = f_vco * exp2(-divq); - - if (fabs(fout - f_pllout) < fabs(best_fout - f_pllout) || !found_something) { - best_fout = fout; - best_divr = divr; - best_divf = divf; - best_divq = divq; - found_something = true; - } - } - } - else + double fout = 0; + int divr = 0; + int divf = 0; + int divq = 0; + if (analyze(simple_feedback, freq_table[i], f_pllout, &fout, &divr, &divf, &divq)) { - for (int divq = 1; divq <= 6; divq++) + found_something = true; + if (abs(fout - f_pllout) < abs(best_fout - f_pllout)) { - double f_vco = f_pfd * (divf + 1) * exp2(divq); - if (f_vco < 533 || f_vco > 1066) continue; - - double fout = f_vco * exp2(-divq); - - if (fabs(fout - f_pllout) < fabs(best_fout - f_pllout) || !found_something) { - best_fout = fout; - best_divr = divr; - best_divf = divf; - best_divq = divq; - found_something = true; - } + f_pllin = freq_table[i]; + best_fout = fout; + best_divr = divr; + best_divf = divf; + best_divq = divq; } } } |