/*********************************************************************************

Copyright(c) 2018 Analog Devices, Inc. All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.

*********************************************************************************/
/*!
 * @file	  main.c
 * @brief	  This code can be used to measure MDMA throughput for different parameters.
 * @details
 *			  - Test combinations can be varied in "testparam.dat" file.
 */

/*=============  INCLUDES  =============*/
#include "Test8_MDMA_Throughput_L2_Core1.h"
/* Managed drivers and/or services include */
#include "../system/adi_initialize.h"

/*=============  LOCAL DEFINES   =============*/

#if !defined(ADI_CACHE_LINE_LENGTH)
/* The ADI_CACHE_* macros were introduced in CCES 2.4.0 in <sys/platform.h>.
 * If using an older toolchain, define them here.
 */
 #if defined(__ADSPARM__)
  #define ADI_CACHE_LINE_LENGTH (32uL)
  #define ADI_CACHE_ALIGN_MIN_4 __attribute__((aligned(ADI_CACHE_LINE_LENGTH)))
 #elif defined(__ADSP215xx__)
  #define ADI_CACHE_LINE_LENGTH (64uL)
  #define ADI_CACHE_ALIGN_MIN_4 _Pragma("align 64")
 #else
  #error Unknown ADI_CACHE_LINE_LENGTH
 #endif
 #define ADI_CACHE_ROUND_UP_SIZE(size, type) \
    (((((((size) * sizeof(type)) \
           + (ADI_CACHE_LINE_LENGTH - 1uL)) \
          / ADI_CACHE_LINE_LENGTH) * ADI_CACHE_LINE_LENGTH) \
      + (sizeof(type) - 1uL)) / sizeof(type))
#endif


/* 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];

/* Source channel data buffer.
 * Aligned at least to an MSIZE-byte boundary.
 */

