LMK05318B: DLL PRIREF and SECREF early/late and frequency detection issues

Part Number: LMK05318B

Tool/software:

Hello,

I'm trying to configure the LMK05318B to operate with two alternative references, PRIREF is 10 MHz and SECREF is 125 MHz.
The GUI tool correctly determined that the TDC is 5 MHz, thus PRIREF shall be divided by R=2, SECREF shall be divied by R=25; the feedback divider is 2500 / 4 / 5 = 125.
If I supply on SECREF 125 MHz and disable every reference input qualification function, the DLL locks as expected, so configuration of dividers should be correct.

I'm trying to enable the reference input monitoring functions to detect when a valid reference is actually present.
The amplitude monitoring function seems to work, however it is not enough to determine if the supplied reference has the right frequency.
So I tried to enable alternately both the early / late function and the frequency detection function,testing the 125 MHz reference on SECREF: none worked.

The GUI tool computed early / late function registers PRIREF_MISSCLK_DIV=00008A and PRIREF_EARLY_CLK_DIV=000070 (for 10 MHz reference),
and SECREF_MISSCLK_DIV=00000D and SECREF_EARLY_CLK_DIV=000007 (for 125 MHz reference).

Given that there's no explanation in the documentation, I'm performing some retro-engineering to try to understand how the detection works:
from what I can understand, a counter operating at 625MHz (2500 MHz of VCO1 divided by 4) counts clock cycles between rising edges of reference. 
For PRIREF (5 MHz) the period count is 625 / 5 = 125 (0x7D), which is exactly in the middle of values 0x70 and 0x8A computed by the GUI tool. 
The values could be correct, but I have no 10 MHz reference to check this. 
For SECREF (125 MHz) the period count is 625 / 125 = 5 (0x05), which is outside the acceptable range computed by the GUI tool (0x07 - 0x0D).
If I modify the register values to from 0x07 / 0x0D to 0x03 / 0x07, the early / late detector pass, however given the high frequency employed and the lower values obtained, 
the measurement precision is very poor. Given that the values computed by the GUI are exactly the double of expected (and working) value, 
I suppose there could be some sort of multiplier, which could be used to double / multiply the internal clock derived by VCO1, used to count the time interval
between reference rising edges.
I already tried to change SECREF_WINDOW_DET_DBLR_EN to 1 (register 0xC9) with no effect.

I'm trying to let frequency detection work.
The GUI tool computed PRIREF_PPM_MIN =SECREF_PPM_MIN = 0x0064 (100) and PRIREF_PPM_MAX =SECREF_PPM_MAX = 0x006E (110).
PRIREF_PPMDIV =SECREF_PPMDIV = 16 (register 0xD8 value is 0xF).
PRIREF_CNTSTRT = 0x00001E85, PRIREF_HOLD_CNTSTRT = 0x000F4280, while SECREF_CNTSTRT = 0x00017D79, SECREF_HOLD_CNTSTRT = 0x000F4248.
With these numbers, SECREF 125 MHz reference always fails detection.
Given that there's no explanation in the documentation, I tried to understand how frequency detection works.
I suppose SECREF_HOLD_CNTSTRT can be used to determine a time interval, expressed as the number of cycles of internal clock divided by SECREF_PPMDIV.
Inside the observation time interval, the number of reference cycles is counted and divided by SECREF_CNTSTRT.
The result is then compared with the range defined by SECREF_PPM_MIN / SECREF_PPM_MAX.
The detection never works, no matter how I change the SECREF_PPM_MIN / SECREF_PPM_MAX values.


Could you please explain clearly how register values for early / late and frequency monitor shall be calculated?

Thanks

