/* This example code can be used to measure EMDMA throughput for different parameters.
 * Test combinations can be varied in "testparam.dat" file.
 */

/*=============  INCLUDES  =============*/
#include "Test5_EMDMA_Throughput_Core1.h"
#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_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];

/* Internal buffer */

DSTALIGN static uint32_t IntDataBufL1[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L1.data")));

DSTALIGN static uint32_t IntDataBufL2[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L2.data")));

/* External buffer */

DSTALIGN static uint32_t ExtDataBufL1[ADI_CACHE_ROUND_UP_SIZE(MAX_MEMCOPY_BUF_SIZE, uint8_t)]__attribute__ ((section(".L1.data")));

DSTALIGN static uint32_t ExtDataBufL2[ADI_CACHE_ROUND_UP_SIZE(MAX_MEMCOPY_BUF_SIZE, uint8_t)]__attribute__ ((section(".L2.data")));


DSTALIGN static uint32_t IntDataBufL3[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L3.data")));

DSTALIGN static uint32_t ExtDataBufL3[ADI_CACHE_ROUND_UP_SIZE(MAX_MEMCOPY_BUF_SIZE, uint8_t)]__attribute__ ((section(".L3.data")));

/* Source and destination descriptors */
static ADI_EMDMA_DESC_STANDARD  DescListL1[MAX_DESCRIPTORS];

/* Callback parameter */
int cbparam=10;

/* To store descriptor start address */
ADI_EMDMA_DESC_STANDARD *pDesc;

/* To store internal buffer start address */
uint32_t* pInt;

/* To store external buffer start address */
uint32_t* pExt;

/* To keep track of the interrupt count */
ADI_EMDMA_TRANSFER_MODE TransferMode;

clock_t cycle_start;
clock_t cycle_stop;
//int cycles;
int cyclesx;
float throughput;

/* Holds list of all the test parameters combinations for which the test should run */

TEST_PARAM TestParamArray[MAX_TEST_COMBINATIONS]__attribute__ ((section(".L2.data")))=
{
	#include "testparam.dat"
};

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

/* To keep track of the failed test combinations */
uint32_t TestFaillog[MAX_TEST_COMBINATIONS];

/* Holds the test suite information*/
TEST_INFO TestInfo =
{
	TestParamArray, 0
};


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

/* Prepares data buffers for Memory DMA copy */
static void PrepareDataBuffers (uint32_t* psrc, uint32_t* pdst, uint32_t count);

/* Verifies memory DMA copy by comparing destination data with source */
static ADI_EMDMA_RESULT VerifyDataCopy (uint32_t* psrc, uint32_t* pdst, uint32_t count);

/* Prepares descriptor list needed for EMDMA */
static void PrepareDescriptors(TEST_PARAM TestParam);

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


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

/*=============  MAIN FUNCTION DEFINITION  =============*/
int main(int argc, char *argv[])
{
    /* Time-out counter to make sure the example exits */
    volatile uint32_t   TimeOutCount = MEMCOPY_TIME_OUT_VAL;

    /* DMA Return code */
    ADI_EMDMA_RESULT      eResult = ADI_EMDMA_SUCCESS;

    /* To keep track of the test iteration */
    int test_iteration=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 */
    eResult = SpuInit();

    int i=0;

    /* Iterate over multiple test combinations */
    if (eResult == ADI_DMA_SUCCESS)
    {
		while(TestParamArray[test_iteration].TestIndex!=-1)
		{
			if(TestParamArray[test_iteration].TestIndex!=0)
			{
				DBG_MSG("\nTesting combination %d\n", TestParamArray[test_iteration].TestIndex);
				DBG_MSG("EMDMA Stream no = %d\n", TestParamArray[test_iteration].eStreamID);
				DBG_MSG("Internal memory type = 0x%x\n", TestParamArray[test_iteration].eIntMem);
				DBG_MSG("External memory type = 0x%x\n", TestParamArray[test_iteration].eExtMem);
				DBG_MSG("Mode = %d\n", TestParamArray[test_iteration].eMode);
				DBG_MSG("Buffer count = %d\n", TestParamArray[test_iteration].BuffCount);

				switch(TestParamArray[test_iteration].eIntMem)
				{
					case L1MEM: pInt = IntDataBufL1; break;
					case L2MEM: pInt = IntDataBufL2; break;
					case L3MEM: pInt = IntDataBufL3; break;
					default:	pInt = (uint32_t*)TestParamArray[test_iteration].eIntMem;
				}

				switch(TestParamArray[test_iteration].eExtMem)
				{
					case L1MEM: pExt = ExtDataBufL1; break;
					case L2MEM: pExt = ExtDataBufL2; break;
					case L3MEM: pExt = ExtDataBufL3; break;
					default:	pExt = (uint32_t*)TestParamArray[test_iteration].eExtMem;
				}


				TransferMode = TestParamArray[test_iteration].eMode;

				eResult = ADI_EMDMA_SUCCESS;

				eResult = adi_emdma_Open (TestParamArray[test_iteration].eStreamID,
										  &EmdmaStreamMem[0],
										  &phStream,
										  EMdmaCallback,
										  NULL
										  );

				/* IF (Failure) */
				if (eResult != ADI_DMA_SUCCESS)
				{
					DBG_MSG("Failed to open EMDMA stream, Error Code: 0x%08X\n", eResult);
				}

				/* Prepare data buffers */
				if(TestParamArray[test_iteration].eMode==ADI_EMDMA_STANDARD_WRITE_MODE)
				{
					PrepareDataBuffers (&pInt[i*TestParamArray[test_iteration].BuffCount], &pExt[i*TestParamArray[test_iteration].BuffCount], TestParamArray[test_iteration].BuffCount);
				}
				else if(TestParamArray[test_iteration].eMode==ADI_EMDMA_STANDARD_READ_MODE)
				{
					PrepareDataBuffers (&pExt[i*TestParamArray[test_iteration].BuffCount], &pInt[i*TestParamArray[test_iteration].BuffCount], TestParamArray[test_iteration].BuffCount);
				}

				PrepareDescriptors(TestParamArray[test_iteration]);

				/* Initialize flag */
				bMemCopyInProgress = true;

				DBG_MSG("DMA copy started\n");

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					eResult = adi_emdma_Copy(
											phStream,
											TestParamArray[test_iteration].eMode,
											&DescListL1,
											1,
											false,
											false
											);
					cycle_start = clock();

					/* IF (Failure) */
					if (eResult != ADI_DMA_SUCCESS)
					{
						DBG_MSG("Failed to initiate EMDMA memory copy, Error Code: 0x%08X\n", eResult);
					}
				}

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					/* Reinitialize the TimeOutCount value */
					TimeOutCount = MEMCOPY_TIME_OUT_VAL;

					/* Wait until DMA copy is complete or the time-out counter expires */
					while ((bMemCopyInProgress) && (TimeOutCount))
					{
						/* Decrement time-out counter */
						TimeOutCount--;
					}


				}

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					/* If no timeout */
					if(TimeOutCount)
					{
						if(TestParamArray[test_iteration].eMode==ADI_EMDMA_STANDARD_WRITE_MODE)
						{
							eResult = VerifyDataCopy (&pInt[i*TestParamArray[test_iteration].BuffCount], &pExt[i*TestParamArray[test_iteration].BuffCount], TestParamArray[test_iteration].BuffCount);
						}
						else if(TestParamArray[test_iteration].eMode==ADI_EMDMA_STANDARD_READ_MODE)
						{
							eResult = VerifyDataCopy (&pExt[i*TestParamArray[test_iteration].BuffCount], &pInt[i*TestParamArray[test_iteration].BuffCount], TestParamArray[test_iteration].BuffCount);
						}

						/* IF (Failure) */
						if (eResult != ADI_DMA_SUCCESS)
						{
							DBG_MSG("Data verification failed\n");

							break;
						}
					}
					else
					{
						 DBG_MSG("Time out\n");
						 eResult = ADI_EMDMA_FAILURE;
					}
				}

				/* IF (Failure) */
				if (adi_emdma_Close (phStream, false) != ADI_DMA_SUCCESS)
				{
					DBG_MSG("Failed to close EMDMA stream\n");
				}

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					DBG_MSG ("Test combination %d passed\n\n",TestParamArray[test_iteration].TestIndex);

					cyclesx = cycle_stop - cycle_start;
					cycles = cyclesx-cycles_offset;

					throughput = TestParamArray[test_iteration].BuffCount*4*SCLK_SPEED/cycles;

					DBG_MSG("Cycles = %ld, Throughput = %f\n\n",cycles, throughput);
				}
				/* ELSE (Failure) */
				else
				{
					DBG_MSG ("Test combination %d failed\n\n",TestParamArray[test_iteration].TestIndex);
					TestInfo.FailedTestInfo[TestInfo.FailCount].TestIndex = TestParamArray[test_iteration].TestIndex;
					TestInfo.FailedTestInfo[TestInfo.FailCount++].ErrorCode = eResult;
				}
			}
			test_iteration++;
		}
	}
	
	/* IF (Success) */
	if ((eResult == ADI_DMA_SUCCESS)&&(TestInfo.FailCount == 0))
	{
		printf ("All done\n");
	}
	/* ELSE (Failure) */
	else
	{
		printf ("Failed for %d test combinations\n",TestInfo.FailCount);
	}

  return 0;
}

