mrfioc2  2.3.0
mrfFracSynth.c
Go to the documentation of this file.
1 /***************************************************************************************************
2 |* mrfFracSynth.c -- EPICS Support Routines for the Micrel SY87739L Fractional-N Synthesizer Chip
3 |*
4 |*--------------------------------------------------------------------------------------------------
5 |* Author: Eric Bjorklund (LANSCE)
6 |* Date: 13 September 2006
7 |*
8 |*--------------------------------------------------------------------------------------------------
9 |* MODIFICATION HISTORY:
10 |* 18 Sep 2008 E.Bjorklund Clean up some compiler warnings
11 |* 13 Sep 2006 E.Bjorklund Original Release
12 |*
13 |*--------------------------------------------------------------------------------------------------
14 |* REFERENCE:
15 |* Micrel SY87739L Product Description. Available at:
16 |* http://www.micrel.com
17 |*
18 \**************************************************************************************************/
19 
20 /**************************************************************************************************
21 |* COPYRIGHT NOTIFICATION
22 |**************************************************************************************************
23 |*
24 |* THE FOLLOWING IS A NOTICE OF COPYRIGHT, AVAILABILITY OF THE CODE,
25 |* AND DISCLAIMER WHICH MUST BE INCLUDED IN THE PROLOGUE OF THE CODE
26 |* AND IN ALL SOURCE LISTINGS OF THE CODE.
27 |*
28 |**************************************************************************************************
29 |*
30 |* This software is distributed under the EPICS Open License Agreement which
31 |* can be found in the file, LICENSE, included with this distribution.
32 |*
33 \*************************************************************************************************/
34 
35 /***************************************************************************************************
36  * mrfFracSynth File Description
37  **************************************************************************************************/
86 
87 /**************************************************************************************************/
88 /* Imported Header Files */
89 /**************************************************************************************************/
90 
91 #ifndef DEBUG_PRINT
92 #define DEBUG_PRINT /* Debug printing always enabled for this module */
93 #endif
94 
95 #include <stdio.h> /* Standard C I/O library */
96 #include <math.h> /* Standard C Math library */
97 
98 #include <epicsTypes.h> /* EPICS Type definitions */
99 #include <iocsh.h> /* EPICS IOC shell support library */
100 #include <registryFunction.h> /* EPICS Registry support library */
101 
102 #include <epicsExport.h> /* EPICS Symbol exporting macro definitions */
103 
104 #include <mrfFracSynth.h> /* MRF Fractional Synthesizer routines */
105 #include <mrfCommon.h> /* MRF event system constants and definitions */
106 #include <debugPrint.h> /* SLAC Debug print utility */
107 #include <mrfFracSynth.h> /* MRF Fractional Synthesizer routines */
108 
109 // FIXME: Is this then really needed?
110 #include <epicsExport.h> /* EPICS Symbol exporting macro definitions */
111 
112 /**************************************************************************************************/
113 /* Configuration Parameters */
114 /**************************************************************************************************/
115 
116 #define MAX_CORRECTION_RATIO (17./14.) /* Maximum value for correction term. */
117 #define MAX_VCO_FREQ 729.0 /* Maximum frequency for voltage-controlled oscillator */
118 #define MIN_VCO_FREQ 540.0 /* Minimum frequency for voltage-controlled oscillator */
119 #define MIN_P_VALUE 17 /* Minimum val for integer part of fractional frequency */
120 #define MAX_FRAC_DIVISOR 31 /* Maximum divisor for fractional frequency value */
121 
122 #define NUM_POST_DIVIDES 31 /* Number of unique post-divider values */
123 #define NUM_POST_DIVIDE_VALS 32 /* Number of post-divider codes */
124 #define NUM_CORRECTIONS 23 /* Number of valid correction values (plus one) */
125 #define NUM_CORRECTION_VALS 8 /* Number of correction value codes */
126 
127 #define MAX_ERROR 100.0 /* Artifical error maximum */
128 #define ZERO_THRESHOLD 1.0e-9 /* Floating point threshold for zero detection */
129 
130 
131 /**************************************************************************************************/
132 /* Define the Fields in the SY87739L Control Word */
133 /**************************************************************************************************/
134 
135 /*---------------------
136  * Define the size of each field
137  */
138 
139 #define CONTROL_MDIV_BITS 3 /* Denominator of correction term */
140 #define CONTROL_NDIV_BITS 3 /* Numerator of correction term */
141 #define CONTROL_POSTDIV_BITS 5 /* Post-Divider */
142 #define CONTROL_MFG_BITS 3 /* Must Be Zero */
143 #define CONTROL_P_BITS 4 /* Integer part of fractional frequency */
144 #define CONTROL_QPM1_BITS 5 /* Value for Q(p-1) term of fractional frequency */
145 #define CONTROL_QP_BITS 5 /* Value for Q(p) term of fractional frequency */
146 #define CONTROL_PREAMBLE_BITS 4 /* Must Be Zero */
147 
148 /*---------------------
149  * Define the offset of each field
150  */
151 
152 #define CONTROL_MDIV_SHIFT 0 /* Denominator of correction term */
153 #define CONTROL_NDIV_SHIFT 3 /* Numerator of correction term */
154 #define CONTROL_POSTDIV_SHIFT 6 /* Post-Divider */
155 #define CONTROL_MFG_SHIFT 11 /* Must Be Zero */
156 #define CONTROL_P_SHIFT 14 /* Integer part of fractional frequency */
157 #define CONTROL_QPM1_SHIFT 18 /* Value for Q(p-1) term of fractional frequency */
158 #define CONTROL_QP_SHIFT 23 /* Value for Q(p) term of fractional frequency */
159 #define CONTROL_PREAMBLE_SHIFT 28 /* Must Be Zero */
160 
161 /*---------------------
162  * Set up a bitmask for each field
163  */
164 
165 #define CONTROL_MDIV_MASK (((1 << CONTROL_MDIV_BITS) - 1) << CONTROL_MDIV_SHIFT)
166 #define CONTROL_NDIV_MASK (((1 << CONTROL_NDIV_BITS) - 1) << CONTROL_NDIV_SHIFT)
167 #define CONTROL_POSTDIV_MASK (((1 << CONTROL_POSTDIV_BITS) - 1) << CONTROL_POSTDIV_SHIFT)
168 #define CONTROL_MFG_MASK (((1 << CONTROL_MFG_BITS) - 1) << CONTROL_MFG_SHIFT)
169 #define CONTROL_P_MASK (((1 << CONTROL_P_BITS) - 1) << CONTROL_P_SHIFT)
170 #define CONTROL_QPM1_MASK (((1 << CONTROL_QPM1_BITS) - 1) << CONTROL_QPM1_SHIFT)
171 #define CONTROL_QP_MASK (((1 << CONTROL_QP_BITS) - 1) << CONTROL_QP_SHIFT)
172 #define CONTROL_PREAMBLE_MASK (((1 << CONTROL_PREAMBLE_BITS) - 1) << CONTROL_PREAMBLE_SHIFT)
173 
174 /*---------------------
175  * Define the field values for the correction factor components
176  */
177 
178 #define CORRECTION_DIV_14 5 /* Numerator or denominator of 14 */
179 #define CORRECTION_DIV_15 7 /* Numerator or denominator of 15 */
180 #define CORRECTION_DIV_16 1 /* Numerator or denominator of 16 */
181 #define CORRECTION_DIV_17 3 /* Numerator or denominator of 17 */
182 #define CORRECTION_DIV_18 2 /* Numerator or denominator of 18 */
183 #define CORRECTION_DIV_31 4 /* Numerator or denominator of 31 */
184 #define CORRECTION_DIV_32 6 /* Numerator or denominator of 32 */
185 
186 
187 /**************************************************************************************************/
188 /* Structure Definitions */
189 /**************************************************************************************************/
190 
191 /*---------------------
192  * Post-Divide Structure
193  */
194 typedef struct { /* PostDivideStruct */
195  epicsFloat64 Divisor; /* Divisor value */
196  epicsUInt32 Code; /* Field code for post-divide value */
198 
199 /*---------------------
200  * Correction Factor Structure
201  */
202 typedef struct { /* CorrectionStruct */
203  epicsFloat64 Ratio; /* Correction factor ratio value */
204  epicsUInt16 nDiv; /* Field code for dividend (N) */
205  epicsUInt16 mDiv; /* Field code for divisor (M) */
207 
208 /*---------------------
209  * Correction Factor Component Value Structure
210  */
211 typedef struct { /* CorrectionValStruct */
212  epicsInt32 Value; /* Field code value */
213  epicsInt32 Class; /* Component class (1 or 2) */
215 
216 /*---------------------
217  * Fractional Synthesizer Component Structure
218  */
219 typedef struct { /* FracSynthComponents */
220  epicsFloat64 Error; /* Deviation from desired output frequency */
221  epicsFloat64 EffectiveFreq; /* Actual frequency generated by these components */
222  epicsInt32 PostDivIndex; /* Index into post-divider table */
223  epicsInt32 CorrectionIndex; /* Index into correction factor table */
224  epicsInt32 P; /* Integer part of fractional frequency */
225  epicsInt32 Qp; /* Value of Q(p) term of fractional frequency */
226  epicsInt32 Qpm1; /* Value of Q(p-1) term of fractional frequency */
228 
229 /**************************************************************************************************/
230 /* Tables for Creating and Decoding the SY87739L Control Word */
231 /**************************************************************************************************/
232 
233 /*---------------------
234  * Post-Divider Table:
235  *---------------------
236  * Contains legal post-divider values and their control-word field codes
237  */
238 
239 static const PostDivideStruct PostDivideList [NUM_POST_DIVIDES] = {
240  { 1.0, 0x00}, { 2.0, 0x02}, { 3.0, 0x03}, { 4.0, 0x04}, { 5.0, 0x05}, { 6.0, 0x06},
241  { 7.0, 0x07}, { 8.0, 0x08}, { 9.0, 0x09}, {10.0, 0x0A}, {11.0, 0x0B}, {12.0, 0x0C},
242  {13.0, 0x0D}, {14.0, 0x0E}, {15.0, 0x0F}, {16.0, 0x10}, {18.0, 0x11}, {20.0, 0x12},
243  {22.0, 0x13}, {24.0, 0x14}, {26.0, 0x15}, {28.0, 0x16}, {30.0, 0x17}, {32.0, 0x18},
244  {36.0, 0x19}, {40.0, 0x1A}, {44.0, 0x1B}, {48.0, 0x1C}, {52.0, 0x1D}, {56.0, 0x1E},
245  {60.0, 0x1F}
246 };
247 
248 /*---------------------
249  * Post-Divider Value Table:
250  *---------------------
251  * Translates the post-divider field code into the actual post-divider value.
252  */
253 
254 static epicsInt32 PostDivideValList [NUM_POST_DIVIDE_VALS] = {
255  1, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
256  16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 40, 44, 48, 52, 56, 60
257 };
258 
259 /*---------------------
260  * Correction Factor Table:
261  *---------------------
262  * Contains legal correction factor ratios and their control-word field codes.
263  */
264 
265 static const CorrectionStruct CorrectionList [NUM_CORRECTIONS] = {
267  {(14./18.), CORRECTION_DIV_14, CORRECTION_DIV_18},
268  {(14./17.), CORRECTION_DIV_14, CORRECTION_DIV_17},
269  {(15./18.), CORRECTION_DIV_15, CORRECTION_DIV_18},
270  {(14./16.), CORRECTION_DIV_14, CORRECTION_DIV_16},
271  {(15./17.), CORRECTION_DIV_15, CORRECTION_DIV_17},
272  {(16./18.), CORRECTION_DIV_18, CORRECTION_DIV_18},
273  {(14./15.), CORRECTION_DIV_14, CORRECTION_DIV_15},
274  {(15./16.), CORRECTION_DIV_15, CORRECTION_DIV_16},
275  {(16./17.), CORRECTION_DIV_16, CORRECTION_DIV_17},
276  {(17./18.), CORRECTION_DIV_17, CORRECTION_DIV_18},
277  {(31./32.), CORRECTION_DIV_31, CORRECTION_DIV_32},
278  {(1.0), CORRECTION_DIV_14, CORRECTION_DIV_14},
279  {(32./31.), CORRECTION_DIV_32, CORRECTION_DIV_31},
280  {(18./17.), CORRECTION_DIV_18, CORRECTION_DIV_17},
281  {(17./16.), CORRECTION_DIV_17, CORRECTION_DIV_16},
282  {(16./15.), CORRECTION_DIV_16, CORRECTION_DIV_15},
283  {(15./14.), CORRECTION_DIV_15, CORRECTION_DIV_14},
284  {(18./16.), CORRECTION_DIV_18, CORRECTION_DIV_16},
285  {(17./15.), CORRECTION_DIV_17, CORRECTION_DIV_15},
286  {(16./14.), CORRECTION_DIV_16, CORRECTION_DIV_14},
287  {(18./15.), CORRECTION_DIV_18, CORRECTION_DIV_15},
288  {(17./14.), CORRECTION_DIV_17, CORRECTION_DIV_14}
289 };/*Correction List*/
290 
291 /*---------------------
292  * Correction Factor Value Table:
293  *---------------------
294  * Translates correction factor field codes into the actual correction value term values
295  */
296 
297 static const CorrectionValStruct CorrectionValList [NUM_CORRECTION_VALS] = {
298  {16, 1}, {16, 1}, {18, 1}, {17, 1}, {31, 2}, {14, 1}, {32, 2}, {15, 1}
299 };/* CorrectionValList*/
300 
301 /**************************************************************************************************
302  * mrfSetEventClockSpeed () -- Determine the Desired Event Clock Speed and Frac Synth Control Word
303  *************************************************************************************************/
364 
365 epicsShareExtern epicsStatus mrfSetEventClockSpeed (
366  epicsFloat64 InputClockSpeed, /* Desired event clock speed in MHz (or zero) */
367  epicsUInt32 InputControlWord, /* Fractional synthesizer control word (or zero) */
368  epicsFloat64 ReferenceFreq, /* SY87739L input reference frequency (in MHz) */
369  epicsFloat64 *OutputClockSpeed, /* Resulting event clock speed in MHz */
370  epicsUInt32 *OutputControlWord, /* Resulting synthesizer control word */
371  epicsInt32 PrintFlag) /* Flag to control what we print in this routine */
372 {
373  /*---------------------
374  * Local Variables
375  */
376  epicsFloat64 ActualClockSpeed; /* Actual frequency generated by the control word */
377  epicsFloat64 Error = 0.0; /* Error (in ppm) between desired & actual freq. */
378 
379  /*---------------------
380  * Zero the output clock speed and control word parameters just in case there is an error.
381  */
382  *OutputClockSpeed = 0.0;
383  *OutputControlWord = 0;
384 
385  /*---------------------
386  * Compute the Fractional Synthesizer control word if one was not provided
387  */
388  if (0 == InputControlWord) {
389 
390  /*---------------------
391  * If a clock speed was not specified either, use the default clock speed.
392  */
393  if (0.0 == InputClockSpeed) {
394  InputClockSpeed = MRF_DEF_CLOCK_SPEED;
395  DEBUGPRINT (DP_WARN, PrintFlag,
396  (" *Warning: Event clock speed not specified, will default to %f MHz.\n",
398  }/*end if neither the control word nor the clock speed were specified*/
399 
400  /*---------------------
401  * Compute the control word for the desired clock speed.
402  * Abort on error.
403  */
404  InputControlWord = FracSynthControlWord (
405  InputClockSpeed,
406  ReferenceFreq,
407  PrintFlag,
408  &Error);
409 
410  if (0 == InputControlWord) {
412  (" *Error: Unable to compute fractional synthesizer control word for %f MHz.\n",
413  InputClockSpeed));
414  return ERROR;
415  }/*end if could not compute fractional synthesizer control word*/
416 
417  }/*end if control word not specified*/
418 
419  /*---------------------
420  * Analyze the control word for programming errors and get the actual frequency it will produce.
421  * Abort if there are any serious problems.
422  */
423  ActualClockSpeed = FracSynthAnalyze (InputControlWord, ReferenceFreq, PrintFlag);
424  if (0.0 == ActualClockSpeed) {
426  (" *Error: Fractional Synthesizer control word 0x%08x is invalid.\n",
427  InputControlWord));
428  return ERROR;
429  }/*end if control word was invalid*/
430 
431  /*---------------------
432  * If a clock speed was not specified, use the actual frequency generated by the control word.
433  */
434  if (0.0 == InputClockSpeed)
435  InputClockSpeed = ActualClockSpeed;
436 
437  /*---------------------
438  * Check to make sure the difference between the requested and the actual frequencies
439  * is within +/- 100 ppm.
440  */
441  if (0.0 == Error)
442  Error = 1.e6 * (ActualClockSpeed - InputClockSpeed) / InputClockSpeed;
443 
444  if (fabs(Error) >= 100.0) {
446  (" *Error: Requested frequency (%f) and actual frequency (%f) ",
447  InputClockSpeed, ActualClockSpeed));
449  ("differ by %6.2f ppm.\n Difference must be less than +/- 100 ppm.\n",
450  Error));
451  return ERROR;
452  }/*end if error is too large*/
453 
454  /*---------------------
455  * If we made it this far, everything checked out OK.
456  * Return the event clock speed and the fractional sythesizer control word.
457  */
458  *OutputClockSpeed = InputClockSpeed;
459  *OutputControlWord = InputControlWord;
460  return OK;
461 
462 }/*end mrfSetEventClockSpeed()*/
463 
464 /**************************************************************************************************
465  * FracSynthControlWord () -- Construct Control Word For SY87739L Fractional Synthesizer
466  **************************************************************************************************/
550 
551 
552 epicsShareExtern epicsUInt32 FracSynthControlWord (
553  epicsFloat64 DesiredFreq, /* Desired output frequency */
554  epicsFloat64 ReferenceFreq, /* SY87739L input reference frequency */
555  epicsInt32 debugFlag, /* Flag for debug/informational output */
556  epicsFloat64 *Error) /* Error value (in parts per million) */
557 {
558 
559  /*---------------------
560  * Local Variables
561  */
562  FracSynthComponents Best; /* Best overall parameters seen so far */
563  FracSynthComponents BestFracFreq; /* Best fractional frequency parameters seen so far */
564  epicsUInt32 ControlWord; /* Computed control word value */
565  epicsFloat64 CorrectionErr; /* Error value for current correction factor */
566  epicsFloat64 CorrectionFreq; /* Frequency after applying the correction factor */
567  epicsInt32 CorrectionIndex; /* Index to correction factor list */
568  epicsFloat64 d; /* FP representation of the current denominator */
569  epicsFloat64 EffectiveFreq = 0.0; /* Effective output frequency */
570  epicsFloat64 FracFreqErr; /* Best fractional frequency error seen so far */
571  epicsFloat64 FractionalFreq; /* Desired fractional frequency */
572  epicsFloat64 FreqErr; /* Error value for the desired frequency */
573  epicsInt32 i, j, k, n; /* Loop indicies */
574  epicsInt32 Numerator = 1; /* Best numerator found for current denominator */
575  epicsFloat64 OldCorrectionErr; /* Error value for previous correction factor */
576  epicsInt32 p; /* Integer part of fractional frequency */
577  epicsInt32 p1; /* Numerator of fractional frequency ratio */
578  epicsInt32 Qp; /* Number of "P" pulses in the frac. div. circuit */
579  epicsInt32 Qpm1; /* Number of "P-1" pulses in the frac. div. circuit */
580  epicsFloat64 TestFreq; /* Fractional frequency to try in correction loop */
581 
582  /*---------------------
583  * Initialize the "Best Fractional Frequency So Far" parameters
584  */
585  BestFracFreq.Error = MAX_ERROR; /* Deviation from desired output frequency */
586  BestFracFreq.EffectiveFreq = 0.0; /* Actual frequency generated by these components */
587  BestFracFreq.PostDivIndex = 0; /* Index into post-divider table */
588  BestFracFreq.CorrectionIndex = 0; /* Index into correction factor table */
589  BestFracFreq.P = 0; /* Integer part of fractional frequency */
590  BestFracFreq.Qp = 1; /* Value of Q(p) term of fractional frequency */
591  BestFracFreq.Qpm1 = 1; /* Value of Q(p-1) term of fractional frequency */
592 
593  Best = BestFracFreq; /* Best overall parameters */
594 
595  /*---------------------
596  * Post-Divider Loop:
597  *---------------------
598  * Check all post-divider values that would put the VCO frequency within
599  * the allowable range (540 - 729 MHz).
600  */
601  Best.Error = MAX_ERROR;
602  for (i=0; i < NUM_POST_DIVIDES; i++) {
603  epicsFloat64 PostDivide = PostDivideList[i].Divisor;
604 
605  /*---------------------
606  * Compute the VCO frequency and make sure it is within the allowable range.
607  */
608  epicsFloat64 VcoFreq = DesiredFreq * PostDivide;
609 
610  if (VcoFreq >= MAX_VCO_FREQ)
611  break;
612 
613  if (VcoFreq >= MIN_VCO_FREQ) {
614 
615  /*---------------------
616  * We have a VCO frequency inside the allowable range.
617  * Now compute the desired fractional frequency.
618  *
619  * The desired fractional frequency is derived by dividing the VCO frequency by the
620  * reference frequency. The actual fractional-N frequency is created by a fractional-N
621  * P/P-1 divider which attempts to represent the desired fractional frequency as a
622  * rational fraction (with a divisor less than 32) of the reference frequency.
623  *
624  * Note that the actual fractional-N frequency may not be exactly identical to the
625  * desired fractional frequency.
626  */
627  FractionalFreq = VcoFreq / ReferenceFreq;
628  BestFracFreq.Error = MAX_ERROR;
629 
630  /*---------------------
631  * Divisor Loop:
632  *---------------------
633  * Search for a divisor that will produce the closest fractional-N P/P-1 frequency to
634  * the desired fractional frequency. If we are lucky, we will find a number that
635  * exactly divides the desired fractional frequency and we can stop the search here.
636  * If not, we will have to try different numerator and correction factor combinations
637  * in order to find the closest fit.
638  */
639  for (j=1; j <= MAX_FRAC_DIVISOR; j++) {
640  d = j; /* Floating point representation of denominator */
641  CorrectionIndex = 0; /* No correction factor */
642 
643  /*---------------------
644  * Compute the integer and fractional parts of the fractional-N frequency
645  */
646  p1 = (epicsInt32)(FractionalFreq * j);
647  p = (p1 / j) + 1;
648  Qpm1 = j - (p1 % j);
649  Qp = j - Qpm1;
650 
651  /*---------------------
652  * Compute the actual frequency generated by the fractional-N P/P-1 divider
653  * for these values. Also compute the error between the actual fractional
654  * frequency and the desired fractional frequency.
655  */
656  EffectiveFreq = (double)p - ((double)Qpm1 / (double)(Qp + Qpm1));
657  FracFreqErr = fabs (FractionalFreq - EffectiveFreq);
658 
659  /*---------------------
660  * If the current divisor does not exactly divide the desired fractional frequency,
661  * search for a numerator and correction-factor pair that will come closest to
662  * generating the desired fractional frequency.
663  */
664  if (FracFreqErr >= ZERO_THRESHOLD) {
665  FracFreqErr = MAX_ERROR;
666 
667  /*---------------------
668  * Numerator Loop:
669  *---------------------
670  * Search for the numerator/correction-factor pair with the lowest error.
671  */
672  for (n = 1; n <= j; n++) {
673  TestFreq = (double)p - ((double)n / d);
674  OldCorrectionErr = MAX_ERROR;
675 
676  /*---------------------
677  * Correction Factor Loop:
678  *---------------------
679  * Search for the correction factor that produces the lowest error
680  * with the current numerator/denominator pair. Note that since the
681  * correction factor list is arranged in ascending order, we can
682  * stop the search as soon as the error value stops decreasing.
683  */
684  for (k = 1; k < NUM_CORRECTIONS; k++) {
685  CorrectionFreq = CorrectionList[k].Ratio * TestFreq;
686  CorrectionErr = fabs (FractionalFreq - CorrectionFreq);
687  if (CorrectionErr > OldCorrectionErr) break;
688  OldCorrectionErr = CorrectionErr;
689 
690  /*---------------------
691  * If we found a numerator/correction-factor pair with a lower error
692  * than the previous error for this denominator, replace the previous
693  * values.
694  */
695  if (CorrectionErr < FracFreqErr) {
696  EffectiveFreq = CorrectionFreq;
697  FracFreqErr = CorrectionErr;
698  Numerator = n;
699  CorrectionIndex = k;
700  }/*end if we found a better numerator/correction pair*/
701 
702  }/*end correction factor loop*/
703  }/*end numerator loop*/
704 
705  /*---------------------
706  * At this point, we now have the best numerator/correction-factor pair
707  * for the current denominator. Recompute the Q(p) and Q(p-1) values
708  * based on the new numerator value.
709  *
710  * It could happen that the numerator/denominator/correction set we found
711  * produces the desired fractional frequency exactly (FracFreqErr == 0).
712  * If this occurs, we will set FracFreqErr to the zero-threshold value so that
713  * the algorithm gives preference to solutions that do not require a correction
714  * factor.
715  */
716  Qpm1 = Numerator;
717  Qp = j - Numerator;
718  if (FracFreqErr < ZERO_THRESHOLD)
719  FracFreqErr = ZERO_THRESHOLD;
720 
721  }/*end denominator does not exactly divide the desired fractional frequency*/
722 
723  /*---------------------
724  * Store the parameters for the numerator/correction pair that produces the lowest
725  * fractional frequency error for this denominator. If it turns out that the
726  * denominator exactly divides the desired fractional frequency (FracFreqErr == 0),
727  * exit the denominator loop now.
728  */
729  if (FracFreqErr < BestFracFreq.Error) {
730  BestFracFreq.Error = FracFreqErr;
731  BestFracFreq.EffectiveFreq = EffectiveFreq;
732  BestFracFreq.P = p;
733  BestFracFreq.Qp = Qp;
734  BestFracFreq.Qpm1 = Qpm1;
735  BestFracFreq.CorrectionIndex = CorrectionIndex;
736  if (FracFreqErr < ZERO_THRESHOLD) break;
737  }/*end if fractional frequency is best so far*/
738 
739  }/*end denominator loop*/
740 
741  /*---------------------
742  * Adjust the fractional frequency error for the current post divider.
743  * If the adjusted frequency error is less than the current best, make
744  * this the current best. If the new parameters produce an exact match
745  * (FreqErr == 0), exit the post-divider loop now.
746  */
747  FreqErr = (BestFracFreq.Error * ReferenceFreq) / PostDivide;
748  if (FreqErr < Best.Error) {
749  Best = BestFracFreq;
750  Best.PostDivIndex = i;
751  Best.Error = FreqErr;
752  Best.EffectiveFreq = (BestFracFreq.EffectiveFreq * ReferenceFreq) / PostDivide;
753  if (FreqErr < ZERO_THRESHOLD) break;
754  }/*end if this post-divide solution is the best so far*/
755 
756  }/*end if VCO frequency is within range*/
757  }/*end for each post divider*/
758 
759  /*---------------------
760  * Abort if we could not come up with a set of parameters that would produce the
761  * desired frequency.
762  */
763  if (MAX_ERROR == Best.Error) return 0;
764 
765  /*---------------------
766  * Construct the control word from the parameters found in the search above.
767  */
768  CorrectionIndex = Best.CorrectionIndex;
769  ControlWord =
770  ((CorrectionList[CorrectionIndex].mDiv << CONTROL_MDIV_SHIFT) & CONTROL_MDIV_MASK) |
771  ((CorrectionList[CorrectionIndex].nDiv << CONTROL_NDIV_SHIFT) & CONTROL_NDIV_MASK) |
772  ((PostDivideList[Best.PostDivIndex].Code << CONTROL_POSTDIV_SHIFT) & CONTROL_POSTDIV_MASK) |
773  (((Best.P - 1) << CONTROL_P_SHIFT) & CONTROL_P_MASK) |
775  ((Best.Qp << CONTROL_QP_SHIFT) & CONTROL_QP_MASK);
776 
777  /*---------------------
778  * Compute the frequency that will actually be generated from these parameters.
779  */
780  EffectiveFreq = CorrectionList[CorrectionIndex].Ratio * ReferenceFreq *
781  ((double)Best.P - ((double)Best.Qpm1 /(double)(Best.Qp + Best.Qpm1))) /
782  PostDivideList[Best.PostDivIndex].Divisor;
783 
784  /*---------------------
785  * Return the error (in parts-per-million) between the desired and actual frequencies.
786  */
787  *Error = 1.e6 * (EffectiveFreq - DesiredFreq) / DesiredFreq;
788 
789  /*---------------------
790  * Output debug information about the results of this call
791  */
792  DEBUGPRINT (DP_INFO, debugFlag,
793  ("Desired Frequency = %f, Control Word = %08X\n", DesiredFreq, ControlWord));
794  DEBUGPRINT (DP_INFO, debugFlag,
795  ("Effective Frequency = %15.12f, Error = %5.3f ppm\n", EffectiveFreq, *Error));
796 
797  /*---------------------
798  * Return the computed control word.
799  */
800  return ControlWord;
801 
802 }/*end FracSynthControlWord()*/
803 
804 /**************************************************************************************************
805  * FracSynthAnalyze () -- Analyze an SY87739L Fractional Synthesizer Control Word
806  **************************************************************************************************/
842 
843 
844 epicsShareExtern epicsFloat64 FracSynthAnalyze (
845  epicsUInt32 ControlWord, /* Control word to analyze */
846  epicsFloat64 ReferenceFreq, /* SY87739L input reference frequency */
847  epicsInt32 PrintFlag) /* Flag to control what we print in this routine */
848 {
849 
850  /*---------------------
851  * Local Variables
852  */
853  epicsFloat64 CorrectionTerm;
854  epicsFloat64 EffectiveFreq = 0.0; /* Computed effective output frequency */
855  int Error = 0; /* Error flag from control word analysis */
856  epicsInt32 i; /* Local loop counter */
857  epicsUInt32 MDiv; /* Coded value of the correction term denominator */
858  epicsInt32 MDivClass; /* Class designator of the correction denominator */
859  epicsFloat64 MDivVal; /* Actual value of the correction term denominator*/
860  epicsUInt32 Mfg; /* MFG field extracted from the control word */
861  epicsUInt32 NDiv; /* Coded value of the correction term numerator */
862  epicsInt32 NDivClass; /* Class designator of the correction numerator */
863  epicsFloat64 NDivVal; /* Actual value of the correction term numerator */
864  epicsUInt32 P; /* Coded value of the frac freq integer component */
865  epicsFloat64 PVal; /* Actual value of the frac freq integer component*/
866  epicsUInt32 PostDivide; /* Coded value of the post-divider */
867  epicsFloat64 PostDivideVal; /* Actual value of the post-divider */
868  epicsUInt32 Preamble; /* Preamble field extracted from the control word */
869  epicsUInt32 Qp; /* Number of times to divide by P */
870  epicsUInt32 Qpm1; /* Number of times to divide by P-1 */
871  epicsFloat64 VcoFreq; /* Computed VCO frequency */
872 
873  /*---------------------
874  * Decompose the control word into its constituent elements
875  */
876  Preamble = (ControlWord & CONTROL_PREAMBLE_MASK) >> CONTROL_PREAMBLE_SHIFT;
877  Qp = (ControlWord & CONTROL_QP_MASK) >> CONTROL_QP_SHIFT;
878  Qpm1 = (ControlWord & CONTROL_QPM1_MASK) >> CONTROL_QPM1_SHIFT;
879  P = (ControlWord & CONTROL_P_MASK) >> CONTROL_P_SHIFT;
880  Mfg = (ControlWord & CONTROL_MFG_MASK) >> CONTROL_MFG_SHIFT;
881  PostDivide = (ControlWord & CONTROL_POSTDIV_MASK) >> CONTROL_POSTDIV_SHIFT;
882  NDiv = (ControlWord & CONTROL_NDIV_MASK) >> CONTROL_NDIV_SHIFT;
883  MDiv = (ControlWord & CONTROL_MDIV_MASK) >> CONTROL_MDIV_SHIFT;
884 
885  /*---------------------
886  * Translate the elements of the correction term fraction from their encoded values
887  * to their actual values.
888  */
889  MDivVal = (double)CorrectionValList[MDiv].Value;
890  NDivVal = (double)CorrectionValList[NDiv].Value;
891 
892  /*---------------------
893  * Obtain the class number for each of the elements of the correction term fraction.
894  */
895  MDivClass = CorrectionValList[MDiv].Class;
896  NDivClass = CorrectionValList[NDiv].Class;
897 
898  /*---------------------
899  * Translate the integer part (P) if the fractional frequency and the post-divider from
900  * their encoded values to their actual values.
901  */
902  PVal = (double)(P + MIN_P_VALUE);
903  PostDivideVal = (double)PostDivideValList[PostDivide];
904 
905  /**********************************************************************************************/
906  /* Check for fatal errors that would prevent us from completing the analysis */
907  /**********************************************************************************************/
908 
909  DEBUGPRINT (DP_DEBUG, PrintFlag,
910  ("Analysis of Control Word 0x%08X:\n", ControlWord));
911 
912  /*---------------------
913  * Check for fractional frequency denominator being zero.
914  */
915  if ((Qp + Qpm1) == 0) {
916  DEBUGPRINT (DP_FATAL, PrintFlag, (" *Error: Q(p) + Q(p-1) [%u + %u] is 0.\n", Qp, Qpm1));
917  Error = 1;
918  }/*end if fractional denominator is zero*/
919 
920  /*---------------------
921  * Abort if there are any fatal errors that would prevent us from computing the effective
922  * frequency.
923  */
924  if (Error) return 0.0;
925 
926  /**********************************************************************************************/
927  /* Analyze the control word */
928  /**********************************************************************************************/
929 
930  /*---------------------
931  * Compute the components of the effective output frequency generated by this control word.
932  */
933  CorrectionTerm = NDivVal / MDivVal;
934  VcoFreq = (PVal - ((double)Qpm1 / (double)(Qp + Qpm1))) * CorrectionTerm * ReferenceFreq;
935  EffectiveFreq = VcoFreq / PostDivideVal;
936 
937  /*---------------------
938  * Display the control word fields and how they combine to produce the effective output
939  * frequency.
940  */
941  Error = 0;
942  DEBUGPRINT (DP_DEBUG, PrintFlag,
943  (" P = %d, Q(p) = %u, Q(p-1) = %u, Post Divider = %d\n",
944  (int)PVal, Qp, Qpm1, (int)PostDivideVal));
945  DEBUGPRINT (DP_DEBUG, PrintFlag,
946  (" Correction Term (N/M) = %d/%d = %f, Reference Frequency = %3.1f MHz.\n",
947  (int)NDivVal, (int)MDivVal, CorrectionTerm, ReferenceFreq));
948  DEBUGPRINT (DP_DEBUG, PrintFlag,
949  (" VCO Frequency = %f MHz. Effective Frequency = %15.12f MHz.\n",
950  VcoFreq, EffectiveFreq));
951 
952  /**********************************************************************************************/
953  /* Check for error conditions */
954  /**********************************************************************************************/
955 
956  /*---------------------
957  * Check for preamble field not zero
958  */
959  if (Preamble != 0) {
960  Error = 1;
961  DEBUGPRINT (DP_ERROR, PrintFlag,
962  (" *Error: PREAMBLE field (bits %d-%d) is 0x%X.\n",
964  Preamble));
965  DEBUGPRINT (DP_ERROR, PrintFlag,
966  (" Should be 0x0.\n"));
967  }/*end if preamble field is not zero*/
968 
969  /*---------------------
970  * Check for MFG field not zero
971  */
972  if (Mfg != 0) {
973  Error = 1;
974  DEBUGPRINT (DP_ERROR, PrintFlag,
975  (" *Error: MFG field (bits %d-%d) is 0x%X.\n",
977  DEBUGPRINT (DP_ERROR, PrintFlag,
978  (" Should be 0x0.\n"));
979  }/*end if mfg field is not zero*/
980 
981  /*---------------------
982  * Check for fractional denominator too big.
983  * (one more than the maximum actually does seem to work, so we'll allow it to pass).
984  */
985  if ((Qp + Qpm1) > MAX_FRAC_DIVISOR) {
986  if ((Qp + Qpm1) > (MAX_FRAC_DIVISOR + 1))
987  Error = 1;
988  DEBUGPRINT (DP_ERROR, PrintFlag,
989  (" *Error: Q(p) + Q(p-1) [%u + %u] is %u.\n", Qp, Qpm1, (Qp+Qpm1)));
990  DEBUGPRINT (DP_ERROR, PrintFlag,
991  (" Sum should be less than or equal to %d.\n", MAX_FRAC_DIVISOR));
992  }/*end if fractional divisor is too large*/
993 
994  /*---------------------
995  * Check for correction term too big.
996  */
997  if ((NDivVal / MDivVal) > MAX_CORRECTION_RATIO) {
998  Error = 1;
999  DEBUGPRINT (DP_ERROR, PrintFlag,
1000  (" *Error: Correction Term Ratio = (N/M) = (%d/%d) = %f is too big.\n",
1001  (int)NDivVal, (int)MDivVal, (NDivVal/MDivVal)));
1002  DEBUGPRINT (DP_ERROR, PrintFlag,
1003  (" Should be less than (17/14) = %f\n", MAX_CORRECTION_RATIO));
1004  }/*end if correction term ratio is too big*/
1005 
1006  /*---------------------
1007  * Check for correction term numerator and denominator being from different classes.
1008  */
1009  if (NDivClass != MDivClass) {
1010  Error = 1;
1011  DEBUGPRINT (DP_ERROR, PrintFlag,
1012  (" *Error: Correction Term numerator (%d) is not compatible with",
1013  (int)NDivVal));
1014  DEBUGPRINT (DP_ERROR, PrintFlag,
1015  (" Correction Term Denominator (%d).\n", (int)MDivVal));
1016  DEBUGPRINT (DP_ERROR, PrintFlag,
1017  (" Valid numerator values are:"));
1018  for (i=1; i < NUM_CORRECTION_VALS; i++) {
1019  if (CorrectionValList[i].Class == MDivClass)
1020  DEBUGPRINT (DP_ERROR, PrintFlag, (" %d", CorrectionValList[i].Value));
1021  }/*end search for valid numerators*/
1022 
1023  DEBUGPRINT (DP_ERROR, PrintFlag, (".\n"));
1024  }/*end if correction term numerator/denominator pair is invalid*/
1025 
1026  /*---------------------
1027  * Check for VCO frequency out of range
1028  */
1029  if ((VcoFreq > MAX_VCO_FREQ) || (VcoFreq < MIN_VCO_FREQ)) {
1030  Error = 1;
1031  DEBUGPRINT (DP_ERROR, PrintFlag,
1032  (" *Error: VCO Frequency (%f MHz.) is outside the valid range.\n", VcoFreq));
1033  DEBUGPRINT (DP_ERROR, PrintFlag,
1034  (" Should be between %5.1f MHz. and %5.1f MHz.\n",
1036  }/*end if VCO Frequency is out of range*/
1037 
1038  /*---------------------
1039  * If the analysis did not uncover any serious errors, return the effective output frequency.
1040  */
1041  if (Error) return 0.0;
1042  else return EffectiveFreq;
1043 
1044 }/*end FracSynthAnalyze()*/
1045 
1046 /**************************************************************************************************/
1047 /* EPICS IOC Shell Registery */
1048 /* */
1049 
1050 
1051 /**************************************************************************************************/
1052 /* FracSynthControlWord() -- Construct a Fractional Synthesizer Control Word */
1053 /**************************************************************************************************/
1054 
1055 #ifndef HOST_BUILD
1056 
1057 static const iocshArg FracSynthControlWordArg0 = {"DesiredFreq", iocshArgDouble};
1058 static const iocshArg *const FracSynthControlWordArgs[1] = {&FracSynthControlWordArg0};
1059 static const iocshFuncDef FracSynthControlWordDef = {"FracSynthControlWord", 1,
1060  FracSynthControlWordArgs};
1061 
1062 static
1063 void FracSynthControlWordCall (const iocshArgBuf *args) {
1064  epicsFloat64 Dummy;
1065  FracSynthControlWord (args[0].dval, MRF_FRAC_SYNTH_REF, DP_DEBUG, &Dummy);
1066 }/*end FracSynthControlWordCall()*/
1067 
1068 
1069 /**************************************************************************************************/
1070 /* FracSynthAnalyze() -- Analyze a Fractional Synthesizer Control Word */
1071 /**************************************************************************************************/
1072 
1073 static const iocshArg FracSynthAnalyzeArg0 = {"ControlWord", iocshArgInt};
1074 static const iocshArg *const FracSynthAnalyzeArgs[1] = {&FracSynthAnalyzeArg0};
1075 static const iocshFuncDef FracSynthAnalyzeDef = {"FracSynthAnalyze", 1,
1076  FracSynthAnalyzeArgs};
1077 
1078 static
1079 void FracSynthAnalyzeCall (const iocshArgBuf *args) {
1080  FracSynthAnalyze (args[0].ival, MRF_FRAC_SYNTH_REF, DP_DEBUG);
1081 }/*end FracSynthAnalyzeCall()*/
1082 
1083 
1084 /**************************************************************************************************/
1085 /* EPICS Registrar Function for this Module */
1086 /**************************************************************************************************/
1087 
1088 static
1089 void FracSynthRegistrar () {
1090  iocshRegister (&FracSynthControlWordDef , FracSynthControlWordCall );
1091  iocshRegister (&FracSynthAnalyzeDef , FracSynthAnalyzeCall);
1092 }/*end FracSynthRegistrar()*/
1093 
1094 epicsExportRegistrar (FracSynthRegistrar);
1095 
1096 #endif
1097 
#define MIN_P_VALUE
Definition: mrfFracSynth.c:119
epicsShareExtern epicsUInt32 FracSynthControlWord(epicsFloat64 DesiredFreq, epicsFloat64 ReferenceFreq, epicsInt32 debugFlag, epicsFloat64 *Error)
Definition: mrfFracSynth.c:552
#define CORRECTION_DIV_15
Definition: mrfFracSynth.c:179
#define CONTROL_QP_SHIFT
Definition: mrfFracSynth.c:158
#define DEBUGPRINT(interest, globalFlag, args)
Definition: debugPrint.h:102
epicsUInt16 nDiv
Definition: mrfFracSynth.c:204
#define CONTROL_NDIV_SHIFT
Definition: mrfFracSynth.c:153
#define CORRECTION_DIV_16
Definition: mrfFracSynth.c:180
#define CONTROL_P_SHIFT
Definition: mrfFracSynth.c:156
epicsFloat64 Ratio
Definition: mrfFracSynth.c:203
#define CONTROL_QPM1_SHIFT
Definition: mrfFracSynth.c:157
#define MRF_DEF_CLOCK_SPEED
Definition: mrfCommon.h:100
#define NUM_CORRECTIONS
Definition: mrfFracSynth.c:124
epicsFloat64 Error
Definition: mrfFracSynth.c:220
epicsFloat64 EffectiveFreq
Definition: mrfFracSynth.c:221
epicsInt32 PostDivIndex
Definition: mrfFracSynth.c:222
#define CONTROL_MFG_BITS
Definition: mrfFracSynth.c:142
#define DP_INFO
Definition: debugPrint.h:92
Definition: evrdump.c:37
epicsFloat64 Divisor
Definition: mrfFracSynth.c:195
#define CONTROL_PREAMBLE_MASK
Definition: mrfFracSynth.c:172
#define CORRECTION_DIV_14
Definition: mrfFracSynth.c:178
#define CONTROL_POSTDIV_MASK
Definition: mrfFracSynth.c:167
#define CONTROL_PREAMBLE_BITS
Definition: mrfFracSynth.c:146
epicsShareExtern epicsStatus mrfSetEventClockSpeed(epicsFloat64 InputClockSpeed, epicsUInt32 InputControlWord, epicsFloat64 ReferenceFreq, epicsFloat64 *OutputClockSpeed, epicsUInt32 *OutputControlWord, epicsInt32 PrintFlag)
Definition: mrfFracSynth.c:365
#define OK
Definition: mrfFracSynth.h:81
#define NUM_POST_DIVIDE_VALS
Definition: mrfFracSynth.c:123
#define MAX_ERROR
Definition: mrfFracSynth.c:127
#define MAX_FRAC_DIVISOR
Definition: mrfFracSynth.c:120
epicsExportRegistrar(FracSynthRegistrar)
#define CONTROL_NDIV_MASK
Definition: mrfFracSynth.c:166
#define CONTROL_QP_MASK
Definition: mrfFracSynth.c:171
#define CONTROL_MDIV_MASK
Definition: mrfFracSynth.c:165
#define CONTROL_MDIV_SHIFT
Definition: mrfFracSynth.c:152
#define CORRECTION_DIV_32
Definition: mrfFracSynth.c:184
#define ERROR
Definition: mrfFracSynth.h:88
#define MIN_VCO_FREQ
Definition: mrfFracSynth.c:118
#define ZERO_THRESHOLD
Definition: mrfFracSynth.c:128
#define CONTROL_POSTDIV_SHIFT
Definition: mrfFracSynth.c:154
#define CONTROL_P_MASK
Definition: mrfFracSynth.c:169
#define MAX_CORRECTION_RATIO
Definition: mrfFracSynth.c:116
#define DP_ERROR
Definition: debugPrint.h:90
#define DP_FATAL
Definition: debugPrint.h:89
#define CORRECTION_DIV_18
Definition: mrfFracSynth.c:182
#define MAX_VCO_FREQ
Definition: mrfFracSynth.c:117
#define MRF_FRAC_SYNTH_REF
Definition: mrfCommon.h:99
epicsUInt32 Code
Definition: mrfFracSynth.c:196
#define CORRECTION_DIV_17
Definition: mrfFracSynth.c:181
#define NUM_POST_DIVIDES
Definition: mrfFracSynth.c:122
#define DP_WARN
Definition: debugPrint.h:91
#define CONTROL_MFG_MASK
Definition: mrfFracSynth.c:168
#define CONTROL_PREAMBLE_SHIFT
Definition: mrfFracSynth.c:159
#define CORRECTION_DIV_31
Definition: mrfFracSynth.c:183
epicsUInt16 mDiv
Definition: mrfFracSynth.c:205
epicsShareExtern epicsFloat64 FracSynthAnalyze(epicsUInt32 ControlWord, epicsFloat64 ReferenceFreq, epicsInt32 PrintFlag)
Definition: mrfFracSynth.c:844
Definition: mrfFracSynth.c:194
epicsInt32 CorrectionIndex
Definition: mrfFracSynth.c:223
#define CONTROL_MFG_SHIFT
Definition: mrfFracSynth.c:155
#define CONTROL_QPM1_MASK
Definition: mrfFracSynth.c:170
#define NUM_CORRECTION_VALS
Definition: mrfFracSynth.c:125
#define DP_DEBUG
Definition: debugPrint.h:93