Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stm32f4xx: Dma2 & USART1 + Stm32f429idiscovery BSP #2936

Merged
merged 14 commits into from Mar 26, 2022

Conversation

LeonMatthesKDAB
Copy link
Contributor

@LeonMatthesKDAB LeonMatthesKDAB commented Jan 7, 2022

Pull Request Overview

This PR originates from my need to get Tock OS running on a Stm32f429i discovery board.
The board layout was copied & adapted from the Nucleo_f429zi code.

The ST-Link connector that this board uses to connect to the PC is only connected to USART1, which must be configured with the DMA2 controller.
This PR therefore adds the ability for Tock OS to enable the DMA2 & USART1 on the Stm32f4xx platforms.

Even though the Stm32f429iDISC BSP should probably not be added to Tock OS, adding the DMA2 and USART1 is surely useful.

Testing Strategy

This pull request was tested by connecting to the the STM32F429IDiscovery board via it's ST-Link interface.
UART output this way is working for both in- and output. The Tock OS console is accessible that way.

I'd be happy to receive suggestions on how to automate testing for this 馃

TODO

  • Merge more code of Dma1 & Dma2 into the shared dma module (e.g. DmaClock)
  • Fix wildcard use super::* in dma1.rs and dma2.rs
  • Adapt other Stm32f4xx chips&boards to the new API

Help Wanted

As this introduces a new dma peripheral, as well as a new USART stream, it breaks the internal Kernel API in quite a few places.
Is this approach viable, or should complete API backward-compatibility be the target? This doesn't change any User-Space APIs though.

I've kept Dma1&2 as separate types for now, so that Dma2 peripherals can not accidentally be passed into the Dma1 or vice-versa.
That does however mean, that the code for Usart, as well as all BSPs based on the Stm32f4xx boards need to be adapted to deal with this. If someone knows a way to implement this backwards-compatible, that would be great!

Currently, I've added a generic DmaPeripheral as well as Stream enum types that simply wrap either a Dma1Peripheral/Dma2Peripheral or Stream respectively, so that i.e. Usart can deal with either.
Feedback on this approach is much appreciated. I can also see something based on traits and dyn references to be viable, but I'm unsure if this is suitable in the embedded context of Tock OS.

Documentation Updated

  • As this change is entirely chip and board specific code, no additional changes in doc should be required.

Formatting

  • Ran make prepush.

@github-actions github-actions bot added the stm32 Change pertains to the stm32 family of MCUSs label Jan 7, 2022
@hudson-ayers
Copy link
Contributor

I haven't had time to look at this in-depth, but this does seem like a nice contribution that we would be happy to take upstream. To answer one of your questions:

As this introduces a new dma peripheral, as well as a new USART stream, it breaks the internal Kernel API in quite a few places. Is this approach viable, or should complete API backward-compatibility be the target? This doesn't change any User-Space APIs though.

Looking at the changes, it seems you just mean that the APIs for interacting with UART etc. on the STM32f4 have changed? If so, that is fine. Changing a HIL would be an issue, but that does not seem to be happening here -- if all of the changes are contained within the stm32 chip/board that is fine.

@LeonMatthesKDAB
Copy link
Contributor Author

Looking at the changes, it seems you just mean that the APIs for interacting with UART etc. on the STM32f4 have changed? If so, that is fine. Changing a HIL would be an issue, but that does not seem to be happening here -- if all of the changes are contained within the stm32 chip/board that is fine.

Yes exactly, all the changes are within the chip/board crates. This PR doesn't change any Tock user-land APIs.
However the BSP's for these chips will require adjustment if they use USART.

But now that I know I'm on the right track, I'll get to cleaning up, so this PR is in a mergeable state.

Instead of providing a Dma1Clock and a Dma2Clock, we can use a shared
class for both DMA's, as they share the exact same code.
@hudson-ayers
Copy link
Contributor

Yes exactly, all the changes are within the chip/board crates. This PR doesn't change any Tock user-land APIs. However the BSP's for these chips will require adjustment if they use USART.

But now that I know I'm on the right track, I'll get to cleaning up, so this PR is in a mergeable state.

Awesome! In order for CI to pass, this PR will need to make the relevant adjustments to upstream (stm) boards such that they build with the new USART API. Let me know if you have trouble with that

@LeonMatthesKDAB
Copy link
Contributor Author

Alright, will do. Adjusting the other BSPs shouldn't be difficult.

I'm currently doing a bit more cleanup to the DMA module, to reduce code duplication between Dma1 & Dma2 as much as possible. Will probably upload that change next week.

To reduce the code duplication between Dma1&Dma2, the Stream class is
moved into the shared Dma module.
Dma1 and Dma2 then implement the new StreamServer trait, that allows
them to serve their own Streams.

