Skip to content

Conversation

EliteTK
Copy link

@EliteTK EliteTK commented Sep 6, 2025

It was really bugging me, okay?

The PLL frequency calculation will now produce the correct result for all integer output frequencies and a truncated result (e.g. 1234.5 -> 1234) for all non-integer output frequencies instead of accumulating an error.

I've tested this on an STM32F411CE.

I'm pretty confident that the "truncated or correct" guarantee is right, but I was a bit paranoid so I also brute forced all configurations (including many which would be unstable) of my F411CE's RCC to check: https://paste.rs/ZaZ3k.py

This addresses #3408 .

@EliteTK EliteTK marked this pull request as draft September 7, 2025 09:59
@EliteTK
Copy link
Author

EliteTK commented Sep 7, 2025

Hmm, nevermind, I forgot about the u32 problem. Although I have an alternative idea.

@EliteTK EliteTK force-pushed the more_accurate_frequency branch from b45501e to 5c6779f Compare September 7, 2025 11:31
This will produce the correct result for all integer output frequencies
and a truncated result for all non-integer output frequencies instead of
accumulating an error.

This is done by introducing a time::Scale type which is just a rational
representing the numerator <-> denominator relationship of a scaling
factor register.
@EliteTK EliteTK force-pushed the more_accurate_frequency branch from 5c6779f to 08f590e Compare September 7, 2025 11:34
@EliteTK
Copy link
Author

EliteTK commented Sep 7, 2025

Okay, ended up with a different solution which uses u64 for the calculations. There's also now a new internal time::Scale type to which an RCC prescaler / divider / multiplier can be converted (and the Div/Mul impls for those now use time::Scale just to simplify things).

This is also working on my STM32 but should now work for the whole range of high speed external clock inputs (of which the HSI clock is a member).

Worth noting that the division by the peripheral prescalers is still done in the "imprecise" way. But the saving grace is that if you're aiming for an integer clock post prescale, then you will need to start with an integer clock anyway, so someone targeting a very precise peripheral clock shouldn't have any issue.

@EliteTK EliteTK marked this pull request as ready for review September 7, 2025 11:49
@Dirbaio
Copy link
Member

Dirbaio commented Sep 7, 2025

FIx looks good in essence (doing src as u64 * mul / (div * prediv) instead of src / prediv * mul / div).

The implementation is complex however. I'd prefer if it was done without introducing a new struct Scale and impls. I think it should be possible to just do the src as u64 * mul / (div * prediv) calculation in the RCC code directly. Perhaps change the build.rs impls to impl From<xxx> for u32 instead of Div/Mul.

@EliteTK
Copy link
Author

EliteTK commented Sep 7, 2025

Can't do From<xxx> for u32 as neither xxx nor u32 are from embassy-stm32.

Also, while working on this, I realised there's a numerator and denominator meaning that From<xxx> for u32 might itself result in imprecision (although I'm not aware of a case where this would actually happen).

@EliteTK
Copy link
Author

EliteTK commented Sep 7, 2025

Yeah, just checked, a bunch of f1 and f3 chips have Usbpre which has a 1.5 divisor, and Pllmul which has a 6.5 multiplier. So implementing From<Usbpre> for u32 or From <Pllmul> for u32 in these cases would lead to imprecise results. They're not the right chip family but the current Scale solution could be applied for more precise clocks for those families too.

(Edit: Although it is extremely rare honestly...)

@Dirbaio
Copy link
Member

Dirbaio commented Sep 7, 2025

Usbpre which has a 1.5 divisor, and Pllmul which has a 6.5 multiplier

oh no you're absolutely right. I forgot about those. Making a fraction struct makes sense then, hmm.

@EliteTK
Copy link
Author

EliteTK commented Sep 12, 2025

I can split this into multiple commits if you like. One to add the rational type and one to utilise it in the timer code.

Would also be happy to further discuss how to proceed with this change or something else entirely, either here or on matrix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants