/* This example code shows how MDMA can be used in descriptor based mode to emulate circular
 * buffering supported by EMDMA. It also compared the performance between the two approaches for a given use case.
 *
 *
 */

/*=============  INCLUDES  =============*/
#include "Test6_MDMA_circular_buffer_Core1.h"
/* Managed drivers and/or services include */
#include "../system/adi_initialize.h"
volatile clock_t clock_start;
volatile clock_t clock_stop;
volatile clock_t cycles;
volatile clock_t cycles_offset;

/*=============  LOCAL DEFINES   =============*/
/* When the processor's L1 and/or L2 cache is enabled the receive data buffers must */
/* be aligned on cache line boundaries. If they are not then any data access by the core may        */
/* pull a buffer into the cache while the DMA is operating on the data buffer. Both the core and    */
/* the DMA are masters on the bus. The core will be performing cache operations. The DMA engine will*/
/* not be. So it is important for the application to both align data that will be operated on by the*/
/* DMA and to separate this data from other application data.                                       */
#define DSTALIGN ADI_CACHE_ALIGN_MIN_4
#define SRCALIGN __attribute__((aligned(32)))

/*=============  DATA  =============*/
/* DMA Stream Handle */
static ADI_DMA_STREAM_HANDLE   hMemDmaStream;

/* Source DMA Handle */
static ADI_DMA_CHANNEL_HANDLE  hSrcDmaChannel;

/* Destination DMA Handle */
static ADI_DMA_CHANNEL_HANDLE  hDestDmaChannel;

/* Flag to register Memory DMA copy completion */
static volatile bool bMemCopyInProgress;

/* Memory to handle DMA Stream */
static uint8_t MemDmaStreamMem[ADI_DMA_STREAM_REQ_MEMORY];

/* DMA Stream Handle */
static ADI_EMDMA_STREAM_HANDLE   phStream;

/* Flag to register Memory DMA copy completion */
static volatile bool bMemCopyInProgress;

/* Memory to handle DMA Stream */
static uint8_t EmdmaStreamMem[ADI_EMDMA_STREAM_REQ_MEMORY];

SRCALIGN static uint32_t SrcDataBufL1[MEMCOPY_BUF_SIZE];//__attribute__ ((section(".L1.data")));


SRCALIGN static uint32_t DataBufL3[MEMCOPY_BUF_SIZE]__attribute__ ((section(".L3.data")));

DSTALIGN static uint32_t DestDataBufL1[ADI_CACHE_ROUND_UP_SIZE(MEMCOPY_BUF_SIZE, uint8_t)];//__attribute__ ((section(".L1.data")));

/* To keep track of the total number of failures */
int fail_count=0;

/* EMDMA Descriptor lists*/
static ADI_EMDMA_DESC_STANDARD_CIRCULAR  EmdmaDescListWrite;
static ADI_EMDMA_DESC_STANDARD_CIRCULAR  EmdmaDescListRead;

/* MDMA Descriptor lists */
static ADI_DMA_DESC_LIST_MODE  MDMASrcListWrite[1];
static ADI_DMA_DESC_LIST_MODE  MDMADstListWrite[2];
static ADI_DMA_DESC_LIST_MODE  MDMASrcListRead[2];
static ADI_DMA_DESC_LIST_MODE  MDMADstListRead[1];

static ADI_DMA_DESC_LIST_MODE  MDMASrcListWriteMsize32[1];
static ADI_DMA_DESC_LIST_MODE  MDMADstListWriteMsize32[2];
static ADI_DMA_DESC_LIST_MODE  MDMASrcListReadMsize32[2];
static ADI_DMA_DESC_LIST_MODE  MDMADstListReadMsize32[1];


/*=============  LOCAL FUNCTION DECLARATIONS  =============*/
/* Initializes SPU service */
static ADI_SPU_RESULT SpuInit(void);

