mDMA implements DMA APIs for mbed. It is inspired by modDMA and simpleDMA. Compared with other mbed DMA implementations, mDMA has new features like 1) support LLI 2) support more than 4KB data transfer 3) support vectorized transfer. 4) support burst transfer. 5) Improved memory-memory transfer. It could beat memcpy 6) The library implementation fit the code structure of mbed sdk. Currently only support LPC1768 but could be extended to other platforms.

Dependents:   test_mDMA

dma_api.c

Committer:
steniu01
Date:
2015-03-09
Revision:
1:9421d79fb372
Parent:
0:8e50c5fd42f6

File content as of revision 1:9421d79fb372:

#include "dma_api.h"
#include <assert.h>


int _channel_num = 8;

//Define LPC1768 DMA IRQ. Different platforms might have different name
IRQn_Type _DMA_IRQ = DMA_IRQn;

//DMA channel control register bit set
#define DMA_CCxControl_TransferSize_Pos 0
#define DMA_CCxControl_SBSize_Pos 12
#define DMA_CCxControl_DBSize_Pos 15
#define DMA_CCxControl_SWidth_Pos 18
#define DMA_CCxControl_DWidth_Pos 21
#define DMA_CCxControl_SI_Pos 26
#define DMA_CCxControl_DI_Pos 27
#define DMA_CCxControl_I_Pos 31

//DMA Channel config register bit set
#define DMA_CCxConfig_E_Pos 0
#define DMA_CCxConfig_SrcPeripheral_Pos 1
#define DMA_CCxConfig_DestPeripheral_Pos 6
#define DMA_CCxConfig_TransferType_Pos 11
#define DMA_CCxConfig_IE_Pos 14
#define DMA_CCxConfig_ITC_Pos 15
#define DMA_CCxConfig_L_Pos 16
#define DMA_CCxConfig_A_Pos 17
#define DMA_CCxConfig_H_Pos 18

// DMA interrupt callback
func_ptr dma_irq_finish[8];
func_ptr dma_irq_error[8];



// Optimisation 1. Use burst mode
#define BURST_ENABLED 1
// Optimisation 2. Pack bytes/half  to word
#define  WORD_TRANSFER 1
// define whether transfer large size data supported
//#define LARGE_DATA_TRANSFER 1


// map the trigger index
int trigger_value [33] = {
    -1, // ALWAYS ,
    0, // _SSP0_TX,
    1, // _SSP0_RX,
    2, // _SSP1_TX,
    3, // _SSP1_RX,
    4, // _ADC,
    5, // _I2S0,
    6, // _I2S1,
    7, // _DAC,
    8, // _UART0_TX,
    9, // _UART0_RX,
    10, // _UART1_TX,
    11, // _UART1_RX,
    12, // _UART2_TX,
    13, // _UART2_RX,
    14, // _UART3_TX,
    15, // _UART3_RX,
    24, // _MATCH0_0,
    25, // _MATCH0_1,
    26, // _MATCH1_0,
    27, // _MATCH1_1,
    28, // _MATCH2_0,
    29, // _MATCH2_1,
    30, // _MATCH3_0,
    31 // _MATCH3_1,
};



typedef struct DMA_InitTypeDef{
    uint32_t DMA_DstAddr; //Specifies the destination base address for DMAy Channelx. 
    uint32_t DMA_SrcAddr;     // Specifies the source base address for DMAy Channelx. 
    uint32_t DMA_TransferSize;// Specifies the source  transfer size  
    uint32_t DMA_SrcBurst; // Specifies the source  burst size    
    uint32_t DMA_DestBurst; // Specifies the destination burst size   
    uint32_t DMA_SrcWidth; //Specifies the source transfer width   
    uint32_t DMA_DestWidth; // Specifies the destination transfer width   
    uint32_t DMA_LLI; // Specifies the next link address   
    LLI* LLI_list[32]; // Declare an array to store up to 32 linked lists. We probably won't need that much
    unsigned int LLI_count; 
    bool DMA_SrcInc;  // Specifies whether the source is incremented or not 
    bool DMA_DestInc; // Specifies whether the destination is incremented or not 
    bool DMA_TermInt; // Specifies whether the terminal count interrupt enabled or not 
    uint32_t DMA_TriggerSource; // Specifies the source trigger 
    uint32_t DMA_TriggerDestination; // Specifies the destination trigger 
    TransferType  DMA_TransferType;
}DMA_InitTypeDef ;



