/*
 *  ======== main.c ========
 */

#include <xdc/std.h>

#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Clock.h>

#include <ti/sysbios/knl/Task.h>
#include <ti/csl/tistdtypes.h>
#include <ti/csl/cslr_spi.h>
#include <ti/csl/soc.h>
#include <ti/csl/tistdtypes.h>
#include <ti/csl/csl_chip.h>
#include <ti/csl/csl_types.h>
#include <ti/csl/csl_edma3.h>
#include <ti/csl/csl_edma3Aux.h>
#include <stdio.h>

#include "spi_common.h"

#define TEST_ACNT 2
#define TEST_BCNT 1024
#define TEST_CCNT 1

#define TEST_SYCTYPE_AB     1
#define TEST_SYCTYPE_A      0

#define BUF_SIZE TEST_BCNT
#define BUF_NUM 5

#pragma DATA_SECTION(dstBuf, ".gem1_data")
#pragma DATA_ALIGN(dstBuf, 8)
Uint16 dstBuf[BUF_NUM][BUF_SIZE];

#pragma DATA_SECTION(srcBuf, ".gem0_data")
#pragma DATA_ALIGN(srcBuf, 8)
Uint16 srcBuf[BUF_NUM][BUF_SIZE];

CSL_Uint64 profileTxStart;
CSL_Uint64 profileTxStop;
CSL_Uint64 profileRxStart;
CSL_Uint64 profileRxStop;
CSL_Uint64 profileTx;
CSL_Uint64 profileRx;

Uint32 global_address (Uint32 addr)
{
	Uint32 corenum;
	corenum = CSL_chipReadReg(CSL_CHIP_DNUM);

	addr = addr + (0x10000000 + corenum*0x1000000);
	return addr;
}

void Enable_Edma_Channels(void)
{
   	// Trigger channel
   	CSL_edma3HwChannelControl(hChannel0,CSL_EDMA3_CMD_CHANNEL_ENABLE,NULL);

   	// Trigger channel
   	CSL_edma3HwChannelControl(hChannel1,CSL_EDMA3_CMD_CHANNEL_ENABLE,NULL);

}

void Setup_Edma_Init ()
{

    // EDMA Module Initialization
	CSL_edma3Init(NULL);
 	// EDMA Module Open
    hModule = CSL_edma3Open(&moduleObj,CSL_TPCC_2,NULL,&EdmaStat);


	// SPI Tx Channel Open - Channel 2 for Tx (SPIXEVT)
	chParam.regionNum  = CSL_EDMA3_REGION_GLOBAL;
	chSetup.que        = CSL_EDMA3_QUE_0;
	chParam.chaNum     = CSL_EDMA3_CHA_30;

	//This might have to change to CSL_TPCC_2
	hChannel0 = CSL_edma3ChannelOpen(&ChObj0, CSL_TPCC_2, &chParam, &EdmaStat);
	chSetup.paramNum   = chParam.chaNum; //CSL_EDMA3_CHA_30;
    CSL_edma3HwChannelSetupParam(hChannel0,chSetup.paramNum);

	// SPI Rx Channel Open - Channel 3 for Rx (SPIREVT)
	chParam.regionNum  = CSL_EDMA3_REGION_GLOBAL;
	chSetup.que        = CSL_EDMA3_QUE_0;
	chParam.chaNum     = CSL_EDMA3_CHA_31;

	hChannel1 = CSL_edma3ChannelOpen(&ChObj1, CSL_TPCC_2, &chParam, &EdmaStat);
	chSetup.paramNum = chParam.chaNum; //CSL_EDMA3_CHA_31;
    CSL_edma3HwChannelSetupParam(hChannel1,chSetup.paramNum);

    Enable_Edma_Channels();
}

