
#include <csl_tsc.h>
#include <stdio.h>
#include <string.h>
#include <csl_edma3.h>
#include <csl_edma3Aux.h>
#include <csl_intc.h>
#include "EdmaIntDispatcher.h"
#include "timestamp.h"

#define BUFLEN_W 			80
#define BYTES_IN_A_WORD		4

//#pragma DATA_SECTION(loBuf, ".G0L2");
#pragma DATA_SECTION(rmRdBuf, ".G1L2");
#pragma DATA_SECTION(rmWrBuf, ".G1L2");
#pragma DATA_ALIGN(loRdBuf, 8);
#pragma DATA_ALIGN(rmRdBuf, 8);
#pragma DATA_ALIGN(loWrBuf, 8);
#pragma DATA_ALIGN(rmWrBuf, 8);
Uint32 loRdBuf[BUFLEN_W];
Uint32 rmRdBuf[BUFLEN_W];
Uint32 loWrBuf[BUFLEN_W];
Uint32 rmWrBuf[BUFLEN_W];

Uint32 loRdBufGA;	//global address of loRdBuf
Uint32 loWrBufGA;

volatile Uint32      bEdmaCh0Complete = 0; 


/* Edma handle */
CSL_Edma3Handle     hModule; 

//#pragma FUNC_CANNOT_INLINE(playWithBufs);
void playWithBufs(
	Uint32 inBuf[restrict], 
	Uint32 outBuf[restrict],
	Uint32 length_w
	) {
	
	Uint32 i;
	
	_nassert((Uint32)inBuf % 4 == 0);			
	_nassert((Uint32)outBuf % 4 == 0);
	
	#pragma MUST_ITERATE(4, ,4);
	for (i = 0; i < length_w; i++) {
		outBuf[i] = inBuf[i];
	}
}


//---------edma related parameters-------------
CSL_Edma3HwSetup            hwSetup;
CSL_Edma3Obj                edmaObj;
CSL_Edma3ParamHandle        hParamBasic;
CSL_Edma3ChannelObj         chObj;
CSL_Edma3CmdIntr            regionIntr;
CSL_Edma3CmdDrae            regionAccess;
CSL_Edma3ChannelHandle      hChannel;
CSL_Edma3ParamSetup         myParamSetup;
CSL_Edma3Context            context;
CSL_Edma3ChannelAttr        chAttr;
CSL_Status                  status;
Uint16						paramNum;
CSL_Edma3HwDmaChannelSetup	dmahwSetup[CSL_EDMA3_NUM_DMACH] =
								CSL_EDMA3_DMACHANNELSETUP_DEFAULT;
CSL_Edma3HwQdmaChannelSetup qdmahwSetup[CSL_EDMA3_NUM_QDMACH] =
								CSL_EDMA3_QDMACHANNELSETUP_DEFAULT;

int tccCh0;
//---------edma related parameters-------------


Uint32 CoreNum = 0;
//Uint32 RegionNum;
Int32 RegionNum;
Uint16						paramNum;

unsigned long long			llTemp;
    		
CSL_Status                  	status;

/* Intc declaration */
CSL_IntcContext                 intcContext; 
CSL_IntcEventHandlerRecord      EventHandler[30];
CSL_IntcObj                     intcObjEdma;
CSL_IntcHandle                  hIntcEdma;
 
CSL_IntcGlobalEnableState       state;
CSL_IntcEventHandlerRecord      EventRecord;
CSL_IntcParam                   vectId;


// ISR for the EDMA interrupt for logical Channel 0, sets bEdmaCh0Complete = 1
void tccCh0Fxn(void) 
{
//	printf("tccCh0 ISR invoked\n");
    bEdmaCh0Complete = 1;
}

void bailOut() {
	printf("bailing out\n");
}