/* Prepares data buffers for Memory DMA copy */
static void PrepareDataBuffers (void);

/* Verifies memory DMA copy by comparing destination data with source */
static int VerifyDataCopy (void);

/* Callback for MDMA service */
static void MdmaCallback(void *pCBParam, uint32_t Event, void *pArg);

/* Callback for EMDMA service */
static void EmdmaCallback(void *pCBParam, uint32_t Event, void *pArg);

static void PrepareEMDMADescriptors(void);
static void PrepareMDMADescriptors (void);
static void PrepareMDMADescriptorsMsize32 (void);

/* For cycle count */
volatile clock_t clock_stop;
volatile clock_t clock_start;
volatile int EMDMA_Normal_Write_Cycles;
volatile int EMDMA_Normal_Read_Cycles;
volatile int EMDMA_Circular_Write_Cycles;
volatile int EMDMA_Circular_Read_Cycles;

volatile int MDMA_MSIZE4_Write_Cycles;
volatile int MDMA_MSIZE4_Read_Cycles;
volatile int MDMA_MSIZE32_Write_Cycles;
volatile int MDMA_MSIZE32_Read_Cycles;

volatile int MDMA_MSIZE4_Circ_Write_Cycles;
volatile int MDMA_MSIZE4_Circ_Read_Cycles;
volatile int MDMA_MSIZE32_Circ_Write_Cycles;
volatile int MDMA_MSIZE32_Circ_Read_Cycles;

/*=============  EXTERNAL FUNCTION DECLARATIONS  =============*/

