# New DMA: Overview

In addition to the four DMA channels that were available with NITRO, the TWL hardware has four new DMA channels. The new DMA allows more precise configuration than was possible with the legacy DMA. This section describes the new DMA.

## MINDmaConfig Structure

The new DMA uses the members of the `MINDmaConfig` structure as parameters when it is running. This structure contains information that is not likely to change for each DMA operation. It does not contain values that will change on a case-by-case basis, such as the overall transfer size or the source/destination addresses for the transfer.

Specifically, the structure contains the following values.

Member Name | Description | Related Register | Initial Value
--- | --- | --- | ---
`intervalTimer` | Interval timer for block transfers. | d15-0 in DMAxBCNT | `MI_NDMA_NO_INTERVAL`
`prescaler` | Prescaler of interval timer for block transfers. | d17-16 in DMAxBCNT | `MI_NDMA_INTERVAL_PS_1`
`blockWord` | Number of words in the block transfer. | d19-16 in DMAx_CNT | `MI_NDMA_BWORD_1`
`wordCount` | Number of words in the transfer. | DMAxWCNT | `MI_NDMA_AT_A_TIME`

Separate structures are provided for each of the four new DMA channels within the library, so DMA transfers can be performed using different settings for each channel.

Call the `MI_InitNDmaConfig` function to initialize the `MINDmaConfig` structure members for all NDMA channels. You do not have to call this function directly because it is called from the `MI_InitNDma` function (which in turn is called from the `OS_Init` function).

Call the `MI_GetNDmaConfig` function to copy the `MINDmaConfig` structure set for the current new DMA channel to a `MINDmaConfig` structure prepared by the user (developer). Call the `MI_SetNDmaConfig` function to copy a `MINDmaConfig` structure prepared by the user (developer) to the `MINDmaConfig` structure set for the new DMA channel.

The `MI_SetNDmaInterval`, `MI_GetNDmaIntervalTimer`, `MI_GetNDmaIntervalPrescaler`, `MI_SetNDmaBlockWord`, `MI_GetNDmaBlockWord`, MI_SetNDmaWordCount, and `MI_GetNDmaWordCount` functions overwrite or get some of the members of the `MINDmaConfig` structure set for the new DMA.

To initialize settings that have been overwritten, use the `MI_InitNDmaConfig` function described earlier.

## DMA Operation Instructions

The following instructions exist for the new DMA.

`MI_NDmaCopy*` Uses the new DMA to copy.
`MI_NDmaFill*` Uses the new DMA to fill with the specified data.
`MI_NDmaClear*` Uses the new DMA to fill with 0s.
`MI_NDmaSend*` Uses the new DMA to send data to a fixed address.
`MI_NDmaRecv*` Uses the new DMA to reads data from a fixed address.
`MI_NDmaPipe*` Uses the new DMA to send data from a fixed address to another fixed address.

Reading or writing data from a fixed address makes frequent use of operations on the I/O registers.

For each of these, there are derivative functions that have minor differences in terms of their operation. Below is an example that explains the `MI_NDmaCopy*` functions, but the naming conventions shown below apply to other functions as well.

### (1) MI_NDmaCopy and MI_NDmaCopyAsync

Functions that end in `Async` are asynchronous functions. Conversely, functions that do not are synchronous functions. Synchronous functions are guaranteed to wait for new DMA operations to complete, but control leaves asynchronous functions without waiting for completion. Asynchronous functions can find out that a DMA operation has completed, either by setting a callback function that is called when the operation completes, or by using the `MI_IsNDmaBusy` function, which determines whether the given new DMA channel is in use. The `MI_WaitNDma` function waits until the given new DMA channel becomes usable.

### (2) MI_NDmaCopy and MI_NDmaCopyEx

Functions that include `Ex` are provided with an external `MINDmaConfig` structure. Conversely, functions without "`Ex`" use the `MINDmaConfig` structures that are maintained by the library and already configured. Functions with `Ex` are effective for initiating temporary DMA operations using different parameters from those that are normally used.

### (3) MI_NDmaCopy and MI_NDmaCopy_Dev

Functions that end in `_Dev` synchronize the start of DMA operations with an interrupt from a peripheral device. Conversely, functions without "`_Dev`" start operations immediately. See DMA Start Timing for the values that can be set as peripheral devices.

### (4) MI_NDmaCopy and MI_NDmaCopy_SetUp

Functions that end in `_SetUp` only perform configuration, and do not start DMA operations. The actual DMA operations start after `MI_NDmaRestart` is called.

These four specifiers yield a total of 16 variants of the `MI_NDmaCopy*` function.

