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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add async transfer #46

Merged
merged 5 commits into from
Nov 25, 2024
Merged

add async transfer #46

merged 5 commits into from
Nov 25, 2024

Conversation

Randall-Scharpf
Copy link
Contributor

This PR supports adafruit/circuitpython#9385 (which currently needs some updates, but will need updates in this submodule regardless).

Why

  • Interest in running SPI on the SAM D51 as a secondary device has been expressed by others (and is useful to our team at BruinSpace).
  • Running SPI as a secondary device comes with the possibility that no main device is attached, so the SPI interface must not block indefinitely even if the time taken to complete a single transfer is infinite.
  • CircuitPython implements SPI on the SAM D51 using DMA. The DMA controller on the SAM D51 does not set the "Channel Transfer Complete" flag for a transfer handled by a SERCOM set to be an SPI client unless the clock pin for the peripheral is externally driven.
  • The current implementation of DMA uses one C function to set up the DMA controller and then busy wait on the "Channel Transfer Complete" flag to be set before it will return.

So, running SPI as a secondary device while only using the existing DMA controller implementation will require that devices accept the possibility of going completely non-responsive when starting a transfer if there is no attached main device. This is unacceptable for many applications.

Changes

  • Added three-method interface to asynchronous DMA interfacing (shared_dma_transfer_start, shared_dma_transfer_finished, and shared_dma_transfer_close).
  • Added an improved DMA channel allocation scheme with dedicated methods, allowing run-time allocation instead of compile-time allocation.
    • Why? It's now possible to start a DMA and then do something else. It could be that "something else" is "start another DMA transfer". So, we should make it possible to use more than one DMA channel at a time.
  • Added a dma_descr_t which identifies and tracks the status of a DMA transfer. This is needed so that if multiple DMA transfers are started, each one can individually be checked for completion and closed out. It allows a completed transfer which is succeeded by another on the same channel to distinguish itself from the new transfer and correctly resolve as complete.
  • Re-wrote existing synchronous DMA transfer code to reference new asynchronous DMA transfer code. The implementation is logically identical (though with a bit more data overhead), but this de-duplicates code between the synchronous and asynchronous code while also providing straightforward means to test the asynchronous code.

Testing

  • The full build of CircuitPython here is able to asynchronously do SPI transfers (with a Python interface which is still in development, but by using this interface as is).
  • The full build of CircuitPython here is able to use functions which rely on synchronous DMA transfers without detectable behavioral differences from the implementation before these changes.

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

Looks great! Thank you!

@tannewt tannewt merged commit d321022 into adafruit:main Nov 25, 2024
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