/*=============  MAIN FUNCTION DEFINITION  =============*/
int main(int argc, char *argv[])
{
    /* MDMA Return code */
    ADI_DMA_RESULT eMDMAResult = ADI_DMA_SUCCESS;

    /* EMDMA Return code */
    ADI_EMDMA_RESULT eEMDMAResult = ADI_EMDMA_SUCCESS;

    /* Power service return code */
    ADI_PWR_RESULT ePWRResult = ADI_PWR_SUCCESS;

    /* SPU service return code */
    ADI_SPU_RESULT eSPUResult = ADI_SPU_SUCCESS;

    int failures = 0;

    /* Initialize managed drivers and/or services */
    adi_initComponents();


    clock_start=clock();
    clock_stop=clock();
    cycles_offset=clock_stop-clock_start;
    //printf("%d\n",cycles_offset);


	/* Initialize SPU */
    eSPUResult = SpuInit();
    CHECK_RESULT(eSPUResult);

    /* Initialize power API */
    ePWRResult = adi_pwr_Init(0, 25000000);
    CHECK_RESULT(ePWRResult);

    /* Open a Memory DMA Stream */
    eMDMAResult = adi_mdma_Open (  ADI_DMA_MEMDMA_S3,
							  	  &MemDmaStreamMem[0],
								  &hMemDmaStream,
								  &hSrcDmaChannel,
								  &hDestDmaChannel,
								  &MdmaCallback,
								  NULL);
    CHECK_RESULT(eMDMAResult);

    eEMDMAResult = adi_emdma_Open (	ADI_DMA_EMDMA_S0,
    								&EmdmaStreamMem[0],
									&phStream,
									&EmdmaCallback,
									NULL);
    CHECK_RESULT(eEMDMAResult);

	/* Prepare EMDMA descriptors */
	PrepareEMDMADescriptors();

	/* Prepare MDMA descriptors */
	PrepareMDMADescriptors();
	PrepareMDMADescriptorsMsize32();

	/* Initialize flag */
	bMemCopyInProgress = true;

	//Perform write and read circular buffer DMA to/from DDR3 memory using EMDMA
	#ifdef  ENABLE_EMDMA_CIRCULAR

		/* Prepare data buffers */
		PrepareDataBuffers ();

		/* Flush the source and Flush-invalidate destination buffer */
		flush_data_buffer((void *)DataBufL3,
						(void *)((char *)DataBufL3 + MEMCOPY_BUF_SIZE*4),
						ADI_FLUSH_DATA_INV);

		bMemCopyInProgress = true;

		//Write circular EMDMA
		eEMDMAResult = adi_emdma_Copy(	phStream,
										ADI_EMDMA_STANDARD_WRITE_MODE,
										(void*)&EmdmaDescListWrite,
										1,
										true,
										false);
		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		EMDMA_Circular_Write_Cycles= clock_stop - clock_start;
		printf("\n\nEMDMA circular write cycles = %d",EMDMA_Circular_Write_Cycles-cycles_offset );

		bMemCopyInProgress = true;

		//Read circular EMDMA
		eEMDMAResult = adi_emdma_Copy(
									phStream,
									ADI_EMDMA_STANDARD_READ_MODE,
									(void*)&EmdmaDescListRead,
									1,
									true,
									false);

		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		EMDMA_Circular_Read_Cycles= clock_stop - clock_start;
		printf("\n\nEMDMA circular read cycles = %d",EMDMA_Circular_Read_Cycles-cycles_offset );

		failures = VerifyDataCopy();

		if(failures==0)
			printf("\n\nEMDMA write and read passed...\n\n");
		else
			printf("\n\nEMDMA write and read failed...\n\n");

	#endif

	//Perform write and read circular buffer DMA to/from DDR3 memory using MDMA with MSIZE=4 bytes
	#ifdef ENABLE_MDMA_MSIZE4_CIRCULAR

		PrepareDataBuffers ();

		bMemCopyInProgress = true;

		eMDMAResult = adi_mdma_CopyList(
										hMemDmaStream,
										MDMADstListWrite,
										MDMASrcListWrite);
		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		MDMA_MSIZE4_Circ_Write_Cycles= clock_stop - clock_start;
		printf("\n\nMDMA MSIZE4 circular write cycles = %d", MDMA_MSIZE4_Circ_Write_Cycles-cycles_offset );

		bMemCopyInProgress = true;

		eMDMAResult = adi_mdma_CopyList(
										hMemDmaStream,
										MDMADstListRead,
										MDMASrcListRead);
		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		MDMA_MSIZE4_Circ_Read_Cycles= clock_stop - clock_start;
		printf("\n\nMDMA MSIZE4 circular read cycles = %d", MDMA_MSIZE4_Circ_Read_Cycles-cycles_offset );

		failures = VerifyDataCopy();

		if(failures==0)
			printf("\n\nMDMA MSIZE4 write and read passed...\n\n");
		else
			printf("\n\nMDMA MSIZE4 write and read failed...\n\n");

	#endif

	//Perform write and read circular buffer DMA to/from DDR3 memory using MDMA with MSIZE=32 bytes
	#ifdef ENABLE_MDMA_MSIZE32_CIRCULAR

		PrepareDataBuffers ();

		bMemCopyInProgress = true;

		eMDMAResult = adi_mdma_CopyList(
										hMemDmaStream,
										MDMADstListWriteMsize32,
										MDMASrcListWriteMsize32 );
		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		MDMA_MSIZE4_Circ_Write_Cycles= clock_stop - clock_start;
		printf("\n\nMDMA MSIZE32 circular write cycles = %d", MDMA_MSIZE4_Circ_Write_Cycles-cycles_offset );

		bMemCopyInProgress = true;

		eMDMAResult = adi_mdma_CopyList(
										hMemDmaStream,
										MDMADstListReadMsize32 ,
										MDMASrcListReadMsize32);
		clock_start=clock();
		while(bMemCopyInProgress);
		clock_stop=clock();
		MDMA_MSIZE4_Circ_Read_Cycles= clock_stop - clock_start;
		printf("\n\nMDMA MSIZE32 circular read cycles = %d", MDMA_MSIZE4_Circ_Read_Cycles-cycles_offset );

		failures = VerifyDataCopy();

		if(failures==0)
			printf("\n\nMDMA MSIZE32 write and read passed...\n\n");
		else
			printf("\n\nMDMA MSIZE32 write and read failed...\n\n");

	#endif

	return 0;
}

