Skip to content

I2s clock #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed

I2s clock #240

wants to merge 2 commits into from

Conversation

YruamaLairba
Copy link
Contributor

Hello, i started to work on setting up i2s clock through the HAL. This is the first step for an i2s HAL implementation

@mgottschlag
Copy link
Contributor

I think the clock configuration interface should not specify R/N/M directly, but should rather specify a target plli2sclk frequency. Most STM32F4 models do not support the PLLI2SM divider but rather use PLLM instead, in which case only specifying PLLI2SR and PLLI2SN would result in varying resulting frequencies depending on the PLLM automatically selected by pll_setup().

Whatever external script the user uses to determine the best clock configuration (w.r.t. your comment in the other pull request) can just output that intermediate plli2sclk frequency.

@mgottschlag
Copy link
Contributor

Note: I am currently trying to write HAL wrapper for SAI, so I need basically the same clock configuration code as you do.

Which MCU model are you using? I am trying to figure out the differences in clock configuration - so far I found the following:

  • Some models have a separate PLL for SAI, some do not.
  • Some have separate M factors for the different PLLs, some do not.
  • Some select the I2S clock source in CFGR, some in DCKCFGR.
Models 405/407/415/417 411 413/423 427/429/437/439 446 469/479
I2S 2 5 5 2 3 2
SAI 0 0 1 1 2 1
Separate PLL for SAI ✔️ ✔️ ✔️
PLLI2SM in RCC_PLLI2SCFGR ✔️ ✔️ ✔️
PLLSAIM in RCC_PLLSAICFGR ✔️
PLLI2SSRC in RCC_PLLI2SCFGR ✔️
I2SSRC in RCC_CFGR ✔️ ✔️ ✔️ ✔️
I2S1/2SRC in RCC_DCKCFGR ✔️ ✔️
SAI1A/BSRC in RCC_DCKCFGR ✔️ ✔️ ✔️
SAI1/2SRC in RCC_DCKCFGR ✔️

Implementing a universal solution might be more difficult than I expected.

@mgottschlag
Copy link
Contributor

STM32F410 do not even have an I2S PLL. This is the full table generated from all reference manuals I could find:

Models 401 405/407/415/417 410 411 412 413/423 427/429/437/439 446 469/479
I2S 2 2 3 5 5 5 2 3 2
SAI 0 0 0 0 0 1 1 2 1
I2SPLL ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
SAIPLL ✔️ ✔️ ✔️
PLLI2SM in RCC_PLLI2SCFGR ✔️ ✔️ ✔️ ✔️
PLLSAIM in RCC_PLLSAICFGR ✔️
PLLI2SSRC in RCC_PLLI2SCFGR ✔️ ✔️
I2SSRC in RCC_CFGR ✔️ ✔️ ✔️ ✔️ ✔️
I2S1/2SRC in RCC_DCKCFGR ✔️ (1x) ✔️ ✔️ ✔️
SAI1A/BSRC in RCC_DCKCFGR ✔️ ✔️ ✔️
SAI1/2SRC in RCC_DCKCFGR ✔️

@YruamaLairba
Copy link
Contributor Author

sorry, i initially open this draft without knowing there was so many difference between f4 chips, and without knowing there was already another work on i2s. I didn't drop it just because the other work don't address yet the i2s clock. As you saw, it's look like a nightmare, so i gave up.

I think the clock configuration interface should not specify R/N/M directly, but should rather specify a target plli2sclk frequency.

IMHO, specify a target plli2sclk is only useful to keep coherence with the rest on the clock system. I don't find it convenient in practice for many reason:

  • it's complicated for HAL developer to calculate prescalers values
  • It doesn't really simplify the life of HAL users, they have to do some complex calculation to use the desired sampling frequency anyway.
  • it tend to hide the fact you have a difference between specified and real clock while this difference can be relevant.

Which MCU model are you using?

stm32f411 on a nucleo board

Whatever external script the user uses to determine the best clock configuration (w.r.t. your comment in the other pull request) can just output that intermediate plli2sclk frequency.

I don't understand what you are trying to say here. I actually did those scripts, and it determine bests combination of plli2sclk and i2s configuration to obtain a particular sampling frequency. Not smart though, it just try all combination

@mgottschlag
Copy link
Contributor

IMHO, specify a target plli2sclk is only useful to keep coherence with the rest on the clock system. I don't find it convenient in practice for many reason:

Yeah, you are right. I'll give it a try, it should not actually be that difficult to implement.

@mgottschlag
Copy link
Contributor

https://github.com/mgottschlag/stm32f4xx-hal/tree/wip contains a full (although currently completely untested) implementation for generation of I2S and SAI clocks for all MCUs.

The situation is bad, but not quite as bad as I expected at least if we limit ourselves to one I2S and one SAI frequency and do not try to generate more than two audio frequencies from two PLLs. If collecting the PLL target frequencies/requirements is detached from the actual PLL configuration, we have four different "PLL styles" and only few "clock selection styles":

a) We have MCUs without I2S PLL (F410), MCUs with multiple PLLs but shared "M", MCUs where the I2SPLL is used for SAI (F413/423), and MCUs with multiple PLLs and individual "M" dividers.
b) We have MCUs with one or two I2S clocks.

