From c1d49fe58b8538aa2228d5de35ba939906e111b9 Mon Sep 17 00:00:00 2001 From: Mats Engstrom Date: Tue, 31 Dec 2019 00:06:59 +0100 Subject: [PATCH] Add iteration over list of frequencies for best solution This patch adds support for icepll to try multiple input frequencies to find the best crystal for the desired output frequency. The values tested comes either from a specified file using-B, or from a default set of frequencies from Mousers "normally stocked" crystal oscillators. --- icepll/icepll.cc | 267 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 183 insertions(+), 84 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 \n"); + printf(" Find best input frequency for desired PLL Output frequency\n"); + printf(" using frequencies read from \n"); + printf("\n"); printf(" -f \n"); printf(" Save PLL configuration as Verilog to file\n"); printf(" If is - then the Verilog is written to stdout.\n"); @@ -67,86 +75,21 @@ void help(const char *cmd) exit(1); } -int main(int argc, char **argv) +bool analyze( + bool simple_feedback, double f_pllin, double f_pllout, + double *best_fout, int *best_divr, int *best_divf, int *best_divq + ) { -#ifdef __EMSCRIPTEN__ - EM_ASM( - if (ENVIRONMENT_IS_NODE) - { - FS.mkdir('/hostcwd'); - FS.mount(NODEFS, { root: '.' }, '/hostcwd'); - FS.mkdir('/hostfs'); - FS.mount(NODEFS, { root: '/' }, '/hostfs'); - } - ); -#endif - - double f_pllin = 12; - double f_pllout = 60; - bool simple_feedback = true; - const char* filename = NULL; - bool file_stdout = false; - const char* module_name = NULL; - bool save_as_module = false; - bool quiet = false; - - int opt; - while ((opt = getopt(argc, argv, "i:o:Smf:n:q")) != -1) - { - switch (opt) - { - case 'i': - f_pllin = atof(optarg); - break; - case 'o': - f_pllout = atof(optarg); - break; - case 'S': - simple_feedback = false; - break; - case 'm': - save_as_module = true; - break; - case 'f': - filename = optarg; - break; - case 'n': - module_name = optarg; - break; - case 'q': - quiet = true; - break; - default: - help(argv[0]); - } - } - - if (optind != argc) - help(argv[0]); - - // Shall save as module, but no filename was given. - // Write to stdout. - if (save_as_module && filename == NULL) - filename = "-"; - - // If filename is "-", then use stdout as output stream. - // That implies quiet mode. - if (filename != NULL && strcmp(filename, "-") == 0) - { - file_stdout = true; - quiet = true; - } - bool found_something = false; - double best_fout = 0; - int best_divr = 0; - int best_divf = 0; - int best_divq = 0; + *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. - 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); @@ -174,11 +117,11 @@ int main(int argc, char **argv) { 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; + 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; } } @@ -192,11 +135,11 @@ int main(int argc, char **argv) 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; + 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; } } @@ -204,6 +147,162 @@ int main(int argc, char **argv) } } + 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__ + EM_ASM( + if (ENVIRONMENT_IS_NODE) + { + FS.mkdir('/hostcwd'); + FS.mount(NODEFS, { root: '.' }, '/hostcwd'); + FS.mkdir('/hostfs'); + FS.mount(NODEFS, { root: '/' }, '/hostfs'); + } + ); +#endif + + double f_pllin = 12; + double f_pllout = 60; + bool simple_feedback = true; + const char* filename = NULL; + 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:bB:q")) != -1) + { + switch (opt) + { + case 'i': + f_pllin = atof(optarg); + break; + case 'o': + f_pllout = atof(optarg); + break; + case 'S': + simple_feedback = false; + break; + case 'm': + save_as_module = true; + break; + case 'f': + filename = optarg; + break; + 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; + default: + help(argv[0]); + } + } + + if (optind != argc) + help(argv[0]); + + // Shall save as module, but no filename was given. + // Write to stdout. + if (save_as_module && filename == NULL) + filename = "-"; + + // If filename is "-", then use stdout as output stream. + // That implies quiet mode. + if (filename != NULL && strcmp(filename, "-") == 0) + { + file_stdout = true; + 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; + + 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++) + { + 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)) + { + found_something = true; + if (abs(fout - f_pllout) < abs(best_fout - f_pllout)) + { + f_pllin = freq_table[i]; + best_fout = fout; + best_divr = divr; + best_divf = divf; + best_divq = divq; + } + } + } + } + double f_pfd = f_pllin / (best_divr + 1);; double f_vco = f_pfd * (best_divf + 1);