/*  Callback from MDMA Manager */
static void MdmaCallback(void *pCBParam, uint32_t Event, void *pArg)
{
	ADI_DMA_EVENT Event1 = (ADI_DMA_EVENT)Event;

	if(Event1 == ADI_DMA_EVENT_DESC_LIST_PROCESSED)
	{
           bMemCopyInProgress = false;
	}
}

/*  Callback from EMDMA Manager */
static void EmdmaCallback(void *pCBParam, uint32_t Event, void *pArg)
{
	ADI_EMDMA_EVENT Event1 = (ADI_EMDMA_EVENT)Event;

	if((Event1 == ADI_EMDMA_EVENT_STANDARD_WRITE_DESC_PROCESSED)||(Event1 == ADI_EMDMA_EVENT_STANDARD_READ_DESC_PROCESSED))
	{
           bMemCopyInProgress = false;
	}
}


/* Prepares data buffers for Memory DMA copy */
static void PrepareDataBuffers (void)
{
    for (int i = 0u; i < MEMCOPY_BUF_SIZE; i++)
    {
    	SrcDataBufL1[i]  = rand();
    	DestDataBufL1[i]  = 0;
    	DataBufL3[i]  = 0;
    }
}


/* Verifies memory DMA copy by comparing destination data with source */
static int VerifyDataCopy (void)
{
	int failures = 0;

	for (int i = 0u; i < MEMCOPY_BUF_SIZE; i++)
	{
        if (SrcDataBufL1[i] != DestDataBufL1[i])
        	failures++;
    }

    /* Return success */
    return failures;
}

/* Initializes SPU */
static ADI_SPU_RESULT SpuInit(void)
{
	ADI_SPU_RESULT eResult = ADI_SPU_SUCCESS;

    /* Memory required for the SPU operation */
    uint8_t             SpuMemory[ADI_SPU_MEMORY_SIZE];

    /* SPU handle */
    ADI_SPU_HANDLE      hSpu;

    /* Initialize SPU Service */
    eResult = adi_spu_Init(0u, SpuMemory, NULL, NULL, &hSpu);
    CHECK_RESULT(eResult);

     /* Make MDMA0 Source to generate secure transactions */
    eResult = adi_spu_EnableMasterSecure(hSpu, MDMA3_SPU_PID, true);
    CHECK_RESULT(eResult);

     /* Make EMDMA to generate secure transactions */
    eResult = adi_spu_EnableMasterSecure(hSpu, EMDMA_SPU_PID, true);
    CHECK_RESULT(eResult);

   return eResult;
}

