Dynamic system clock setting and comport baud setting
            
                            
                                  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.