SRCALIGN static uint8_t SrcDataBufL1[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L1.data")));

SRCALIGN static uint8_t SrcDataBufL2[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L2.data")));

/* Destination channel data buffer.
 * Aligned at least to an MSIZE-byte boundary, and to the cache line size.
 */

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

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



	SRCALIGN static uint8_t SrcDataBufL3[MAX_MEMCOPY_BUF_SIZE]__attribute__ ((section(".L3.data")));

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


/* 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
};

uint32_t MeasuredBWSrc;
uint32_t MeasuredBWDst;

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

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

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

/* Callback for MDMA service */
static void MdmaCallback(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_DMA_RESULT      eResult = ADI_DMA_SUCCESS;

    /* To keep track of the test iteration */
    int test_iteration=0;

    /* To store source buffer start address */
    uint8_t* psrc;

    /* To store destination buffer start address */
   	uint8_t* pdst;

   	/* To store MSIZE value */
   	uint32_t msizevalue;

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

	/* Initialize SPU */
    eResult = SpuInit();

    /* Initialize power API */
    if(adi_pwr_Init(0, 25000000)!=ADI_PWR_SUCCESS)
    {
    	eResult = ADI_DMA_FAILURE;
    }

    if (eResult == ADI_DMA_SUCCESS)
    {
		while(TestParamArray[test_iteration].TestIndex!=-1)
		{
			if(TestParamArray[test_iteration].TestIndex!=0)
			{
				msizevalue = 1<<(TestParamArray[test_iteration].eMsizeCfg>>BITP_DMA_CFG_MSIZE);

				DBG_MSG("\nTesting combination %d\n", TestParamArray[test_iteration].TestIndex);
				DBG_MSG("MDMA Stream no = %d\n", TestParamArray[test_iteration].eStreamID);
				DBG_MSG("Source memory type = 0x%x\n", TestParamArray[test_iteration].eSourceMem);
				DBG_MSG("Destination memory type = 0x%x\n", TestParamArray[test_iteration].eDestMem);
				DBG_MSG("Buffer count = %d\n", TestParamArray[test_iteration].BuffCount);
				DBG_MSG("MSIZE value = %d\n", msizevalue);

				switch(TestParamArray[test_iteration].eSourceMem)
				{
					case L1MEM: psrc = SrcDataBufL1; break;
					case L2MEM: psrc = SrcDataBufL2; break;

					case L3MEM: psrc = SrcDataBufL3; break;

					default:	psrc = (uint8_t*)TestParamArray[test_iteration].eSourceMem;
				}

				switch(TestParamArray[test_iteration].eDestMem)
				{
					case L1MEM: pdst = DestDataBufL1; break;
					case L2MEM: pdst = DestDataBufL2; break;

					case L3MEM: pdst = DestDataBufL3; break;

					default:	pdst = (uint8_t*)TestParamArray[test_iteration].eDestMem;
				}

				eResult = ADI_DMA_SUCCESS;

				/* Open a Memory DMA Stream */
				 eResult = adi_mdma_Open (TestParamArray[test_iteration].eStreamID,
										  &MemDmaStreamMem[0],
										  &hMemDmaStream,
										  &hSrcDmaChannel,
										  &hDestDmaChannel,
										  NULL,
										  NULL);

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

				/* Set DMA Callback Function. No need to set for source channel since callback is not supported for it */
				eResult = adi_dma_UpdateCallback (hDestDmaChannel, MdmaCallback, hMemDmaStream);

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

				/* Prepare data buffers */
				PrepareDataBuffers (psrc, pdst, TestParamArray[test_iteration].BuffCount, msizevalue);

				/* Initialize flag */
				bMemCopyInProgress = true;

				DBG_MSG("DMA copy started\n");

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					eResult = adi_mdma_Copy1D(
								hMemDmaStream,
								pdst,
								psrc,
								TestParamArray[test_iteration].eMsizeCfg,
								TestParamArray[test_iteration].BuffCount);

					/* IF (Failure) */
					if (eResult != ADI_DMA_SUCCESS)
					{
						DBG_MSG("Failed to initiate One-shot 1D 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)
						{
							/* Verify data copy */
							eResult = VerifyDataCopy (psrc, pdst, TestParamArray[0].BuffCount, msizevalue);

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

				/* Measure the actual BW */
				if(eResult == ADI_DMA_SUCCESS)
				{
					  eResult = adi_mdma_BWMeasure(
								 hSrcDmaChannel,
								 &MeasuredBWSrc,
								 TestParamArray[test_iteration].BuffCount*msizevalue);
				}
				if(eResult == ADI_DMA_SUCCESS)
				{
					  eResult = adi_mdma_BWMeasure(
								 hDestDmaChannel,
								 &MeasuredBWDst,
								 TestParamArray[test_iteration].BuffCount*msizevalue);
				}

				if(eResult==ADI_DMA_SUCCESS)
				{
					  DBG_MSG("\n\nSource throughput=%f, Destination throughput=%f MB/s\n\n",((float)MeasuredBWSrc)/1000000.0, ((float)MeasuredBWDst)/1000000.0);
				}
				else
				{
					  DBG_MSG("\n\nThroughput measurement failed\n\n");
				}

				/* IF (Failure) */
				if (adi_mdma_Close (hMemDmaStream, false) != ADI_DMA_SUCCESS)
				{
					DBG_MSG("Failed to close MDMA stream\n");
				}

				/* IF (Success) */
				if (eResult == ADI_DMA_SUCCESS)
				{
					DBG_MSG ("Test combination %d passed\n\n",TestParamArray[test_iteration].TestIndex);
				}
				/* 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;
}

/*=============  LOCAL FUNCTION DEFINITIONS  =============*/
/*
 *  Callback from MDMA Manager
 *
 * Parameters
 *  - [in]  pCBParam    Callback parameter supplied by application
 *  - [in]  Event       Callback event
 *  - [in]  pArg        Callback argument
 *
 * Returns  None
 *
 */
static void MdmaCallback(void *pCBParam, uint32_t Event, void *pArg)
{
    /* CASEOF (Event) */
    switch ((ADI_DMA_EVENT)Event)
    {
        /* CASE (Processed a one-shot/circular buffer) */
        case (ADI_DMA_EVENT_BUFFER_PROCESSED):
            /* Update memory copy status flag */
            bMemCopyInProgress = false;
            break;

        case (ADI_DMA_EVENT_ERROR_INT):
				DBG_MSG("Error even occurred...\n");
				break;
        default:
            break;
    }
}


/*
 * Prepares data buffers for Memory DMA copy.
 *
 * Parameters
 *  - [in]  psrc    Source buffer start address
 *  - [in]  count   No. of bytes to initialize
 *  - [in]  msize   MSIZE in terms of bytes
 *
 * Returns
 *  None
 *
 */
static void PrepareDataBuffers (uint8_t* psrc, uint8_t* pdst, uint32_t count, uint32_t msize)
{
    /* Loop variable */
    uint32_t    i;

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


/*
 * Verifies memory DMA copy by comparing destination data with source.
 *
 * Parameters
 *  - [in]  psrc    Source buffer start address
 *  - [in]  pdst	Destination buffer start address
 *  - [in]  count   No. of bytes to initialize
 *  - [in]  msize   MSIZE in terms of bytes
 *
 * Returns
 *  - ADI_DMA_SUCCESS   - Data verification success
 *  - ADI_DMA_FAILURE   - Data verification failed
 *
 */
static ADI_DMA_RESULT VerifyDataCopy (uint8_t* psrc, uint8_t* pdst, uint32_t count, uint32_t msize)
{
    /* Loop variable */
    uint32_t        i;

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

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

/*
 * Initializes SPU
 *
 * Parameters 			None
 *
 * Returns
 *  - ADI_DMA_SUCCESS   - SPU initialization passed
 *  - ADI_DMA_FAILURE   - SPU initialization failed
 *
 */
static ADI_DMA_RESULT SpuInit(void)
{
	ADI_DMA_RESULT eResult = ADI_DMA_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_DMA_FAILURE;
    }

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

    /* Make MDMA0 Destination to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, MDMA0_DST_DMA9_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 0 Destination\n");
        eResult = ADI_DMA_FAILURE;
    }

    /* Make MDMA1 Source to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, MDMA1_SRC_DMA18_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 1 Source\n");
        eResult = ADI_DMA_FAILURE;
    }

    /* Make MDMA1 Destination to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, MDMA1_DST_DMA19_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 1 Destination\n");
        eResult = ADI_DMA_FAILURE;
    }

    /* Make MDMA2 to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, MDMA2_SRC_DMA39_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 2 \n");
        eResult = ADI_DMA_FAILURE;
    }

    /* Make MDMA3 to generate secure transactions */
    if(adi_spu_EnableMasterSecure(hSpu, MDMA3_SRC_DMA43_SPU_PID, true) != ADI_SPU_SUCCESS)
    {
        DBG_MSG("Failed to enable Master secure for MDMA 2 \n");
        eResult = ADI_DMA_FAILURE;
    }

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

	/* Make MDMA0 Destination to generate secure transactions */
	if(adi_spu_EnableMasterSecure(hSpu, MDMA4_DST_DMA46_SPU_PID, true) != ADI_SPU_SUCCESS)
	{
		DBG_MSG("Failed to enable Master secure for MDMA 0 Destination\n");
		eResult = ADI_DMA_FAILURE;
	}

	/* Make MDMA1 Source to generate secure transactions */
	if(adi_spu_EnableMasterSecure(hSpu, MDMA5_SRC_DMA47_SPU_PID, true) != ADI_SPU_SUCCESS)
	{
		DBG_MSG("Failed to enable Master secure for MDMA 1 Source\n");
		eResult = ADI_DMA_FAILURE;
	}

	/* Make MDMA1 Destination to generate secure transactions */
	if(adi_spu_EnableMasterSecure(hSpu, MDMA5_DST_DMA48_SPU_PID, true) != ADI_SPU_SUCCESS)
	{
		DBG_MSG("Failed to enable Master secure for MDMA 1 Destination\n");
		eResult = ADI_DMA_FAILURE;
	}

	/* Make MDMA2 to generate secure transactions */
	if(adi_spu_EnableMasterSecure(hSpu, MDMA6_SRC_DMA49_SPU_PID, true) != ADI_SPU_SUCCESS)
	{
		DBG_MSG("Failed to enable Master secure for MDMA 2 \n");
		eResult = ADI_DMA_FAILURE;
	}

	/* Make MDMA3 to generate secure transactions */
	if(adi_spu_EnableMasterSecure(hSpu, MDMA7_SRC_DMA51_SPU_PID, true) != ADI_SPU_SUCCESS)
	{
		DBG_MSG("Failed to enable Master secure for MDMA 2 \n");
		eResult = ADI_DMA_FAILURE;
	}

   return eResult;
}

/*****/

/*
** EOF
*/
