Dynamic system clock setting and comport baud setting
evanh
Posts: 16,032
in Propeller 2
Here's a semi-general routine for changing the system clock frequency. Only the XMUL component is a variable but all constants are still obeyed. Also adjusts the comport clock divider at the same time. It's from my library-like working code that I've been tweaking lots.
I'm partly wondering if anyone is interested and am open to input on redesign.
I'm partly wondering if anyone is interested and am open to input on redesign.
'=============================================== 'Set new sysclock frequency from a dynamic XMUL(xtalmul) 'and also adjusts the diag comport to suit ' Note: Uses the CORDIC. This means that any already running operations will be lost ' input: xtalmul, asyn_baud ' result: clk_mode, clk_freq 'scratch: pa, pb, temp1 setclkfrq 'recalculate sysclock hertz using cordic qmul xtalmul, ##(XTALFREQ / XDIV / XDIVP) 'integer component of pre-divided crystal frequency mov pa, xtalmul mul pa, #round((float(XDIV*XDIVP)+0.5) * (float(XTALFREQ)/float(XDIV)/float(XDIVP) - float(XTALFREQ/XDIV/XDIVP))) qdiv pa, #(XDIV * XDIVP) 'fractional component of pre-divided crystal frequency getqx clk_freq 'result of integer component getqx pa 'result of fractional component add clk_freq, pa 'de-error the integer rounding { 'this section disabled, more compact alternative below 'recalculate baud divider (clk_freq / asyn_baud) of diag comport using cordic ' low bauds won't operate at high sysclocks, the divider only has 16-bit reach 'apply max of *64 to copy of clk_freq to achieve 16.6 format encod temp1, clk_freq 'bit position of msb subr temp1, #31 'distance from bit position 31 fle temp1, #6 'cap at distance 6 mov pa, clk_freq shl pa, temp1 'shift significant bits up to bit position 31 'apply remaining of /64 to copy of asyn_baud subr temp1, #6 'remaining distance mov pb, asyn_baud shr pb, temp1 'shift down to make up distance 6 if needed, truncates baud 'comport divider (clk_freq / asyn_baud) in 16.6 format qdiv pa, pb getqx pa shl pa, #10 'WXPIN format = %DDDDDDDDDDDDDDDD.DDDDDD_xxxxx_FFFFF sets pa, #7 'comport 8N1 framing } 'recalculate baud divider (clk_freq / asyn_baud) of diag comport using cordic ' low bauds won't operate at high sysclocks, the divider only has 16-bit reach qdiv clk_freq, asyn_baud 'comport divider qfrac #1, asyn_baud 'remainder scale factor, 2**32 / baud getqx pa 'comport divider getqy pb 'divider remainder, for .6 fraction getqx temp1 'scale factor qmul pb, temp1 'use scale factor on remainder to provide a "big" fraction getqx pb 'fractional component of comport divider rolword pa, pb, #1 '16.16 comport divider sets pa, #7 'comport 8N1 framing (bottom 10 bits should be replaced but 9's enough) 'make sure not transmitting on comport before adjusting hardware .txwait rqpin inb, #DIAGTXPIN wc 'transmiting? (C high == yes) if_c jmp #.txwait wxpin pa, #DIAGTXPIN 'set tx baud and framing (divider format is 16.0 if divider >= 1024.0) wxpin pa, #DIAGRXPIN 'set rx baud and framing (divider format is 10.6 if divider < 1024.0) 'adjust hardware to new XMUL sysclock frequency andn clk_mode, #%11 'clear the two select bits to force RCFAST selection hubset clk_mode '**IMPORTANT** Switches to RCFAST using known prior mode mov clk_mode, xtalmul 'replace old with new ... sub clk_mode, #1 'range 1-1024 shl clk_mode, #8 or clk_mode, ##(1<<24 + (XDIV-1)<<18 + XPPPP<<4 + XOSC<<2) hubset clk_mode 'setup PLL mode for new frequency (still operating at RCFAST) or clk_mode, #XSEL 'add PLL as the clock source select waitx ##22_000_000/100 '~10ms (at RCFAST) for PLL to stabilise hubset clk_mode 'engage! Switch back to newly set PLL ret wcz
Comments
That's cool, always nice to see a full HW range being used
Does that do a round() on the baud result so it centres the baud value on the error band ?
I see rounding mentioned on the SysCLK calc, but not on the baud Calc ?
I've found some commercial USB Bridge chips are a tad lazy in their rounding code, and they can have larger errors of Given_Baud / Requested_Baud as a result.
That's because the baud divider calculation requires six fractional bits in the smartpin parameter and so the method used is in effect a 64-bit result: 32.32 bits which are truncated to 16.6 bits.
I'm not too surprised given how robust I had to be sans a FPU. It came as a slight shock actually.
It’s good for those who want to change frequencies while running. Not sure how many that will be. Worth putting in the tricks and traps thread with a note for dynamically switching clock frequency.
Although, that part of the routine is tiny compared to the general handling of fractions and rounding errors.
Yeah, given the size of the routine, I thought it would be good for people to try it out and make comments first.
Do Baud requests of
113386
and
116582
resolve to the same /29 /6 ?
How does the fractional baud value change with those variations ?
The test wasn't intended fit snugly, just comparing against what I had before is all.
Setting up the scope to look at the transmitted data, the tx bit timing is damn accurate. I've now chosen XDIV = 31, XMUL = 20 and XDIVP = 28 and this nets me an almost clean 460.8 kHz sysclock. Perfect for 4x the baud.
And the scope is telling me the result is spot on accurate. Time to lock down what data I'm sending ...
EDIT: Yep, all good at 460.8 kHz sysclock. Doh! Ruins all my earlier results.
EDIT2: 230.4 kHz sysclock works too. And seems to be find above this too. That's amazing. Even 115.2 kHz works but nothing in between does.
EDIT3: Just tested again without the recently added improved rounding and fractional calculations and this way still has the bad spots above 1 MHz sysclock like I had said. So the buggy testing wasn't the cause of that. Minor correction: the errors seem to petter out just above 2 MHz sysclock, so the earlier estimate of below 4 MHz was a tad over the mark. So, all good for the opening post routine.
Are those P2.Tx or P2.Rx tests ?
It makes sense that at very low divides, you need a correct clock multiple, as it's not clear how the fractional adjust applies at very low divides.
I'm also not clear on how the P2's .6 fractional bits is actually applied, as a single byte is 10 bit slots that can be adjusted, so 3 bits of fraction could usefully apply there ?
Did you scope that to see how the fractional bits apply ?
The EFM8UB3 has a /2 then /N and you can set a Txmit speed of 24MHz if you really want to, but receive cannot detect start-edges at 24M.
The highest reliable/practical baud for receive on UB3 I've found is 8MBd.8.n.2, and the 2 stops bits I think are needed because at such low divides, there are not enough baudclks to move from mid-stop-bit, to start-bit-edge modes. The added stop bit buys 3 more baudclks, which also helps with small baud offsets and continual send packets.
That practical step size depends on the device somewhat.
I just checked a FT231X, and it looks to have the same 24MHz virtual baud clock (24M/N) that the EFM8UB3 does and the FT232H does.
ie that means I can dial up these 24M/N candidates, (0.481% steps at 115200)
115384
116505
115942 and they all measure within ~ 80ppm of the expected 24M/N value. (FT232H will be closer, as that uses a crystal)
Not that it matters or that never tried, the proof is done without oddball bauds, but I'm limited to what the Linux driver allows, not the hardware limits, on the PC side.
The numbers I gave above work on Windows - do they not work on Linux ?
Do you have a linux driver/device for Silabs CP210x series ? I'm curious now to check Linux allows a baud value of 3 to be passed to the driver.
If you run a NCO correction over 64 baudclk, you give the appearance of being able to resolve to an average baud of Baudclk/64, but the actual bit sampling applies per data bit, relative to the start bit.
Any 'extra/bonus' averaging across multiple bytes is not relevant, unless you are using the baud-rate for some long term timing, which is very rare.
It's probably better to analyse this from a peak timing deviation on any single bit edge, than an average-timing-value.
If the NCO correction carries over between TX bytes, that means any Autobaud result will vary.
Where ? I do not see any mention of a bug in the serial baud generator ?
Just discussions around rounding, and jitter, and how to specify Baud errors ?
I don't see anything evanh has written that suggests 'not working as designed' in his tests ?
evanh is dropping the sysclk down to extreme levels, so the division from sysclk to baud is quite low, and seeing where it breaks. (on TX)
That's useful, as some users will want to drop sysclk as low as they can, for power reasons, but keep reasonable baud rates...
It's also important that both ends of any system choose the nearest valid baud value to what the user requested.
Some small MCUs now offer 9600 Baud Rx, when operating form 32.768kHz - that's a division of just 3.41333'
Oh. Okay. No problem, then.
I had unexpected variations in my data source that wasn't anything to do with the serial comport. It caused me confusion until I verified that the comport bit timings were spot on. Only then did I actually look for the source of the anomaly. Once eliminated all was good again.
I'm very impressed with the outcome. It looks like smartpins can do async serial fine from a sysclock of 2x the baud and up. On the tx side at least.
There, my tests show UB3 can run faster in Tx than Rx, and Rx seems stable at up to baudclk/3 (SysCLK/3 on P2) provided I use 2 stop bits.
I think there. the hand-over from mid-stop bit to start-bit-edge needs a little cushion, and and extra clock or two here also helps with baud creep effects, if sending long messages.
I've sent 1762 byte bursts with no added gaps in my tests, as that's the USB-Buffer-pipeline limit.
Be interesting to see the results of your tests on Rx side with P2, with SysClk divide and stop bit counts.
There was an open question around the fractional NCO handling.
Q: Does that reset every character, or does it roll-over across multiple characters ?
The former makes Autobaud more predictable, and a fix could be applied if users know the exact syslks between their autobaud edges, Jitter is only inside characters & fixed.
all good at 460.8 kHz
230.4 kHz works too. And seems to be fine above this too.
115.2 kHz works but nothing in between does.
That makes sense, as exact divides will be needed at lowest sysclks, as any NCO adder at lowest divides is a massive time jump.
Rx side is likely to start working reliably around 3x sysclks
The baud timer resets on every character.
Good, thanks.
That means the practical/available fractional correction will vary with character length.
It will be 5 bits for a 32 length character (not common) and 3 bits for the 8 data bits in a byte-char.
It is not until you are over 4x that the granularity step drops to 3%, so all values under 4 are going to need some user care and checking.
What are the limitations?
I wasn't trying to carefully measure the limits of the comms. It was a "are the calculated dividers good enough for a for a borderline setup?" The answer was yes, better than expected.