Computational complexity stays as it was if I2S and SAI are not used. It is increased due to the brute-force approach of guessing good PLL configurations if either multiple clocks are generated from the same PLL (F410 if I2S is used, where the code tries up to 60 * 400 PLL configurations, although usually a lot less) and if the SAI divider is automatically determined (in which case up to 32 * 60 configurations are tested - this code path can be optimized quite a bit still). I still have to try how well the code is optimized. The complexity prevents implementation as a const fn, though - that was my first approach.

Note that an additional difference between the MCUs is that the STM32F413/423 have a different SAI clock divider range ([1, 31] instead of [1, 32]).

My next steps are to perform some refactoring and to ensure that everything works on my STM32F429.

@YruamaLairba
Copy link
Contributor Author

@mgottschlag you see very motivated :) . Why you don't create a draft pull request ? This will signal to others that you are working on something. Adding to this, you can see if you code compile for all target each time you push your work on your github account.

For guessing PLL configurations, it may worth to look at the dark art of procedural macro. You can write one to ensure the guessing job is done at compile time and throwing compile error if a valid configuration cannot be found.

@mgottschlag mgottschlag mentioned this pull request Jan 2, 2021
6 tasks
bors bot added a commit that referenced this pull request Jan 10, 2021
247: rcc: I2S and SAI clocks r=therealprof a=mgottschlag

# Preface

Some of this code is probably pretty ugly. In parts, the code looks pretty bad, and it imposes pretty arbitrary limitations (see below) in terms of functionality. I propose merging something like this on the basis that it is strictly more powerful than the previous implementation - in cases where no I2S or SAI clocks are requested, the old code is still used providing identical performance and identical results.

# Situation

The current RCC code only provides the sysclk, but no I2S and SAI clocks. Future pull requests (#212? I am currently working on support for SAI) require these clocks. Different MCUs have wildly differing clock trees, though. Some microcontrollers (e.g. STM32F411/446) have separate I2SPLLM dividers, others do not. F413/423 do not have an SAI PLL even though SAI is supported. F410 does not even have an I2S PLL and the I2S clock is generated by the main PLL. The different amount of SAIs and I2S instances causes differences in the clock selection code. See #240 for a table of all those differences. Note: The table does not include that F413/423 have a different SAI divider range compared to the other models.

# Approach

This wide range of different configurations and the flexible, yet very model-specific connections (e.g., SAI clocks can usually be generated by the I2S PLL and vice versa) make writing a completely flexible solution difficult. The computational complexity of such a solution to generate the ideal results might very likely be prohibitive for any runtime implementation.

I therefore decided on an approach with some limitations to keep both implementation and computational complexity down. In particular, I2S clocks, if required, are always generated by the I2S PLL, and likewise SAI clocks are always generated by the SAI PLL, with special cases for the models where these PLLs do not exist. This means that there can be only one I2S frequency (and likewise SAI frequency) that does not match the provided I2S_CKIN external frequency. This should be enough for all applications except for applications which implement resampling between e.g. 44100 and 48000 Hz audio and have to implement an I2S master for both.

The code decouples clock selection (the bottom half of the table in #240) from PLL optimization to reduce the numbers of special cases required in each part - clock selection is implemented in the form of I2sClocks and SaiClocks types in rcc.rs.

# Alternatives

I believe the restrictions described above are valid for now. If, eventually, we want a more complete and more flexible interface and do not care about API backwards compatibility, we can implement clock tree configuration in the form of an external compile-time tool - either via procedural macros or via a library that can be used within build.rs. In this case, the RCC library should probably be changed to provide two functions "RCC.apply_static_config()" or "RCC.apply_runtime_config()", where the former just applies a set of precalculated config options and the latter executes the current cheap yet limited runtime configuration algorithms.

# Status/Testing

At the moment, most of the code is still completely untested and I only have a STM32F429 discovery board for testing:

- [x] Main PLL (most models): The old main PLL code to generate sysclk has been tested with a blinky example on a STM32F429 board. Most models should behave identically.
- [ ] Main PLL (STM32F410) when an I2S clock has been requested: TODO, does anybody have an STM32F410 and can run any blinky example modified to request ".i2s_clk(64.mhz())"?
- [ ] I2S PLL (STM32F405/407/415/419/427/429/437/439): WIP. If anyone working on I2S wants to spend some time on debugging, feel free ;-)
- [x] SAI PLL (STM32F427-429, 469, 479): The code seems to generate the correct MCK signal.
- [ ] I2S PLL (STM32F411-413, 423, 447): I do not have the required MCU. Most of the code is identical to the other models, though. Let me test on the 429 first before you give it a go.
- [x] SAI PLL (STM32F446): I do not have the required MCU. The code path should be identical to the STM32F429 without main PLL though, and that works.

Once the tests on the F429 are done, I need help with the other models - and I could use help with the F410 right away. Do not expect the code to work as-is, though.

Co-authored-by: Mathias Gottschlag <[email protected]>
Co-authored-by: Mathias Gottschlag <[email protected]>
@YruamaLairba
Copy link
Contributor Author

I2s clock configuration is addressed in PR #247

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