void Setup_Edma_Params (Uint32 srcBuf, Uint32 dstBuf){
	// Parameter Handle Open
	// Open all the handles and keep them ready
	paramHandle0            = CSL_edma3GetParamHandle(hChannel0,CSL_EDMA3_CHA_30,&EdmaStat);
  	paramHandle1            = CSL_edma3GetParamHandle(hChannel1,CSL_EDMA3_CHA_31,&EdmaStat);

    paramSetup.aCntbCnt     = CSL_EDMA3_CNT_MAKE(TEST_ACNT,(TEST_BCNT));
	paramSetup.srcDstBidx   = CSL_EDMA3_BIDX_MAKE(TEST_ACNT,0 );
	paramSetup.srcDstCidx   = CSL_EDMA3_CIDX_MAKE(0,0);
	paramSetup.cCnt         = TEST_CCNT;
	paramSetup.option       = CSL_EDMA3_OPT_MAKE(FALSE,FALSE,FALSE,TRUE,CSL_EDMA3_CHA_30,CSL_EDMA3_TCC_NORMAL, \
	      CSL_EDMA3_FIFOWIDTH_NONE,FALSE,CSL_EDMA3_SYNC_A,CSL_EDMA3_ADDRMODE_INCR,CSL_EDMA3_ADDRMODE_INCR);
	if( ((Uint32)srcBuf & 0xFFF00000) == 0x00800000)
		paramSetup.srcAddr      = (Uint32)(global_address((Uint32)srcBuf));
	else
		paramSetup.srcAddr      = (Uint32)(srcBuf);

	#ifdef _BIG_ENDIAN
    	paramSetup.dstAddr      = ((Uint32)&(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIDAT0)) + (4-TEST_ACNT);
	#else
    	paramSetup.dstAddr      = (Uint32)&(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIDAT0);
	#endif

	paramSetup.linkBcntrld  = CSL_EDMA3_LINKBCNTRLD_MAKE(CSL_EDMA3_LINK_NULL,0);

	CSL_edma3ParamSetup(paramHandle0,&paramSetup);
	CSL_edma3ParamSetup(paramHandle0_reload,&paramSetup);

    paramSetup.aCntbCnt     = CSL_EDMA3_CNT_MAKE(TEST_ACNT,TEST_BCNT);
	paramSetup.srcDstBidx   = CSL_EDMA3_BIDX_MAKE(0,TEST_ACNT );
	paramSetup.srcDstCidx   = CSL_EDMA3_CIDX_MAKE(0,0);
	paramSetup.cCnt         = TEST_CCNT;
	paramSetup.option       = CSL_EDMA3_OPT_MAKE(FALSE,FALSE,FALSE,TRUE,CSL_EDMA3_CHA_31,CSL_EDMA3_TCC_NORMAL, \
	      CSL_EDMA3_FIFOWIDTH_NONE,FALSE,CSL_EDMA3_SYNC_A,CSL_EDMA3_ADDRMODE_INCR,CSL_EDMA3_ADDRMODE_INCR);

	#ifdef _BIG_ENDIAN
    	paramSetup.srcAddr      = ((Uint32)&(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIBUF)) + (4 -TEST_ACNT);
	#else
    	paramSetup.srcAddr      = (Uint32)&(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIBUF);
	#endif

	if( ((Uint32)dstBuf & 0xFFF00000) == 0x00800000)
		paramSetup.dstAddr      = (Uint32)(global_address((Uint32)dstBuf));
	else
		paramSetup.dstAddr      = (Uint32)dstBuf;

	paramSetup.linkBcntrld  = CSL_EDMA3_LINKBCNTRLD_MAKE(CSL_EDMA3_LINK_NULL,0);

	CSL_edma3ParamSetup(paramHandle1,&paramSetup);
	CSL_edma3ParamSetup(paramHandle1_reload,&paramSetup);
}

void Test_Edma(void)
{
	// Wait for interrupt
    regionIpr.region  = CSL_EDMA3_REGION_GLOBAL;
	regionIpr.intr    = 0;
	regionIpr.intrh   = 0;
	do{
		CSL_edma3GetHwStatus(hModule,CSL_EDMA3_QUERY_INTRPEND,&regionIpr);
	}while ((regionIpr.intr & (1<<31)) != (1<<31));	//channel_31

	//profileTxStop = CSL_tscRead();

	do{
		CSL_edma3GetHwStatus(hModule,CSL_EDMA3_QUERY_INTRPEND,&regionIpr);
	}while ((regionIpr.intr & (1<<30)) != (1<<30));	//channel_30
	//profileRxStop = CSL_tscRead();

	/* Clear pending interrupt */
	CSL_edma3HwControl(hModule,CSL_EDMA3_CMD_INTRPEND_CLEAR, &regionIpr);
}

void Close_Edma()
{
    CSL_FINST(hModule->regs->TPCC_SECR,TPCC_TPCC_SECR_SECR2,RESETVAL);
  	CSL_FINST(hModule->regs->TPCC_SECR,TPCC_TPCC_SECR_SECR3,RESETVAL);

 	CSL_edma3ChannelClose(hChannel0);
 	CSL_edma3ChannelClose(hChannel1);
 	CSL_edma3Close(hModule);
}

