raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.25k stars 838 forks source link

vcocalc.py gives results that set_sys_clock_khz rejects #1665

Open warcow105 opened 3 months ago

warcow105 commented 3 months ago

Don't know if this is an issue or if I am just overlooking something. I am setting custom clocks in CMakeLists because I am using a non-standard crystal. Everything seemed to be working except when I would try to switch frequencies during operation(for runtime selectable speed boost). After set_sys_clock_pll I would lose printf, even if I set the clock before initial stdio_init_all.

Then I decided to take a step back and let set_sys_clock_khz change the frequencies for me but that failed too. The settings I was supplying to set_sys_clock_khz kept getting rejected, but they were calculated by vcocalc. I took a quick look at the code and check_sys_clock_khz does a check "!(vco_khz % (postdiv1 * postdiv2))" which is where my values were failing. For now I just put a similar check in vcocalc to block those results from being given. Now with the newly calculated values I can change frequencies and everything works. Am I missing something or is this a bug?

peterharperuk commented 3 months ago

I would guess it's a bug in the script

kilograham commented 3 months ago

what settings was it giving you that failed?

warcow105 commented 3 months ago

what settings was it giving you that failed? @kilograham

Here is one that fails the check that I got using ./vcocalc.py -i 24.576 140 #140mhz #SYS_CLK_KHZ=139947 #PLL_COMMON_REFDIV=4 #PLL_SYS_VCO_FREQ_KHZ=1259520 #PLL_SYS_POSTDIV1=3 #PLL_SYS_POSTDIV2=3

Here is one from my corrected version of vcocalc, it also locks refdiv to 4 #140mhz #SYS_CLK_KHZ=139776 #PLL_COMMON_REFDIV=4 #PLL_SYS_VCO_FREQ_KHZ=1118208 #PLL_SYS_POSTDIV1=4 #PLL_SYS_POSTDIV2=2

The top one fails the !(vco_khz % (postdiv1 * postdiv2)) check in check_sys_clock_khz.

Michael

unixb0y commented 2 months ago

I noticed the same. You can use this script running on the Pico itself which gives actually accurate values:

    uint32_t freq_khz = 360000;
    uint vco_freq_out = 0;
    uint post_div1_out = 0;
    uint post_div2_out = 0;
    for (uint32_t i = 0; i<140000; i+=5000) {
        uint32_t freq_check = freq_khz + i;
        printf("Checking frequency: %d\n", freq_check);
        bool res = check_sys_clock_khz(freq_check, &vco_freq_out, &post_div1_out, &post_div2_out);
        printf("Result: success = %d, values: %d, %d, %d\n\n", res, vco_freq_out, post_div1_out, post_div2_out);
        vco_freq_out = 0;
        post_div1_out = 0;
        post_div2_out = 0;
        sleep_ms(100);
    }

For instance, vcocalc.py says that 370MHz works, while it does in fact not:

python3 vcocalc.py -i 12 370
Requested: 370.0 MHz
Achieved: 370.0 MHz
REFDIV: 2
FBDIV: 185 (VCO = 1110.0 MHz)
PD1: 3
PD2: 1

Output from Pico:

Checking frequency: 360000
Result: success = 1, values: 1440000000, 4, 1

Checking frequency: 365000
Result: success = 0, values: 0, 0, 0

Checking frequency: 370000
Result: success = 0, values: 0, 0, 0

Checking frequency: 375000
Result: success = 1, values: 1500000000, 4, 1

Checking frequency: 380000
Result: success = 1, values: 1140000000, 3, 1

Checking frequency: 385000
Result: success = 0, values: 0, 0, 0

Checking frequency: 390000
Result: success = 1, values: 1560000000, 4, 1

Checking frequency: 395000
Result: success = 0, values: 0, 0, 0

Checking frequency: 400000
Result: success = 1, values: 1200000000, 3, 1
warcow105 commented 2 months ago

Here is my edited vcocalc if it helps anyone. It includes the refdiv locking ability. ps. my python isn't the best so suggest changes as you see fit.

#!/usr/bin/env python3

import argparse

# Fixed hardware parameters
fbdiv_range = range(16, 320 + 1)
postdiv_range = range(1, 7 + 1)
ref_min = 5
refdiv_min = 1
refdiv_max = 63

def validRefdiv(string):
    if ((int(string) < refdiv_min) or (int(string) > refdiv_max)):
        raise ValueError("Number must be in the range of " + str(refdiv_min) + " to " + str(refdiv_max))
    return string

parser = argparse.ArgumentParser(description="PLL parameter calculator")
parser.add_argument("--input", "-i", default=12, help="Input (reference) frequency. Default 12 MHz", type=float)
parser.add_argument("--ref-min", default=5, help="Override minimum reference frequency. Default 5 MHz", type=float)
parser.add_argument("--vco-max", default=1600, help="Override maximum VCO frequency. Default 1600 MHz", type=float)
parser.add_argument("--vco-min", default=750, help="Override minimum VCO frequency. Default 750 MHz", type=float)
parser.add_argument("--lock-refdiv", help="Lock REFDIV to specified number in the range of " + str(refdiv_min) + " to " + str(refdiv_max), type=validRefdiv)
parser.add_argument("--low-vco", "-l", action="store_true", help="Use a lower VCO frequency when possible. This reduces power consumption, at the cost of increased jitter")
parser.add_argument("output", help="Output frequency in MHz.", type=float)
args = parser.parse_args()

refdiv_range = range(refdiv_min, max(refdiv_min, min(refdiv_max, int(args.input / args.ref_min))) + 1)
if args.lock_refdiv:
    print("LOCKING REFDIV TO " + str(args.lock_refdiv))
    refdiv_range = {int(args.lock_refdiv)}

best = (0, 0, 0, 0, 0)
best_margin = args.output

for refdiv in refdiv_range:
    for fbdiv in (fbdiv_range if args.low_vco else reversed(fbdiv_range)):
        vco = args.input / refdiv * fbdiv
        if vco < args.vco_min or vco > args.vco_max:
            continue
        # pd1 is inner loop so that we prefer higher ratios of pd1:pd2
        for pd2 in postdiv_range:
            for pd1 in postdiv_range:
                out = vco / pd1 / pd2
                margin = abs(out - args.output)
                if ((vco * 1000) % (pd1 * pd2)):
                    pass
                    #print("skipped")
                else:
                    if margin < best_margin:
                        best = (out, fbdiv, pd1, pd2, refdiv)
                        best_margin = margin

if best[0] > 0:
    print("Requested: {} MHz".format(args.output))
    print("Achieved: {} MHz".format(best[0]))
    print("REFDIV: {}".format(best[4]))
    print("FBDIV: {} (VCO = {} MHz)".format(best[1], args.input / best[4] * best[1]))
    print("PD1: {}".format(best[2]))
    print("PD2: {}".format(best[3]))
else:
    print("UNABLE TO COME UP WITH A SOLUTION....")

Michael