A further refactoring is achieved by adding a StreamPeripheral trait,
which is implmeneted in Dma1Peripheral & Dma2Peripheral and returns all
the constants that might be different between DMA peripherals.
This way the dma::Stream never has to match over the peripheral, but
only ever over to stream to access the correct registers. The data to
write into the registers will be provided by the Peripheral.
This should also make adding new peripherals cleaner, as the Stream
doesn't need to be touched, but only the Peripheral enum.
Not all boards will use the new Dma features, but they must still be
adapted to fit the new API.
@LeonMatthesKDAB
Copy link
Contributor Author

I have done quite a few more changes to reduce the code duplication between Dma1 & Dma2.

Most notably, I've extracted a StreamPeripheral interface that both Dma1Peripheral and Dma2Peripheral need to implement.
Furthermore I have changed the dma::Stream, so that it only uses match on the streamid, instead of the peripheral, as that could cause access to the incorrect stream by accident, and also bloated the code base a lot.
In my opinion this makes the Stream internal code a lot cleaner and should make implementing new peripherals a breeze.

make prepush now completes successfully on my latest commit.
All boards & chips that are based on the stm32f4xx have been adapted to compile with the new API. Usart on the stm32f429idiscovery board I have works without problem.

If you like the new changes, can you please approve the ci workflows, so that I can check whether everything still works?

@LeonMatthesKDAB LeonMatthesKDAB marked this pull request as ready for review January 26, 2022 13:48
@LeonMatthesKDAB LeonMatthesKDAB changed the title [WIP] Stm32f4xx: Dma2 & USART1 + Stm32f429idiscovery BSP Stm32f4xx: Dma2 & USART1 + Stm32f429idiscovery BSP Jan 26, 2022
Comment on lines 94 to 97
pub struct Dma2<'a> {
registers: StaticRef<DmaRegisters>,
clock: DmaClock<'a>,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate a bit on the decision to make Dma1 and Dma2 different types, rather than having a single Dma struct with multiple instantiations? I understand this design is necessary for StreamServer to have Peripheral as an associated type rather than a generic type, but it does lead to some code duplication between Dma1 and Dma2. Does using a generic parameter for StreamServer cause issues with propagation of generic parameters throughout the code base?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that mostly because I wanted the streams from the different Dma's to be of different types. I realize now that parameterizing the Stream over the Peripheral would also have worked.

Another benefit of the current approach is that it is impossible to use a Dma with the wrong Peripheral type.
Because the Stream is associated with the Dma on a type-level basis.
If the Stream were made generic over the Peripheral, i.e. Stream<Dma1Peripheral>, one might accidentally create such a stream for the wrong Dma instance.

It's definitely a tradeoff between having code duplication vs. more checks at compile time.
With the current implementation the Dma is propagated to the types of Usart & SPI, so usart1 is a Usart<Dma2>.
In theory, we could simply have a single DmaPeripheral enum, that contains all peripherals, with a single, non-generic Stream struct implementation.
This way we wouldn't have any code duplication and both Usart and Spi would no longer have to be generic over the Dma or its peripheral.
The disadvantage then would be, that one could create a Usart instance for usart1 and accidentally pass it a stream of dma1, instead of dma2. Currently, this is impossible, as they are of different types.

We could have these checks at runtime, with the Peripheral reporting which Dma it belongs to and the system panicking if it's set up incorrectly, like I have added for checking whether the peripheral is added to the right stream.

So we basically have three options here:

  1. Keep Stream<DmaX>
  2. Change to Stream<DmaXPeripheral>
  3. Remove Generics from Stream & add runtime checks

In order of most code duplication & compile-time safety to least.
It's probably best if you decide what fits best with the current design-philosophy of Tock OS :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static checks are good, and the code duplication here looks manageable (i.e. it's pretty formulaic and unlikely to need to change much if at all).

I think it makes sense to combine these into a single dma.rs file, and move this thread to a comment in the source explaining what static properties this structure is designed to enforce.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to Brad's suggestion; thanks for explaining the reasoning here!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

馃憤
I'll move everything into the shared dma module then and add a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -1,17 +1,18 @@
use core::fmt::Debug;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we name this something more descriptive than "mod"? I'm not sure what this file contains that is not in the other dma files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recombined the dma subfolder into a dma.rs file

The dma1 and dma2 submodules were too small to justify their existence -
therefore it makes more sense to combine their code into a single dma
module.
@bradjc bradjc added the last-call Final review period for a pull request. label Feb 25, 2022
@LeonMatthesKDAB
Copy link
Contributor Author

@bradjc Do you mind approving the github workflow runs?
I'd prefer it if the entire CI took a pass at the PR :)

@hudson-ayers
Copy link
Contributor

@bradjc Do you mind approving the github workflow runs? I'd prefer it if the entire CI took a pass at the PR :)

Started

@LeonMatthesKDAB
Copy link
Contributor Author

@bradjc It's been nearly a month since the "last call" label has been added.

When can this PR be merged?

@hudson-ayers
Copy link
Contributor

bors r+

@bors
Copy link
Contributor

bors bot commented Mar 26, 2022

@bors bors bot merged commit 55fbff7 into tock:master Mar 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
last-call Final review period for a pull request. stm32 Change pertains to the stm32 family of MCUSs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants