#include <xdc/std.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <c6x.h>

#include <ti/csl/csl_cacheAux.h>
#include <ti/sdo/edma3/drv/sample/bios6_edma3_drv_sample.h>


extern cregister volatile unsigned int DNUM;


/* OPT Field specific defines */
#define OPT_SYNCDIM_SHIFT                   (0x00000002u)
#define OPT_TCC_MASK                        (0x0003F000u)
#define OPT_TCC_SHIFT                       (0x0000000Cu)
#define OPT_ITCINTEN_SHIFT                  (0x00000015u)
#define OPT_TCINTEN_SHIFT                   (0x00000014u)
#define OPT_ITCCHEN_SHIFT                   (0x00000017u)
#define OPT_TCCHEN_SHIFT                    (0x00000016u)
#define OPT_STATIC_SHIFT                    (0x00000003u)

#define N 16384
#define M 128

/* ======================================================================== */
/*  Kernel-specific alignments                                              */
/* ======================================================================== */
#pragma DATA_SECTION(x,  ".my_sect_ddr");
#pragma DATA_ALIGN(x,  8);
float x [2*N];
float *const ptr_x  = x;

#pragma DATA_SECTION(y, ".my_sect_l2sram");
#pragma DATA_ALIGN(y,  64);
float y [2*M];
float *const ptr_y  = y;


int main () {
    /* -------------------------------------------------------------------------------- */
    /*                                        Variables                                 */
    /* -------------------------------------------------------------------------------- */
    int i;
    clock_t t_start, t_stop, t_overhead, t_opt;
    /* -------------------------------------------------------------------------------- */


    /* -------------------------------------------------------------------------------- */
    /*                                      Initialization                              */
    /* -------------------------------------------------------------------------------- */

    // Intialize hardware timers
    TSCL = 0; TSCH = 0;

    // Compute the overhead of calling clock twice to get timing info

    t_start = _itoll(TSCH, TSCL);
    t_stop  = _itoll(TSCH, TSCL);
    t_overhead = t_stop - t_start;

    // Initialize input vector
    for (i = 0; i < 2*N; i++) {
        x[i] = (float)(i + 1);
    }
    CACHE_wbInvAllL2(CACHE_WAIT);
    /* -------------------------------------------------------------------------------- */


    /* -------------------------------------------------------------------------------- */
    /*                                          EDMA                                    */
    /* -------------------------------------------------------------------------------- */
    // EDMA variables
    EDMA3_DRV_Handle hEDMA;
    EDMA3_DRV_Result EDMA_Result = EDMA3_DRV_SOK;
    uint32_t EDMA_ID = 0;
    EDMA3_DRV_PaRAMRegs EDMA_PaRAM = {0,0,0,0,0,0,0,0,0,0,0,0};

    // Channel options
    uint32_t EDMA_TCC  = EDMA3_DRV_TCC_ANY; // Transfer complete code (TCC)
    uint32_t EDMA_chID = EDMA3_DRV_DMA_CHANNEL_ANY; // Channel ID

    // Initialisation EDMA
    hEDMA = edma3init(EDMA_ID, &EDMA_Result);

    // Flush the Source Buffer
    if (EDMA_Result == EDMA3_DRV_SOK) {
        EDMA_Result = Edma3_CacheFlush((unsigned int)ptr_x, 2*N*sizeof(float));
    }

    /* Invalidate the Destination Buffer */
    if (EDMA_Result == EDMA3_DRV_SOK) {
        EDMA_Result = Edma3_CacheInvalidate((unsigned int)ptr_y, 2*M*sizeof(float));
    }

    // Request a Channel from Resource Manager
    EDMA_Result = EDMA3_DRV_requestChannel (hEDMA, &EDMA_chID, &EDMA_TCC, (EDMA3_RM_EventQueue)0, NULL, NULL);
    if (EDMA_Result == EDMA3_DRV_SOK) {
        printf("DMA channel %d request successful.\n", EDMA_chID);
    } else {
        printf("DMA channel %d request failed!\n", EDMA_chID);
    }

    if (EDMA_Result == EDMA3_DRV_SOK) {
        // OPT field of PaRAM Set
        EDMA_PaRAM.opt &= 0xFFFFFFFCu; // Src & Dest are in INCR modes
        EDMA_PaRAM.opt |= ((EDMA_TCC << OPT_TCC_SHIFT) & OPT_TCC_MASK);   // Program the TCC
        EDMA_PaRAM.opt |= (1 << OPT_TCINTEN_SHIFT); // Enable Final transfer completion interrupt
        EDMA_PaRAM.opt |= (1 << OPT_SYNCDIM_SHIFT); // AB-Sync Transfer Mode
        EDMA_PaRAM.opt |= (1 << OPT_STATIC_SHIFT); // Set the static bit

        EDMA_PaRAM.srcAddr    = (unsigned int)(ptr_x);
        EDMA_PaRAM.destAddr   = (unsigned int)(ptr_y);
        EDMA_PaRAM.srcBIdx    = (short)(2*M*sizeof(float));
        EDMA_PaRAM.destBIdx   = (short)(2*sizeof(float));
        EDMA_PaRAM.srcCIdx    = 0;
        EDMA_PaRAM.destCIdx   = 0;
        EDMA_PaRAM.aCnt       = (unsigned short)(2*sizeof(float));
        EDMA_PaRAM.bCnt       = (unsigned short)(M);
        EDMA_PaRAM.cCnt       = 1;
        EDMA_PaRAM.bCntReload = 0;
        EDMA_PaRAM.linkAddr   = 0xFFFFu;

        // Write the PaRAM Set.
        EDMA_Result = EDMA3_DRV_setPaRAM(hEDMA, EDMA_chID, &EDMA_PaRAM);
        if (EDMA_Result != EDMA3_DRV_SOK) {
            printf("EDMA3 set PaRAM Failed with error code: %d!\n", EDMA_Result);
        }
    }

    t_start = _itoll(TSCH, TSCL);
    EDMA_Result = EDMA3_DRV_enableTransfer (hEDMA, EDMA_chID, EDMA3_DRV_TRIG_MODE_MANUAL);
    EDMA_Result = EDMA3_DRV_waitAndClearTcc(hEDMA, EDMA_TCC);
    t_stop = _itoll(TSCH, TSCL);
    t_opt  = (t_stop - t_start) - t_overhead;
    if (EDMA_Result != EDMA3_DRV_SOK) {
        printf("EDMA3 Transfer Failed with error code: %d!\n", EDMA_Result);
    } else {
        printf("\tEDMA3 Transfer cycles: %d\n", t_opt);
    }
    EDMA_Result = EDMA3_DRV_freeChannel (hEDMA, EDMA_chID);
    if (EDMA_Result != EDMA3_DRV_SOK) {
            printf("EDMA3 Free Channel Failed with error code: %d!\n", EDMA_Result);
    }
    /* -------------------------------------------------------------------------------- */

    printf("STOP\n");
}
