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

Request: better handling of DMA buffers for realtime audio using I2S #1062

Closed
sourcebox opened this issue Jan 5, 2024 · 6 comments · Fixed by #1189
Closed

Request: better handling of DMA buffers for realtime audio using I2S #1062

sourcebox opened this issue Jan 5, 2024 · 6 comments · Fixed by #1189

Comments

@sourcebox
Copy link

Background: realtime audio requires low latency, therefore small buffers (like 16-64 samples) are required. Also, with sample rates like 48kHz and above in combination with multichannel audio, copying of data should be avoided.

Currently, the write_dma_circular() method of I2sTx takes a single buffer which has to be of more the 4k size. Any new output data then has to be pushed into the buffer.

The desired solution is to configure two independent buffers of any allowed size (1..4095) bytes that are transferred alternately. These can be of small size to allow low latency. The transfer API should then provide a mutable reference to the buffer that is currently not transferred by the DMA so that it can be filled with new data. Ideally, the API should provide both, a sync and an async method to accomplish this. The async method could work in conjunction with a waker called from a transfer complete interrupt.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jan 5, 2024

Any pointers to a Rust HAL which implements things in a suitable way?

Technically I'm not in favor of two buffers - we split the buffer into chunks anyway (which is why we currently require a minimal size of the buffer so we can split it into the default chunks). A chunk is the smallest region we can know of if it's owned by DMA or CPU.

I'm also not sure if we can safely give mutable access to parts of the DMA buffer - that's why I'm asking for an implementation which does it

@sourcebox
Copy link
Author

Any pointers to a Rust HAL which implements things in a suitable way?

I can't give you an example here, because I'm just familiar with STM32 which is doing things completely different since it does not have a link-list DMA but uses double buffering and half-transfer flags and interrupts.

I'm also not sure if we can safely give mutable access to parts of the DMA buffer - that's why I'm asking for an implementation which does it

That's the reason 2 buffers are needed: you cannot give access to the buffer that is currently transferred. It's even forbidden by the owner bit in the DMA control register. With circular buffers, you always need at least 2, otherwise a continuous transfer cannot be achieved.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jan 5, 2024

A pointer to an STM32 example would be still helpful. I know DMA works totally different there but would still be interesting

I'm also not sure if we can safely give mutable access to parts of the DMA buffer - that's why I'm asking for an implementation which does it

That's the reason 2 buffers are needed: you cannot give access to the buffer that is currently transferred. It's even forbidden by the owner bit in the DMA control register. With circular buffers, you always need at least 2, otherwise a continuous transfer cannot be achieved.

The owner bit is for a single descriptor (pointing to max 4095 bytes) - ideally for the circular transfer we would reference both buffers from the descriptor list which is technically the same thing

Anyways thanks for listing your requirements - I think it makes clear what you want to achieve. We'll see how the implementation will look like

@sourcebox
Copy link
Author

If I understand the TRM correctly, you can also append the desciptor list on the fly. So one possible solution to make the handling more secure could be to remove the buffer that is currently filled by the CPU (or read in case when you receive data) and add it back to the lister later after the CPU has done its task.

@bjoernQ
Copy link
Contributor

bjoernQ commented Jan 5, 2024

Sure, our hardware is quite flexible so many solutions are possible

@seijikun
Copy link

seijikun commented Mar 1, 2024

At least as far as I understand it:

The owner bit is not meant to forbid working with the chunk of the buffer that's transferred by the dma descriptor in question. It's basically just to tell you which chunk has been sent to the device completely and can now be overwritten collision-free (which is important for audio).

From a hardware POV, it's not problematic in anyway to overwrite the buffer while it's being transferred though. This is actively done and very useful with framebuffers in the lcd component of esp-idf.

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

Successfully merging a pull request may close this issue.

4 participants