Shop OBEX P1 Docs P2 Docs Learn Events
Any Spin2 code existing for determining PLL parameters? — Parallax Forums

Any Spin2 code existing for determining PLL parameters?

roglohrogloh Posts: 5,787
edited 2020-07-10 05:50 in Propeller 2
Does anyone have any working SPIN2 code that (optimally?) determines the divider parameters for the MMM...MM, PPPP and DDDDDD fields in the clock mode related to setting up the PLL?

I am considering modifying my video driver timing code such that it would allow systems with different crystals by just taking the desired final P2 frequency required for the desired resolution and then working out the necessary clock mode value automatically. Currently it just uses predefined clock mode constants assuming 20MHz crystals for example, and it won't work with other values unless you go manually patch this yourself which could get tedious. If I can't find code I might just keep it like that, but I vaguely thought there were some samples of code around the forums that did this - had a bit of a search but couldn't find it.
    CLK297MHz   = %1_010011_0100101000_1111_10_00 '(20MHz/20)*297/1 = 297   MHz
    CLK252MHz   = %1_000100_0000111110_1111_10_00 '(20MHz/5) * 63/1 = 252   MHz
    CLK250MHz   = %1_000011_0000110001_1111_10_00 '(20MHz/4) * 50/1 = 250   MHz
    CLK240MHz   = %1_000100_0000111011_1111_10_00 '(20MHz/5) * 60/1 = 240   MHz
    CLK220MHz   = %1_000001_0000010101_1111_10_00 '(20MHz/2) * 22/1 = 220   MHz
    CLK216MHz   = %1_000100_0000110101_1111_10_00 '(20MHz/5) * 54/1 = 216   MHz
    CLK200MHz   = %1_000100_0000110001_1111_10_00 '(20MHz/5) * 50/1 = 200   MHz
...