static void EMdmaCallback(void *pCBParam, uint32_t Event, void *pArg)
{
	cycle_stop = clock();
	 bMemCopyInProgress = false;
}

static void PrepareDataBuffers (uint32_t* psrc, uint32_t* pdst, uint32_t count)
{
    /* Loop variable */
    uint32_t    i;

    /* Generate Source data, Clear destination buffer */
    for (i = 0u; i < count; i++)
    {
    	psrc[i]  = (uint8_t)rand();
    	pdst[i]  = 0;
    }

	/* Flush the source buffer */
	flush_data_buffer((void *)psrc,
					(void *)((char *)psrc + count*4),
					ADI_FLUSH_DATA_NOINV);

	/* Flush and invalidate destination buffer */
	flush_data_buffer((void *)pdst,
					(void *)((char *)pdst + count*4),
					ADI_FLUSH_DATA_INV);
}

static void PrepareDescriptors(TEST_PARAM TestParam)
{
	int i=0;

	DescListL1[i].pNext = NULL; 		/* pNext */
	DescListL1[i].Chnptr = 0;
	DescListL1[i].ExtModify = 1;
	DescListL1[i].pExtStartAddress = &pExt[i*TestParam.BuffCount];
	DescListL1[i].pIntStartAddress = &pInt[i*TestParam.BuffCount];
	DescListL1[i].Count = TestParam.BuffCount;
	DescListL1[i].IntModify = 1;

	/* Flush descriptors */
	/*flush_data_buffer((void *)pDesc,
					(void *)((char *)pDesc + sizeof(DescListL1)),
					ADI_FLUSH_DATA_NOINV);*/

}

static ADI_EMDMA_RESULT VerifyDataCopy (uint32_t* psrc, uint32_t* pdst, uint32_t count)
{
    /* Loop variable */
    uint32_t        i;

    /* Compare the destination data with source */
    for (i = 0u; i < count; i++)
    {
        /* IF (Destination data not same as source) */
        if (psrc[i] != pdst[i])
        {
            /* Return error */
            return (ADI_EMDMA_FAILURE);
        }
    }

    /* Return success */
    return (ADI_EMDMA_SUCCESS);
}

static ADI_EMDMA_RESULT SpuInit(void)
{
	ADI_EMDMA_RESULT eResult = ADI_EMDMA_SUCCESS;

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

    /* SPU handle */
    ADI_SPU_HANDLE      hSpu;

    /* Initialize SPU Service */
    if(adi_spu_Init(0u, SpuMemory, NULL, NULL, &hSpu) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to initialize SPU service\n");
        eResult = ADI_EMDMA_FAILURE;
    }

    /* Make EMDMA Source to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, EMDMA_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 0 Source\n");
        eResult = ADI_EMDMA_FAILURE;
    }

   return eResult;
}

/*****/

/*
** EOF
*/
