Peripheral features
The enhanced direct memory access (eDMA) controller is a second-generation module Capable of performing complex data transfers with minimal intervention from a host processor.
- Multiple channels implementation that performs complex data transfers with minimal intervention from a host processor
- Each DMA channel can be routed to different peripheral slots through DMAMUX for transfer between peripheral and memory
- Each DMA channel can be routed to always-on source through DMAMUX, Unlike the peripheral DMA sources, where the peripheral controls the flow of data during DMA transfers, the sources that are always enabled provide no such "throttling" of the data transfers, it is useful for transfer between memory and memory.
- Channel arbitration:Fixed-priority and round-robin
- Fixed-Priority Channel service request from the highest priority channel is selected to execute
- Round-robin Channels are serviced starting with the highest channel number and rotating through to the lowest channel number without regard to the channel priority levels.
- Transfer control descriptor(TCD) All data movement via dual-address transfers: read from the source, written to destination are controlled by TCD
- TCD is a 32-byte array stored in eDMA peripheral local memory for each channel.
- TCD represent a transfer request from an application that consists of the minor loop and major loop
- Minor loop A loop that control bytes count to be transferred while a dma request coming
- Major loop A loop that controls the counts of the minor loop will be performed As a channel activates, the appropriate reads/writes perform until the minor loop byte transfer count has transferred, it is a indivisible operation and cannot be halted. The major loop Count decremented each time the minor loop is completed. After the major iteration count is exhausted, a transfer represented by TCD complete.
- TCD support enhanced addressing modes for the source and destination addresses to do with different user scenario.
- address offset per reading/writing
- address offset per minor loop(offset will be applied to the source or destination address after each request serviced which may include several read/write)
- address offset per major loop(offset will be applied to the source or destination address after all the bytes transfer done)
- address range can be specified by the MODULO feature for source/destination address, it is useful to implement a circular data queue.
- TCD support Channel linking feature(The capability of the EDMA channel to activate another EDMA channel) which is helpful to implement multi channel chained transfer, there are two channel linking mode support by eDMA list as below:
- channel linking can perform at the major loop completion
- Channel linking can perform at the minor loop completion
- TCD support load a software TCD into hardware TCD registers automatically on completion of current TCD by specify the memory address of the software TCD to be linked, it is called scatter/gather.
- It is helpful to the data transfers from non-contiguous blocks of memory to others using a series of smaller contiguous-block transfers, such as below cases,
- For the ping-pong buffer implementation, there is always TCD is working on sending/receiving data while it leave the time for software to do with received data in previous TCD transfer or prepare for data to be sent in future transfer with keep a continuous transfer
- For a complex car entertainment system which get shared speaker while with multiple audio source like bluetooth-connected phone and CD player whose drivers get provide non-contiguous data buffers. Audio system is required to smoothly switch to different audio source in different scenario
- Channel activation via one of three methods.
- Explicit software initiation by START bit in TCD register CSR(perform minor loop transfer with each software trigger).
- Initiation via a channel-to-channel linking mechanism for continuous transfers
- Peripheral-paced hardware requests, one per channel
- Channel completion reported via programmable interrupt requests.
- One interrupt per channel, which can be asserted at
- completion of the major loop.
- half completion of the major loop which may be useful for ping-pong buffer.
- Programmable error terminations per channel and logically summed together to form one error interrupt to the interrupt controller
How this peripheral works
Once the eDMA channel is activated through software/peripheral, the eDMA request input signal will be registered internally and then routed through the eDMA engine: first through the control module, then into the program model and channel arbitration. In the next cycle, the channel arbitration performs, using the fixed-priority or round-robin algorithm. After arbitration completes, the activated channel TCD will be loaded into the eDMA engine.
The modules associated with the data transfer sequence through the required source reads and destination write to perform the actual data movement continues until the minor byte count has transferred.
After the minor bytes count has transferred, the eDMA engine performs the required updates to certain fields in the appropriate TCD, for example,
- offset applied to SADDR(source address)/DADDR(destination address)
- major loop count in CITER decrement. If the major loop count is exhausted, additional operations are performed. These include:
- Final address adjustments and reloading of the BITER field into the CITER.
- The assertion of an optional interrupt request also occurs at this time, as does a possible fetch of a new TCD from memory using the scatter/gather address pointer included in the descriptor (if scatter/gather is enabled).
How this driver is designed to make this peripheral works.
The eDMA peripheral is powerful with complex transfer features supported. To satisfy different user getting different demands for the feature sets/optimization level/flexibility/abstraction level, 2 parallel layers are provided in this driver (DO NOT MIX THE USAGE OF THESE 2 LAYERS!!!):
- Functional Layer is provided with highly optimized implementation and highly flexible usage of the peripheral features. All hardware features are supported while requiring user get a decent understanding of the hardware detail of eDMA so that user know how to organize these functional API together to meet the requirement of application.
- Transactional layer is provided with average optimization level, average flexibility and not all features are covered. It is for user who want to ramp up the usage of peripheral quickly without dig much into this specific peripheral with only common knowledge of DMA peripheral model. It achieve this goal by hiding the processing of interrupt handling,processing of scatter-gather and processing of convention among software TCD/hardware TCD/human-understandable transfer attributes. To distinguish transactional layer from functional layer easily, all transactional API get Transfer in the name.
Below introduce the detail data structure/API for the two layers.
- Functional Layer
- Transfer Configuration The most important data structure usage is to configure the transfer attributes. The driver provide edm_channel_transfer_config_t whose members expose all the transfer attributes supported by peripheral. However, it may still complex / fussy for user who need not such many features. A helper function EDMA_GetChannelDefaultTransferConfig is provided to get a ready-to-use TCD configuration with only basic information required. User can create the edma_channel_transfer_config_t himself/herself if he/she is comfortable with that way or use the helper function EDMA_GetChannelDefaultTransferConfig to get initial data structure and then fine-tune needed members per requirement.
- Channel Identifier All channel is represented with a enumerator provided in edma_channel_t that user need not bother with whether channel 0 shall be 0 or 1 for register access.
- Functional layer provide multiple API function groups
- eDMA initialization and De-initialization Interfaces
- The interfaces target for whole module configuration which includes:
- the general configurations of the module, such as the arbitration type/continuous link/minor loop mapping/ halt on error.
- All channels specific transfer configuration.
- The interfaces provide helper function assist user to get a ready-to-use edma_config_t with easy-to-understand parameter filled to assist the usage of Init API.
- eDMA non-channel functional interfaces
- The functional interfaces used for the eDMA general configurations, the configuration apply for all channels thus need not call this group of function for each channel.
- eDMA channel functional interfaces
- The interfaces provide a api that support configure the full transfer parameters of a channel.
- The interfaces provide helper function assist user to get a ready-to-use edma_channel_transfer_config_t with easy-to-understand parameter filled to assist the usage of Init API.
- The interfaces provide configuration interface to configure separate transfer attribute into channel's eDMA hardware TCD register directly which is referred as hardware TCD. It is efficiency and useful when the majority of last transfer attribute still apply for next transfer and only limited fields need to updated.
- eDMA software TCD Functional Interfaces
- The interfaces to create a software TCD with provided edma_channel_transfer_config_t. This is used for scatter-gather feature.
- Note
- that memory of the edma_channel_tcd_t shall not be occupied until its transfer is done rather than return of this function. And in cache enabled system, make sure the memory is strongly ordered cause DMA and CPU is different master of bus and DMA module have no knowledge of what is kept in cache.
- eDMA Channel Status Interfaces
- The interfaces in the function group can be used to get/clear the channel status
- eDMA Channel Interrupt Interfaces
- The interfaces in the function group can be used to Enable/Disable channel interrupt
Transactional Layer
To distinguish from Functional layer, all transactional API get Transfer in the naming of the API.
In parallel with the Functional layer, the driver provide the transactional layer remove user the effort below thus user can easily get EDMA peripheral features enabled with least coding effort and much less knowledge requirement on this peripheral
- User need not spent effort on prepare the interrupt handler function from vector entry to the user code.
- User need not configure the scatter gather feature himself/herself but transfer are automatically linked on the sequence of submit the transfer configuration.
- User need not understand the relation-ship between software TCD and hardware TCD and how they are relevant to each other.
Comparing to functional layer,
- Transactional layer use the edma_channel_handle_t to represent a channel and user need the EDMA_CreateHandle function to initialize the handle. This handler will be stored inside driver thus channel state is retained in software level which is crucial in interrupt context.
- Transactional use same data structure edma_channel_transfer_config_t to configure the transfer to keep the consistent usage experience across this driver, but application shall not specify the psLinkTCD in edma_channel_transfer_config_t, since the transactional interface will link the multiple transfer request automatically.
One of the key feature in transactional layer comparing to functional layer is user need not setup interrupt related stuff(like enabling the system level interrupt enable/disable, prepare the vector entry function) but still get user's interrupt handling code executed once interrupt happens.
- Interrupt handling codes are automatically active when user get this driver files into application project. This is provided by the 'double weak' mechanism. You can find the detail in the general section of API Reference Manual. In brief, SDK place a weak function A in the vector entry for this peripheral. The default implementation of this function call another weak function B. By default, when a interrupt happen, the code will be executed as A (Weak) -> B (Weak). This driver provide the function B thus once user get the driver file into the application project, when interrupt happened, code will be executed as A (Weak) -> B (Non-Weak, in this driver) thus user is no longer need to do with the interrupt handling himself. If user get his/her own requirement of interrupt handling, define the A in the application and interrupt will be routed to his/her own interrupt function.
- Hidden interrupt handling doesn't mean user get no chance to insert his/her processing logic in the interrupt handling. User is requested to provide the callback function on EDMA_TransferCreateHandle and when interrupt happens, callback will be invoked immediately that user codes can be executed when interrupt happens.
Another important feature in transactional layer is the simplified transfer management for scatter-gather Scatter-Gather feature is powerful but not easy to understand and easy to introduce mistakes, such as De-active software TCD too early. Transactional layer maintain a buffer to store/link/maintain the required transfer internally thus application need only know which kind of transfer that it want to be executed and submit these transfers to this software layer and the left work will be done by driver. Note that user is still required to provide the memory address and size for the first time cause driver are not allowed to allocate memory itself to avoid memory leakage.
- Transactional layer provide flexibility that user can freely raise the transfer request anytime before or after start of the data transfer. The later submitted transfer will be automatically executed after previously transfer. is done by submit transfer with function EDMA_TransferSubmitSingleTransfer once the mentioned internal buffer still get empty slots to storing the new request.
- Note
- on doing with the race-condition, the driver use the way of disabling the global interrupt. This is not allowed for some application. For these application, user is not suggested to use the transactional layer.
- Transactional layer provide another important feature that user can configure the transfer in a loop which means user want the transfer in loop as A -> B -> C -> A -> ... and continuously update the buffer represented by these transfers without update transfer attributes during runtime. This is common in audio processing. A function EDMA_SubmitLoopTransfer is provided for this case. Note that Loop Transfer shall be setup before start of transfer.
Several function groups are provided for the transactional layer
- Transaction Initialization Interface
- Init the channel with a user provided handle memory data structure with callback requested for inserting user code on completion of transfer
- Interface for user to provide the memory used as internal software TCD buffer
- Transaction Transfer Configuration Interface
- Interfaces to submit transfer in loop mode or non-loop mode
- Transaction Start/Stop Interface
- Start/Stop/Abort the transfer
- Transaction Interrupt Handler
- Interrupt handling function which is called in the 'double weak' function. User is not necessary to understand it but can take it as reference code to understand how this driver work
How to use this driver
Functional Layer
- Configuration option on compiling
- FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL: Definition of this macro means this driver will not ungate/gate EMDA peripheral inside driver. Application code need to do with the clock gate/ungate himself/herself.
- Steps shall be covered in application codes rather than in driver
- The eDMA request source must be configured properly by the Direct Memory Access Multiplexer(DMAMUX).
- For the transfer between the memory and peripheral, the peripheral request source must be routed to a particular DMA channel.
- For the transfer between memory and memory, the DMAMUX support always-enabled DMA sources which are useful for the cases need software activation, that is to say, the software doesn't need to activate the channel instead of enabling the channel hardware request if always-enabled DMA sources are routed to a specified DMA channel.
- The eDMA peripheral driver clock gate may not be handled by the driver on some platforms if FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL is defined, so please make sure that the eDMA clock gate is enabled before eDMA driver interface being invoked if driver does not handle it.
- If interrupt mode is used, setup the interrupt handler entry to execute the application codes
- Driver Interface invoking Sequence
- Initialize the eDMA peripheral Use the eDMA Initialization and De-initialization Interfaces. eDMA driver provides an interface EDMA_Init which is target for whole module configurations.
- If the application would like to submit transfer configuration by EDMA_Init, please reference steps Prepare transfer configuration and then skip to step Trigger the transfer.
- If the channel request is enabled in the transfer configuration, please skips steps Trigger the transfer also.
- [Optional]Application can use eDMA non-channel functional interfaces for specific general feature enable.
- Prepare transfer configuration
- Use the edma_channel_transfer_config_t to prepare the transfer attributes and use the EDMA_GetChannelDefaultTransferConfig to assist the creation of the transfer configure. 3 typical modes are supported on getting the default configuration
- Memory To Memory: Transfer data from memory to memory which means tranSfer address of source/destination address is incremental.
- Memory To Peripheral: Transfer data from memory to peripheral where destination address is fixed. A typical case is sending a buffer by UART send register.
- Peripheral To Memory: Transfer data from peripheral to memory buffer where source address is fixed. A typical case is receiving data into memory buffer on UART RX.
- Configure the advanced transfer attributes by update the transfer configuration structure like channel link.
- With prepared transfer configuration, user can configure these attributes to hardware TCD or software TCD that peripheral can recognize it and execute it.
- Hardware TCD, User can use the EDMA_InitChannel to set the transfer or use the EDMA channel configuration function group to configure separate transfer attribute one by one.
- Software TCD, User can use the Software TCD interface to configure the transfer but note that software TCD memory can not be released until its represented transfer is done or abort.
- Submit the transfer
- Application can submit the transfer configurations by calling EDMA_SetChannelTransferConfig.
- If the software TCD is used, application can submit the software TCD by calling function EDMA_InstallChannelSoftwareTCD , otherwise this step should be skipped.
- Trigger the transfer
- User can trigger transfer manually using EDMA_SoftwareTriggerChannelStart
- [Recommended] User can leave the trigger action be done by the DMAMUX as mentioned in the first step, but need to enable the request by calling EDMA_EnableChannelRequest.
- Track the completion of transfer
- De-initialize the eDMA peripheral channel eDMA driver provides an interface EDMA_DeinitChannel which will reset all the channel relate transfer control descriptor registers.
Transactional Layer
- Configuration option on compiling:
- FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL: Definition of this macro means this driver will not ungate/gate EMDA peripheral inside driver. Application code need to do with the clock gate/ungate himself/herself.
- EDMA_ENTER_CRITICAL_SECTION/EDMA_LEAVE_CRITICAL_SECTION These access protection macros is disabled by default in driver, application could overwrite it for the tcd pool access protection if multiple task would like to access the same channel.
- Steps shall be covered in application codes rather than in driver
- Same as functional layer except that interrupt entry need not be setup by user.
- Driver Interface invoking Sequence
- Initialize the eDMA peripheral Use the eDMA Initialization and De-initialization Interfaces. eDMA driver provides an interface EDMA_Init which is target for whole module configurations, channel transfer configuration should not be submitted with EDMA_Init while using transactional layer.
- [Optional]Application can use eDMA non-channel functional interfaces for specific general feature enable.
- Init the handle by calling EDMA_TransferCreateHandle with optional callback provided and provide driver the software TCD memory and how many TCD slots supported. Note that user shall make sure this memory is always alive unless it is no longer used and make sure the memory in a strongly ordered memory region.
- Configure the Transfer
- For Loop transfer, call EDMA_TransferSubmitLoopTransfer, note that do not start the transfer before loop configuration is done
- For non-loop transfer, call EDMA_TransferSubmitSingleTransfer, user can submit the transfer request any time the install software TCD buffer still get empty slots. User can submit the non-loop transfer request before/after start of the transfer.
- Start/Stop/Abort channel use the EDMA_TransferStart/EDMA_TransferStop/EDMA_TransferAbort
- Handle transfer completion
- User's code will be executed on completion of transfer or transfers where it is provided in callback function on EDMA_TransferCreateHandle.
- The callback function can be called on completion of each TCD and callback function will be assigned with parameter that how many TCDs are completed that you need to re-setup these memory buffer. When system is in heavy load by other higher priority interrupt that there maybe the case that 2 or more TCD completion trigger only one callback.
Typical Use Cases
- Single transfer(using edma channel functional layer) For a single one shot transfer,
* config.psChannelTransferConfig[0]= &psTransfer;
*
For advance transfer case with modulo feature * psTransfer.pSrcAddr = xxx;
* psTransfer.pDstAddr = xxx;
* psTransfer.eSrcWidthOfEachTransfer = kEDMA_TransferWidth32Bits;
* psTransfer.eDstWidthOfEachTransfer = kEDMA_TransferWidth32Bits;
* psTransfer.i16SrcOffsetOfEachTransfer = 4U;
* psTransfer.i16DstOffsetOfEachTransfer = 4U;
* psTransfer.u32BytesEachRequest = 4;
* psTransfer.u32TotalBytes = 256;
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
- Scatter gather transfer(using edma channel functional layer)
* psTransfer->psLinkTCD = tcd;
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
- Loop transfer(using edma software TCD interfaces)
*
* psTransfer0->psLinkTCD = tcd[1];
* psTransfer1->psLinkTCD = tcd[0];
*
- Channel link(using edma initialization and de-initialization interface)
* psTransfer0->bEnableChannelMinorLoopLink = true;
* psTransfer0->eMinorLoopLinkChannel = 1;
*
* config.psChannelTransferConfig[0] = &psTransfer0;
* config.psChannelTransferConfig[1] = &psTransfer1;
*
*
* config.bEnableChannelMinorLoopLink = true;
* config.eMinorLoopLinkChannel = channel1;
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
*
- Single transfer (using edma transactional layer)
- Continuous single transfer request(using transactional layer dynamic scatter gather feature)
* psTransfer->srcAddrModulo = xxx;
*
* .....
*
- Loop transfer request(using transactional layer)
Transfer between peripheral(one fifo only) and memory (using edma channel functional layer)
Peripheral to memory:
* config.psChannelTransferConfig[0]= &psTransfer;
*
Memory to peripheral:
* 256, kEDMA_TransferWidth32Bits, kEDMA_ChannrlTransferMemoryToPeripheral);
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
*
Transfer between peripheral(multi fifo, assume fifo number 4, fifo address offset 4) and memory.
Peripheral to memory, reading 4 fifo data from peripheral and write to continuous memory block(using edma channel functional layer)
* psTransfer.i16SrcReadOffset = 4U;
* psTransfer.bEnableSrcMinorLoopOffset = true;
* psTransfer.i32MinorLoopOffset = -16U;
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
*
Memory to peripheral, writing data from continuous memory block to 4 fifo
* 256, kEDMA_TransferWidth32Bits, kEDMA_ChannrlTransferMemoryToPeripheral);
* psTransfer.i16DstOffsetOfEachTransfer = 4U;
* psTransfer.bEnableDstMinorLoopOffset = true;
* psTransfer.i32MinorLoopOffset = -16U;
*
* config.psChannelTransferConfig[0]= &psTransfer;
*
*