Comments

  • jmgjmg Posts: 15,173
    Did you want this to be design-time, compile-time, or run time calculations ?
    Usually for design-time, I use a calculator like consoleCalc and enter

    fVCO = 216
    fXTAL = 20
    then
    N=0;;N=N+1;fVCO/(fXTAL/N)
    hit <cr> 5 times or until a close to integer result appears
    ans = 10.8
    ans = 21.6
    ans = 32.4
    ans = 43.2
    ans = 54

    N = 5

    that one is simple, so let's try 26MHz
    fXTAL = 26

    N=0;; N=N+1;fVCO/(fXTAL/N)
    hit <cr> 13 times
    ans = 8.30769230769230769230769230769
    ans = 16.6153846153846153846153846154
    ans = 24.9230769230769230769230769231
    ans = 33.2307692307692307692307692308
    ans = 41.5384615384615384615384615385
    ans = 49.8461538461538461538461538462
    ans = 58.1538461538461538461538461538
    ans = 66.4615384615384615384615384615
    ans = 74.7692307692307692307692307692
    ans = 83.0769230769230769230769230769
    ans = 91.3846153846153846153846153846
    ans = 99.6923076923076923076923076923
    ans = 108
    N = 13
    An exact solution is possible for 2MHz PFD,

    or you can extract the error as ratio
    N=0;;N=N+1;ni=round(fVCO/(fXTAL/N));1-fVCO/(ni*fXTAL/N)
    hit <cr> 13 times
    ans = -0.0384615384615384615384615384615
    ans = 0.0226244343891402714932126696833
    ans = 0.00307692307692307692307692307692
    ans = -0.00699300699300699300699300699301
    ans = 0.010989010989010989010989010989
    ans = 0.00307692307692307692307692307692
    ans = -0.00265251989389920424403183023873
    ans = -0.00699300699300699300699300699301
    ans = 0.00307692307692307692307692307692
    ans = -9.2678405931417979610750695088e-4
    ans = -0.0042265426880811496196111580727
    ans = 0.00307692307692307692307692307692
    ans = 0

    fXTAL/13 is 0ppm error, but fXTAL/10 is 926ppm low, in some cases, that may be ok.
  • Thanks jmg, it's actually for runtime. I'm also attemping hacking something up for this myself...basic idea is provide crystalFreq, desiredFreq, ErrorFreqMax and compute clock mode. Here's the concept:

    I loop through P values, starting at 1, then use 2-30 by 2's.
    Then I loop through the D values from 1-64, computing the nearest M, and M+1 required to obtain the VCO freq, for this D, P and desiredFreq.
    Then I compute the resulting output frequency with these P,D,M values, check the error and retain the best values encountered so far. If the error ever hits 0 I exit with those values. I also skip when VCO<100MHz or VCO > 350MHz. I also probably should skip to the next value once the PLL input is less than 250-500kHz perhaps? I heard jitter becomes bad then...?

    At the end I check the error tolerance is within spec and then return the successful clock mode value or 0 = failure.
  • Reminder of an online calculator that gives the Best Rational Approximations to Real Numbers:
    https://forums.parallax.com/discussion/comment/1483060/#Comment_1483060
  • roglohrogloh Posts: 5,787
    edited 2020-07-10 10:24
    I've come up with this SPIN2 code (untested) . I hope it would work if I have it correct. There may possibly be some overflow cases I need to deal with and perhaps some optimisations to stop earlier.
    PUB computeClockMode(desiredHz, crystalHz, toleranceHz) : mode | vco, finput, f, p, d, m, error, bestError
        mode := 0
        bestError := -1
        repeat p from 0 to 30 step 2 
            ' compute the ideal VCO frequency at this value of P
            f := (p) ? desiredHz : p*desiredHz
            if f > 350000000
                quit
    
            repeat d from 1 to 64
                'compute PLL input frequency
                finput := crystalHz/d
                if finput < 250000 ' getting too low
                    quit
    
                ' determine closest M value needed for this VCO frequency and input frequency
                m := f / finput
    
                ' check for out of divider range
                if m +> 1024 
                    quit
    
                if m == 0  ' zero is special and gets a second chance
                    m++
    
                ' compute actual VCO frequency at this M, D
                vco := finput * m
                if vco +< 100000000 or vco +> 350000000
                    next
                
                ' compute the error and check next higher M value where possible
                error := abs(f - vco)
                if m < 1024
                    if error > abs(f - (vco + finput))
                        error := abs(f - (vco + finput))
                        m++
                
                ' keep track of best allowed frequency error case and divider bits yet
                if error +< bestError and error +< toleranceHz
                    bestError := error
                    mode := ((d-1) << 18) + ((m-1) << 8) + (((p/2 - 1) & $f) << 4)
    
                ' quit when perfect match found
                if error == 0
                    quit
    
            if error == 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) + (crystalHz < 16000000) ? %1111 : %1011
    
    

  • roglohrogloh Posts: 5,787
    edited 2020-07-10 15:05
    I tidied up the above and wrapped it inside a rudimentary test harness for Fastspin. I think the algorithm I use is working, based on some specific examples I hand verified and it will generate the clock mode value for the given inputs, with the P2 clock output frequency ranging from 10-350MHz.

    What would be the lowest input frequency we could still input to the P2 PLL that would be "reasonable" to still reliably scale up in the PLL for video use without jitter? I am assuming around 250kHz but perhaps this is too low. Is 500kHz a better value, or something else?

    This sample harness could be ported over to official SPIN2 if there was a nice serial object that could do tx/rx to replace SmartSerial, though that one is a bit nicer because it adds the printf capability. The clock mode computation method should port as is.
    CON
      BAUD = 230400 
      
      MINVCO = 100000000
      MAXVCO = 350000000
      MINPLLIN = 250000
    
      _clkmode  = %1_000100_0000110001_1111_10_00 '(20MHz/5) * 50/1 = 200   MHz
      _clkfreq = 200_000000
    
    OBJ
        ser : "../spin/SmartSerial"
    
    PUB testClock() | f, mode, d, m, p, fin, pll, tolerance
        clkset(_clkmode, _clkfreq) ' setup default startup clock
        ser.start(63,62,0,BAUD)
        waitcnt(cnt + 1*clkfreq)
        repeat
            ser.printf("Input crystal frequency (1_000_0000-50_000_000 Hz): ")
            fin := getdec()
            if fin < 1000000 or fin > 500000000
                next
            ser.printf("Input error tolerance (Hz): ")
            tolerance := getdec()
            
            ser.printf("Clock mode test with crystal frequency = %d Hz, tolerance = %d\n", fin, tolerance)
            repeat f from 10000000 to 350000000 step 250000
                mode := computeClockMode(f, fin , tolerance)
                d:=(mode >> 18) & $3f
                m:=(mode >> 8) & $3ff
                p:=(mode >> 4) & $f
                pll := fin/(d+1) * (m+1) 
                if p<15
                    pll := pll / ((p+1)*2)
               
                ser.printf("Requesting %d Hz - ", f)
                if mode <> 0
                    ser.printf("clkmode is $%x (d=%d, m=%d, p=%d), fout=%d Hz, error=%d Hz\n", mode, d, m, p, pll, abs(pll-f))
                else
                    ser.printf("failed to meet tolerance of %d Hz\n", tolerance)
    
        repeat
    
    PUB getdec() : num | ch
        num := 0
        repeat
            ch := ser.rx()
            if ch == 13
                ser.tx(13)
                ser.tx(10)
                return
            if ch < "0" or ch > "9"
                next
            ser.tx(ch)
            num := num * 10 + ch-"0"
            
    
    PUB computeClockMode(desiredHz, crystalHz, toleranceHz) : mode | vco, finput, f, p, d, m, error, bestError
        mode := 0
        bestError := -1
        repeat p from 0 to 30 step 2 
            ' compute the ideal VCO frequency at this value of P
            f := desiredHz 
            if p <> 0
                if f > MAXVCO/p
                    quit
                f := f * p
            if f > MAXVCO
                quit
            ' scan through D values
            repeat d from 1 to 64
                'compute PLL input frequency
                finput := crystalHz/d
                if finput < MINPLLIN ' getting too low
                    quit
    
                ' determine closest M value needed for this VCO frequency and input frequency
                m := f / finput
    
                ' check for out of divider range
                if m +> 1024 
                    quit
    
                if m == 0  ' zero is special and gets a second chance
                    m++
    
                ' compute actual VCO frequency at this M, D
                vco := finput * m
                if vco +< MINVCO 
                    quit
                if vco +> MAXVCO
                    next
                
                ' compute the error and check next higher M value where possible
                error := abs(f - vco)
                if m < 1024
                    if error > abs(f - (vco + finput))
                        error := abs(f - (vco + finput))
                        m++
                
                ' keep track of best allowed frequency error case and divider bits yet
                if error +< bestError and error +< toleranceHz+1
                    bestError := error
                    mode := ((d-1) << 18) + ((m-1) << 8) + (((p/2 - 1) & $f) << 4)
    
                ' quit when 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) + (crystalHz < 16000000) ? %1111 : %1011
    
    
    
  • roglohrogloh Posts: 5,787
    edited 2020-07-10 14:07
    Here's a snippet of the output it generates
    Input crystal frequency (1_000_0000-50_000_000 Hz): 20000000
    Input error tolerance (Hz): 40000
    Clock mode test with crystal frequency = 20000000 Hz, tolerance = 40000
    Requesting 10000000 Hz - clkmode is $0100044B (d=0, m=4, p=4), fout=10000000 Hz, error=0 Hz
    Requesting 10250000 Hz - clkmode is $014C7A5B (d=19, m=122, p=5), fout=10250000 Hz, error=0 Hz
    Requesting 10500000 Hz - clkmode is $01243E5B (d=9, m=62, p=5), fout=10500000 Hz, error=0 Hz
    Requesting 10750000 Hz - clkmode is $011C2A4B (d=7, m=42, p=4), fout=10750000 Hz, error=0 Hz
    Requesting 11000000 Hz - clkmode is $01040A4B (d=1, m=10, p=4), fout=11000000 Hz, error=0 Hz
    Requesting 11250000 Hz - clkmode is $011C2C4B (d=7, m=44, p=4), fout=11250000 Hz, error=0 Hz
    Requesting 11500000 Hz - clkmode is $010C164B (d=3, m=22, p=4), fout=11500000 Hz, error=0 Hz
    Requesting 11750000 Hz - clkmode is $011C2E4B (d=7, m=46, p=4), fout=11750000 Hz, error=0 Hz
    Requesting 12000000 Hz - clkmode is $0100054B (d=0, m=5, p=4), fout=12000000 Hz, error=0 Hz
    Requesting 12250000 Hz - clkmode is $011C304B (d=7, m=48, p=4), fout=12250000 Hz, error=0 Hz
    Requesting 12500000 Hz - clkmode is $0100043B (d=0, m=4, p=3), fout=12500000 Hz, error=0 Hz
    Requesting 12750000 Hz - clkmode is $011C324B (d=7, m=50, p=4), fout=12750000 Hz, error=0 Hz
    Requesting 13000000 Hz - clkmode is $01040C4B (d=1, m=12, p=4), fout=13000000 Hz, error=0 Hz
    Requesting 13250000 Hz - clkmode is $011C344B (d=7, m=52, p=4), fout=13250000 Hz, error=0 Hz
    Requesting 13500000 Hz - clkmode is $01101A3B (d=4, m=26, p=3), fout=13500000 Hz, error=0 Hz
    Requesting 13750000 Hz - clkmode is $01040A3B (d=1, m=10, p=3), fout=13750000 Hz, error=0 Hz
    Requesting 14000000 Hz - clkmode is $01101B3B (d=4, m=27, p=3), fout=14000000 Hz, error=0 Hz
    Requesting 14250000 Hz - clkmode is $0124383B (d=9, m=56, p=3), fout=14250000 Hz, error=0 Hz
    Requesting 14500000 Hz - clkmode is $01101C3B (d=4, m=28, p=3), fout=14500000 Hz, error=0 Hz
    Requesting 14750000 Hz - clkmode is $01243A3B (d=9, m=58, p=3), fout=14750000 Hz, error=0 Hz
    Requesting 15000000 Hz - clkmode is $0100053B (d=0, m=5, p=3), fout=15000000 Hz, error=0 Hz
    Requesting 15250000 Hz - clkmode is $01243C3B (d=9, m=60, p=3), fout=15250000 Hz, error=0 Hz
    Requesting 15500000 Hz - clkmode is $01101E3B (d=4, m=30, p=3), fout=15500000 Hz, error=0 Hz
    Requesting 15750000 Hz - clkmode is $01243E3B (d=9, m=62, p=3), fout=15750000 Hz, error=0 Hz
    Requesting 16000000 Hz - clkmode is $01101F3B (d=4, m=31, p=3), fout=16000000 Hz, error=0 Hz
    Requesting 16250000 Hz - clkmode is $01040C3B (d=1, m=12, p=3), fout=16250000 Hz, error=0 Hz
    Requesting 16500000 Hz - clkmode is $0110203B (d=4, m=32, p=3), fout=16500000 Hz, error=0 Hz
    Requesting 16750000 Hz - clkmode is $0124423B (d=9, m=66, p=3), fout=16750000 Hz, error=0 Hz
    Requesting 17000000 Hz - clkmode is $0110213B (d=4, m=33, p=3), fout=17000000 Hz, error=0 Hz
    Requesting 17250000 Hz - clkmode is $0124443B (d=9, m=68, p=3), fout=17250000 Hz, error=0 Hz
    Requesting 17500000 Hz - clkmode is $0100063B (d=0, m=6, p=3), fout=17500000 Hz, error=0 Hz
    Requesting 17750000 Hz - clkmode is $0124463B (d=9, m=70, p=3), fout=17750000 Hz, error=0 Hz
    Requesting 18000000 Hz - clkmode is $01101A2B (d=4, m=26, p=2), fout=18000000 Hz, error=0 Hz
    Requesting 18250000 Hz - clkmode is $019CDA2B (d=39, m=218, p=2), fout=18250000 Hz, error=0 Hz
    Requesting 18500000 Hz - clkmode is $014C6E2B (d=19, m=110, p=2), fout=18500000 Hz, error=0 Hz
    Requesting 18750000 Hz - clkmode is $011C2C2B (d=7, m=44, p=2), fout=18750000 Hz, error=0 Hz
    Requesting 19000000 Hz - clkmode is $0124382B (d=9, m=56, p=2), fout=19000000 Hz, error=0 Hz
    Requesting 19250000 Hz - clkmode is $019CE62B (d=39, m=230, p=2), fout=19250000 Hz, error=0 Hz
    Requesting 19500000 Hz - clkmode is $014C742B (d=19, m=116, p=2), fout=19500000 Hz, error=0 Hz
    Requesting 19750000 Hz - clkmode is $019CEC2B (d=39, m=236, p=2), fout=19750000 Hz, error=0 Hz
    Requesting 20000000 Hz - clkmode is $0100052B (d=0, m=5, p=2), fout=20000000 Hz, error=0 Hz
    Requesting 20250000 Hz - clkmode is $019CF22B (d=39, m=242, p=2), fout=20250000 Hz, error=0 Hz
    Requesting 20500000 Hz - clkmode is $014C7A2B (d=19, m=122, p=2), fout=20500000 Hz, error=0 Hz
    
Sign In or Register to comment.