Andrea

  • Hi Andrea,

    I hear your feedback on needing more documentation. That is on our list to add to future datasheet revisions. 

    For now...

    The frequency detect should be configured in the GUI based on the XO + REF frequency error + some margin. This requires user update you want more stringent settings. For example, if your XO has an accuracy of +/- 50 ppm and your REF has an accuracy of +/- 5 ppm, then you should configure the freq detect lock ppm to at least 50+5+5 (as margin) = 60 ppm.

    In terms of the calculations for the freq detect, this is the code used in the TICS Pro Wizard:

    def compute_ppm_settings(self, x):                                
            """ Compute & return freq ref ppm register settings"""
    
            # in_freq = ppm_freq = ref_freq / ppm_div
            # t_meas = hold_cntstrt / hold_freq = 1 / (accuracy * 1e-6) * in_freq / hold_freq * hold_freq / in_freq / hold_freq = 1 / (accuracy * 1e-6 * hold_freq) 
            # hold_cntstrt = t_meas * hold_freq # Agreed
            # in_cntstrt = ref_cntstrt = hold_cntstrt * in_freq / hold_freq = t_meas * in_freq
            # lock_cnt = ref_ppm_min = 1 / (accuracy * 1e-6) * lock_ppm / accuracy = 
    
            temp_ref = ["PRI", "SEC"][x]
    
            gd['{}REF_PPMDIV'.format(temp_ref)].iValue = 3 ##THIS IS A REGISTER, div by 16
    
            lock_ppm = self.ref_ppm_valid[x]
            unlk_ppm = self.ref_ppm_invalid[x]
            avg_count = self.ref_avg_count[x]
            accuracy_ppm = self.ref_accuracy_ppm[x]
            in_freq = self.ref_freq[x] / 16    # ppm_freq = ref_freq / ppm_div 
            hold_freq = self.xo_freq * int(OSCIN_DBLR_EN.iValue + 1) 
    
            in_cntstrt, hold_cntstrt, lock_cnt, unlk_cnt, ppm_meas_time = self.REFPPM2CNT(lock_ppm,unlk_ppm,avg_count,accuracy_ppm,in_freq,hold_freq)
    
            gd['{}REF_CNTSTRT'.format(temp_ref)].iValue = in_cntstrt 	#THIS IS A REGISTER
            gd['{}REF_HOLD_CNTSTRT'.format(temp_ref)].iValue = hold_cntstrt #THIS IS A REGISTER
            gd['{}REF_PPM_MIN'.format(temp_ref)].iValue = lock_cnt		#THIS IS A REGISTER
            gd['{}REF_PPM_MAX'.format(temp_ref)].iValue = unlk_cnt		#THIS IS A REGISTER
            gd['s{}REF_PPM_TIMER'.format(temp_ref)].sValue = ee(ppm_meas_time, "s", 2) 
    
            return ppm_meas_time
    
        def REFPPM2CNT(self,lock_ppm,unlk_ppm,avg_count,accuracy_ppm,in_freq,hold_freq,counter_width=28,thresh_width=15):
            """
            this one trips when the XO input (hold_freq) counter hits zero
    
            Inputs:
            lock_ppm:       float   lock detect threshold
            unlk_ppm:       float   unlock detect threshold
            avg_count:      int     # averages
            accuracy_ppm:   float   lock detect required accuracy
            in_freq:        float   frequency lock target (Hz)
            hold_freq:        float   comparison frequency (Hz)   
    
            Returns:
            (in_cntstrt, hold_cntstrt, lock_cnt, unlk_cnt, lock_ppm, unlk_ppm, ppm_meas_time)
            """
            hold_cntstrt_tmp = Fraction("1e6") / accuracy_ppm * avg_count
            in_cntstrt = ceil(hold_cntstrt_tmp * in_freq / hold_freq)
            hold_cntstrt = round(in_cntstrt * hold_freq / in_freq)
            lock_cnt = round(avg_count * lock_ppm / accuracy_ppm)
            unlk_cnt = round(avg_count * unlk_ppm / accuracy_ppm)
            ppm_meas_time = hold_cntstrt / hold_freq
            
            return in_cntstrt, hold_cntstrt, lock_cnt, unlk_cnt, ppm_meas_time

    The early/late window detector settings are auto-set based on the reference input frequency used. In cases where the reference validation fails because of the early/late window detector, I recommend increasing the window size. It is possible the input is too noisy. 

    In terms of the calculations of the early and late, this is the code used in TICS Pro Wizard:

    def ref_early_late_det_update(self, x, bypass_warning = False):
    	self.early_clk_div_req_margin = 3
    	self.early_clk_div_min = 3
    	self.miss_clk_div_req_margin = 3
    
            temp_ref = ["PRI", "SEC"][x] # THIS IS THE REF FREQ
    
            if self.ref_freq[x]:
    
                vco_div_freq = self.vco1_freq / 4 
    
                # Set 1.25 GHz REF clock if required.
                high_freq = False
             
                for refNum in [0, 1]: #['PRI', 'SEC']:
                    #Need to enable if at least one reference requires the high freq REF, typically > 100MHz.
                    if (self.ref_freq[refNum] > 0) and \
                        (floor(vco_div_freq / self.ref_freq[refNum]) - self.early_clk_div_req_margin < self.early_clk_div_min):
                        WriteParameter("REF_CMOS_CLK_1p25G_EN", 1)   # THIS IS A REGISTER WRITE, enable high freq clock for higher freq REF                
                        vco_div_freq *= 2
                        break
                    else:
                        WriteParameter("REF_CMOS_CLK_1p25G_EN", 0) # THIS IS A REGISTER WRITE
               
                ## EARLY
                if gd["{}REF_EARLY_DET_EN".format(temp_ref)].iValue == 1: # THIS IS A REGISTER READ
    
                    if self.ref_freq[x] >= 2e3: # Early detector only supports down to 2kHz
                        
                        temp_early_cntr = floor(vco_div_freq / self.ref_freq[x]) - self.ref_early_margin[x] - self.early_clk_div_req_margin
                        if floor(vco_div_freq / self.ref_freq[x]) - self.early_clk_div_req_margin < self.early_clk_div_min: # If the actual clk div value is below the min, then disable the early detector
                            early_det_valid = False
                            max_ref_freq = vco_div_freq / (self.early_clk_div_min + self.early_clk_div_req_margin) # For BAW VCO, the max ref frequency is 2500e6 / 12
                            if not bypass_warning: UpdateStatusBar("{}REF frequency is too high. Maximum reference frequency for early detect = {}.".format(temp_ref, ee(max_ref_freq, "Hz", 3)))
                        elif temp_early_cntr < self.early_clk_div_min: # if reference frequency is low enough but the early detect margin counter is too high, then limit the counter                        
                            max_early_margin = floor(vco_div_freq / self.ref_freq[x]) - self.early_clk_div_min - self.early_clk_div_req_margin
                            if not bypass_warning: UpdateStatusBar("The early margin counter for {}REF is too high. Maximum value = {}.".format(temp_ref, max_early_margin))
                            if(max_early_margin==0):
                                early_det_valid = True
                                temp_early_cntr = max_early_margin
                                gd["s{}REF_EARLY_MARGIN".format(temp_ref)].iValue = temp_early_cntr # THIS IS A REGISTER WRITE
                                early_calc_s = 1 / self.ref_freq[x] - temp_early_cntr / vco_div_freq
                                gd["s{}REF_EARLY_calc".format(temp_ref)].sValue = ee(early_calc_s, "s", 2)
    
                                WriteParameter("{}REF_EARLY_CLK_DIV".format(temp_ref), temp_early_cntr) # THIS IS A REGISTER WRITE
                                UpdateStatusBar("The early margin counter for {}REF must not exceed {}.".format(temp_ref, int(max_early_margin)))
                            
                            else:
                                early_det_valid = False
                        elif temp_early_cntr > 2**22 - 1: # REF_EARLY_CLK_DIV has 22 bits. This should never occur because that requires ref_freq = 150 Hz.
                            early_det_valid = False
                            min_early_margin = floor(vco_div_freq / self.ref_freq[x]) - (2**22 - 1) - self.early_clk_div_req_margin
                            if not bypass_warning: UpdateStatusBar("The early margin counter for {}REF is too low. Minimum value = {}.".format(temp_ref, min_early_margin))
                        else:
                            early_det_valid = True
                            WriteParameter("{}REF_EARLY_CLK_DIV".format(temp_ref), temp_early_cntr)     # THIS IS A REGISTER WRITE                   
                            early_calc_s = 1 / self.ref_freq[x] - temp_early_cntr / vco_div_freq
                            gd["s{}REF_EARLY_calc".format(temp_ref)].sValue = ee(early_calc_s, "s", 2)
    
                    else:
                        early_det_valid = False
                        if not bypass_warning: UpdateStatusBar("Early Detector is not available for {}REF frequency below 2 kHz.".format(temp_ref))
    
                else:
                    early_det_valid = False
    
                ## MISSING (or LATE)
                if gd["{}REF_MISSCLK_EN".format(temp_ref)].iValue == 1: # THIS IS A REGISTER READ
    
                    if self.ref_freq[x] >= 2e3: # Late detector only supports down to 2kHz
    
                        temp_late_cntr = floor(vco_div_freq / self.ref_freq[x]) * (self.ref_late[x] + 1) + self.miss_clk_div_req_margin + self.ref_late_margin[x]
    
                        if temp_late_cntr > 2**22 - 1:
                            
                            late_det_valid = False
                            max_late_cntr = floor((2**22 - 1 - self.miss_clk_div_req_margin) / floor(vco_div_freq / self.ref_freq[x]) ) - 1
                            max_late_margin = (2**21 - 1) - (max_late_cntr + 1) * floor(vco_div_freq / self.ref_freq[x]) - self.miss_clk_div_req_margin
                            if not bypass_warning: UpdateStatusBar("The number of missing clocks and / or the late margin for {}REF is too high.\nLimit the number of missing clocks to {} and limit the late margin to {}.".format(temp_ref, max_late_cntr, max_late_margin)                   )
                        
                        else:
    
                            late_det_valid = True
                            gd['{}REF_MISSCLK_DIV'.format(temp_ref)].iValue = temp_late_cntr # THIS IS A REGISTER WRITE
                            late_calc_s = temp_late_cntr / vco_div_freq - 1 / self.ref_freq[x]
                            gd["s{}REF_LATE_calc".format(temp_ref)].sValue = ee(late_calc_s, "s", 2)
    
                    else:
    
                        late_det_valid = False
                        if not bypass_warning: UpdateStatusBar("Late / Missing Detector is not available for {}REF frequency below 2 kHz.".format(temp_ref))
    
                else:
                    late_det_valid = False
    
            else:
                early_det_valid = False
                late_det_valid = False
                if not bypass_warning: UpdateStatusBar("Invalid {}REF frequency. Early and late clock detector counters not computed.".format(temp_ref))
    
            if not early_det_valid:
                gd["s{}REF_EARLY_calc".format(temp_ref)].sValue = 'n/a'
                gd["{}REF_EARLY_DET_EN".format(temp_ref)].iValue = 0
    
            if not late_det_valid:
                gd["s{}REF_LATE_calc".format(temp_ref)].sValue = 'n/a'

    Another note:

    Can you try disabling all the detectors then steadily enable them one by one to see which cause the reference validation to fail? For example, what if you have the freq detector enabled but the early/late disabled, does the reference validate?

    Let me know if you have questions on the code snippet attached. I added comments where the register is written compared to a GUI variable (not tied to a register).

    Regards,

    Jennifer

    Regards,

    Jennifer

  • Hello Jennifer,
    it's nice to find you again on this channel!

    Thanks for the snippets, indeed I searched the files of the newer version of TICS Pro, and found the files as well.

    I tried to enable the detectors one by one as suggested.
    The frequency detector never works for 125MHz reference on SECREF.
    I can confirm that the early / late works, but only after a coefficient correction as explained.

    For frequency detection:
    I tried to repeat the calculations, and obtained the very same results.
    I think I understood how the logic engine works, using a counter clocked by XO (possibly doubled) to count with
    period SECREF_HOLD_CNTSTRT to determine a time interval, and another counter clocked by SECREF,
    divided by SECREF_PPMDIV factor, to count several periods of SECREF_CNTSTRT cycles.
    When the time has elapsed, current value of the second counter should be near 0 if there's no accuracy error. 
    In case the reference clock was slightly faster (+ppm error) the counter will have a positive value, proportional to the ppm error. 
    This could be compared with the SECREF_PPM_MIN and SECREF_PPM_MAX values to determine if the error
    is inside acceptable range or not.
    However, if the reference clock was slightly slower (-ppm error), the counter will have a value slightly lower than SECREF_CNTSTRT.
    In this case a direct comparison with thresholds will fail for sure, because the value is quite big, unless some internal logic was designed to consider
    the counter value as a signed integer value, and negate this value when the sign is negative.
    At the moment I have no way to adjust the 125MHz reference I supply on SECREF by desired ppm to try,
    nor I know if the frequency is slightly lower or higher than exact value. 
    That said, the frequency detector always fails, even if I configure an high ppm range, surely really higher than the sum of accuracy errors.

    For the early / late:
    indeed in the code an undocumented register is set if the counter values are too small: REF_CMOS_CLK_1p25G_EN
    Searched for a correspondence on the PDF, but it's not there.
    A close look at LMK05318B.ini file seems to point that this is bit5 of R68 (on PDF it's a reserved bit).
    I suspect however that this will affect both PRIREF and SECREF calculations: there is maybe a bug on the GUI tool,
    because in my case PRIREF is computed with bit cleared (normal situation), SECREF is computed with bit asserted.
    Please advise.

    Andrea

  • I finally managed to let it work!

    For frequency detection:
    I'm using the XO without doubler, so I had to recalculate everything for the new setting. 

    For the early / late:
    I modified the configuration writing 1 to REF_CMOS_CLK_1p25G_EN bit5 of R68. 
    Then recalculated early late coefficients considering 1250 MHz clock instead of 625 MHz.

    Thanks

    Andrea