Functions | Synchronous/Asynchronous | MINDmaConfig | Start DMA
--- | --- | --- | ---
No `_SetUp` (Run DMA)
`MI_NDmaCopy` | Synchronous | Stored in the library | Immediately
`MI_NDmaCopyAsync` | Asynchronous | Stored in the library | Immediately
`MI_NDmaCopyEx` | Synchronous | User-defined | Immediately
`MI_NDmaCopyExAsync` | Asynchronous | User-defined | Immediately
`MI_NDmaCopy_Dev` | Synchronous | Stored in the library | Interrupt from peripheral device
`MI_NDmaCopyAsync_Dev` | Asynchronous | Stored in the library | Interrupt from peripheral device
`MI_NDmaCopyEx_Dev` | Synchronous | User-defined | Interrupt from peripheral device
`MI_NDmaCopyExAsync_Dev` | Asynchronous | User-defined | Interrupt from peripheral device
With `_SetUp` (Only perform configuration for running DMA)
`MI_NDmaCopy_SetUp` | Synchronous | Stored in the library | Immediately
`MI_NDmaCopyAsync_SetUp` | Asynchronous | Stored in the library | Immediately
`MI_NDmaCopyEx_SetUp` | Synchronous | User-defined | Immediately
`MI_NDmaCopyExAsync_SetUp` | Asynchronous | User-defined | Immediately
`MI_NDmaCopy_Dev_SetUp` | Synchronous | Stored in the library | Interrupt from peripheral device
`MI_NDmaCopyAsync_Dev_SetUp` | Asynchronous | Stored in the library | Interrupt from peripheral device
`MI_NDmaCopyEx_Dev_SetUp` | Synchronous | User-defined | Interrupt from peripheral device
`MI_NDmaCopyExAsync_Dev_SetUp` | Asynchronous | User-defined | Interrupt from peripheral device

## Stopping New DMA

Call `MI_StopNDma` to stop the specified new DMA channel.

Call `MI_StopAllNDma` to stop all new DMA channels. This function calls `MI_StopNDma` for the new DMA channels 0-3 in order.

## Arbitration Method

You can select the the arbitration method used between the new DMA channels. Two options are available: the fixed method and the round-robin method.

With the fixed method, fixed priorities are set. If a request for a DMA channel is issued whose priority is higher than the DMA channel that is currently running, the current request is paused once the block transfer has completed for the number of words specified in `WORDCNT`, and the DMA transfer for the higher-priority DMA channel is run. Conversely, if a request is issued for a DMA channel that is lower in priority than the one currently running, the incoming request is put on hold.

With the round-robin method, the MI library looks up the DMA channel during the startup request, and once the block transfer has completed for `WORDCNT` words (this number is specified for each general-purpose DMA channel), the ownership rights to the AHB bus alternates in round-robin fashion in the order DMA0, DMA1, DMA2, DMA3, DSP or ARM9, DMA0, DMA1, DMA2, ... (including all DMA channels for which there are DMA transfer requests).

When using the round-robin method, you can set the number of cycles for which access is possible during requests for the AHB bus of the DSP or ARM9.

The following function is used to configure the arbitration method and the number of cycles that are accessible when the round-robin method is used. `MI_SetNDmaArbitrament`

The following functions are used to get the values that are currently set.
`MI_GetNDmaArbitrament`
`MI_GetNDmaArbitramentRoundRobinCycle`

## Priority

The priorities for the AHB bus are shown below.

If using the fixed method for DMA arbitration:

Old DMAC0 > Old DMAC1 > Old DMAC2 > Old DMAC3 > New DMAC0 > New DMAC1 > New DMAC2 > New DMA3 > DSP > ARM9

If using the round-robin method for DMA arbitration:

The new DMA priorities are removed, and the bus ownership is passed around in the following order:
Old DMAC0 > Old DMAC1 > Old DMAC2 > Old DMAC3 > (New DMAC0 = New DMAC1 = New DMAC2 = New DMA3) > DSP > ARM9

## DMA Start Timing

Among the new DMA functions, functions that end in `_Dev`, such as `MI_NDmaCopy_Dev`, can make the timing of starting DMA dependent on a startup request based on an interrupt from a peripheral device.?Functions that do not end in `_Dev` start immediately.

You can specify this by passing the `MINDmaDevice` type value (`u32 typedef`) as an argument of a function. The following table shows the values and start timings.

`MINDmaDevice` Type Value | New DMA Start Timing
--- | ---
`MI_NDMA_TIMING_TIMER0` | DMA startup request from Timer 0
`MI_NDMA_TIMING_TIMER1` | DMA startup request from Timer 1
`MI_NDMA_TIMING_TIMER2` | DMA startup request from Timer 2
`MI_NDMA_TIMING_TIMER3` | DMA startup request from Timer 3
`MI_NDMA_TIMING_CARD` | DMA startup request from Card
`MI_NDMA_TIMING_V_BLANK` | DMA startup request from V-Blank
`MI_NDMA_TIMING_H_BLANK` | DMA startup request from H-Blank
`MI_NDMA_TIMING_DISP` | DMA startup request synchronized with display
`MI_NDMA_TIMING_DISP_MMEM` | DMA startup request synchronized with the work RAM display
`MI_NDMA_TIMING_GXFIFO` | DMA startup request from the geometry command FIFO
`MI_NDMA_TIMING_CAMERA` | DMA startup request from camera transfer

## Revision History

2008/11/19 Added startup timing information.
2008/09/11 Initial version.

CONFIDENTIAL