13.5.4 Indirect Addressing Operation

In the descriptor-based operation, DMA begins its operation upon triggered by fetching its descriptor(s) to retrieve the Payload Pointer(s) to be used for the data transfer, and the Descriptor Pointer Offsets to be used to determine the next Descriptor Pointer. It then proceeds to perform its data transfer operation by reading the payload from the location given in the Source Payload Pointer (SPP) or from the descriptor if the Source Descriptor Table (SDT) is enabled, and subsequently writing it to the location given in the Destination Payload Pointer (DPP) or from the descriptor if the Destination Descriptor Table (DDT) is enabled.

To properly support this mode of operation, at least one Descriptor Table must be preloaded into the memory mapped locations with the appropriate Descriptor Pointer(s) also programmed into the SADDR[23:0] or DADDR[23:0] registers or both. All other control and configuration registers must also be programmed accordingly.

Figure   1 illustrates the flow of operation with an example when only the Source Descriptor Table (SDT) is enabled.

Indirect Addressing

#include <xc.h>

// Descriptor table structure
typedef struct {
    uint32_t PayloadPointer : 16;
    uint32_t Size           : 2;
    int32_t OffsetNoMatch   : 7;
    int32_t OffsetIfMatch   : 7;
} DescriptorTable;

// Source and destination arrays
uint32_t __attribute__((address(0x4100))) dma_src_arr[5] = {0x1111, 0x2222, 0x3333, 0x4444, 0x5555};
uint32_t __attribute__((address(0x4500))) dma_dst_arr[10] = {0};

DescriptorTable __attribute__((address(0x5000))) dma_dst_dscr[5] = {0};

int main(void)
{
// Setup destination descriptor table
dma_dst_dscr[0].PayloadPointer = (uint32_t)&dma_dst_arr[0]; // copy src to dst_array[0]
dma_dst_dscr[0].Size = 2;                                   // 32-bit transfer
dma_dst_dscr[0].OffsetNoMatch = 1;                          // DPO = DPO + 1
    
dma_dst_dscr[1].PayloadPointer = (uint32_t)&dma_dst_arr[2]; // copy src to dst_array[2]
dma_dst_dscr[1].Size = 2;                                   // 32-bit transfer
dma_dst_dscr[1].OffsetNoMatch = 1;                          // DPO = DPO + 1
    
dma_dst_dscr[2].PayloadPointer = (uint32_t)&dma_dst_arr[4]; // copy src to dst_array[4]
dma_dst_dscr[2].Size = 2;                                   // 32-bit transfer
dma_dst_dscr[2].OffsetNoMatch = 1;                          // DPO = DPO + 1
    
dma_dst_dscr[3].PayloadPointer = (uint32_t)&dma_dst_arr[6]; // copy src to dst_array[6]
dma_dst_dscr[3].Size = 2;                                   // 32-bit transfer
dma_dst_dscr[3].OffsetNoMatch = 1;                          // DPO = DPO + 1
    
dma_dst_dscr[4].PayloadPointer = (uint32_t)&dma_dst_arr[8]; // copy src to dst_array[8]
dma_dst_dscr[4].Size = 2;                                   // 32-bit transfer
dma_dst_dscr[5].OffsetNoMatch = 1;                          // DPO = DPO + 1
    
// Reset DMA module and channel
DMACONbits.ON = 0;
DMA0CH = 0;
    
// Set up DMA address limits
DMALOW = 0x4000;
DMAHIGH = 0xFFFFFF;
    
// Destination Descriptor Enable
DMA0CHbits.DDTEN = 1;
    
DMA0CHbits.SAMODE = 1;                 // increment source address by SIZE
DMA0CHbits.TRMODE = 2;                 // Continuous mode
DMA0CHbits.SIZE = 2;                   // 32-bit data transfer
    
    
DMA0SRC = (uint32_t)dma_src_arr;       // Set source address
DMA0DST = (uint32_t)dma_dst_dscr;      // Set descriptor table address
DMA0CNT = 5;                           // Number of transfers
    
// Enable DMA channel and module
DMA0CHbits.CHEN = 1;
DMACONbits.ON = 1;
    
// Perform SW triggered transfer
DMA0CHbits.CHREQ = 1;
while (DMA0CHbits.CHREQ == 1);
    
// After the transfer dma_dst_arr should reflect the following values
// dma_dst_arr[0] = 0x1111
// dma_dst_arr[2] = 0x2222
// dma_dst_arr[4] = 0x3333
// dma_dst_arr[6] = 0x4444
// dma_dst_arr[8] = 0x5555
// .. with the other array members being zero/not modified

while (1);
return 1;
}
Figure 13-10. SDT-Enabled Operation - Random to Block Locations
Note:
  1. DPO1M1 = DPO1M0 = 1 for +4, SIZE1[1:0] = 10 for 32-bit transfer
  2. DPO2M1 = DPO2M0 = 2 for +8, SIZE2[1:0] = 10 for 32-bit transfer

Figure   2 illustrates the flow of operation with an example when only the Destination Descriptor Table is enabled. This essentially follows the transaction of the indirect addressing operation with the extracted Payload Pointer being completely independent from one another, thus allowing for random access of any memory mapped locations.

Figure 13-11. DDT-Enabled Operation - Block to Random Locations
Note:
  1. DPO1M1 = DPO1M0 = 1 for +4, SIZE1[1:0] = 10 for 32-bit transfer
  2. DPO2M1 = DPO2M0 = 2 for +8, SIZE2[1:0] = 10 for 32-bit transfer

With the descriptor-based operation utilizing indirect addressing, random locations can be accessed consecutively by one single DMA channel without requiring any CPU intervention. This helps to protect the precious system resources.