P2-ES crystal inaccuracy? Bad code setting the clock?
DavidZemon
Posts: 2,973
Is anyone else seeing a relatively significant inaccuracy with the crystal oscillator? Or am I setting the clock incorrectly? The following code is blinking the LED at ~0.86 Hz (13 blinks in 15 seconds) and if I reset the board and run `58 blink` from TAQOZ, I get about 0.6 Hz (9 blinks in 15 seconds).
And the print statements come across the terminal as garbage (surely a baudrate mismatch - haven't scoped it yet to see what baudrate is coming across the wire).
Full code here: https://github.com/DavidZemon/HelloP2GCC/blob/2940554e72ccc921ff2599c95ac99e13cca51658/blinky.c
Full code here: https://github.com/DavidZemon/HelloP2GCC/blob/2940554e72ccc921ff2599c95ac99e13cca51658/common.c
And the print statements come across the terminal as garbage (surely a baudrate mismatch - haven't scoped it yet to see what baudrate is coming across the wire).
Full code here: https://github.com/DavidZemon/HelloP2GCC/blob/2940554e72ccc921ff2599c95ac99e13cca51658/blinky.c
#include "common.h" #include <propeller.h> #include <stdio.h> static const uint32_t XI = 20000000; static const uint32_t INPUT_DIVIDER = 4; static const uint32_t VCO_MULTIPLIER = 72; static const uint32_t FINAL_DIVIDER = 2; void main () { int i; const uint32_t CLOCK_FREQ = compute_clock(XI, INPUT_DIVIDER, VCO_MULTIPLIER, FINAL_DIVIDER); printf("Attempting to set clock for %d Hz\n", CLOCK_FREQ); waitx(CLOCK_FREQ); const errot_t err = set_clock_pll(INPUT_DIVIDER, VCO_MULTIPLIER, FINAL_DIVIDER); if (err) printf("Error! %d\n", err); else printf("Running at %d\n", CLOCK_FREQ); while (1) { drive_invert(58); waitx(CLOCK_FREQ / 2); } }
Full code here: https://github.com/DavidZemon/HelloP2GCC/blob/2940554e72ccc921ff2599c95ac99e13cca51658/common.c
#include "common.h" #include <propeller.h> void waitx (const uint32_t clockCycles) { __asm__ __volatile ("waitx %0" : : "r" (clockCycles)); } static errot_t set_clock_mode (const bool enablePll, uint32_t inputDivider, uint32_t vcoMultiplier, uint32_t finalDivider, const xi_status_t xiStatus, const clock_source_t clockSource) { __asm__ __volatile("hubset #0"); uint32_t configuration = 0; if (enablePll) { configuration = 1 << 24; } if (64 > inputDivider) { inputDivider &= 0b11111; configuration |= inputDivider << 18; } else { return INVALID_INPUT_DIVIDER; } if (1024 > vcoMultiplier) { vcoMultiplier &= 0b1111111111; configuration |= vcoMultiplier << 8; } else { return INVALID_VCO_MULTIPLIER; } if (30 < finalDivider || finalDivider % 2) return INVALID_FINAL_DIVIDER; else if (finalDivider) { finalDivider = (finalDivider >> 1) - 1; configuration |= finalDivider << 4; } else { configuration |= 0b1111 << 4; } configuration |= xiStatus << 2; // enable crystal+PLL, stay in 20MHz+ mode __asm__ __volatile("hubset %[_configuration]" : :[_configuration] "r"(configuration)); // wait ~10ms for crystal+PLL to stabilize waitx(200000); // now switch to PLL configuration |= clockSource; __asm__ __volatile("hubset %[_configuration]" : :[_configuration] "r"(configuration)); } errot_t set_clock_pll (uint32_t inputDivider, uint32_t vcoMultiplier, uint32_t finalDivider) { return set_clock_mode(true, inputDivider, vcoMultiplier, finalDivider, XI_15PF, CLK_SRC_PLL); } uint32_t compute_clock (const uint32_t xi, const uint32_t inputDivider, const uint32_t vcoMultiplier, const uint32_t finalDivider) { const uint32_t frequency = xi * vcoMultiplier / inputDivider; if (finalDivider) { return frequency / finalDivider; } else { return frequency; } }
Comments
I'm certainly not a C expert, but if vcoMultiplier was higher (if can go to 1023 I think), couldn't it overflow 32 bit boundaries when multiplied by 20 000 000?
forums.parallax.com/discussion/comment/1452025/#Comment_1452025
Also if you care about phase noise or spurs, it would be best to avoid input dividers 3-8. forums.parallax.com/discussion/comment/1459802/#Comment_1459802
However, using inputDivider=4 should not cause any issues visible on the serial port or with a blinking LED.
For the print examples, I set the baudrate in the loadp2 statement in your Makefile to 115200. And, I'm getting good text returned (chess, hello, malloctest, dry, etc...), but common.c doesn't return any text on execution (possibly my baud setting of 115200 in loadp2 is not helping in that example). I'm running on macOS, so my port ID was also changed:
dgately
There are also functions in EXTEND which allow you to specify various clock configuration parameters so you can experiment interactively. You can do so safely if you work from the initial RCFAST, type in a line or a function and make sure that the last word to execute is RCFAST. Something like this: so that P8 is outputting half the CPU clock which you can monitor with a scope. So that selects 15PF loading, XI divider of 4 = 5MHZ, 50 VCO multiplier (effectively) = 250MHZ, no PLL divide, then selects the external crystal as the CPU clock for 5 seconds before switching back to RCFAST and returning to the console.
Ah, I didn't realize that taqoz is using rcfast. Makes sense... It can't possibly know the crystal setting
Thank you! I missed the "-1" on those two completely. Makes sense of course. I don't understand the pppp calculation though... It doesn't seem to match up at all with what I read in the docs. I'll review the docs again later when I'm not on my phone
Here's what I changed: https://github.com/DavidZemon/HelloP2GCC/commit/0ce4d36f1eab78a7cdd44abbdfe364b2af97086c
Depends just how accurate you chase... in the middle setting, initial Xtal error should be under 10ppm as below..
FYI, here is the pull range of the CAP changes, 20MHz xtal only, no > 100MHz heating (those numbers move ~ -15ppm as Xtal warms from faster P2
I'm not sure how safe it is to change cap settings on the fly, be interesting to see if it is tolerated ?
Given the P2 fails to start if not enough time is allowed in RC-> XTAL, (no PLL) I'd say it is sensitive to noise on the Xtal pins, which would make runtime CL changes risky.
For higher precision levels, you will need a TCXO or VCTCXO - GPS ones are cheap.
I don't actually care about the accuracy of the crystal or the PLL or anything - i assumed it was plenty accurate enough to correctly read "50.00ms" for T/2 of a wave and therefore assumed that the 0.01 ms difference was due to use of waitx instead of waitct1. Fixing my code to be as accurate as possible is of interest.
Makes sense.
The smart pins have a mode of 'for X whole periods of a pin, measure SysCLKS' that it would be nice to see some working code for.
If you set X there to 10, you could capture sysclks per period to 0.1 sysclks average precision. ie confirm any SW loop timing, right down to 1 tick.
Do you mean the old code, or the new code?
I have blinky.c running and I can't duplicate the problem of needing 16/144/1. 2/16/2 and 2/32/4 were fine. The main issue I saw was that printf assumes an 80MHz clock. If it deviates, the data is not received properly. The printf before clock setting doesn't work for this reason.
The initial 1 second delay operates at RCFAST, but uses timing for the PLL frequency. So it could be 4-10 seconds. That long delay made me think it wasn't working.
A possible hunch on the 16/144/1: 20e6 * 144 = 2.88e9 > 2^31. It would overflow a signed int32. Although I think it would make that case fail, instead of all others.
I may have typo'd something in my earlier post, or mis-remembered, or idk what. But it does work for 16/144/1. It does not work for 16/288/2, which makes sense now that you point out it is overflowing my 32 bit (unsigned) variable. I tried expanding to 64 bits, but loadp2 hangs (I think I might have seen other people saying the same thing over in the p2gcc forum, regarding "large" binaries?). I won't concern myself with that for the moment... i'm happy that my code seems to functional now, with the exception of the overflow. And thank you again for pointing out that the serial routine is expecting 80 MHz... makes sense doesn't it, since it's all based on P1? Anyway, I'm comfortable now that the logic is correct in my latest version of the code and will stick with simply 1/4/1 so that I have working serial output (until I get the smart pins working, that is).
Now I can move forward with other hardware exploration
That may be because the VCO is getting marginal ?
/18 *288 is asking for 360MHz vco, which I think is more into a grey zone that may need active cooling.
/2*16 is asking for 320MHz, just a little bit easier ?
I know this won't directly apply for you yet, but here's my current smartpin comport config preset: Note I've had to accommodate 32-bit signed integer maths of the assembler. Only just now readjusted it for >255 MHz sysclock rates. The baud_rate / 16 will impact precision of slower baud rates that don't cleanly divide by 16.
BTW: I just used P2ES sysclock config of 16/288/2 (180 MHz) with diag reporting on this very smartpin preset without issue.
This definitely helps - thank you. I'll come back to it after I replace my bit-banged blinking LED with a smartpin.