void Initiate_Buffers(Uint16 aCnt, Uint16 bCnt, Uint16 cCnt, Int16 srcBIdx, Int16 dstBIdx, Int16 srcCIdx, Int16 dstCIdx,Uint32 srcBuff,Uint32 dstBuff, Bool abSync)
{
	Uint8* srcArrayPtr = (Uint8*)srcBuff;
	Uint8* dstArrayPtr = (Uint8*)dstBuff;
	Uint8* srcFramePtr = (Uint8*)srcBuff;
	Uint8* dstFramePtr = (Uint8*)dstBuff;

	Uint8 cnt = 0;
	int i,j,k;
	for (i = 0; i < cCnt; i++)
	{
		for (j = 0; j < bCnt ;j++)
		{
			for (k = 0;k < aCnt;k++)
			{
				srcArrayPtr[k] = cnt++;
				dstArrayPtr[k] = 0;
			}
			srcArrayPtr = srcArrayPtr + srcBIdx;
			dstArrayPtr = dstArrayPtr + dstBIdx;
		}
		if (abSync) {
			srcFramePtr = srcFramePtr + srcCIdx;
			srcArrayPtr = srcFramePtr;
			dstFramePtr = dstFramePtr + dstCIdx;
			dstArrayPtr = dstFramePtr;
		} else
		{
		    srcFramePtr = srcArrayPtr + srcCIdx - srcBIdx;
			srcArrayPtr = srcFramePtr;
			dstFramePtr = dstArrayPtr + dstCIdx - dstBIdx;
			dstArrayPtr = dstFramePtr;
		}
	}
}

