Direct Memory Access (DMA)
Circle supports Direct Memory Access (DMA) using the platform DMA controller of the Raspberry Pi. This is implemented in the class CDMAChannel. The Raspberry Pi 5 has an additional DMA controller in the RP1 southbridge, which is controlled by the class CDMAChannelRP1.
CDMAChannel
#include <circle/dmachannel.h>
-
class CDMAChannel
-
CDMAChannel::CDMAChannel(unsigned nChannel, CInterruptSystem *pInterruptSystem = 0)
Creates an instance of
CDMAChanneland allocates a channel of the platform DMA controller.nChannelmust beDMA_CHANNEL_NORMAL(normal DMA engine),DMA_CHANNEL_LITE(lite (or normal) DMA engine),DMA_CHANNEL_EXTENDED(“large address” DMA4 engine, on Raspberry Pi 4 and 5 only) or an explicit channel number (0-15).pInterruptSystemis a pointer to the instance ofCInterruptSystemand is only needed for interrupt operation.
Note
Currently only “large address” DMA4 engines are supported on the Raspberry Pi 5. Any dynamic DMA channel allocation (non-explicit channel number) will use one of these engines. The defined DREQSource* values are currently valid for the Raspberry Pi 1-4 and Zero only.
-
void CDMAChannel::SetupMemCopy(void *pDestination, const void *pSource, size_t nLength, unsigned nBurstLength = 0, boolean bCached = TRUE)
Setup a DMA memory copy transfer from
pSourcetopDestinationwith lengthnLength.nBurstLength> 0 increases the speed, but may congest the system bus.bCacheddetermines, if the source and destination address ranges are in cached memory.
-
void CDMAChannel::SetupIORead(void *pDestination, uintptr nIOAddress, size_t nLength, TDREQ DREQ)
Setup a DMA read transfer from the I/O port
nIOAddresstopDestinationwith lengthnLength.DREQpaces the transfer from these devices:
DREQSourceNone (no wait)
DREQSourceEMMC
DREQSourcePCMRX
DREQSourceSMI
DREQSourceSPIRX
DREQSourceUARTRX
-
void CDMAChannel::SetupIOWrite(uintptr nIOAddress, const void *pSource, size_t nLength, TDREQ DREQ)
Setup a DMA write transfer to the I/O port
nIOAddressfrompSourcewith lengthnLength.DREQpaces the transfer to these devices:
DREQSourceNone (no wait)
DREQSourceEMMC
DREQSourcePCMTX
DREQSourcePWM
DREQSourcePWM1 (on Raspberry Pi 4 only)
DREQSourceSMI
DREQSourceSPITX
DREQSourceUARTTX
-
void CDMAChannel::SetupCyclicIOWrite(uintptr ulIOAddress, const void *ppSources[], unsigned nBuffers, size_t ulLength, TDREQ DREQ)
Setup a cyclic DMA write transfer to the I/O port
ulIOAddress(ARM-side or bus address) fornBuffersconcatenated DMA buffers (max. 4) atppSources(pointer to array of pointers) with lengthulLengthbytes per buffer.DREQpaces the transfer (seeCDMAChannel::SetupIOWrite()for the possible devices). The transfer starts from first buffer again, when last buffer has been sent.
-
void CDMAChannel::SetupMemCopy2D(void *pDestination, const void *pSource, size_t nBlockLength, unsigned nBlockCount, size_t nBlockStride, unsigned nBurstLength = 0)
Setup a 2D DMA memory copy transfer of
nBlockCountblocks ofnBlockLengthlength frompSourcetopDestination. SkipnBlockStridebytes after each block on destination. Source is continuous. The destination cache, if any, is not touched.nBurstLength> 0 increases speed, but may congest the system bus. This method can be used to copy data to the framebuffer and is not supported withDMA_CHANNEL_LITE.
-
void CDMAChannel::SetCompletionRoutine(TDMACompletionRoutine *pRoutine, void *pParam)
Sets a DMA completion routine for interrupt operation.
pRoutineis called, when the transfer is completed.pParamis a user parameter, which is handed over to the completion routine.TDMACompletionRoutinehas the following prototype:
-
typedef void TDMACompletionRoutine(unsigned nChannel, unsigned nBuffer, boolean bStatus, void *pParam)
nChannel is the channel number. nBuffer is the number of the cyclic buffer (always 0 for non-cyclic transfers). bStatus is TRUE, if the transfer completed successfully.
Note
The method SetCompletionRoutine() has to be called before each asynchronous transfer. The completion routine is reset after the completion of the transfer, so that the next transfer can be synchronous, if it is intended. For cyclic transfers the completion routine is called multiple times and is not reset, when an DMA interrupt occurs. It is reset, when the transfer is canceled.
-
void CDMAChannel::Start(void)
Starts the DMA transfer, which has been setup before.
-
boolean CDMAChannel::Wait(void)
Waits for the completion of the DMA transfer (for synchronous non-interrupt operation without completion routine). Returns
TRUE, if the transfer was successful.
-
boolean CDMAChannel::GetStatus(void)
Returns
TRUE, if the last completed transfer was successful.
CDMAChannelRP1
#include <circle/dmachannel-rp1.h>
-
class CDMAChannelRP1
This class controls the RP1 DMA controller of the Raspberry Pi 5, which is normally used to transfer data between peripherals in the RP1 southbridge and the system memory.
-
CDMAChannelRP1::CDMAChannelRP1(unsigned nChannel, CInterruptSystem *pInterruptSystem)
Creates an instance of
CDMAChannelRP1for RP1 DMA channelnChannel(0-7). There is currently no dynamic channel allocation for RP1 DMA channels.pInterruptSystemis a pointer to the interrupt system object.
-
void CDMAChannelRP1::SetupMemCopy(void *pDestination, const void *pSource, size_t ulLength, boolean bCached = TRUE)
Setup a DMA memory copy transfer from
pSourcetopDestinationwith lengthnLengthbytes.bCacheddetermines, if the source and destination address ranges are in cached memory.
-
void CDMAChannelRP1::SetupIORead(void *pDestination, uintptr ulIOAddress, size_t ulLength, TDREQ DREQ, unsigned nIORegWidth = 4)
Setup a DMA read transfer from the I/O port
ulIOAddress(ARM-side or bus address) topDestinationwith lengthnLengthbytes.nIORegWidthspecifies the width of the accessed I/O register (1 or 4 bytes).DREQpaces the transfer from these devices:DREQSourceNone (no wait)
DREQSourceSPI0RX
DREQSourceSPI0TX
DREQSourceSPI1RX
DREQSourceSPI1TX
DREQSourceSPI2RX
DREQSourceSPI2TX
DREQSourceSPI3RX
DREQSourceSPI3TX
DREQSourceSPI5RX
DREQSourceSPI5TX
DREQSourcePWM0
DREQSourceI2S0RX
DREQSourceI2S0TX
DREQSourceI2S1RX
DREQSourceI2S1TX
-
void CDMAChannelRP1::SetupCyclicIORead(void *ppDestinations[], uintptr ulIOAddress, unsigned nBuffers, size_t ulLength, TDREQ DREQ, unsigned nIORegWidth = 4)
Setup a cyclic DMA read transfer from the I/O port
ulIOAddress(ARM-side or bus address) fornBuffersconcatenated DMA buffers (max. 4) atppDestinations(pointer to array of pointers) with lengthnLengthbytes per buffer.nIORegWidthspecifies the width of the accessed I/O register (1 or 4 bytes).DREQpaces the transfer (seeCDMAChannelRP1::SetupIORead()for the possible devices). The transfer starts from first buffer again, when last buffer has been filled.
-
void CDMAChannelRP1::SetupIOWrite(uintptr ulIOAddress, const void *pSource, size_t ulLength, TDREQ DREQ, unsigned nIORegWidth = 4)
Setup a DMA write transfer to the I/O port
ulIOAddress(ARM-side or bus address) frompSourcewith lengthnLengthbytes.nIORegWidthspecifies the width of the accessed I/O register (1 or 4 bytes).DREQpaces the transfer (seeCDMAChannelRP1::SetupIORead()for the possible devices).
-
void CDMAChannelRP1::SetupCyclicIOWrite(uintptr ulIOAddress, const void *ppSources[], unsigned nBuffers, size_t ulLength, TDREQ DREQ, unsigned nIORegWidth = 4)
Setup a cyclic DMA write transfer to the I/O port
ulIOAddress(ARM-side or bus address) fornBuffersconcatenated DMA buffers (max. 4) atppSources(pointer to array of pointers) with lengthnLengthbytes per buffer.nIORegWidthspecifies the width of the accessed I/O register (1 or 4 bytes).DREQpaces the transfer (seeCDMAChannelRP1::SetupIORead()for the possible devices). The transfer starts from first buffer again, when last buffer has been sent.
-
void CDMAChannelRP1::SetCompletionRoutine(TCompletionRoutine *pRoutine, void *pParam)
Sets a DMA completion routine for interrupt operation.
pRoutineis called, when a transfer is completed.pParamis a user parameter, which is handed over to the completion routine. For cyclic transfer the completion routine is called after each buffer again.TCompletionRoutinehas the following prototype:
-
typedef void CDMAChannelRP1::TCompletionRoutine(unsigned nChannel, unsigned nBuffer, boolean bStatus, void *pParam)
nChannelis the index of the RP1 DMA channel (0-7).nBufferis the index of the cyclic buffer (0-N, 0 if not cyclic).bStatusisTRUEfor a successful transfer,FALSEon error.pParamis the user parameter, handed over toSetCompletionRoutine().
-
void CDMAChannelRP1::Start(void)
Starts the DMA transfer, which has been setup before.
-
void CDMAChannelRP1::Cancel(void)
Cancels a running DMA transfer and waits for its termination.
DMA buffers
#include <circle/synchronize.h>
-
DMA_BUFFER(type, name, num)
Defines a buffer with
nameandnumelements oftypeto be used for DMA transfers.See doc/dma-buffer-requirements.txt for more information on DMA buffers.
Cache maintenance
#include <circle/synchronize.h>
-
void CleanAndInvalidateDataCacheRange(uintptr nAddress, size_t nLength)
Cleans and invalidates a memory range in the data cache.