/**
  * @brief  Check whether the address is with memory ranage.
  * @param  src: the physical address
  * @retval 0 or 1
  */
inline static bool is_memory(uint32_t addr);

/**
 * @brief  Static function. Automatice cacluate the transfer type according to the source and destination address
 * @param  src_addr. Source starting address.
 * @param  dst_addr. Destination starting address.
 * @retval Transfer type. It can be M2M,M2P,P2M or P2P
 */
static TransferType get_transfer_type(uint32_t src_addr, uint32_t dst_addr );

/**
  * @brief  Static function. Get the channel address according to the channel number
  * @param  channel. The chosen channel number
  * @retval Chosen channel base address
  */
    
inline static LPC_GPDMACH_TypeDef* return_channel(int channel)
{
    return (LPC_GPDMACH_TypeDef*)(LPC_GPDMACH0_BASE + 0x20*channel);
}


/**
  * @brief    Enable DMA Burst mode for optimisation
  */


void DMA_destination(DMA_InitTypeDef* DMA_InitStruct, uint32_t dst, unsigned int width, bool inc)
{
    DMA_InitStruct->DMA_DstAddr = dst;
    DMA_InitStruct->DMA_SrcBurst = 0x00; // 1 byte To be done
    DMA_InitStruct->DMA_DestWidth = (width >>4);
    DMA_InitStruct->DMA_DestInc = inc;
}


void DMA_source(DMA_InitTypeDef* DMA_InitStruct, uint32_t src, unsigned int width, bool inc)
{
    DMA_InitStruct->DMA_SrcAddr = src;
    DMA_InitStruct->DMA_SrcBurst = 0x00; // 1 byte
    DMA_InitStruct->DMA_SrcInc = inc;
    DMA_InitStruct->DMA_SrcWidth = (width >> 4);
}

    
void DMA_trigger_source(DMA_InitTypeDef* DMA_InitStruct, TriggerType trig)
{
    DMA_InitStruct->DMA_TriggerSource = trigger_value[trig];
}


void DMA_trigger_destination(DMA_InitTypeDef* DMA_InitStruct, TriggerType trig)
{
    DMA_InitStruct->DMA_TriggerDestination = trigger_value[trig];
}
// Currently, not support more than 4K transfer in LLI
void DMA_next(DMA_InitTypeDef* DMA_InitStruct, const uint32_t src, const uint32_t dst, uint32_t size)
{
      //static int count = 0; 
      DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count] = malloc(sizeof(LLI));
      DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_dst = dst;
      DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_src = src;
      DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_next = 0; // 0 not NULL as the programmer manual says LLI_next should be 0 if it is the last of the lists
      DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_control = size & 0xFFF; 
      DMA_InitStruct->LLI_count++;
//  TBD: need to add assert to check the input size;
}


/*
void DMA_next(DMA_InitTypeDef* DMA_InitStruct, LLI* list)
{
  DMA_InitStruct->DMA_LLI = (uint32_t)list; 
}
*/