void Setup_SPI_Init ()
{
	//enable_spi();

    /* Reset SPI */
    ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR0=
    	CSL_SPI_SPIGCR0_RESET_IN_RESET<<CSL_SPI_SPIGCR0_RESET_SHIFT;

    /* Take SPI out of reset */
    ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR0=
    	CSL_SPI_SPIGCR0_RESET_OUT_OF_RESET<<CSL_SPI_SPIGCR0_RESET_SHIFT;

    /* Configure SPI as master */
    ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR1=
        CSL_SPI_SPIGCR1_CLKMOD_INTERNAL<<CSL_SPI_SPIGCR1_CLKMOD_SHIFT|
        CSL_SPI_SPIGCR1_MASTER_MASTER<<CSL_SPI_SPIGCR1_MASTER_SHIFT;

    /* Configure SPI in 4-pin SCS mode */
    ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIPC0=
        CSL_SPI_SPIPC0_SOMIFUN_SPI<<CSL_SPI_SPIPC0_SOMIFUN_SHIFT|
        CSL_SPI_SPIPC0_SIMOFUN_SPI<<CSL_SPI_SPIPC0_SIMOFUN_SHIFT|
        CSL_SPI_SPIPC0_CLKFUN_SPI<<CSL_SPI_SPIPC0_CLKFUN_SHIFT|
        CSL_SPI_SPIPC0_SCS0FUN0_SPI<<CSL_SPI_SPIPC0_SCS0FUN0_SHIFT;

	/* Put SPI in Lpbk mode*/
	((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR1 |=
		CSL_SPI_SPIGCR1_LOOPBACK_ENABLE<<CSL_SPI_SPIGCR1_LOOPBACK_SHIFT;

    /* Chose SPIFMT0 */
	((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIDAT1=
		CSL_SPI_SPIDAT1_DFSEL_FORMAT0<<CSL_SPI_SPIDAT1_DFSEL_SHIFT;

    /* Configure add a delay,SHIFTDIR=MSB,POLARITY=HIGH,PHASE=IN,CHARLEN=16*/
    ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIFMT[0]=
    	CSL_SPI_SPIFMT_WAITENA_DISABLE<<CSL_SPI_SPIFMT_WAITENA_SHIFT|
    	CSL_SPI_SPIFMT_SHIFTDIR_MSB<<CSL_SPI_SPIFMT_SHIFTDIR_SHIFT|
    	CSL_SPI_SPIFMT_POLARITY_LOW<<CSL_SPI_SPIFMT_POLARITY_SHIFT|
    	CSL_SPI_SPIFMT_PHASE_NO_DELAY<<CSL_SPI_SPIFMT_PHASE_SHIFT|
    	0x1<<CSL_SPI_SPIFMT_PRESCALE_SHIFT|
    	0x10<<CSL_SPI_SPIFMT_CHARLEN_SHIFT;

	((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIINT0 =
		CSL_SPI_SPIINT0_ENABLEHIGHZ_ENABLE<<CSL_SPI_SPIINT0_ENABLEHIGHZ_SHIFT|
		CSL_SPI_SPIINT0_OVRNINTENA_ENABLE<<CSL_SPI_SPIINT0_OVRNINTENA_SHIFT|
		CSL_SPI_SPIINT0_BITERRENA_ENABLE<<CSL_SPI_SPIINT0_BITERRENA_SHIFT|
		CSL_SPI_SPIINT0_DESYNCENA_ENABLE<<CSL_SPI_SPIINT0_DESYNCENA_SHIFT|
		CSL_SPI_SPIINT0_PARERRENA_ENABLE<<CSL_SPI_SPIINT0_PARERRENA_SHIFT|
		CSL_SPI_SPIINT0_TIMEOUTENA_ENABLE<<CSL_SPI_SPIINT0_TIMEOUTENA_SHIFT|
		CSL_SPI_SPIINT0_DLENERRENA_ENABLE<<CSL_SPI_SPIINT0_DLENERRENA_SHIFT;
}

void SPI_DMA_Request(void){

	 /* Enable communication */
   ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR1|=
       CSL_SPI_SPIGCR1_ENABLE_ENABLE<<CSL_SPI_SPIGCR1_ENABLE_SHIFT;

	((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIINT0 |=
			CSL_SPI_SPIINT0_DMAREQEN_ENABLE<<CSL_SPI_SPIINT0_DMAREQEN_SHIFT;

	while(!(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIFLG & 0x00000200));
	//profileTxStart = CSL_tscRead();

	while(!(((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIFLG & 0x00000100));
	//profileRxStart = CSL_tscRead();

   ((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIGCR1|=
		   CSL_SPI_SPIGCR1_ENABLE_DISABLE<<CSL_SPI_SPIGCR1_ENABLE_SHIFT;

	((CSL_SpiRegsOvly) CSL_SPI_REGS)->SPIINT0 |=
			CSL_SPI_SPIINT0_DMAREQEN_DISABLE<<CSL_SPI_SPIINT0_DMAREQEN_SHIFT;
}


Bool Verify_Transfer(Uint16 aCnt, Uint16 bCnt, Uint16 cCnt, Int16 srcBIdx, Int16 dstBIdx, Int16 srcCIdx, Int16 dstCIdx,Uint32 srcBuff,Uint32 dstBuff, Bool abSync)
{
	Uint8* srcArrayPtr = (Uint8*)srcBuff;
	Uint8* dstArrayPtr = (Uint8*)dstBuff;
	Uint8* srcFramePtr = (Uint8*)srcBuff;
	Uint8* dstFramePtr = (Uint8*)dstBuff;

	int i,j,k;
	for (i = 0; i < cCnt; i++)
	{
		for (j = 0; j < bCnt ;j++)
		{
			for (k = 0;k < aCnt;k++)
			{
				if (srcArrayPtr[k] != dstArrayPtr[k])
				{
					return FALSE;
				}
			}
			srcArrayPtr = srcArrayPtr + srcBIdx;
			dstArrayPtr = dstArrayPtr + dstBIdx;
		}
		if (abSync) {
			srcFramePtr = srcFramePtr + srcCIdx;
			srcArrayPtr = srcFramePtr;
			dstFramePtr = dstFramePtr + dstCIdx;
			dstArrayPtr = dstFramePtr;
		} else
		{
		    srcFramePtr = srcArrayPtr + srcCIdx - srcBIdx;
			srcArrayPtr = srcFramePtr;
			dstFramePtr = dstArrayPtr + dstCIdx - dstBIdx;
			dstArrayPtr = dstFramePtr;
		}
	}
	return TRUE;
}


/*
 *  ======== main ========
 */
Void main()
{
    volatile Uint32 failFlag = FALSE;
    int i = 0;

    //Initiate src/dst buffers
	for (i=0; i<BUF_NUM; i++)
	{
		Initiate_Buffers(TEST_ACNT,TEST_BCNT, TEST_CCNT,TEST_ACNT ,TEST_ACNT, TEST_ACNT*TEST_BCNT, TEST_ACNT*TEST_BCNT,(Uint32)srcBuf[i],(Uint32)dstBuf[i], CSL_EDMA3_SYNC_A);
	}
	//Initialize EDMA for SPI transfer
	Setup_Edma_Init();

   	//Configure SPI in loopback mode and enable DMA interrupt support
	Setup_SPI_Init();

	//CSL_tscEnable();

	for(i=0;i<BUF_NUM;i++){
		//Enable EDMA for SPI transfer
		Setup_Edma_Params((Uint32)srcBuf[i],(Uint32)dstBuf[i]);

		//Enable SPI DMA Request
		SPI_DMA_Request();

		//Check EDMA transfer completion status
		Test_Edma();

		//Verify src/dst buffers after transfer
		failFlag = Verify_Transfer(TEST_ACNT,TEST_BCNT, TEST_CCNT,TEST_ACNT ,TEST_ACNT, TEST_ACNT*TEST_BCNT, TEST_ACNT*TEST_BCNT,(Uint32)srcBuf[i],(Uint32)dstBuf[i], CSL_EDMA3_SYNC_A);

		if (failFlag == TRUE){
			System_printf("data verification passed\n");
		}
		else
			System_printf("data verification failed\n");
	}
	System_printf("end of test\n");
	BIOS_exit(0);
    //BIOS_start();     /* enable interrupts and start SYS/BIOS */
}