void edmaClearErrorRegisters(
	CSL_Edma3ChannelHandle      hChannel
) {
	CSL_Edma3ChannelErr 		chErrClear;
	
	chErrClear.missed = TRUE;
    chErrClear.secEvt = TRUE;
    CSL_edma3HwChannelControl (hChannel, CSL_EDMA3_CMD_CHANNEL_DISABLE, NULL);
    CSL_edma3HwChannelControl (hChannel, CSL_EDMA3_CMD_CHANNEL_CLEARERR, &chErrClear);
    CSL_edma3HwChannelControl (hChannel, CSL_EDMA3_CMD_CHANNEL_CLEAR, NULL);
    CSL_edma3HwChannelControl (hChannel, CSL_EDMA3_CMD_CHANNEL_ENABLE, NULL);
}
		



	

void edmaQueryErrorRegisters(
	CSL_Edma3ChannelHandle      hChannel
) {
	Uint32 errStat;
	CSL_BitMask32 evtMissedStat[3];
	
	CSL_edma3GetHwChannelStatus(hChannel,CSL_EDMA3_QUERY_CHANNEL_ERR, &errStat);
	printf("channel errStat: 0x%08X\n", errStat);
	
	
	CSL_edma3GetHwStatus(hModule, CSL_EDMA3_QUERY_EVENTMISSED, evtMissedStat);
	printf("edma module event missed stat 0: 0x%08X\n", evtMissedStat[0]);
	printf("edma module event missed stat 1: 0x%08X\n", evtMissedStat[1]);
	printf("edma module event missed stat 2: 0x%08X\n", evtMissedStat[2]);
}

