aboutsummaryrefslogtreecommitdiffstats
path: root/icepll
diff options
context:
space:
mode:
authorMats Engstrom <mats.engstrom@gmail.com>2019-12-31 00:06:59 +0100
committerMats Engstrom <mats.engstrom@gmail.com>2019-12-31 00:06:59 +0100
commitc1d49fe58b8538aa2228d5de35ba939906e111b9 (patch)
tree84a1d940538f9b2256de395f97958952a8a6857a /icepll
parent0ec00d892a91cc68e45479b46161f649caea2933 (diff)
downloadicestorm-c1d49fe58b8538aa2228d5de35ba939906e111b9.tar.gz
icestorm-c1d49fe58b8538aa2228d5de35ba939906e111b9.tar.bz2
icestorm-c1d49fe58b8538aa2228d5de35ba939906e111b9.zip
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.
Diffstat (limited to 'icepll')
-rw-r--r--icepll/icepll.cc207
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;
}
}
}