/* Prepares EMDMA descriptors */
static void PrepareEMDMADescriptors(void)
{
	int i;

	EmdmaDescListWrite.pNext = 				NULL;
	EmdmaDescListWrite.Chnptr = 			ADI_EMDMA_UNMASK_DMA_INT;
	EmdmaDescListWrite.ExtModify = 			1;
	EmdmaDescListWrite.pExtStartAddress = 	(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	EmdmaDescListWrite.pIntStartAddress = 	SrcDataBufL1;
	EmdmaDescListWrite.Count = 				MEMCOPY_BUF_SIZE;
	EmdmaDescListWrite.IntModify=			1;
	EmdmaDescListWrite.pExtBaseAddress=		&DataBufL3;
	EmdmaDescListWrite.ExtLength=			MEMCOPY_BUF_SIZE;

	EmdmaDescListRead.pNext = 				NULL;
	EmdmaDescListRead.Chnptr = 				ADI_EMDMA_UNMASK_DMA_INT;
	EmdmaDescListRead.ExtModify = 			1;
	EmdmaDescListRead.pExtStartAddress = 	(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	EmdmaDescListRead.pIntStartAddress = 	DestDataBufL1;
	EmdmaDescListRead.Count = 				MEMCOPY_BUF_SIZE;
	EmdmaDescListRead.IntModify=			1;
	EmdmaDescListRead.pExtBaseAddress=		&DataBufL3;
	EmdmaDescListRead.ExtLength=			MEMCOPY_BUF_SIZE;

}

/* Prepares MDMA descriptors for 4 bytes MSIZE transfer*/
static void PrepareMDMADescriptors (void)
{
	int i;

	//Split the MDMA into two descriptor based MDMA transfers
	int count1=MEMCOPY_BUF_SIZE-INDEX_OFFSET;
	int count2=MEMCOPY_BUF_SIZE-count1;

	MDMASrcListWrite[0].pStartAddress=		SrcDataBufL1;
	MDMASrcListWrite[0].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMASrcListWrite[0].YCount=				0;
	MDMASrcListWrite[0].YModify=			0;
	MDMASrcListWrite[0].XCount=				MEMCOPY_BUF_SIZE;
	MDMASrcListWrite[0].XModify=			MDMA_MSIZE_BYTES;
	MDMASrcListWrite[0].bCallbackWhenDone= 	false;
	MDMASrcListWrite[0].pNext=				NULL;

	MDMADstListWrite[0].pStartAddress=		(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	MDMADstListWrite[0].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMADstListWrite[0].YCount=				0;
	MDMADstListWrite[0].YModify=			0;
	MDMADstListWrite[0].XCount=				count1;
	MDMADstListWrite[0].XModify=			MDMA_MSIZE_BYTES;
	MDMADstListWrite[0].bCallbackWhenDone= 	false;
	MDMADstListWrite[0].pNext=				&MDMADstListWrite[1];

	MDMADstListWrite[1].pStartAddress=		DataBufL3;
	MDMADstListWrite[1].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMADstListWrite[1].YCount=				0;
	MDMADstListWrite[1].YModify=			0;
	MDMADstListWrite[1].XCount=				count2;
	MDMADstListWrite[1].XModify=			MDMA_MSIZE_BYTES;
	MDMADstListWrite[1].bCallbackWhenDone= 	true;
	MDMADstListWrite[1].pNext=				NULL;

	MDMASrcListRead[0].pStartAddress=		(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	MDMASrcListRead[0].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMASrcListRead[0].YCount=				0;
	MDMASrcListRead[0].YModify=				0;
	MDMASrcListRead[0].XCount=				count1;
	MDMASrcListRead[0].XModify=				MDMA_MSIZE_BYTES;
	MDMASrcListRead[0].bCallbackWhenDone= 	false;
	MDMASrcListRead[0].pNext= 				&MDMASrcListRead[1];

	MDMASrcListRead[1].pStartAddress=		DataBufL3;
	MDMASrcListRead[1].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMASrcListRead[1].YCount=				0;
	MDMASrcListRead[1].YModify=				0;
	MDMASrcListRead[1].XCount=				count2;
	MDMASrcListRead[1].XModify=				MDMA_MSIZE_BYTES;
	MDMASrcListRead[1].bCallbackWhenDone= 	false;
	MDMASrcListRead[1].pNext= 				NULL;

	MDMADstListRead[0].pStartAddress=		DestDataBufL1;
	MDMADstListRead[0].Config=				ADI_DMA_MSIZE_4BYTES|ADI_DMA_PSIZE_4BYTES;
	MDMADstListRead[0].YCount=				0;
	MDMADstListRead[0].YModify=				0;
	MDMADstListRead[0].XCount=				MEMCOPY_BUF_SIZE;
	MDMADstListRead[0].XModify=				MDMA_MSIZE_BYTES;
	MDMADstListRead[0].bCallbackWhenDone= 	true;
	MDMASrcListRead[1].pNext= 				NULL;

}

/* Prepares MDMA descriptors for 32 bytes MSIZE transfer*/
static void PrepareMDMADescriptorsMsize32 (void)
{
	int i;

	//Split the MDMA into two descriptor based MDMA transfers
	int count1=(MEMCOPY_BUF_SIZE-INDEX_OFFSET)/8;
	int count2=(MEMCOPY_BUF_SIZE/8)-count1;

	MDMASrcListWriteMsize32[0].pStartAddress=		SrcDataBufL1;
	MDMASrcListWriteMsize32[0].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMASrcListWriteMsize32[0].YCount=				0;
	MDMASrcListWriteMsize32[0].YModify=				0;
	MDMASrcListWriteMsize32[0].XCount=				MEMCOPY_BUF_SIZE/8;
	MDMASrcListWriteMsize32[0].XModify=				32;
	MDMASrcListWriteMsize32[0].bCallbackWhenDone= 	false;
	MDMASrcListWriteMsize32[0].pNext=				NULL;

	MDMADstListWriteMsize32[0].pStartAddress=		(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	MDMADstListWriteMsize32[0].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMADstListWriteMsize32[0].YCount=				0;
	MDMADstListWriteMsize32[0].YModify=				0;
	MDMADstListWriteMsize32[0].XCount=				count1;
	MDMADstListWriteMsize32[0].XModify=				32;
	MDMADstListWriteMsize32[0].bCallbackWhenDone= 	false;
	MDMADstListWriteMsize32[0].pNext=				&MDMADstListWriteMsize32[1];

	MDMADstListWriteMsize32[1].pStartAddress=		DataBufL3;
	MDMADstListWriteMsize32[1].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMADstListWriteMsize32[1].YCount=				0;
	MDMADstListWriteMsize32[1].YModify=				0;
	MDMADstListWriteMsize32[1].XCount=				count2;
	MDMADstListWriteMsize32[1].XModify=				32;
	MDMADstListWriteMsize32[1].bCallbackWhenDone= 	true;
	MDMADstListWriteMsize32[1].pNext=				NULL;

	MDMASrcListReadMsize32[0].pStartAddress=		(void*)(((uint32_t)&DataBufL3)+INDEX_OFFSET*4);
	MDMASrcListReadMsize32[0].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMASrcListReadMsize32[0].YCount=				0;
	MDMASrcListReadMsize32[0].YModify=				0;
	MDMASrcListReadMsize32[0].XCount=				count1;
	MDMASrcListReadMsize32[0].XModify=				32;
	MDMASrcListReadMsize32[0].bCallbackWhenDone= 	false;
	MDMASrcListReadMsize32[0].pNext= 				&MDMASrcListReadMsize32[1];

	MDMASrcListReadMsize32[1].pStartAddress=		DataBufL3;
	MDMASrcListReadMsize32[1].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMASrcListReadMsize32[1].YCount=				0;
	MDMASrcListReadMsize32[1].YModify=				0;
	MDMASrcListReadMsize32[1].XCount=				count2;
	MDMASrcListReadMsize32[1].XModify=				32;
	MDMASrcListReadMsize32[1].bCallbackWhenDone= 	false;
	MDMASrcListReadMsize32[1].pNext= 				NULL;

	MDMADstListReadMsize32[0].pStartAddress=		DestDataBufL1;
	MDMADstListReadMsize32[0].Config=				ADI_DMA_MSIZE_32BYTES|ADI_DMA_PSIZE_8BYTES;
	MDMADstListReadMsize32[0].YCount=				0;
	MDMADstListReadMsize32[0].YModify=				0;
	MDMADstListReadMsize32[0].XCount=				MEMCOPY_BUF_SIZE/8;
	MDMADstListReadMsize32[0].XModify=				32;
	MDMADstListReadMsize32[0].bCallbackWhenDone= 	true;
	MDMASrcListReadMsize32[1].pNext= 				NULL;

}

/*****/

