Shop OBEX P1 Docs P2 Docs Learn Events
P2 PLL/Clock considerations — Parallax Forums

P2 PLL/Clock considerations

The little picture above shows how I have understood the PLL/Clock generation in P2.
As one can see there are 2 Dividers and a PLL multiplier. This makes reaching the
desired clock possible using many combinations of the factors available.

  • Which limits apply to fa and fb?
  • Are there any constrains regarding divider and mulitplier factors?
  • Is it better to divide the clock down and multiply then again or is it better to not divide in the first place?
  • Are there any parts of the P2 that use fa or fb?
  • What is the intention of the final divider that makes fb to fcpu? It seems redundant to me

Thanks for your time

Comments

  • The main constraint is: Fb (PLL output) 100Mhz to 350MHz. A PLL synchronizes a variable frequency oscillator to a reference frequency. These oscillators have certain operating frequency range.

    The post divider is necessary if you want to run below 100MHz.

    Phase noise is reduced by keeping Fa (divider output) as high as possible. This is mostly a concern for radio transmitters. So don't divide down unnecessarily. The PLL was improved with rev B so it works well with lower reference frequency. With 20MHz input, any divider value should be ok for most applications.

  • pik33pik33 Posts: 2,350

    Here is a frequency table I generated some time ago to make setting the frequency/knowing the real frequency set: https://forums.parallax.com/discussion/173054/a-frequency-table-for-20-mhz-quartz-attached

    As these are frequencies from the upper range, divider B is considered 1.

  • This begs another question: is there any way to bypass the PLL/multiplier/divider and just let the P2 be driven at whatever freq appears on the XIN pin?

  • It has to be noted that PNut / Propeller Tool (and I think flexspin uses the same algorithm?) are very good at calculating the clock settings for you. The algorithm used is described here: https://forums.parallax.com/discussion/comment/1486815/#Comment_1486815

  • Thank you all very much!
    This information will help me

  • SuracSurac Posts: 176
    edited 2021-03-22 20:34

    @JRoark said:
    This begs another question: is there any way to bypass the PLL/multiplier/divider and just let the P2 be driven at whatever freq appears on the XIN pin?

    • if you program Divider A to 1, PLL Multiplier to 1 and Divider B to 1, you basicly use the Xtal frequency (as i understand the clock generator), but it will be very intresting how fast this can follow external clocks.
    • As i understand there is no real "external clock" mode put into P2.

    Most Xtal oscilators work like in the above diagram. So perhaps you can use a external clock on one of the xtal input pins.

  • evanhevanh Posts: 15,192
    edited 2021-03-22 21:05

    @JRoark said:
    This begs another question: is there any way to bypass the PLL/multiplier/divider and just let the P2 be driven at whatever freq appears on the XIN pin?

    Yep, there is a couple of options. Both supported in Spin2 and documented near end of the manual under the heading of "Clock Setup":
    Both involve not defining the _clkfreq constant.

    • First option is set just the constant _xtlfreq only. This means use the attached crystal's frequency as the clock source.
    • Second option is set just the constant _xinfreq only. This means use the externally supplied clock source attached at XI pin of the prop2.

    PS: The HUBSET mode bits of each mode are also provided in the same table.
    PPS: And I see the two shared hubRAM variables are still located way off at $40 and $44 rather than the agreed $10 through $1f. Chip, you might want to get that fixed.

  • PPS: And I see the two shared hubRAM variables are still located way off at $40 and $44 rather than the agreed $10 through $1f. Chip, you might want to get that fixed.

    yes i also found the mode and frequency in hubstart+$40 and $44.

  • pik33pik33 Posts: 2,350

    @Wuerfel_21 said:
    It has to be noted that PNut / Propeller Tool (and I think flexspin uses the same algorithm?) are very good at calculating the clock settings for you. The algorithm used is described here: https://forums.parallax.com/discussion/comment/1486815/#Comment_1486815

    Yes, but I (1) wanted to set the frequency on the fly while changing video modes and (2) know what I already set. So I made this table. Or rather a program which generated it for me.

  • SuracSurac Posts: 176
    edited 2021-03-22 21:27

    https://forums.parallax.com/discussion/comment/1486815/#Comment_1486815

    viewing the pseudocode provided here the considerations seem to be:

    • fa should be >=250 KHz (but using a 20MHz crystal and using Divider A maximum factor 64 already gives 312.5 KHz so we are always safe)
    • fb shoud be >=99Mhz and <= 201MHz (So all frequencies below 99MHz must be achieved using Divider B. Overclocking beyond 201 MHz makes it neccersary to violate the <=201MHz constraint)

    The code needs some tweaking to exit the loops if it found a good value combination or found.
    Best would be to run all loops and keep track of the best result

  • Cluso99Cluso99 Posts: 18,069

    And to add a warning for anyone reading this, when changing frequencies, there are constraints on how to do this. Evan worked out the method to do it reliably so I’ll let him point you to the reference.

  • evanhevanh Posts: 15,192
    edited 2021-03-22 22:59

    @Cluso99 said:
    And to add a warning for anyone reading this, when changing frequencies, there are constraints on how to do this. Evan worked out the method to do it reliably so I’ll let him point you to the reference.

    If you're using a high level language like Spin then it's handled for you. EDIT2: Spin2 provides the clkset() function.

    If you are hitting the metal and want to use Pasm code to adjust the clock frequency then have a read here - https://forums.parallax.com/discussion/comment/1466528/#Comment_1466528
    and related handover discussions - https://forums.parallax.com/discussion/comment/1466702/#Comment_1466702
    Also, the hardware doc details it as well. Chip originally recommended and documented only using the "reuse prior mode" method. Others have since added the $F0 mode as an alternative, myself included. Chip hasn't said don't do this as yet, but he's also never given it a blessing. The existence of $F0 in the hardware doc is likely due to others adding it.
    EDIT: Here is Chip's only comment on $F0 to date - https://forums.parallax.com/discussion/comment/1466494/#Comment_1466494
    BTW: I don't use $F0 in my wrapper code, nor does Chip with Spin2. I do use it for quick hacks without the wrapper.

  • cgraceycgracey Posts: 14,133
    edited 2021-03-23 00:35

    Here is the code in the compiler that determines the PLL setting for a requested frequency. You can read the comments to get an idea of its decision making:

    ;
    ;
    ; Calculate PLL setting
    ;
    ; on entry:     eax = input frequency in Hz
    ;               ebx = desired output frequency in Hz
    ;               ecx = max allowable error in Hz
    ;
    ; on exit:      eax = PLL mode with crystal bits cleared (eax[3:2]=0)
    ;               ebx = actual output frequency in Hz
    ;               c = 1 if setting found
    ;
    pll_calc:       mov     [@@xinfreq],eax
                    mov     [@@clkfreq],ebx
                    mov     [@@errfreq],ecx
    
                    mov     [@@found],0             ;clear the found flag in case no success
                    mov     [@@error],ecx           ;set initial error allowance
    
                    cmp     [@@xinfreq],250000      ;xinfreq must be 250KHz to 500MHz
                    jb      @@abort
                    cmp     [@@xinfreq],500000000
                    ja      @@abort
    
                    cmp     [@@clkfreq],3333333     ;clkfreq must be 3.333333MHz to 500MHz
                    jb      @@abort
                    cmp     [@@clkfreq],500000000
                    ja      @@abort
    
    
                    mov     [@@pppp],0              ;sweep post divider from 1,2,4,6,..30
    
    @@loop1:        mov     eax,[@@pppp]            ;determine post divider value
                    shl     eax,1
                    jnz     @@notzero
                    inc     eax
    @@notzero:      mov     [@@post],eax
    
                    mov     [@@divd],64             ;sweep xin divider from 64 to 1
    
    @@loop2:        mov     eax,[@@xinfreq]         ;fpfd = round(xinfreq / divd)
                    shl     eax,1                   ;x2 for later rounding
                    mov     edx,0                   ;xinfreq --> edx:eax
                    div     [@@divd]                ;divide edx:eax by divd
                    inc     eax                     ;round quotient
                    shr     eax,1
                    mov     [@@fpfd],eax
    
                    mov     eax,[@@post]            ;mult = round((post * divd) * clkfreq / xinfreq)
                    mul     [@@divd]                ;multiply post by divd --> eax
                    shl     eax,1                   ;x2 for later rounding
                    mul     [@@clkfreq]             ;multiply by clkfreq --> edx:eax
                    div     [@@xinfreq]             ;divide edx:eax by xinfreq
                    inc     eax                     ;round quotient
                    shr     eax,1
                    mov     [@@mult],eax
    
                    mov     eax,[@@xinfreq]         ;fvco = round(xinfreq * mult / divd)
                    shl     eax,1                   ;x2 for later rounding
                    mul     [@@mult]                ;multiply xinfreq by mult --> edx:eax
                    cmp     [@@divd],edx            ;if divd > edx then safe to divide
                    ja      @@safe
                    mov     eax,0                   ;else, fvco = 0
                    jmp     @@unsafe
    @@safe:         div     [@@divd]                ;divide edx:eax by divd
                    inc     eax                     ;round quotient
                    shr     eax,1
    @@unsafe:       mov     [@@fvco],eax
    
                    mov     eax,[@@fvco]            ;fout = round(fvco / post)
                    shl     eax,1                   ;x2 for later rounding
                    mov     edx,0                   ;fvco --> edx:eax
                    div     [@@post]                ;divide edx:eax by post
                    inc     eax                     ;round quotient
                    shr     eax,1
                    mov     [@@fout],eax
    
                    mov     eax,[@@fout]            ;abse = absolute(fout - clkfreq)
                    sub     eax,[@@clkfreq]
                    jnc     @@pos
                    neg     eax
    @@pos:          mov     [@@abse],eax
    
    
                    cmp     eax,[@@error]           ;does this setting have lower or same error?
                    ja      @@nope
    
                    cmp     [@@fpfd],250000         ;is fpfd at least 250KHz?
                    jb      @@nope
    
                    cmp     [@@mult],1024           ;is mult 1024 or less?
                    ja      @@nope
    
                    cmp     [@@fvco],99000000       ;is fvco at least 99MHz?
                    jb      @@nope
    
                    cmp     [@@fvco],201000000      ;is fvco no more than 201MHz?
                    jbe     @@yep
    
                    mov     eax,[@@clkfreq]         ;is fvco no more than clkfreq + errfreq?
                    add     eax,[@@errfreq]
                    cmp     [@@fvco],eax
                    ja      @@nope
    
    
    @@yep:          mov     [@@found],1             ;found the best setting so far, set flag
    
                    mov     eax,[@@abse]            ;update error to abse
                    mov     [@@error],eax
    
                    mov     eax,[@@divd]            ;set the divider field
                    dec     eax
                    shl     eax,18
                    mov     [@@mode],eax
    
                    mov     eax,[@@mult]            ;set the multiplier field
                    dec     eax
                    shl     eax,8
                    or      [@@mode],eax
    
                    mov     eax,[@@pppp]            ;set the post divider field
                    dec     eax
                    and     eax,1111b
                    shl     eax,4
                    or      [@@mode],eax
    
                    or      [@@mode],01000003h      ;set the pll-enable bit and select the pll
    
                    mov     eax,[@@fout]            ;save the pll frequency
                    mov     [@@freq],eax
    
    
    @@nope:         dec     [@@divd]                ;decrement divd and loop if not 0
                    jnz     @@loop2
    
                    inc     [@@pppp]                ;increment pppp and loop if under 16
                    cmp     [@@pppp],16
                    jb      @@loop1
    
                    mov     eax,[@@mode]            ;get mode into eax
                    mov     ebx,[@@freq]            ;get freq into ebx
    @@abort:        shr     [@@found],1             ;get found flag into c
                    ret
    
    
    ddx             @@xinfreq
    ddx             @@clkfreq
    ddx             @@errfreq
    ddx             @@found
    ddx             @@error
    ddx             @@abse
    ddx             @@pppp
    ddx             @@post
    ddx             @@divd
    ddx             @@fpfd
    ddx             @@mult
    ddx             @@fvco
    ddx             @@fout
    ddx             @@mode
    ddx             @@freq
    
  • For people who need some SPIN2 code to determine the clock mode for a given desired P2 operating frequency using the PLL here is the algorithm I use in my video driver to scan through and determine the clock mode setting to be used with HUBSET for minimizing the frequency error. You can customize the error tolerance, and if it can't meet the error tolerance a zero is returned so you know this requested frequency is outside the range of possible values:

    CON
    ' clock source used below
        #0, CLKSRC_XTAL, CLKSRC_XIN
    
    ' setup one of these based on your P2 HW input clock,
    ' this will only be used if the PLL settings get automatically computed (see code below)
        'CLKIN_HZ = _xtalfreq ' also only enable CLKSRC_XTAL below as CLKSRC
        'CLKIN_HZ = _xinfreq  ' also only enable CLKSRC_XIN below as CLKSRC
        CLKIN_HZ = 20000000 ' assume 20MHz crystal by default
    
        CLKSRC = CLKSRC_XTAL ' enable this for crystal clock source (default)
        'CLKSRC = CLKSRC_XIN ' enable this for direct input clock source on XI (no crystal)
    
    ' parameters used when automatically determining PLL settings
        TOLERANCE_HZ = 500000    ' pixel clock accuracy will be constrained by this when no exact ratios are found
        MAXVCO_HZ    = 350000000 ' for safety, but you could try to overclock even higher at your own risk
        MINVCO_HZ    = 100000000
        MINPLLIN_HZ  = 500000    ' setting lower can find more PLL ratios but may begin to introduce more PLL jitter
    
    DAT
    PUB computeClockMode(desiredHz) : mode | vco, finput, f, p, div, m, error, bestError
        bestError := -1
        repeat p from 0 to 30 step 2
            ' compute the ideal VCO frequency f at this value of P
            if p <> 0
                if desiredHz > MAXVCO_HZ/p ' test it like this to not overflow
                    quit
                f := desiredHz * p
            else
                f := desiredHz
                if f > MAXVCO_HZ
                    quit
            ' scan through D values, and find best M, retain best case
            repeat div from 1 to 64
                'compute the PLL input frequency from the crystal through the divider
                finput := CLKIN_HZ/div
                if finput < MINPLLIN_HZ ' input getting too low, and only gets lower so quit now
                    quit
    
                ' determine M value needed for this ideal VCO frequency and input frequency
                m := f / finput
    
                ' check for the out of divider range case
                if m +> 1024
                    quit
    
                ' zero is special and gets a second chance
                if m == 0
                    m++
    
                ' compute the actual VCO frequency at this particular M, D setting
                vco := finput * m
                if vco +< MINVCO_HZ
                    quit
                if vco +> MAXVCO_HZ
                    next
    
                ' compute the error and check next higher M value if possible, it may be closer
                error := abs(f - vco)
                if m < 1024 and (vco + finput) +< MAXVCO_HZ
                    if error > abs(f - (vco + finput))
                        error := abs(f - (vco + finput))
                        m++
    
                ' retain best allowed frequency error and divider bits found so far
                if error +< bestError and error +< TOLERANCE_HZ+1
                    bestError := error
                    mode := ((div-1) << 18) + ((m-1) << 8) + (((p/2 - 1) & $f) << 4)
    
                ' quit whenever perfect match found
                if bestError == 0
                    quit
    
            if bestError == 0
                quit
    
        ' final clock mode format is this #%0000_000E_DDDD_DDMM_MMMM_MMMM_PPPP_CCSS
        if mode
            ' also set 15 or 30pF capacitor loading based on input crystal frequency
            mode |= (1<<24) ' enable PLL
            if (CLKSRC == CLKSRC_XTAL) ' enable oscillator and caps for crystal
                mode |= (CLKIN_HZ < 16000000) ? %1111 : %1011
            else
                mode |= %0111 ' don't enable oscillator
    
    
  • SuracSurac Posts: 176
    edited 2021-03-23 09:21

    Here is a small Python 3.7 program that will construct the neccersary pasm code to initialy set the clock
    It will show up to 3 combinations and prefersts low preDivider and low Error values. Feel free to comment
    and change

    please change the upper most lines in the file to meet your needs! You need to input the desired
    values in the program file. The ist no gui.

    Edit python file extension is not allowed in forum so i post the .py file as a .txt file. please rename the file to *.py
    befor using it

Sign In or Register to comment.