void edmaQueryChannelStatus(
	CSL_Edma3ChannelHandle      hChannel
	){
	
	Bool stat;
	CSL_Edma3ChannelErr 		chErr;
	
	CSL_edma3GetHwChannelStatus(hChannel,CSL_EDMA3_QUERY_CHANNEL_STATUS, &stat);
	printf("channel ER     stat: 0x%08X\n", stat);


	CSL_edma3GetHwChannelStatus(hChannel,CSL_EDMA3_QUERY_CHANNEL_ERR, &chErr);
	printf("channel missed stat: 0x%08X\n", chErr.missed);
 	printf("channel secEvt stat: 0x%08X\n", chErr.secEvt);
 	
 	printf("channel ER reg     : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->ER, hChannel->chaNum, hChannel->chaNum));

 	printf("channel ERH reg    : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->ERH, hChannel->chaNum, hChannel->chaNum));

 	printf("channel EER reg    : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->EER, hChannel->chaNum, hChannel->chaNum));
 	printf("channel EERH reg   : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->EERH, hChannel->chaNum, hChannel->chaNum));

 	printf("channel EESR reg   : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->EESR, hChannel->chaNum, hChannel->chaNum));

 	printf("channel EMR reg    : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->EMR, hChannel->chaNum, hChannel->chaNum));

 	printf("channel EMRH reg   : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->EMRH, hChannel->chaNum, hChannel->chaNum));

 	printf("channel SER reg    : 0x%08X\n", 
 		CSL_FEXTR(hChannel->regs->SER, hChannel->chaNum, hChannel->chaNum));


}


void edmaQueryTransferCompletionInterrupt() {
	
	CSL_Edma3CmdIntr 			transferCompletionInterruptMask;
	
	printf("querying tc interrupt register\n");
	
	CSL_edma3GetHwStatus(hModule, CSL_EDMA3_QUERY_INTRPEND, &transferCompletionInterruptMask);
	printf("interrupt pending status: intrh: 0x%08X  intr: 0x%08X\n", 
		transferCompletionInterruptMask.intrh, transferCompletionInterruptMask.intr);
}

//void edmaClearTransferCompletionInterrupt() {
//	DEBUG(printf("clearing tc interrupt register\n");)
//	CSL_edma3HwControl (hModule, CSL_EDMA3_CMD_INTRPEND_CLEAR, &transferCompletionInterruptMask);
//}

void edmaQueryActivityStat() {
	CSL_Edma3ActivityStat	stat;
	
	CSL_edma3GetHwStatus(hModule, CSL_EDMA3_QUERY_ACTIVITY, &stat);
	printf("edma module activity status	\n");
	printf("active          %d\n", stat.active);
	printf("evtActive       %d\n", stat.evtActive);
	printf("outstandingTcc  %d\n", stat.outstandingTcc);
	printf("qevtActive      %d\n", stat.qevtActive);
	printf("queActive       %d\n", stat.queActive);
	printf("trActive        %d\n", stat.trActive);
}

void intcQueryStatus() {
	Uint32 stat;
	CSL_intcGetHwStatus(hIntcEdma, CSL_INTC_QUERY_PENDSTATUS, &stat);
	printf("CSL_INTC_QUERY_PENDSTATUS	: 0x%08X\n", stat);
}

void edmaInit() {
	
//	printf("edmaInit\n");	
	
	//--------------------------do edma setup-----------------------//
	
	RegionNum = EDMA_REGION;
	
	/* Module initialization */
	status = CSL_edma3Init(&context);
    if (status != CSL_SOK) { bailOut(); return;}
    
    /* Edma module open */
    hModule = CSL_edma3Open(&edmaObj,CSL_EDMA3,NULL,&status);
    if ( (hModule == NULL) || (status != CSL_SOK)) { bailOut(); return; }
    
    tccCh0 = 3;		//channel 3
    
//////    /* Edma module setup sets default paramnum and queue for ALL dma and qdma channels */
//////    dmahwSetup[tccCh0].paramNum = 0;    // no need to change paramNum from default, but you could do it here
//////	  dmahwSetup[tccCh0].que = CSL_EDMA3_QUE_0;
    
    hwSetup.dmaChaSetup = dmahwSetup;
    hwSetup.qdmaChaSetup = qdmahwSetup;

    status = CSL_edma3HwSetup(hModule,&hwSetup);
    if (status != CSL_SOK) { bailOut(); return; }

	/* Channel 0 open */
    chAttr.regionNum = RegionNum;
    chAttr.chaNum = tccCh0; 
    hChannel = CSL_edma3ChannelOpen(&chObj, CSL_EDMA3, &chAttr, &status);   
    if ( (hChannel == NULL) || (status != CSL_SOK)) { bailOut(); return; }
    
    /* Get the PaRAM number associated with this channel */
    status = CSL_edma3GetHwChannelSetupParam(hChannel,&paramNum);
    if (status != CSL_SOK) { bailOut(); return; }
    
	/* Get a handle to the param set */
    hParamBasic = CSL_edma3GetParamHandle(hChannel,paramNum,&status);
    if (hParamBasic == NULL) { bailOut(); return; }


    /* Enable interrupts (EDMA3's IER) */
    regionIntr.region = RegionNum;
	llTemp  = (unsigned long long)1 << tccCh0;
    regionIntr.intr = _loll(llTemp);   
    regionIntr.intrh = _hill(llTemp);
    status = CSL_edma3HwControl(hModule,CSL_EDMA3_CMD_INTR_ENABLE,&regionIntr);    
    //Note: Interrupts from all EDMA channels actually map to the same event: CSL_INTC_EVENTID_EDMA3CC_<region>,
    //but they can be distinguished in the application interrupt dispatcher by querying the interrupt pending 
    //register (IPR) of EDMA3CC to learn which channels have enabled interrupts.
    
    if (status != CSL_SOK) { bailOut(); return; }
    
    edmaClearErrorRegisters(hChannel);
}

void intcInit() {
	
//	printf("intcInit\n");
	
	//--------------------------do intc setup-----------------------//
	/* Intc module initialization */
    intcContext.eventhandlerRecord = EventHandler;
    intcContext.numEvtEntries = 30;
    CSL_intcInit(&intcContext);
    
    /* Enable NMIs */
    CSL_intcGlobalNmiEnable();
    
    /* Enable global interrupts */
    CSL_intcGlobalEnable(&state);
    
    /* Open an intc handle for the edma interrupt */
    vectId = CSL_INTC_VECTID_4;
    
    //The following maps CSL_INTC_EVENTID... identified event from the edma peripheral
    //to the cpu interrupt identified by CSL_INTC_VECTID...  EVENTID is the input to 
    //intc and VECTID is the output from intc.  
    hIntcEdma = CSL_intcOpen (
    		&intcObjEdma, 
    		CSL_INTC_EVENTID_EDMA3CC_GINT+CoreNum,		//global region
            &vectId , 
            NULL);

#if ( EDMADISPATCH_METHOD == EDMADISPATCH_IEVAL )
	// if the faster IEVAL option is chosen for the edmaIntDispatcher, then this interrupt
	// should be masked out of the Dropped Interrupt detection to avoid generating INTERR
    CSL_intcHwControl(hIntcEdma,CSL_INTC_CMD_EVTDROPDISABLE,NULL);
#endif
	//--------------------------done intc setup-----------------------//
	
    /* Associate an EDMA event handler with the INTC module, ie. make this the EDMA ISR */
    EventRecord.handler = &edmaIntDispatcher;
    EventRecord.arg = (void*)(hModule);
    CSL_intcPlugEventHandler(hIntcEdma,&EventRecord);
    //Note that hIntcEdma identifies a particular interrupt from intc, in this case, vectId.
    //That is, we are setting up the handler for intc's vectId interrupt vector output.
    
    /* Enable edma interrupt  */
    CSL_intcHwControl(hIntcEdma,CSL_INTC_CMD_EVTENABLE,NULL);
    
    /* Clear the edma interrupt (Q: is this required?) */
    CSL_intcHwControl(hIntcEdma,CSL_INTC_CMD_EVTCLEAR,NULL);

    /* Hook a completion code to a function */
    edmaEventHook(tccCh0, tccCh0Fxn);  
    //This is only configuring our own interrupt dispatcher/event handler.
    //It's not affecting the INTC module in any way.
}

void edmaClose(
    CSL_Edma3ChannelHandle      hChannel
) {
	CSL_Status                  status;
	
//	printf("edmaClose\n");
	
    /* Channel close */    
    status = CSL_edma3ChannelClose(hChannel);
    if (status != CSL_SOK) { bailOut(); return; }

	/* Edma module close */    
    status = CSL_edma3Close(hModule);    
    if (status != CSL_SOK) { bailOut(); return; }
}

void intcClose(CSL_IntcHandle hIntc) {
	CSL_intcClose(hIntc);
}


void edmaSetupParam(
		Uint32 *inBuf,
		Uint32 *outBuf,
		Uint32 length_b
		) {
		
//		printf("edmaSetupParam\n");
		
		/* Edma parameter entry Setup */
	    myParamSetup.option = CSL_EDMA3_OPT_MAKE(CSL_EDMA3_ITCCH_DIS,
	                                             CSL_EDMA3_TCCH_DIS,
	                                             CSL_EDMA3_ITCINT_DIS,
	                                             CSL_EDMA3_TCINT_EN,
	                                             tccCh0,
	                                             CSL_EDMA3_TCC_NORMAL,
	                                             CSL_EDMA3_FIFOWIDTH_NONE,
	                                             CSL_EDMA3_STATIC_DIS,
	                                             CSL_EDMA3_SYNC_A,
	                                             CSL_EDMA3_ADDRMODE_INCR,
	                                             CSL_EDMA3_ADDRMODE_INCR);  
		//note: Make sure source and destination addreesses (inBuf and outBuf)
		//are global (0x1yyyyyyy), as EDMA transfers fail if set up with 
		//local addresses (0x0yyyyyyy).
	    myParamSetup.srcAddr = (Uint32)inBuf;                  			 	// source address
	    myParamSetup.aCntbCnt = CSL_EDMA3_CNT_MAKE(length_b, 1);            // Acnt Bcnt
	    myParamSetup.dstAddr = (Uint32)outBuf;
//	    printf("srcAddr: 0x%08X\n", myParamSetup.srcAddr);
//	    printf("dstAddr: 0x%08X\n", myParamSetup.dstAddr);
	    
	    myParamSetup.srcDstBidx = CSL_EDMA3_BIDX_MAKE(0,0);                  // B src and dst index 
	    myParamSetup.linkBcntrld = CSL_EDMA3_LINKBCNTRLD_MAKE (
	    				//hParamBasic,//0xffff,//
	    				CSL_EDMA3_LINK_NULL,
	                    0);           // link and Bcnt reload
	    myParamSetup.srcDstCidx = CSL_EDMA3_CIDX_MAKE(0,0);                  // C src and dst index
	    myParamSetup.cCnt = 1;                                               // C cnt
	    status = CSL_edma3ParamSetup(hParamBasic,&myParamSetup);
	    if (status != CSL_SOK) { bailOut(); return; }
}


void playWithBufsEDMA(
	Uint32 inBuf[restrict], 
	Uint32 outBuf[restrict],
	Uint32 length_b
	) {
	
	Int32 stat;
	
	//---------------------------------------------------------------//
	//EDMA TESTS
	
	//edma copy operations
//	printf("inBuf  address: %08x\n", (Uint32)inBuf);
//	printf("outBuf address: %08x\n", (Uint32)outBuf);
	
	/* Initialize and setup edma */
	edmaInit();
	
	/* Initialize and setup the corresponding intc features */
	intcInit();
	
	/* Set up specific EDMA transfer parameters */
	edmaSetupParam(inBuf, outBuf, length_b);
	
	/* Query various error statuses */
//	printf("\n----- pre transfer diagnosis ----\n");
//	edmaQueryChannelStatus(hChannel);
//	edmaQueryErrorRegisters(hChannel);
//	edmaQueryActivityStat();
//	edmaQueryTransferCompletionInterrupt();
//	intcQueryStatus();
	
	
	/* Manually trigger the channel */
	printf("\n----- triggering the channel now ----\n");
		
	ts5 = CSL_tscRead();
	
    status = CSL_edma3HwChannelControl(hChannel,CSL_EDMA3_CMD_CHANNEL_SET,NULL);
    if (status != CSL_SOK) { bailOut(); return; }
	
	/* Wait for edma completion */
    while (!bEdmaCh0Complete);
    
    ts11 = CSL_tscRead();
    
    /* Compare memory buffers */
    stat = memcmp(inBuf, outBuf, length_b);
    
    ts12 = CSL_tscRead();
    
    /* Query various error statuses again */
//    printf("\n----- post transfer diagnosis ----\n");	
//	edmaQueryChannelStatus(hChannel);
//	edmaQueryErrorRegisters(hChannel);
//	edmaQueryActivityStat();
//	edmaQueryTransferCompletionInterrupt();
//	intcQueryStatus();
    
    /* Cleanup and close edma */
    edmaClose(hChannel); 
    
    /* Cleaup and close intc */
    intcClose(hIntcEdma);
    
    /* Report buffer match status */
	if (stat == 0) {	//0 if match, non-zero if not
		printf("Buffers match. Test OK.\n");
	}
	else {
		printf("Buffers do not match. ERROR.\n");
	}
	
	printf("Timestamps: %lld, %lld, %lld, %lld\n", ts5, ts6, ts7, ts11);
	printf("Buffer transfer time from SET to INTR   : %lld\n", ts6 - ts5);
	printf("Time spent in EdmaIntDispatcher func    : %lld\n", ts7 - ts6);
	printf("End of EdmaIntDispatcher to flag assert : %lld\n", ts11 - ts7);
	
}


int main() {
	Uint32 i;
	
	CSL_tscEnable();

	//--------------------------------------------------------------//
	//DIRECT BUFFER ACCESS TESTS
	
	//initialize buffers to non-trivial values
	for (i = 0; i < BUFLEN_W; i++) {
		loRdBuf[i] = i;
		rmRdBuf[i] = i * 2;
	}
	
	memset(loWrBuf, 0, BUFLEN_W * sizeof(Uint32));
	memset(rmWrBuf, 0, BUFLEN_W * sizeof(Uint32));

	//direct buffer copy operations
	ts0 = CSL_tscRead();
	
	playWithBufs(loRdBuf, loWrBuf, BUFLEN_W);
	ts1 = CSL_tscRead();
	
	playWithBufs(rmRdBuf, loWrBuf, BUFLEN_W);
	ts2 = CSL_tscRead();

	playWithBufs(loRdBuf, rmWrBuf, BUFLEN_W);
	ts3 = CSL_tscRead();

	playWithBufs(rmRdBuf, rmWrBuf, BUFLEN_W);
	ts4 = CSL_tscRead();

	
	printf("loRd, loWr cpu  buf operation time: %lld\n",ts1 - ts0);
	printf("rmRd, loWr cpu  buf operation time: %lld\n",ts2 - ts1);
	printf("loRd, rmWr cpu  buf operation time: %lld\n",ts3 - ts2);
	printf("rmRd, rmWr cpu  buf operation time: %lld\n",ts4 - ts3);
	
	/* RESULTS:
	 * loRd, loWr buf operation time: 129
	 * rmRd, loWr buf operation time: 2354
	 * loRd, rmWr buf operation time: 656
	 * rmRd, rmWr buf operation time: 3063
	 * 
	 * loRd, loWr buf operation time: 275
	 * rmRd, loWr buf operation time: 3437
	 * loRd, rmWr buf operation time: 618
	 * rmRd, rmWr buf operation time: 4158
	 * 
	 * loRd, loWr buf operation time: 231
	 * rmRd, loWr buf operation time: 2386
	 * loRd, rmWr buf operation time: 723
	 * rmRd, rmWr buf operation time: 3015
	 */

	//--------------------------------------------------------------//
	//EDMA BUFFER ACCESS TESTS

	//edma copy operations
//	printf("loRd address: %08x\n", (Uint32)loRdBuf);
//	printf("rmRd address: %08x\n", (Uint32)rmRdBuf);
	
	
	
	//note: When adding 0x10000000, make sure the memory address has been
	//casted into a Uint32. The result of 0x10000000 + loRdBuf will increment
	//the address of loRdBuf by sizeof(Uint32) * 0x10000000, because loRdBuf
	//is Uint32 *, and this would then become pointer arithmetic instead of
	//regular arithmetic. 
	loRdBufGA = (Uint32)loRdBuf;
	loWrBufGA = (Uint32)loWrBuf;
	loRdBufGA += 0x10000000;
	loWrBufGA += 0x10000000;
	
//	printf("EDMA with loRd, loWr\n");
	playWithBufsEDMA(
		(Uint32 *)loRdBufGA, 
		(Uint32 *)loWrBufGA, 
		BUFLEN_W * BYTES_IN_A_WORD
		);
	printf("loRd, loWr edma buf operation time: %lld\n",ts6 - ts5);
	
//	printf("EDMA with rmRd, loWr\n");
	playWithBufsEDMA(
		rmRdBuf, 
		(Uint32 *)loWrBufGA, 
		BUFLEN_W * BYTES_IN_A_WORD
		);
	printf("rmRd, loWr edma buf operation time: %lld\n",ts6 - ts5);
	
//	printf("EDMA with loRd, rmWr\n");
	playWithBufsEDMA(
		(Uint32 *)loRdBufGA, 
		rmWrBuf, 
		BUFLEN_W * BYTES_IN_A_WORD
		);
	printf("loRd, rmWr edma buf operation time: %lld\n",ts6 - ts5);
	
//	printf("EDMA with rmRd, rmWr\n");
	playWithBufsEDMA(
		rmRdBuf, 
		rmWrBuf,
		BUFLEN_W * BYTES_IN_A_WORD
		);
	printf("rmRd, rmWr edma buf operation time: %lld\n",ts6 - ts5);
	
}