void DMA_init(int channel, DMA_InitTypeDef* DMA_InitStruct)
{

    LPC_GPDMACH_TypeDef* GPDMA_channel;
    // Get DMA channel address
    GPDMA_channel = return_channel(channel);
    // Calcuate transfer type according to source address and destination address
    DMA_InitStruct->DMA_TransferType = get_transfer_type(DMA_InitStruct->DMA_SrcAddr, DMA_InitStruct->DMA_DstAddr);
    // Reset interrupt pending bits for DMA Channel
    LPC_GPDMA->DMACIntTCClear = 1<<channel;
    LPC_GPDMA->DMACIntErrClr = 1<<channel;
       

    /*--------------------------- DMAy channelx request select register ----------------*/
    // Choose UART or Timer DMA request for DMA inputs 8 through 15
    if ((DMA_InitStruct->DMA_TriggerSource & 0x10) || (DMA_InitStruct->DMA_TriggerDestination & 0x10))
        LPC_SC->DMAREQSEL |= 1 << ((DMA_InitStruct->DMA_TriggerSource || DMA_InitStruct->DMA_TriggerSource) - 24); // I don't know why. The user manual says DMAREQSET is GPDMA register, but in mbed, it is LPC_SC register
    else
        LPC_SC->DMAREQSEL &= ~(1 << ((DMA_InitStruct->DMA_TriggerSource - 24)&& (DMA_InitStruct->DMA_TriggerDestination - 24)));


    /*--------------------------- DMAy channelx source and destination ----------------*/
    // Write to DMA channel source address
    GPDMA_channel->DMACCSrcAddr = DMA_InitStruct->DMA_SrcAddr;
    // Write to DMA channel destination address
    GPDMA_channel->DMACCDestAddr = DMA_InitStruct->DMA_DstAddr;

    #ifdef BURST_ENABLED
    if (DMA_InitStruct->DMA_TransferType == M2M)
    {
      /*always set burst size as 256 as the performance is same no matter what the burst size is. Need to double confirm, it seems the 
        interconnect would wrap the burst size as 4 no matter what the DMA AHB has set. This is to avoid DMA AHB bus take over too many bus bandwidth*/
        DMA_InitStruct->DMA_SrcBurst = 7; // 
        DMA_InitStruct->DMA_DestBurst = 7; 
    }
        #endif

    /*--------------------------- DMAy channelx control register ----------------*/
    // Set TransferSize
    // Set source burst size
    // Set destination burst size
    // Set source width size
    // Set destination width size
    // Set source increment
    // Set destination increment
    // Set destination increment
    // Enable terminal count interrupt
    GPDMA_channel->DMACCControl |= (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos)  |
                                   (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
                                   (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos)  |
                                   (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
                                   (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos)    |
                                   (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos)   |
                                   (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos );

    /*--------------------------- DMAy channelx configuration register ----------------*/
    // Set source periheral trigger. If the source is memory, this bit is ignored. Set according to low 4 bit of DMA_Trigger
    // Set destination periheral trigger. If the destination is memory, this bit is ignored. Set according low 4 bit of DMA_Trigger
    // Set transfer type: M2M, M2P, P2M, M2M
    GPDMA_channel->DMACCConfig |= ((DMA_InitStruct->DMA_TriggerSource & 0x0f) << DMA_CCxConfig_SrcPeripheral_Pos)|    //set SrcPeripheral according low 4 bit of DMA_Trigger
                                  ((DMA_InitStruct->DMA_TriggerDestination & 0x0f) << DMA_CCxConfig_DestPeripheral_Pos)|      //set DstPeripheral according low 4 bit of DMA_Trigger
                                  (DMA_InitStruct->DMA_TransferType << DMA_CCxConfig_TransferType_Pos);
        
    /* set link list items register*/
    if(DMA_InitStruct->LLI_count != 0)
    {
        int j;
        for (j=0; j < DMA_InitStruct->LLI_count; j++) 
        {
            DMA_InitStruct->LLI_list[j]->LLI_control |= GPDMA_channel->DMACCControl;
            if (j< DMA_InitStruct->LLI_count - 1)
                DMA_InitStruct->LLI_list[j]->LLI_next = (uint32_t)DMA_InitStruct->LLI_list[j+1];
            }
            GPDMA_channel->DMACCLLI = (uint32_t)DMA_InitStruct->LLI_list[0];  
        }
            //LPC_GPDMA->DMACSync =0x1;                                                         
}//end of DMA_init


void DMA_start(int channel, DMA_InitTypeDef* DMA_InitStruct, unsigned int length)
{
    LPC_GPDMACH_TypeDef* volatile GPDMA_channel;
    // Get DMA channel address
    GPDMA_channel = return_channel(channel);

    // Set the transfer size
    // Put it here rather than in DMA_Init. So that one could send new transfer data without reinit other registers

    int length_low;
    int length_high;
    int length_unalligned; 
    int length_alligned; 
    
    if ((DMA_InitStruct->DMA_DstAddr % sizeof(long) == 0) && (DMA_InitStruct->DMA_SrcAddr % sizeof(long) == 0) && // Only use word aligned when starting addresss is at 4 bytes boundary
        (length_low >=4) && // Only use word aligned when length is no less than 4
        (DMA_InitStruct->DMA_SrcInc == 1) &&   // Only use word aligned when the source is incretmental 
        (DMA_InitStruct->DMA_DestInc == 1) &&  // Only use word aligned when the address is incretmental
        (DMA_InitStruct->DMA_SrcWidth < 0x2))  // Only use word aligned when the source width is less than 4 bytes      
    {
                
        unsigned int old_SrcWidth = DMA_InitStruct->DMA_SrcWidth;
        unsigned int old_DstWidth = DMA_InitStruct->DMA_DestWidth;
        unsigned int new_width = 2; // New width will be word
            

        length_unalligned = (length & 0x3) >> DMA_InitStruct->DMA_SrcWidth;
        length_alligned = length - length_unalligned;
                 
        int length_new = length_alligned << DMA_InitStruct->DMA_SrcWidth >> new_width;
            
        GPDMA_channel->DMACCConfig &= ~(1ul << DMA_CCxConfig_ITC_Pos); // mask DMA terminate count IRQ
        GPDMA_channel->DMACCControl |= 1ul << DMA_CCxControl_I_Pos;
        // Unmask IE
        GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_IE_Pos;  // unmask DMA Errr IRQ
            
        while (length_new > 0xfff)
        {
                    
            GPDMA_channel->DMACCControl = 0xfff << DMA_CCxControl_TransferSize_Pos |
                                          (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos)  |
                                          (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
                                          (2 << DMA_CCxControl_SWidth_Pos)  |
                                          (2 << DMA_CCxControl_DWidth_Pos) |
                                          (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos)    |
                                          (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos)   |
                                          (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);  
                    
                    
                    
            LPC_GPDMA->DMACConfig = 0x01;
            GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
            length_new -= 0xfff;
                    
            // DMACCSrcAddr currently hold the end address of first part
            GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr  + 1;
            GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr +  1;
            while (DMA_channel_active(channel));
        }
                
        GPDMA_channel->DMACCControl = length_new << DMA_CCxControl_TransferSize_Pos |
                                      (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos)  |
                                      (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
                                      (2 << DMA_CCxControl_SWidth_Pos)  |
                                      (2 << DMA_CCxControl_DWidth_Pos) |
                                      (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos)    |
                                      (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos)   |
                                      (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);  
                                    
        LPC_GPDMA->DMACConfig = 0x01;
        GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
        while (DMA_channel_active(channel));
                
        GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr  + 4;
        GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr +  4;
                
        DMA_InitStruct->DMA_TransferSize = length_unalligned;
        GPDMA_channel->DMACCControl = length_unalligned << DMA_CCxControl_TransferSize_Pos |
                                      (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos)  |
                                      (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
                                      (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos)  |
                                      (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
                                      (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos)    |
                                      (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos)   |
                                      (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);                                                                              
        }
        
        
        else
        {
            GPDMA_channel->DMACCConfig &= ~(1ul << DMA_CCxConfig_ITC_Pos); // mask DMA terminate count IRQ
            GPDMA_channel->DMACCControl |= 1ul << DMA_CCxControl_I_Pos;
            // Unmask IE
            GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_IE_Pos;  // unmask DMA Errr IRQ
            
            while (length > 0xfff)
            {
                GPDMA_channel->DMACCControl = 0xfff << DMA_CCxControl_TransferSize_Pos |
                                               (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos)  |
                                               (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
                                               (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos)  |
                                               (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
                                               (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos)    |
                                               (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos)   |
                                               (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);  
                    
                    
                    
                 LPC_GPDMA->DMACConfig = 0x01;
                 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
                 length -= 0xfff;
                    
                 // DMACCSrcAddr currently hold the end address of first part
                 GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr  + 1;
                 GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr +  1;
                 while (DMA_channel_active(channel));
            }
                

                
            GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr  + 1;
            GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr +  1;
                
            DMA_InitStruct->DMA_TransferSize = length;
        }
                
        
    // Enable Interrupt
    // Unmask ITC
    GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_ITC_Pos;

  
    GPDMA_channel->DMACCControl |= DMA_InitStruct->DMA_TransferSize << DMA_CCxControl_TransferSize_Pos;
        
    DMA_InitStruct->LLI_count = 0; // Clear the LLI lists count so that we could reuse this channel next time
    // Enable DMA 
    LPC_GPDMA->DMACConfig = 0x01;
    GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
}


bool DMA_channel_active(int channel)
{
    LPC_SC->PCONP |= (1 << 29);
    return (LPC_GPDMA->DMACEnbldChns && (1<<channel));
}



void DMA_reset(int channel)
{
    assert (channel <= _channel_num && channel>=0); 
    LPC_SC->PCONP |= (1 << 29);
    LPC_GPDMACH_TypeDef* GPDMA_channel;
    //Get DMA channel address
    GPDMA_channel = return_channel(channel);
    GPDMA_channel->DMACCConfig = 0;
    GPDMA_channel->DMACCControl = 0;
    GPDMA_channel->DMACCLLI = 0;
    GPDMA_channel->DMACCSrcAddr = 0;
    LPC_GPDMA->DMACIntErrClr |= 1 << channel; // clear the interrupt
    LPC_GPDMA->DMACIntTCClear |= 1 << channel;
    dma_irq_error [channel] = 0;
    dma_irq_finish[channel] = 0;  
}


DMA_InitTypeDef* DMA_struct_create(void)
{
    DMA_InitTypeDef* DMA_InitStruct = (DMA_InitTypeDef*) malloc(sizeof(DMA_InitTypeDef));
    /*-------------- Reset DMA init structure parameters values ------------------*/
    DMA_InitStruct->DMA_DstAddr = 0;
    DMA_InitStruct->DMA_SrcAddr = 0;
    DMA_InitStruct->DMA_TransferSize = 0;
    DMA_InitStruct->DMA_SrcBurst = 0;
    DMA_InitStruct->DMA_DestBurst = 0;
    DMA_InitStruct->DMA_SrcWidth = 0;
    DMA_InitStruct->DMA_DestWidth = 0;
    DMA_InitStruct->DMA_LLI = 0;
    DMA_InitStruct->LLI_count = 0 ; 
    DMA_InitStruct->DMA_SrcInc = 0;
    DMA_InitStruct->DMA_DestInc = 0;
    DMA_InitStruct->DMA_TermInt = 1; // always enable terminal count interrupt in default
    DMA_InitStruct->DMA_TriggerSource = trigger_value[ALWAYS];
    DMA_InitStruct->DMA_TriggerDestination = trigger_value[ALWAYS];
    DMA_InitStruct->DMA_TransferType = M2M;
    return DMA_InitStruct;
} //end of DMA_StructInit

void DMA_struct_delete(DMA_InitTypeDef* ptr)
{
    free(ptr);
}

inline static bool is_memory(uint32_t addr)
{
    if ((addr >> 28) == 0 || (addr >> 28)== 1)
        return 1;
    else
        return 0;
}


void DMA_IRQ_handler(void)
{
    //only call the attached function when certain interrupt happened on the right channel
    unsigned i ; 
    uint32_t FinishStatus=0;
    uint32_t ErrStatus=0;
    FinishStatus = LPC_GPDMA->DMACIntTCStat & 0xFF;
    ErrStatus = LPC_GPDMA->DMACIntErrStat & 0xFF;
    
     
    if (FinishStatus !=0) //only checking when there is interrupt happened
        for (i =0; i < 8; i++) // roundrobin checking how many channels get interrupts 
        {
            if (FinishStatus & 1 << i)
                dma_irq_finish[i]();
        }
    if (ErrStatus !=0)          
         for (i =0; i < 8; i++) 
         {
             if (ErrStatus & 1 << i)
                 dma_irq_error[i]();
         }       
  
    LPC_GPDMA->DMACIntTCClear |= FinishStatus;
    LPC_GPDMA->DMACIntErrClr |= ErrStatus;
}


void DMA_IRQ_attach(int channel, int status, func_ptr ptr)
{
    assert(channel <= _channel_num && channel >= 0);
    assert(status == ERR || status == FINISH);
    
    if (status == ERR)
        dma_irq_error[channel] = ptr;
    else if (status == FINISH)
        dma_irq_finish[channel] = ptr;
}



void DMA_IRQ_detach(int channel)
{
    dma_irq_error[channel] = 0;
    dma_irq_finish[channel] = 0;
}



static TransferType get_transfer_type(uint32_t src_addr, uint32_t dst_addr)
{
    if (is_memory(src_addr)) {   //if source is memory
        if(is_memory(dst_addr))  //if destination is memory
            return M2M;    //return M2M if source is memory and destination is memory.
        else    //if destination is peripheral
            return M2P;    //return M2P if source is memory and destination is peripheral
    } else {        //if source is peripheral
        if(is_memory(dst_addr))      //if destination is memory
            return P2M;     //return P2M if source is peripheral and destination is memory
        else        //if destination is peripheral
            return P2P;     //return P2P if source is peripheral and destination is peripheral
    }
}