|
| View previous topic :: View next topic |
| Author |
Message |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Wed Mar 24, 2010 6:20 am Post subject: problems with SampleRate greater than 273 |
|
|
In the Snap example, I'm having problems when I set the SampleRate (in Settings.ini) greater than 273. 273 (kHz) sampling works, 274 (kHz) doesn't.
While debugging this problem (and I still haven't got to the bottom of it), I found something surprising. In /usr/Innovative/Malibu/X3SDFDeviceMap_Mb.cpp starting at line 770 we have
| Code: |
int X3SDFDeviceMap::DecimationFactor(float rate)
{
if (rate==0)
return 1;
F1 = (rate>5.0e6) ? false : true; // Diable above 5 MHz
F3 = (rate>2.5e6) ? false : true; // Disable above 2.5 MHz
F2 = static_cast<int>(std::min(2.5e6f/std::min(rate, 2.5e6f), 32.f));
const int F1mul = F1 ? 4 : 1;
const int F3mul = F3 ? 2 : 1;
const int MaxF2 = static_cast<int>(40.0e6/(rate * F1mul * F3mul * ClkDiv));
F2 = std::min(F2, MaxF2);
const int F2_log = FloorLog2(F2);
const int F2_mul = 1 << F2_log;
int result = F1mul * F2_mul * F3mul * ClkDiv;
return result;
}
|
On entering this function, rate is equal to 274000. The function computes F1 and F3 are true, and therefore F1mul is 4 and F3mul is 2. F2 is 9. ClkDiv is also 2, so MaxF2 is 9 (40,000,000 / 274,000 * 4 * 2 * 2, cast as integer). F2_log is 3 (binary log of 9, rounded to integer) and F2_mul is 8. The result is therefore 128.
The reason this surprises me is that according to the AD7760 datasheet, the maximum decimation rate is 32; a factor of four smaller. So even changing ClkDiv from 2 to 1 won't fix it ....
I'm still digging around, but would welcome any clues. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Wed Mar 24, 2010 6:26 am Post subject: Re: problems with SampleRate greater than 273 |
|
|
| coldwell wrote: |
The reason this surprises me is that according to the AD7760 datasheet, the maximum decimation rate is 32; a factor of four smaller. |
I'm sorry, that's the maximum decimation rate for Filter 2, not overall. 128 is a valid rate. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Wed Mar 24, 2010 10:36 am Post subject: |
|
|
Here's some more information on the problem. To get samples at 274 kHz, the frequency needed to drive the AD7760 ADC is 274,000 * 128 = 35,072,000 Hz, where 128 is the decimation factor (which shows up as "Ad9511::FClockFactor" in the Malibu source).
In Ad9511Pfd::CalculateOutputDivisor, the code tries find an output multiplier from the set 32, 30, 28, 26, ..., 4, 2, 1 that will scale 35,072,000 into the range of the VCO, which is 100-140 MHz. A factor of 4 is too much and a factor of 2 is too little, so the code punts and sets the desired VCO output frequency to the lower bound of its range, 100 MHz.
There are two dividers on the AD9511 PLL called R and N. The PLL pulls the VCO frequency to FReference*N/R. I am using the on-board 24.576 MHz reference frequency. The code calculates values N = 996 and R = 245. With these values, the PLL will try to pull the VCO to 24.576 MHz * 996/245 = 99.9089632653 MHz, which is indeed close to 100 MHz, but nonetheless still below the VCO minimum operating frequency. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Wed Mar 24, 2010 11:10 am Post subject: |
|
|
| coldwell wrote: | | The code calculates values N = 996 and R = 245. With these values, the PLL will try to pull the VCO to 24.576 MHz * 996/245 = 99.9089632653 MHz, which is indeed close to 100 MHz, but nonetheless still below the VCO minimum operating frequency. |
Actually, this problem gets cleaned up farther down in Ad9511::DoSetFrequency where we have
| Code: |
// correct if too low
if(FFrequencyActual < (Vco->Min()/outdiv))
FFrequencyActual = Vco->Min()/outdiv;
|
However, what seems to be missing is that in the call chain
Ad9511::DoSetFrequency(double freq)
Ad9511::SetFrequency(double freq)
X3SDFClock::SetFrequency(double freq)
the frequency could be changed by Ad9511::DoSetFrequency, but because all parameters are passed by value, that information doesn't bubble back up to X3SDFClock::SetFrequency, which then goes on to call
Owner->ConfigureFilters(static_cast<float>(freq));
with the *old* value of freq (274,000), instead of the new one that was determined by the call to Ad9511::DoSetFrequency (100,000,000 / 128).
Sorry about the stream of consciousness here, but I think I'm closing in on the bug. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Wed Mar 24, 2010 1:15 pm Post subject: |
|
|
OK, my head is spinning, but here's where I am now.
In X3SDFClock::SetFrequency, we find
| Code: |
virtual void SetFrequency(double freq)
{
int factor = Owner->DecimationFactor(static_cast<float>(freq));
ClockFactor(factor);
X3SDFInfoIntf & info = dynamic_cast<X3SDFInfoIntf&>(Owner->Input().Info());
info.FClockFactor = factor;
Ad9511::SetFrequency(freq);
Owner->ConfigureFilters(static_cast<float>(freq));
}
|
I think, fundamentally, the problem is that the call to Ad9511::SetFrequency(freq) could change the frequency (e.g. by setting it to the VCO minimum), but that information is not passed on to Owner->ConfigureFilters, which gets the old frequency. Furthermore, the decimation factor depends on the frequency, so when it gets changed the whole thing has to start over.
In the AD7760, the decimation factor is the product of three numbers whose values can be
F1 = 1 or 4
F2 = 1, 2, 4, 8, 16 or 32
F3 = 1 or 2
Furthermore, the ICLK that clocks the sigma-delta modulator could be derived by dividing the MCLK input by 1 or 2. So, overall, the ratio between MCLK and the output data rate (ODR, i.e. SampleRate from Settings.ini) is
ClkDiv * F1 * F2 * F3
Here, X3SDFDeviceMap::ClkDiv is a constant data member set equal to 2 in the constructor.
The MCLK itself is derived by dividing the output of the AD9511 PLL VCO by an even integer between 2 and 32, or 1. Even integers are required to maintain a 50% duty cycle on the MCLK input.
outdiv = 1, 2, 4, 6, 8, ..., 30, 32
The value of this output divisor is calculated by Ad9511Pfd::CalculateOutputDivisor (not the Ad9511:: member of the same name). At this point, the code has already chosen a decimation factor in the AD7760, and so the goal of Ad9511Pfd::CalculateOutputDivisor is to find a divisor that, when multiplied by the MCLK frequency, will put the frequency into the range of the VCO (100 to 140 MHz).
Then, finally, values for the N and R divisors in the PLL are chosen so that the VCO frequency, when multiplied by R and then divided by N, will equal the reference clock frequency.
So, finally, the ratio between the reference clock (24.576 MHz if you use the on-board reference) and the ODR (SampleRate) is
outdiv * ClkDiv * F1 * F2 * F3 * R / N
I think. |
|
| Back to top |
|
 |
jhenderson Site Admin
Joined: 07 Mar 2006 Posts: 2254 Location: So. Cal. USA
|
Posted: Wed Mar 24, 2010 4:03 pm Post subject: |
|
|
I think you're right. If the call to Ad9511::SetFrequency(freq); clamps at the extrema, then the filters could be mis-configured.
If true, then the filters should be configured with:
| Code: | | Owner->ConfigureFilters(static_cast<float>(FrequencyActual())); |
You should be able to recompile the Pci_Mb library to try this. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Thu Mar 25, 2010 9:18 am Post subject: |
|
|
| jhenderson wrote: | I think you're right. If the call to Ad9511::SetFrequency(freq); clamps at the extrema, then the filters could be mis-configured.
If true, then the filters should be configured with:
| Code: | | Owner->ConfigureFilters(static_cast<float>(FrequencyActual())); |
You should be able to recompile the Pci_Mb library to try this. |
I did, but it still doesn't work. The problem is, the decimation factor is computed with the old frequency, so when you configure filters you are using the old decimation factor.
I think maybe the whole AD9511/AD7760 API might be too inflexible. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Thu Mar 25, 2010 9:26 am Post subject: |
|
|
| coldwell wrote: | | I think maybe the whole AD9511/AD7760 API might be too inflexible. |
It seems to me that the way to go, if you must work backwards from the AD7760 output data rate, is to build a list of every possible configuration of ClkDiv, F1, F2, F3 and the output multiplier in the AD9511. If I'm counting right,
ClkDiv = 1 or 2
F1 = 1 or 4
F2 = 1 2 4 8 16 or 32
F3 = 1 or 2
OutMult = 1 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 or 32
so that makes for 816 possible combinations, each one corresponds to a ratio between the VCO frequency and the sample rate. Now search that list for an optimal set of multipliers based on two criteria:
1. The VCO frequency must be in range
2. Use as much filtering as possible on the sigma-delta output
Then configure the N and R multipliers in the PLL based on the VCO frequency you find that way.
I'm working on some code to do this now. |
|
| Back to top |
|
 |
jhenderson Site Admin
Joined: 07 Mar 2006 Posts: 2254 Location: So. Cal. USA
|
Posted: Thu Mar 25, 2010 10:00 am Post subject: |
|
|
| I don't think that's necessary. The current algorithm derives a rational approximation of the multiplier and divider ratio, constrained to the bit resolution of the device. But, new approaches are welcomed. |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Fri Mar 26, 2010 6:46 am Post subject: |
|
|
| jhenderson wrote: | | I don't think that's necessary. |
On the contrary, it is.
For example, the existing Malibu code finds no tunable VCO frequency between the limits of 100 and 140 MHz that can give a sample rate 274 kHz. This is because it chooses fixed AD7760 filter settings (i.e. decimation) before it begins searching for the VCO divider. If the search algorithm were allowed to vary the decimation as well as the VCO divider, then it would find many possible configurations.
I wrote some prototype code (in Emacs Lisp, appended below) that does an exhaustive search. It finds no fewer than 55 valid configurations that produce a sample rate of 274 kHz with a VCO frequency between 100 and 140 MHz. For example,
VCO = 105,216,000 Hz
AD9511 divider = 24
MCLK = 4,384,000 Hz
ClkDiv = 2
ICLK = 2,192,000 Hz
F1 = 1
F2 = 4
F3 = 2 (overall decimation 8 )
SampleRate = 274,000 Hz
The next step is to find the optimal settings from the 55 possibilities. The criteria are
1. Lots of filtering in the AD7760
2. N and R values in the AD9511 PLL that get closest to the requested sample rate.
(not necessarily in that order). I'm working on that now.
I think the algorithm that searches for these configurations needs to have the flexibility to vary all of them. Otherwise, you create gaps in your frequency capabilities where there really are none.
| Code: |
;; Take a list of tuples and an element, return another list
;; with that element stitched on to each of the tuples:
;; e.g. (stitch '(1 2 3) 4)
;; => ((4 . 1) (4 . 2) (4 . 3))
(defun stitch (tuples element)
(mapcar (lambda (x) (cons element x)) tuples))
;; Flatten takes a list of lists and produces a single list
;; e.g. (flatten '((1 2) (3 4)))
;; => (1 2 3 4)
(defun flatten (x)
(apply 'append x))
;; right-associative fold, starting with value init.
;; e.g. (fold-right 'cons '() '(1 2 3))
;; => ((cons 1 (cons 2 (cons 3 '())))
(defun fold-right (func init list)
(if (null list)
init
(apply func (list (car list) (fold-right func init (cdr list))))))
;; cartesian takes two lists and returns their cartesian product
;; e.g. (cartesian '(1 2 3) '(4 5))
;; => ((1 . 4) (1 . 5) (2 . 4) (2 . 5) (3 . 4) (3 . 5))
(defun cartesian (l1 l2)
(flatten (mapcar (lambda (x) (stitch l2 x)) l1)))
;; cartesian-lists takes a list of lists and returns a single list
;; containing the cartesian product of all of the lists.
;; e.g (cartesian-lists '((1 2 3) (4 5)))
;; => ((1 4) (1 5) (2 4) (2 5) (3 4) (3 5))
(defun cartesian-lists (lists)
(fold-right 'cartesian '(()) lists))
;; cartesian-map takes a n-argument function and n lists
;; and returns a single list containing the result of calling that
;; n-argument function for each combination of elements in the list:
;; e.g. (cartesian-map list '(a b) '(c d e) '(f g))
;; => ((a c f) (a c g) (a d f) (a d g) (a e f) (a e g) (b c f)
;; (b c g) (b d f) (b d g) (b e f) (b e g))
(defun cartesian-map (func &rest lists)
(mapcar (lambda (x) (apply func x)) (cartesian-lists lists)))
;; Filter out list elements that don't match predicate
(defun filter (list predicate)
(let (value)
(dolist (elt list value)
(if (funcall predicate elt)
(setq value (append value (list elt)))))))
;; In the AD7760, there are three decimating FIR filters in series
;; after the modulator, each of which can be bypassed.
(setq f1-dividers '(1 4))
(setq f2-dividers '(1 2 4 8 16 32))
(setq f3-dividers '(1 2))
(setq all-ad7760-filter-dividers (list f1-dividers f2-dividers f3-dividers))
;; This returns a list of lists. The first element of each inner list
;; is the ICLK frequency that would be necessary to get the given
;; output data rate using the filter settings that are the remaining
;; elements of that inner list.
(defun possible-iclk-frequencies (output-data-rate)
(mapcar (lambda (x) (cons (* output-data-rate (apply '* x)) x))
(cartesian-lists all-ad7760-filter-dividers)))
;; Quoting the AD7760 datasheet, "The first filter receives data from
;; the modulator at a maximum frequency of 20 MHz ..." The return
;; value is a list of lists with the same format as
;; possible-iclk-frequencies, except that all inner lists that would
;; have an ICLK exceeding 20 MHz are filtered out.
(setq iclk-max 20000000)
(defun allowable-iclk-frequencies (output-data-rate)
(filter (possible-iclk-frequencies output-data-rate)
(lambda (x) (< (car x) iclk-max))))
;; Quoting the AD7760 datasheet, "There are two ways to generate the ICLK:
;;
;; ICLK = MCLK (CDIV* = 1)
;; ICLK = MCLK/2 (CDIV* = 0)"
;;
;; This function returns a list of lists. The first element of an
;; inner list is the MCLK frequency necessary to achieve the given
;; output data rate using the settings that are the remaining elements
;; of that inner list. The second element of an inner list is the
;; MCLK divider (1 or 2), the third element is the resulting ICLK
;; frequency, and the remaining three elements are the AD7760 filter
;; settings for F1, F2 and F3, respectively.
(defun possible-mclk-frequencies (output-data-rate)
(flatten
(mapcar (lambda (x) (list (cons (* 2 (car x)) (cons 2 x))
(cons (car x) (cons 1 x))))
(allowable-iclk-frequencies output-data-rate))))
;; The MCLK is wired to the output of a divider on the AD9511 whose
;; input is itself the PLL VCO output. So the list of all possible
;; VCO frequencies is the outer product of the list of all possible
;; MCLK frequencies and all possible VCO dividers. The list of all
;; possible VCO dividers is the set of even integers between 2 and 32,
;; or 1. The dividers must be even to ensure a 50% duty cycle on the
;; MCLK input of the AD7760.
(setq vco-dividers (cons 1 (number-sequence 2 32 2)))
(defun possible-vco-frequency (configuration multiplier)
(list (cons (* multiplier (car configuration)) (cons multiplier configuration))))
(defun possible-vco-frequencies (output-data-rate)
(flatten
(cartesian-map 'possible-vco-frequency (possible-mclk-frequencies output-data-rate) vco-dividers)))
(possible-vco-frequencies 273000)
;; These are the lower and upper bounds of the VCO tuning range
(setq vco-min 100000000.0)
(setq vco-max 140000000.0)
(defun vco-can-tune (x)
(and (<= x vco-max)
(>= x vco-min)))
;; Compute the list of VCO frequencies within the tunable range and
;; their corresponding dividers that will give mclk Hz after the
;; divider
(defun tunable-vco-frequencies (output-data-rate)
(filter (possible-vco-frequencies output-data-rate)
(lambda (x) (vco-can-tune (car x)))))
(length (tunable-vco-frequencies 274000))
| [/code] |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Fri Mar 26, 2010 12:54 pm Post subject: |
|
|
| coldwell wrote: |
VCO = 105,216,000 Hz
AD9511 divider = 24
MCLK = 4,384,000 Hz
ClkDiv = 2
ICLK = 2,192,000 Hz
F1 = 1
F2 = 4
F3 = 2 (overall decimation 8 )
SampleRate = 274,000 Hz
|
Actually, this VCO frequency is one you can hit exactly using the 24.576 MHz reference frequency. Choose N=137 and R=32 for the PLL dividers. Using the prescaler, B and A counters you can reach N=137 in a six different ways
P = 1, B = 137 (fixed divide)
P = 2, B = 68, A = 1 (dual modulus)
P = 4, B = 34, A = 1 (dual modulus)
P = 8, B = 17, A = 1 (dual modulus)
P = 16, B = 8, A = 9 (dual modulus)
P = 32, B = 4, A = 9 (dual modulus) |
|
| Back to top |
|
 |
coldwell
Joined: 03 Mar 2010 Posts: 38
|
Posted: Mon Mar 29, 2010 10:30 am Post subject: |
|
|
I cleaned up the Emacs Lisp code. The major changes are
1. Frequencies are now in units of kilohertz. To allow space for type tags, Emacs Lisp integers are only 29 bits wide and they sometimes overflow if we use units of Hz.
2. I'm holding the configurations in association lists instead of flat lists; that makes it much easier to see what each element signifies and to access the elements by name.
3. Configurations are sorted so those that use lots of AD7760 filtering come first.
4. It computes values for the PLL N and R counters, breaking the N counter down into its constituent prescaler, A and B counter values for both fixed-dived and dual-modulus modes. I haven't figured out a good way to rank those yet.
5. The output goes into a separate buffer named "output".
Note that all arithmetic is integer arithmetic ... there are no approximations involved. I tested every output data rate between 100 kHz and 1 MHz in 1 kHz steps, and didn't find any for which there were no valid configurations that gave exactly the right output frequency.
I'm posting this now because I don't think I will have time to port the algorithm to Malibu in the near future; our need is dire and immediate so we are going to just hard-code in the configuration we want. |
|
| Back to top |
|
 |
jhenderson Site Admin
Joined: 07 Mar 2006 Posts: 2254 Location: So. Cal. USA
|
Posted: Wed Apr 07, 2010 2:02 pm Post subject: |
|
|
In order to mitigate phase noise, the phase feedback detector frequency must match the actual filter components of the PLL circuitry on the board. The PFD frequency is set at the factory to 100 kHz. Consequently, the values of R in the calculations above is fixed:
R = static_cast<int>(FReference / PhaseDetectorFrequency());
N = static_cast<int>(outdiv * outputFreq * R/FReference);
with a reference of 24.576 MHz, this fixes R at 26. This additional constrain results in the inability to hit the exact frequency desired.
It is possible that the Ad9511Pfd class could be enhanced to improve the accuracy by analyzing a range of R values in close proximity with the PFD frequency, but operating outside of the electrical tolerances on the PFD circuitry and the potential degradation of phase noise represent corresponding risks. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|
© Copyright 2006-2012 Innovative Integration
Powered by phpBB © 2001, 2002 phpBB Group
Based on iCGstation v1.0 Template By Ray © 2003, 2004 iOptional
|
|
|